🐘 🛡️ Advanced Error Handling with TuskLang & PHP

PHP Documentation

🛡️ Advanced Error Handling with TuskLang & PHP

Introduction

Error handling is the safety net that keeps applications running smoothly. TuskLang and PHP let you implement sophisticated error handling with config-driven exception management, error recovery, graceful degradation, and intelligent error reporting that ensures applications remain resilient and user-friendly.

Key Features

- Comprehensive exception handling - Error recovery strategies - Graceful degradation - Error monitoring and reporting - Circuit breaker patterns - Retry mechanisms

Example: Error Handling Configuration

[error_handling]
exceptions: @go("error.ConfigureExceptions")
recovery: @go("error.ConfigureRecovery")
monitoring: @go("error.ConfigureMonitoring")
degradation: @go("error.ConfigureDegradation")

PHP: Error Handler Implementation

<?php

namespace App\ErrorHandling;

use TuskLang\Config; use TuskLang\Operators\Env; use TuskLang\Operators\Metrics; use TuskLang\Operators\Go;

class ErrorHandler { private $config; private $monitor; private $recovery; private $circuitBreaker; public function __construct() { $this->config = new Config(); $this->monitor = new ErrorMonitor(); $this->recovery = new ErrorRecovery(); $this->circuitBreaker = new CircuitBreaker(); $this->setupErrorHandlers(); } public function handleException(\Throwable $exception, $context = []) { $startTime = microtime(true); try { // Log the exception $this->logException($exception, $context); // Record metrics $this->recordErrorMetrics($exception); // Check if recovery is possible if ($this->canRecover($exception)) { return $this->recovery->attemptRecovery($exception, $context); } // Check circuit breaker if ($this->circuitBreaker->isOpen($exception)) { return $this->handleCircuitBreakerOpen($exception); } // Attempt graceful degradation if ($this->shouldDegrade($exception)) { return $this->degradeGracefully($exception, $context); } // Generate user-friendly error return $this->generateUserError($exception); } catch (\Exception $e) { // Fallback error handling return $this->handleFallbackError($e); } finally { $duration = (microtime(true) - $startTime) * 1000; Metrics::record("error_handling_duration", $duration, [ "exception_type" => get_class($exception) ]); } } public function handleError($error, $context = []) { $exception = new \ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']); return $this->handleException($exception, $context); } public function handleFatalError($error) { $exception = new \ErrorException($error['message'], 0, E_ERROR, $error['file'], $error['line']); // Fatal errors require immediate attention $this->sendCriticalAlert($exception); return $this->handleException($exception, ['fatal' => true]); } private function setupErrorHandlers() { // Set up exception handler set_exception_handler([$this, 'handleException']); // Set up error handler set_error_handler([$this, 'handleError']); // Set up fatal error handler register_shutdown_function([$this, 'handleFatalError']); // Set up shutdown function to catch fatal errors register_shutdown_function(function() { $error = error_get_last(); if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) { $this->handleFatalError($error); } }); } private function logException(\Throwable $exception, $context = []) { $logData = [ 'exception' => get_class($exception), 'message' => $exception->getMessage(), 'file' => $exception->getFile(), 'line' => $exception->getLine(), 'trace' => $exception->getTraceAsString(), 'context' => $context, 'timestamp' => date('c'), 'request_id' => $this->getRequestId(), 'user_id' => $this->getCurrentUserId() ]; $this->monitor->logError($logData); } private function recordErrorMetrics(\Throwable $exception) { $exceptionType = get_class($exception); $severity = $this->getExceptionSeverity($exception); Metrics::record("exceptions_total", 1, [ "type" => $exceptionType, "severity" => $severity ]); // Record error rate Metrics::record("error_rate", 1, [ "type" => $exceptionType ]); } private function canRecover(\Throwable $exception) { $recoverableExceptions = $this->config->get('error_handling.recoverable_exceptions', []); foreach ($recoverableExceptions as $recoverable) { if ($exception instanceof $recoverable['class']) { return $recoverable['recoverable'] ?? false; } } return false; } private function shouldDegrade(\Throwable $exception) { $degradationRules = $this->config->get('error_handling.degradation_rules', []); foreach ($degradationRules as $rule) { if ($exception instanceof $rule['exception_class']) { return $rule['should_degrade'] ?? false; } } return false; } private function degradeGracefully(\Throwable $exception, $context) { $degradationStrategy = $this->config->get('error_handling.degradation_strategy', 'fallback'); switch ($degradationStrategy) { case 'fallback': return $this->useFallbackData($context); case 'cached': return $this->useCachedData($context); case 'default': return $this->useDefaultResponse($context); case 'partial': return $this->usePartialResponse($context); default: return $this->useFallbackData($context); } } private function generateUserError(\Throwable $exception) { $errorConfig = $this->config->get('error_handling.user_errors', []); $exceptionType = get_class($exception); if (isset($errorConfig[$exceptionType])) { $config = $errorConfig[$exceptionType]; return [ 'error' => $config['message'], 'code' => $config['code'], 'user_friendly' => true ]; } // Default error response return [ 'error' => 'An unexpected error occurred. Please try again later.', 'code' => 'INTERNAL_ERROR', 'user_friendly' => true ]; } private function handleCircuitBreakerOpen(\Throwable $exception) { $fallbackResponse = $this->config->get('error_handling.circuit_breaker.fallback_response', []); return [ 'error' => $fallbackResponse['message'] ?? 'Service temporarily unavailable', 'code' => 'SERVICE_UNAVAILABLE', 'retry_after' => $this->circuitBreaker->getRetryAfter($exception) ]; } private function handleFallbackError(\Exception $exception) { // Ultimate fallback - log and return generic error $this->monitor->logError([ 'exception' => get_class($exception), 'message' => 'Error in error handler: ' . $exception->getMessage(), 'timestamp' => date('c') ]); return [ 'error' => 'System error. Please contact support.', 'code' => 'SYSTEM_ERROR', 'user_friendly' => true ]; } private function sendCriticalAlert(\Throwable $exception) { $alertManager = new AlertManager(); $alertManager->sendCritical([ 'type' => 'fatal_error', 'exception' => get_class($exception), 'message' => $exception->getMessage(), 'file' => $exception->getFile(), 'line' => $exception->getLine() ]); } private function getExceptionSeverity(\Throwable $exception) { $severityMap = $this->config->get('error_handling.severity_map', []); $exceptionType = get_class($exception); return $severityMap[$exceptionType] ?? 'medium'; } private function getRequestId() { return $_SERVER['HTTP_X_REQUEST_ID'] ?? uniqid(); } private function getCurrentUserId() { // Get current user ID from session or context return $_SESSION['user_id'] ?? null; } }

class ErrorRecovery { private $config; private $strategies = []; public function __construct() { $this->config = new Config(); $this->loadStrategies(); } public function attemptRecovery(\Throwable $exception, $context = []) { $recoveryStrategy = $this->getRecoveryStrategy($exception); if (!$recoveryStrategy) { return null; } try { return $recoveryStrategy->recover($exception, $context); } catch (\Exception $e) { // Recovery failed $this->logRecoveryFailure($exception, $e); return null; } } private function getRecoveryStrategy(\Throwable $exception) { $exceptionType = get_class($exception); foreach ($this->strategies as $strategy) { if ($strategy->canHandle($exceptionType)) { return $strategy; } } return null; } private function loadStrategies() { $strategies = $this->config->get('error_handling.recovery_strategies', []); foreach ($strategies as $strategy) { $this->strategies[] = new $strategy'class'; } } private function logRecoveryFailure(\Throwable $originalException, \Exception $recoveryException) { $logger = new Logger(); $logger->error('Recovery failed', [ 'original_exception' => get_class($originalException), 'original_message' => $originalException->getMessage(), 'recovery_exception' => get_class($recoveryException), 'recovery_message' => $recoveryException->getMessage() ]); } }

class RetryRecoveryStrategy { private $config; private $maxRetries; private $backoffMultiplier; public function __construct($config) { $this->config = $config; $this->maxRetries = $config['max_retries'] ?? 3; $this->backoffMultiplier = $config['backoff_multiplier'] ?? 2; } public function canHandle($exceptionType) { $retryableExceptions = $this->config['retryable_exceptions'] ?? []; return in_array($exceptionType, $retryableExceptions); } public function recover(\Throwable $exception, $context = []) { $operation = $context['operation'] ?? null; if (!$operation) { return null; } for ($attempt = 1; $attempt <= $this->maxRetries; $attempt++) { try { $result = $operation(); if ($result !== null) { return $result; } } catch (\Exception $e) { if ($attempt === $this->maxRetries) { throw $e; } // Wait before retry $delay = $this->calculateDelay($attempt); usleep($delay * 1000000); // Convert to microseconds } } return null; } private function calculateDelay($attempt) { return pow($this->backoffMultiplier, $attempt - 1); } }

class FallbackRecoveryStrategy { private $config; public function __construct($config) { $this->config = $config; } public function canHandle($exceptionType) { $fallbackExceptions = $this->config['fallback_exceptions'] ?? []; return in_array($exceptionType, $fallbackExceptions); } public function recover(\Throwable $exception, $context = []) { $fallbackData = $this->config['fallback_data'] ?? []; $contextKey = $context['context'] ?? 'default'; return $fallbackData[$contextKey] ?? $fallbackData['default'] ?? null; } }

Circuit Breaker Pattern

<?php

namespace App\ErrorHandling\CircuitBreaker;

use TuskLang\Config;

class CircuitBreaker { private $config; private $state = 'closed'; private $failureCount = 0; private $lastFailureTime = null; private $threshold; private $timeout; public function __construct() { $this->config = new Config(); $this->threshold = $this->config->get('error_handling.circuit_breaker.threshold', 5); $this->timeout = $this->config->get('error_handling.circuit_breaker.timeout', 60); } public function isOpen(\Throwable $exception) { $this->recordFailure($exception); if ($this->state === 'open') { if ($this->shouldAttemptReset()) { $this->setHalfOpen(); } return true; } if ($this->failureCount >= $this->threshold) { $this->setOpen(); return true; } return false; } public function recordSuccess() { $this->failureCount = 0; $this->state = 'closed'; } public function getRetryAfter(\Throwable $exception) { if ($this->state === 'open') { $elapsed = time() - $this->lastFailureTime; return max(0, $this->timeout - $elapsed); } return 0; } private function recordFailure(\Throwable $exception) { $this->failureCount++; $this->lastFailureTime = time(); // Record metrics Metrics::record("circuit_breaker_failures", 1, [ "exception_type" => get_class($exception) ]); } private function setOpen() { $this->state = 'open'; $this->lastFailureTime = time(); // Record metrics Metrics::record("circuit_breaker_opened", 1); // Send alert $this->sendCircuitBreakerAlert(); } private function setHalfOpen() { $this->state = 'half-open'; // Record metrics Metrics::record("circuit_breaker_half_open", 1); } private function shouldAttemptReset() { return (time() - $this->lastFailureTime) >= $this->timeout; } private function sendCircuitBreakerAlert() { $alertManager = new AlertManager(); $alertManager->send([ 'type' => 'circuit_breaker_opened', 'failure_count' => $this->failureCount, 'threshold' => $this->threshold, 'timeout' => $this->timeout ]); } }

class CircuitBreakerDecorator { private $circuitBreaker; private $operation; public function __construct(CircuitBreaker $circuitBreaker, callable $operation) { $this->circuitBreaker = $circuitBreaker; $this->operation = $operation; } public function execute() { if ($this->circuitBreaker->isOpen(new \Exception('Circuit breaker check'))) { throw new CircuitBreakerOpenException('Circuit breaker is open'); } try { $result = call_user_func($this->operation); $this->circuitBreaker->recordSuccess(); return $result; } catch (\Exception $e) { $this->circuitBreaker->isOpen($e); throw $e; } } }

Error Monitoring

<?php

namespace App\ErrorHandling\Monitoring;

use TuskLang\Config;

class ErrorMonitor { private $config; private $storage; private $alerts = []; public function __construct() { $this->config = new Config(); $this->storage = $this->getStorage(); $this->loadAlerts(); } public function logError($errorData) { // Store error $this->storage->store($errorData); // Check for alerts $this->checkAlerts($errorData); // Update metrics $this->updateMetrics($errorData); } public function getErrorStats($timeRange = '1h') { return $this->storage->getStats($timeRange); } public function getRecentErrors($limit = 100) { return $this->storage->getRecent($limit); } public function searchErrors($query, $filters = []) { return $this->storage->search($query, $filters); } private function getStorage() { $type = $this->config->get('error_handling.monitoring.storage', 'elasticsearch'); switch ($type) { case 'elasticsearch': return new ElasticsearchStorage($this->config); case 'database': return new DatabaseStorage($this->config); case 'file': return new FileStorage($this->config); default: throw new \Exception("Unknown error storage: {$type}"); } } private function loadAlerts() { $alerts = $this->config->get('error_handling.monitoring.alerts', []); foreach ($alerts as $alert) { $this->alerts[] = new ErrorAlert($alert); } } private function checkAlerts($errorData) { foreach ($this->alerts as $alert) { if ($alert->shouldTrigger($errorData)) { $alert->trigger($errorData); } } } private function updateMetrics($errorData) { $exceptionType = $errorData['exception']; $severity = $this->getErrorSeverity($errorData); Metrics::record("errors_total", 1, [ "type" => $exceptionType, "severity" => $severity ]); // Update error rate Metrics::record("error_rate", 1, [ "type" => $exceptionType ]); } private function getErrorSeverity($errorData) { $severityMap = $this->config->get('error_handling.severity_map', []); $exceptionType = $errorData['exception']; return $severityMap[$exceptionType] ?? 'medium'; } }

class ErrorAlert { private $config; private $triggered = false; private $lastTriggered = null; public function __construct($config) { $this->config = $config; } public function shouldTrigger($errorData) { // Check if alert is already triggered if ($this->triggered && $this->isWithinCooldown()) { return false; } // Check conditions foreach ($this->config['conditions'] as $condition) { if (!$this->evaluateCondition($condition, $errorData)) { return false; } } return true; } public function trigger($errorData) { $this->triggered = true; $this->lastTriggered = time(); $alertManager = new AlertManager(); $alertManager->send($this->config, $errorData); } private function evaluateCondition($condition, $errorData) { switch ($condition['type']) { case 'exception_type': return $errorData['exception'] === $condition['value']; case 'message_contains': return strpos($errorData['message'], $condition['value']) !== false; case 'error_count': return $this->getErrorCount($condition['time_window']) >= $condition['value']; case 'severity': return $errorData['severity'] === $condition['value']; default: return false; } } private function isWithinCooldown() { $cooldown = $this->config['cooldown'] ?? 300; // 5 minutes return (time() - $this->lastTriggered) < $cooldown; } private function getErrorCount($timeWindow) { // Implementation to get error count in time window return 0; // Placeholder } }

Graceful Degradation

<?php

namespace App\ErrorHandling\Degradation;

use TuskLang\Config;

class GracefulDegradation { private $config; private $strategies = []; public function __construct() { $this->config = new Config(); $this->loadStrategies(); } public function degrade($service, $context = []) { $strategy = $this->getStrategy($service); if (!$strategy) { return $this->getDefaultResponse($service); } return $strategy->degrade($context); } public function useFallbackData($context) { $fallbackData = $this->config->get('error_handling.degradation.fallback_data', []); $contextKey = $context['context'] ?? 'default'; return $fallbackData[$contextKey] ?? $fallbackData['default'] ?? null; } public function useCachedData($context) { $cache = new CacheManager(); $cacheKey = $context['cache_key'] ?? 'default'; return $cache->get($cacheKey); } public function useDefaultResponse($context) { $defaultResponses = $this->config->get('error_handling.degradation.default_responses', []); $contextKey = $context['context'] ?? 'default'; return $defaultResponses[$contextKey] ?? $defaultResponses['default'] ?? null; } public function usePartialResponse($context) { $partialData = $context['partial_data'] ?? []; // Return partial data with degradation notice return [ 'data' => $partialData, 'degraded' => true, 'message' => 'Some data may be incomplete due to service issues' ]; } private function loadStrategies() { $strategies = $this->config->get('error_handling.degradation.strategies', []); foreach ($strategies as $service => $strategy) { $this->strategies[$service] = new $strategy'class'; } } private function getStrategy($service) { return $this->strategies[$service] ?? null; } private function getDefaultResponse($service) { return [ 'error' => 'Service temporarily unavailable', 'code' => 'SERVICE_UNAVAILABLE', 'service' => $service ]; } }

class DatabaseDegradationStrategy { private $config; public function __construct($config) { $this->config = $config; } public function degrade($context) { $query = $context['query'] ?? null; if (!$query) { return $this->getDefaultResponse(); } // Try to use read replica if ($this->hasReadReplica()) { return $this->useReadReplica($query); } // Use cached data if ($this->hasCachedData($query)) { return $this->useCachedData($query); } // Return partial data return $this->getPartialData($query); } private function hasReadReplica() { // Check if read replica is available return false; // Placeholder } private function useReadReplica($query) { // Execute query on read replica return null; // Placeholder } private function hasCachedData($query) { $cache = new CacheManager(); return $cache->has($this->getCacheKey($query)); } private function useCachedData($query) { $cache = new CacheManager(); $data = $cache->get($this->getCacheKey($query)); return [ 'data' => $data, 'cached' => true, 'message' => 'Data from cache (database unavailable)' ]; } private function getPartialData($query) { // Return partial data based on query type return [ 'data' => [], 'partial' => true, 'message' => 'Limited data available' ]; } private function getCacheKey($query) { return 'db_cache:' . md5($query); } private function getDefaultResponse() { return [ 'error' => 'Database service unavailable', 'code' => 'DB_UNAVAILABLE' ]; } }

Best Practices

- Handle all exceptions appropriately - Implement graceful degradation - Use circuit breaker patterns - Monitor error rates and patterns - Provide user-friendly error messages - Log errors with context

Performance Optimization

- Use efficient error storage - Implement error sampling - Optimize error recovery - Cache fallback data

Security Considerations

- Sanitize error messages - Avoid information disclosure - Log security-relevant errors - Implement error rate limiting

Troubleshooting

- Monitor error patterns - Check circuit breaker status - Verify error recovery - Test degradation strategies

Conclusion

TuskLang + PHP = error handling that's robust, intelligent, and user-friendly. Handle errors with grace and recover with style.