feat(Production): Complete production deployment infrastructure

- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
This commit is contained in:
2025-10-25 19:18:37 +02:00
parent caa85db796
commit fc3d7e6357
83016 changed files with 378904 additions and 20919 deletions

View File

@@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* API error codes
*
* Covers API integration, external service communication, and response handling
*/
enum ApiErrorCode: string implements ErrorCode
{
case REQUEST_FAILED = 'API001';
case INVALID_RESPONSE = 'API002';
case TIMEOUT = 'API003';
case AUTHENTICATION_FAILED = 'API004';
case SERVICE_UNAVAILABLE = 'API005';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'API';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::SERVICE_UNAVAILABLE => ErrorSeverity::CRITICAL,
self::REQUEST_FAILED,
self::TIMEOUT,
self::AUTHENTICATION_FAILED => ErrorSeverity::ERROR,
self::INVALID_RESPONSE => ErrorSeverity::WARNING,
};
}
public function getDescription(): string
{
return match($this) {
self::REQUEST_FAILED => 'API request execution failed',
self::INVALID_RESPONSE => 'API response format is invalid',
self::TIMEOUT => 'API request timed out',
self::AUTHENTICATION_FAILED => 'API authentication failed',
self::SERVICE_UNAVAILABLE => 'External API service is unavailable',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::REQUEST_FAILED => 'Check request parameters and network connectivity',
self::INVALID_RESPONSE => 'Verify API version and response schema',
self::TIMEOUT => 'Increase timeout or retry with exponential backoff',
self::AUTHENTICATION_FAILED => 'Verify API credentials and permissions',
self::SERVICE_UNAVAILABLE => 'Check service status or use fallback provider',
};
}
public function isRecoverable(): bool
{
return true;
}
public function getRetryAfterSeconds(): ?int
{
return match($this) {
self::TIMEOUT => 30,
self::SERVICE_UNAVAILABLE => 60,
default => null,
};
}
}

View File

@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Authentication and authorization error codes
*
* Covers user authentication, token validation, and access control
*/
enum AuthErrorCode: string implements ErrorCode
{
case CREDENTIALS_INVALID = 'AUTH001';
case TOKEN_EXPIRED = 'AUTH002';
case TOKEN_INVALID = 'AUTH003';
case USER_LOCKED = 'AUTH004';
case SESSION_EXPIRED = 'AUTH005';
case INSUFFICIENT_PRIVILEGES = 'AUTH006';
case UNAUTHORIZED = 'AUTH007';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'AUTH';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::CREDENTIALS_INVALID,
self::USER_LOCKED,
self::INSUFFICIENT_PRIVILEGES,
self::UNAUTHORIZED => ErrorSeverity::ERROR,
self::TOKEN_EXPIRED,
self::TOKEN_INVALID,
self::SESSION_EXPIRED => ErrorSeverity::WARNING,
};
}
public function getDescription(): string
{
return match($this) {
self::CREDENTIALS_INVALID => 'Provided credentials are invalid',
self::TOKEN_EXPIRED => 'Authentication token has expired',
self::TOKEN_INVALID => 'Authentication token is invalid',
self::USER_LOCKED => 'User account is locked',
self::SESSION_EXPIRED => 'User session has expired',
self::INSUFFICIENT_PRIVILEGES => 'Insufficient privileges for this operation',
self::UNAUTHORIZED => 'Unauthorized access attempt',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::CREDENTIALS_INVALID => 'Verify username and password',
self::TOKEN_EXPIRED => 'Refresh authentication token',
self::TOKEN_INVALID => 'Obtain new authentication token',
self::USER_LOCKED => 'Contact administrator to unlock account',
self::SESSION_EXPIRED => 'Log in again to create new session',
self::INSUFFICIENT_PRIVILEGES => 'Request appropriate permissions from administrator',
self::UNAUTHORIZED => 'Authenticate with valid credentials and proper authorization',
};
}
public function isRecoverable(): bool
{
return match($this) {
self::USER_LOCKED => false,
default => true,
};
}
public function getRetryAfterSeconds(): ?int
{
return match($this) {
self::TOKEN_EXPIRED => 0, // Retry immediately after token refresh
self::SESSION_EXPIRED => 0, // Retry immediately after re-authentication
default => null,
};
}
}

View File

@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Business error codes
*
* Covers business rule violations, workflow errors, and domain-specific failures
*/
enum BusinessErrorCode: string implements ErrorCode
{
case BUSINESS_RULE_VIOLATION = 'BIZ001';
case WORKFLOW_STATE_INVALID = 'BIZ002';
case OPERATION_NOT_ALLOWED = 'BIZ003';
case RESOURCE_LOCKED = 'BIZ004';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'BIZ';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::BUSINESS_RULE_VIOLATION,
self::WORKFLOW_STATE_INVALID,
self::OPERATION_NOT_ALLOWED,
self::RESOURCE_LOCKED => ErrorSeverity::ERROR,
};
}
public function getDescription(): string
{
return match($this) {
self::BUSINESS_RULE_VIOLATION => 'Business rule violation occurred',
self::WORKFLOW_STATE_INVALID => 'Invalid workflow state for operation',
self::OPERATION_NOT_ALLOWED => 'Operation not allowed in current context',
self::RESOURCE_LOCKED => 'Resource is locked by another process',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::BUSINESS_RULE_VIOLATION => 'Review business rules and adjust operation',
self::WORKFLOW_STATE_INVALID => 'Check workflow state and allowed transitions',
self::OPERATION_NOT_ALLOWED => 'Verify operation prerequisites and permissions',
self::RESOURCE_LOCKED => 'Wait for lock release or cancel conflicting operation',
};
}
public function isRecoverable(): bool
{
return match($this) {
self::RESOURCE_LOCKED => true,
default => false,
};
}
public function getRetryAfterSeconds(): ?int
{
return match($this) {
self::RESOURCE_LOCKED => 10,
default => null,
};
}
}

View File

@@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Cache error codes
*
* Covers cache operations, invalidation, and driver issues
*/
enum CacheErrorCode: string implements ErrorCode
{
case CONNECTION_FAILED = 'CACHE001';
case READ_FAILED = 'CACHE002';
case WRITE_FAILED = 'CACHE003';
case INVALIDATION_FAILED = 'CACHE004';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'CACHE';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::CONNECTION_FAILED => ErrorSeverity::CRITICAL,
self::READ_FAILED,
self::WRITE_FAILED,
self::INVALIDATION_FAILED => ErrorSeverity::WARNING,
};
}
public function getDescription(): string
{
return match($this) {
self::CONNECTION_FAILED => 'Cache server connection failed',
self::READ_FAILED => 'Cache read operation failed',
self::WRITE_FAILED => 'Cache write operation failed',
self::INVALIDATION_FAILED => 'Cache invalidation failed',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::CONNECTION_FAILED => 'Check cache server status and configuration',
self::READ_FAILED => 'Retry operation or regenerate cached data',
self::WRITE_FAILED => 'Check cache storage capacity and permissions',
self::INVALIDATION_FAILED => 'Manually clear cache or restart cache service',
};
}
public function isRecoverable(): bool
{
return true;
}
public function getRetryAfterSeconds(): ?int
{
return match($this) {
self::CONNECTION_FAILED => 30,
default => null,
};
}
}

View File

@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Console error codes
*
* Covers console command execution, argument parsing, and CLI operations
*/
enum ConsoleErrorCode: string implements ErrorCode
{
case COMMAND_NOT_FOUND = 'CON001';
case INVALID_ARGUMENT = 'CON002';
case EXECUTION_FAILED = 'CON003';
case MISSING_REQUIRED_ARGUMENT = 'CON004';
case INVALID_OPTION = 'CON005';
case COMMAND_TIMEOUT = 'CON006';
case PERMISSION_DENIED = 'CON007';
case OUTPUT_WRITE_FAILED = 'CON008';
case INVALID_COMMAND_STRUCTURE = 'CON009';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'CON';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::EXECUTION_FAILED,
self::PERMISSION_DENIED,
self::INVALID_COMMAND_STRUCTURE => ErrorSeverity::ERROR,
self::COMMAND_NOT_FOUND,
self::INVALID_ARGUMENT,
self::MISSING_REQUIRED_ARGUMENT,
self::INVALID_OPTION,
self::COMMAND_TIMEOUT,
self::OUTPUT_WRITE_FAILED => ErrorSeverity::WARNING,
};
}
public function getDescription(): string
{
return match($this) {
self::COMMAND_NOT_FOUND => 'Console command not found',
self::INVALID_ARGUMENT => 'Invalid command argument provided',
self::EXECUTION_FAILED => 'Command execution failed',
self::MISSING_REQUIRED_ARGUMENT => 'Required command argument is missing',
self::INVALID_OPTION => 'Invalid command option provided',
self::COMMAND_TIMEOUT => 'Command execution timed out',
self::PERMISSION_DENIED => 'Insufficient permissions to execute command',
self::OUTPUT_WRITE_FAILED => 'Failed to write command output',
self::INVALID_COMMAND_STRUCTURE => 'Command has invalid structure or configuration',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::COMMAND_NOT_FOUND => 'Check command name and available commands list',
self::INVALID_ARGUMENT => 'Provide valid argument according to command help',
self::EXECUTION_FAILED => 'Check command logs and fix underlying issue',
self::MISSING_REQUIRED_ARGUMENT => 'Provide all required arguments',
self::INVALID_OPTION => 'Use valid options according to command help',
self::COMMAND_TIMEOUT => 'Increase timeout or optimize command execution',
self::PERMISSION_DENIED => 'Run command with appropriate permissions',
self::OUTPUT_WRITE_FAILED => 'Check output stream and file permissions',
self::INVALID_COMMAND_STRUCTURE => 'Verify command class structure and attribute configuration',
};
}
public function isRecoverable(): bool
{
return true;
}
public function getRetryAfterSeconds(): ?int
{
return null;
}
}

View File

@@ -0,0 +1,140 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Database error codes
*
* Covers database connectivity, queries, transactions, and migrations
*/
enum DatabaseErrorCode: string implements ErrorCode
{
case CONNECTION_FAILED = 'DB001';
case QUERY_FAILED = 'DB002';
case CONSTRAINT_VIOLATION = 'DB003';
case TRANSACTION_FAILED = 'DB004';
case MIGRATION_FAILED = 'DB005';
case MIGRATION_ROLLBACK_FAILED = 'DB006';
case MIGRATION_TABLE_CREATION_FAILED = 'DB007';
case MIGRATION_DEPENDENCY_ERROR = 'DB008';
case MIGRATION_PREFLIGHT_FAILED = 'DB009';
case POOL_EXHAUSTED = 'DB010';
case TIMEOUT = 'DB011';
case DEADLOCK_DETECTED = 'DB012';
case QUERY_SYNTAX_ERROR = 'DB013';
case QUERY_EXECUTION_FAILED = 'DB014';
case ENTITY_NOT_FOUND = 'DB015';
case MIGRATION_NOT_REVERSIBLE = 'DB016';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'DB';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::CONNECTION_FAILED,
self::CONSTRAINT_VIOLATION,
self::MIGRATION_FAILED,
self::MIGRATION_ROLLBACK_FAILED,
self::POOL_EXHAUSTED,
self::DEADLOCK_DETECTED => ErrorSeverity::CRITICAL,
self::QUERY_FAILED,
self::TRANSACTION_FAILED,
self::MIGRATION_DEPENDENCY_ERROR,
self::TIMEOUT,
self::QUERY_EXECUTION_FAILED => ErrorSeverity::ERROR,
self::MIGRATION_TABLE_CREATION_FAILED,
self::MIGRATION_PREFLIGHT_FAILED,
self::MIGRATION_NOT_REVERSIBLE,
self::QUERY_SYNTAX_ERROR,
self::ENTITY_NOT_FOUND => ErrorSeverity::WARNING,
};
}
public function getDescription(): string
{
return match($this) {
self::CONNECTION_FAILED => 'Database connection could not be established',
self::QUERY_FAILED => 'Database query execution failed',
self::CONSTRAINT_VIOLATION => 'Database constraint violation occurred',
self::TRANSACTION_FAILED => 'Database transaction failed',
self::MIGRATION_FAILED => 'Database migration failed',
self::MIGRATION_ROLLBACK_FAILED => 'Database migration rollback failed',
self::MIGRATION_TABLE_CREATION_FAILED => 'Database migration table creation failed',
self::MIGRATION_DEPENDENCY_ERROR => 'Migration dependency validation failed',
self::MIGRATION_PREFLIGHT_FAILED => 'Migration preflight check failed',
self::POOL_EXHAUSTED => 'Database connection pool exhausted',
self::TIMEOUT => 'Database operation timed out',
self::DEADLOCK_DETECTED => 'Database deadlock detected - transaction conflicts with concurrent operation',
self::QUERY_SYNTAX_ERROR => 'Database query contains syntax errors or invalid references',
self::QUERY_EXECUTION_FAILED => 'Database query execution failed due to data or runtime error',
self::ENTITY_NOT_FOUND => 'Requested entity does not exist in the database',
self::MIGRATION_NOT_REVERSIBLE => 'Migration does not support safe rollback - data loss would occur',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::CONNECTION_FAILED => 'Check database server status and connection settings',
self::QUERY_FAILED => 'Review query syntax and database schema',
self::CONSTRAINT_VIOLATION => 'Check data integrity and constraint definitions',
self::TRANSACTION_FAILED => 'Retry transaction or check for deadlocks',
self::MIGRATION_FAILED => 'Review migration scripts and database state',
self::MIGRATION_ROLLBACK_FAILED => 'Manually rollback migration and fix database state',
self::MIGRATION_TABLE_CREATION_FAILED => 'Check database permissions and table creation syntax',
self::MIGRATION_DEPENDENCY_ERROR => 'Add missing dependency migrations or review dependency chains',
self::MIGRATION_PREFLIGHT_FAILED => 'Fix preflight check issues before running migration',
self::POOL_EXHAUSTED => 'Increase connection pool size or optimize queries',
self::TIMEOUT => 'Optimize query performance or increase timeout limits',
self::DEADLOCK_DETECTED => 'Retry transaction immediately - deadlocks are typically resolved automatically',
self::QUERY_SYNTAX_ERROR => 'Review SQL syntax, verify table/column names match schema, check database permissions',
self::QUERY_EXECUTION_FAILED => 'Validate input data format, check for null/out-of-range values, review query logic',
self::ENTITY_NOT_FOUND => 'Verify entity ID is correct, check if entity was deleted, review query criteria',
self::MIGRATION_NOT_REVERSIBLE => 'Create a new forward migration to undo changes instead of rolling back',
};
}
public function isRecoverable(): bool
{
return match($this) {
self::MIGRATION_ROLLBACK_FAILED,
self::MIGRATION_NOT_REVERSIBLE,
self::CONSTRAINT_VIOLATION,
self::QUERY_SYNTAX_ERROR,
self::ENTITY_NOT_FOUND => false, // Not found errors are not transient
default => true,
};
}
public function getRetryAfterSeconds(): ?int
{
return match($this) {
self::CONNECTION_FAILED => 30,
self::TIMEOUT => 60,
self::POOL_EXHAUSTED => 15,
self::TRANSACTION_FAILED => 5,
self::DEADLOCK_DETECTED => 1, // Retry deadlocks immediately (1 second)
self::QUERY_EXECUTION_FAILED => 5, // Retry data errors after brief delay
default => null,
};
}
}

View File

@@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Dependency Injection error codes
*
* Covers DI container operations, binding resolution, and lifecycle management
*/
enum DiErrorCode: string implements ErrorCode
{
case BINDING_NOT_FOUND = 'DI001';
case CIRCULAR_DEPENDENCY = 'DI002';
case RESOLUTION_FAILED = 'DI003';
case INVALID_BINDING = 'DI004';
case SINGLETON_CONFLICT = 'DI005';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'DI';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::CIRCULAR_DEPENDENCY,
self::RESOLUTION_FAILED => ErrorSeverity::CRITICAL,
self::BINDING_NOT_FOUND,
self::INVALID_BINDING,
self::SINGLETON_CONFLICT => ErrorSeverity::ERROR,
};
}
public function getDescription(): string
{
return match($this) {
self::BINDING_NOT_FOUND => 'DI binding not found for requested type',
self::CIRCULAR_DEPENDENCY => 'Circular dependency detected',
self::RESOLUTION_FAILED => 'Dependency resolution failed',
self::INVALID_BINDING => 'Invalid binding configuration',
self::SINGLETON_CONFLICT => 'Singleton instance conflict',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::BINDING_NOT_FOUND => 'Register binding in DI container',
self::CIRCULAR_DEPENDENCY => 'Refactor dependencies to break circular reference',
self::RESOLUTION_FAILED => 'Check dependency constructors and bindings',
self::INVALID_BINDING => 'Fix binding configuration or implementation',
self::SINGLETON_CONFLICT => 'Review singleton lifecycle management',
};
}
public function isRecoverable(): bool
{
return false;
}
public function getRetryAfterSeconds(): ?int
{
return null;
}
}

View File

@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Discovery error codes
*
* Covers attribute scanning, component discovery, and auto-registration
*/
enum DiscoveryErrorCode: string implements ErrorCode
{
case SCAN_FAILED = 'DISC001';
case INVALID_ATTRIBUTE = 'DISC002';
case DUPLICATE_REGISTRATION = 'DISC003';
case CACHE_READ_FAILED = 'DISC004';
case CACHE_WRITE_FAILED = 'DISC005';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'DISC';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::SCAN_FAILED => ErrorSeverity::CRITICAL,
self::INVALID_ATTRIBUTE,
self::DUPLICATE_REGISTRATION => ErrorSeverity::ERROR,
self::CACHE_READ_FAILED,
self::CACHE_WRITE_FAILED => ErrorSeverity::WARNING,
};
}
public function getDescription(): string
{
return match($this) {
self::SCAN_FAILED => 'Discovery scan operation failed',
self::INVALID_ATTRIBUTE => 'Invalid or malformed attribute detected',
self::DUPLICATE_REGISTRATION => 'Duplicate component registration detected',
self::CACHE_READ_FAILED => 'Discovery cache read failed',
self::CACHE_WRITE_FAILED => 'Discovery cache write failed',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::SCAN_FAILED => 'Check file permissions and directory structure',
self::INVALID_ATTRIBUTE => 'Fix attribute syntax and parameters',
self::DUPLICATE_REGISTRATION => 'Remove duplicate registrations or use unique identifiers',
self::CACHE_READ_FAILED => 'Clear discovery cache and rescan',
self::CACHE_WRITE_FAILED => 'Check cache directory permissions',
};
}
public function isRecoverable(): bool
{
return match($this) {
self::SCAN_FAILED => false,
default => true,
};
}
public function getRetryAfterSeconds(): ?int
{
return null;
}
}

View File

@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Entity error codes
*
* Covers entity operations, persistence, and lifecycle management
*/
enum EntityErrorCode: string implements ErrorCode
{
case ENTITY_NOT_FOUND = 'ENT001';
case ENTITY_ALREADY_EXISTS = 'ENT002';
case PERSISTENCE_FAILED = 'ENT003';
case INVALID_ENTITY_STATE = 'ENT004';
case VERSION_CONFLICT = 'ENT005';
case DETACHED_ENTITY = 'ENT006';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'ENT';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::PERSISTENCE_FAILED => ErrorSeverity::CRITICAL,
self::ENTITY_ALREADY_EXISTS,
self::INVALID_ENTITY_STATE,
self::VERSION_CONFLICT => ErrorSeverity::ERROR,
self::ENTITY_NOT_FOUND,
self::DETACHED_ENTITY => ErrorSeverity::WARNING,
};
}
public function getDescription(): string
{
return match($this) {
self::ENTITY_NOT_FOUND => 'Entity not found',
self::ENTITY_ALREADY_EXISTS => 'Entity already exists',
self::PERSISTENCE_FAILED => 'Entity persistence operation failed',
self::INVALID_ENTITY_STATE => 'Entity is in invalid state',
self::VERSION_CONFLICT => 'Entity version conflict detected',
self::DETACHED_ENTITY => 'Operation on detached entity',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::ENTITY_NOT_FOUND => 'Verify entity identifier and existence',
self::ENTITY_ALREADY_EXISTS => 'Use update operation or different identifier',
self::PERSISTENCE_FAILED => 'Check database connection and constraints',
self::INVALID_ENTITY_STATE => 'Validate entity state before operation',
self::VERSION_CONFLICT => 'Reload entity and retry operation',
self::DETACHED_ENTITY => 'Reattach entity to persistence context',
};
}
public function isRecoverable(): bool
{
return match($this) {
self::ENTITY_NOT_FOUND => false,
default => true,
};
}
public function getRetryAfterSeconds(): ?int
{
return match($this) {
self::VERSION_CONFLICT => 1,
default => null,
};
}
}

View File

@@ -0,0 +1,212 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
/**
* Error severity levels with retention policies and alert behavior
*
* Defines severity classification for exceptions with automatic retention
* policies and alert triggering behavior.
*
* Severity Levels:
* - CRITICAL: System failures, data corruption, security breaches (365 days retention)
* - ERROR: Operation failures, validation errors, resource issues (90 days retention)
* - WARNING: Potential issues, degraded performance (30 days retention)
* - NOTICE: Notification of non-critical conditions (14 days retention)
* - INFO: Informational events (7 days retention)
* - DEBUG: Debugging information (1 day retention)
*/
enum ErrorSeverity: string
{
case CRITICAL = 'critical';
case ERROR = 'error';
case WARNING = 'warning';
case NOTICE = 'notice';
case INFO = 'info';
case DEBUG = 'debug';
/**
* Get retention period in days for this severity level
*
* Data retention based on severity:
* - CRITICAL: 365 days (1 year) - Critical errors require long-term analysis
* - ERROR: 90 days (3 months) - Standard error retention
* - WARNING: 30 days (1 month) - Warning-level issues
* - NOTICE: 14 days (2 weeks) - Notice-level events
* - INFO: 7 days (1 week) - Informational events
* - DEBUG: 1 day - Debug information only needed short-term
*/
public function getRetentionDays(): int
{
return match ($this) {
self::CRITICAL => 365,
self::ERROR => 90,
self::WARNING => 30,
self::NOTICE => 14,
self::INFO => 7,
self::DEBUG => 1,
};
}
/**
* Check if this severity level should trigger alerts
*
* Only CRITICAL and ERROR severities trigger automatic alerts
*/
public function shouldAlert(): bool
{
return match ($this) {
self::CRITICAL, self::ERROR => true,
default => false,
};
}
/**
* Get alert priority for this severity level
*
* Returns:
* - URGENT: For CRITICAL errors (immediate attention required)
* - HIGH: For ERROR level (attention required soon)
* - MEDIUM: For WARNING level (review when convenient)
* - LOW: For NOTICE level (review when convenient)
* - null: For INFO and DEBUG (no alerting)
*/
public function getAlertPriority(): ?string
{
return match ($this) {
self::CRITICAL => 'URGENT',
self::ERROR => 'HIGH',
self::WARNING => 'MEDIUM',
self::NOTICE => 'LOW',
default => null,
};
}
/**
* Get numeric weight for severity comparison
*
* Higher weight = more severe
* Useful for sorting and filtering by severity
*/
public function getWeight(): int
{
return match ($this) {
self::CRITICAL => 6,
self::ERROR => 5,
self::WARNING => 4,
self::NOTICE => 3,
self::INFO => 2,
self::DEBUG => 1,
};
}
/**
* Check if this severity is more severe than another
*/
public function isMoreSevereThan(self $other): bool
{
return $this->getWeight() > $other->getWeight();
}
/**
* Check if this severity is less severe than another
*/
public function isLessSevereThan(self $other): bool
{
return $this->getWeight() < $other->getWeight();
}
/**
* Check if this severity requires immediate action
*/
public function requiresImmediateAction(): bool
{
return $this === self::CRITICAL;
}
/**
* Get human-readable description of severity level
*/
public function getDescription(): string
{
return match ($this) {
self::CRITICAL => 'Critical - System failure or security breach requiring immediate action',
self::ERROR => 'Error - Operation failure requiring attention',
self::WARNING => 'Warning - Potential issue or degraded performance',
self::NOTICE => 'Notice - Notification of non-critical condition',
self::INFO => 'Info - Informational event',
self::DEBUG => 'Debug - Debugging information',
};
}
/**
* Get color code for UI representation
*/
public function getColorCode(): string
{
return match ($this) {
self::CRITICAL => '#DC2626', // Red-600
self::ERROR => '#F59E0B', // Amber-500
self::WARNING => '#FBBF24', // Yellow-400
self::NOTICE => '#10B981', // Green-500
self::INFO => '#3B82F6', // Blue-500
self::DEBUG => '#6B7280', // Gray-500
};
}
/**
* Get recommended response time for this severity
*
* Returns time in minutes
*/
public function getRecommendedResponseTime(): ?int
{
return match ($this) {
self::CRITICAL => 15, // 15 minutes
self::ERROR => 60, // 1 hour
self::WARNING => 240, // 4 hours
default => null, // No SLA for NOTICE/INFO/DEBUG
};
}
/**
* Get UI color code (alias for getColorCode for test compatibility)
*/
public function getColor(): string
{
return $this->getColorCode();
}
/**
* Get icon name for UI display
*/
public function getIcon(): string
{
return match ($this) {
self::CRITICAL => 'exclamation-triangle',
self::ERROR => 'exclamation-circle',
self::WARNING => 'exclamation',
self::NOTICE => 'bell',
self::INFO => 'information-circle',
self::DEBUG => 'bug',
};
}
/**
* Compare severity levels (alias for isMoreSevereThan for test compatibility)
*/
public function isHigherThan(self $other): bool
{
return $this->isMoreSevereThan($other);
}
/**
* Map to PSR-3 log level
*/
public function toLogLevel(): string
{
return $this->value;
}
}

View File

@@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Event error codes
*
* Covers event dispatching, handler execution, and event bus operations
*/
enum EventErrorCode: string implements ErrorCode
{
case HANDLER_NOT_FOUND = 'EVENT001';
case DISPATCH_FAILED = 'EVENT002';
case HANDLER_EXECUTION_FAILED = 'EVENT003';
case INVALID_EVENT = 'EVENT004';
case EVENT_BUS_UNAVAILABLE = 'EVENT005';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'EVENT';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::EVENT_BUS_UNAVAILABLE => ErrorSeverity::CRITICAL,
self::DISPATCH_FAILED,
self::HANDLER_EXECUTION_FAILED => ErrorSeverity::ERROR,
self::HANDLER_NOT_FOUND,
self::INVALID_EVENT => ErrorSeverity::WARNING,
};
}
public function getDescription(): string
{
return match($this) {
self::HANDLER_NOT_FOUND => 'Event handler not found',
self::DISPATCH_FAILED => 'Event dispatch operation failed',
self::HANDLER_EXECUTION_FAILED => 'Event handler execution failed',
self::INVALID_EVENT => 'Invalid event object or structure',
self::EVENT_BUS_UNAVAILABLE => 'Event bus is unavailable',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::HANDLER_NOT_FOUND => 'Register event handler or check event name',
self::DISPATCH_FAILED => 'Check event bus connection and retry',
self::HANDLER_EXECUTION_FAILED => 'Review handler logs and fix underlying issue',
self::INVALID_EVENT => 'Validate event structure and required properties',
self::EVENT_BUS_UNAVAILABLE => 'Check event bus service and configuration',
};
}
public function isRecoverable(): bool
{
return match($this) {
self::HANDLER_NOT_FOUND,
self::INVALID_EVENT => false,
default => true,
};
}
public function getRetryAfterSeconds(): ?int
{
return match($this) {
self::EVENT_BUS_UNAVAILABLE => 30,
default => null,
};
}
}

View File

@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* File system error codes
*
* Covers file operations, permissions, and storage issues
*/
enum FileSystemErrorCode: string implements ErrorCode
{
case FILE_NOT_FOUND = 'FS001';
case PERMISSION_DENIED = 'FS002';
case DISK_FULL = 'FS003';
case READ_FAILED = 'FS004';
case WRITE_FAILED = 'FS005';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'FS';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::DISK_FULL => ErrorSeverity::CRITICAL,
self::PERMISSION_DENIED,
self::WRITE_FAILED => ErrorSeverity::ERROR,
self::FILE_NOT_FOUND,
self::READ_FAILED => ErrorSeverity::WARNING,
};
}
public function getDescription(): string
{
return match($this) {
self::FILE_NOT_FOUND => 'File or directory not found',
self::PERMISSION_DENIED => 'File system permission denied',
self::DISK_FULL => 'Disk storage is full',
self::READ_FAILED => 'File read operation failed',
self::WRITE_FAILED => 'File write operation failed',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::FILE_NOT_FOUND => 'Verify file path and existence',
self::PERMISSION_DENIED => 'Check file permissions and ownership',
self::DISK_FULL => 'Free up disk space or increase storage capacity',
self::READ_FAILED => 'Check file integrity and permissions',
self::WRITE_FAILED => 'Check disk space and write permissions',
};
}
public function isRecoverable(): bool
{
return match($this) {
self::FILE_NOT_FOUND => false,
default => true,
};
}
public function getRetryAfterSeconds(): ?int
{
return null;
}
}

View File

@@ -0,0 +1,114 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Filesystem error codes
*
* Covers file operations, directory operations, permissions, and I/O errors
*/
enum FilesystemErrorCode: string implements ErrorCode
{
case FILE_NOT_FOUND = 'FS001';
case FILE_READ_FAILED = 'FS002';
case FILE_WRITE_FAILED = 'FS003';
case FILE_DELETE_FAILED = 'FS004';
case FILE_COPY_FAILED = 'FS005';
case FILE_METADATA_FAILED = 'FS006';
case PERMISSION_DENIED = 'FS007';
case DIRECTORY_CREATE_FAILED = 'FS008';
case DIRECTORY_LIST_FAILED = 'FS009';
case PATH_TRAVERSAL_ATTEMPT = 'FS010';
case INVALID_PATH = 'FS011';
case DISK_FULL = 'FS012';
case FILE_LOCKED = 'FS013';
case SYMLINK_FAILED = 'FS014';
case COMPRESSION_FAILED = 'FS015';
case SERIALIZER_NOT_FOUND = 'FS016';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'FS';
}
public function getNumericCode(): int
{
return (int) substr($this->value, 2); // Extract numeric part after 'FS'
}
public function getSeverity(): ErrorSeverity
{
return match ($this) {
self::PATH_TRAVERSAL_ATTEMPT => ErrorSeverity::CRITICAL,
self::PERMISSION_DENIED,
self::DISK_FULL => ErrorSeverity::ERROR,
self::FILE_NOT_FOUND,
self::FILE_LOCKED,
self::DIRECTORY_LIST_FAILED => ErrorSeverity::WARNING,
default => ErrorSeverity::NOTICE
};
}
public function getDescription(): string
{
return match ($this) {
self::FILE_NOT_FOUND => 'The requested file could not be found',
self::FILE_READ_FAILED => 'Failed to read file contents',
self::FILE_WRITE_FAILED => 'Failed to write to file',
self::FILE_DELETE_FAILED => 'Failed to delete file',
self::FILE_COPY_FAILED => 'Failed to copy file',
self::FILE_METADATA_FAILED => 'Failed to retrieve file metadata',
self::PERMISSION_DENIED => 'Insufficient permissions for file operation',
self::DIRECTORY_CREATE_FAILED => 'Failed to create directory',
self::DIRECTORY_LIST_FAILED => 'Failed to list directory contents',
self::PATH_TRAVERSAL_ATTEMPT => 'Path traversal attempt detected',
self::INVALID_PATH => 'Invalid file path',
self::DISK_FULL => 'Disk space exhausted',
self::FILE_LOCKED => 'File is locked by another process',
self::SYMLINK_FAILED => 'Failed to create symbolic link',
self::COMPRESSION_FAILED => 'File compression/decompression failed',
self::SERIALIZER_NOT_FOUND => 'Serializer not found in registry',
};
}
public function getRecoveryHint(): string
{
return match ($this) {
self::FILE_NOT_FOUND => 'Verify the file path exists',
self::PERMISSION_DENIED => 'Check file/directory permissions and ownership',
self::DISK_FULL => 'Free up disk space or increase storage capacity',
self::FILE_LOCKED => 'Wait for other process to release the file lock',
self::DIRECTORY_CREATE_FAILED => 'Verify parent directory exists and is writable',
self::SERIALIZER_NOT_FOUND => 'Register the required serializer in SerializerRegistry',
self::PATH_TRAVERSAL_ATTEMPT => 'Use validated paths only, avoid user-controlled path components',
default => 'Check file system logs for more details'
};
}
public function isRecoverable(): bool
{
return match ($this) {
self::FILE_LOCKED,
self::DISK_FULL => true,
default => false
};
}
public function getRetryAfterSeconds(): ?int
{
return match ($this) {
self::FILE_LOCKED => 5, // Retry after 5 seconds
self::DISK_FULL => 60, // Retry after 1 minute
default => null
};
}
}

View File

@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* HTTP error codes
*
* Covers HTTP protocol errors, client/server errors, and network issues
*/
enum HttpErrorCode: string implements ErrorCode
{
case BAD_REQUEST = 'HTTP001';
case NOT_FOUND = 'HTTP002';
case METHOD_NOT_ALLOWED = 'HTTP003';
case RATE_LIMIT_EXCEEDED = 'HTTP004';
case INTERNAL_SERVER_ERROR = 'HTTP005';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'HTTP';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::INTERNAL_SERVER_ERROR => ErrorSeverity::CRITICAL,
self::RATE_LIMIT_EXCEEDED => ErrorSeverity::ERROR,
self::BAD_REQUEST,
self::NOT_FOUND,
self::METHOD_NOT_ALLOWED => ErrorSeverity::WARNING,
};
}
public function getDescription(): string
{
return match($this) {
self::BAD_REQUEST => 'Request is malformed or invalid',
self::NOT_FOUND => 'Requested resource not found',
self::METHOD_NOT_ALLOWED => 'HTTP method not allowed for this resource',
self::RATE_LIMIT_EXCEEDED => 'API rate limit exceeded',
self::INTERNAL_SERVER_ERROR => 'Internal server error occurred',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::BAD_REQUEST => 'Check request format and parameters',
self::NOT_FOUND => 'Verify resource URL and existence',
self::METHOD_NOT_ALLOWED => 'Use correct HTTP method for this endpoint',
self::RATE_LIMIT_EXCEEDED => 'Wait and retry after rate limit window',
self::INTERNAL_SERVER_ERROR => 'Contact support or retry later',
};
}
public function isRecoverable(): bool
{
return match($this) {
self::INTERNAL_SERVER_ERROR => false,
default => true,
};
}
public function getRetryAfterSeconds(): ?int
{
return match($this) {
self::RATE_LIMIT_EXCEEDED => 60,
default => null,
};
}
}

View File

@@ -0,0 +1,92 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* MCP (Model Context Protocol) error codes
*
* Covers MCP server operations, tool execution, and protocol communication
*/
enum McpErrorCode: string implements ErrorCode
{
case TOOL_NOT_FOUND = 'MCP001';
case TOOL_EXECUTION_FAILED = 'MCP002';
case INVALID_TOOL_ARGUMENTS = 'MCP003';
case RESOURCE_NOT_FOUND = 'MCP004';
case PROTOCOL_ERROR = 'MCP005';
case SERVER_UNAVAILABLE = 'MCP006';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'MCP';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::SERVER_UNAVAILABLE,
self::PROTOCOL_ERROR => ErrorSeverity::CRITICAL,
self::TOOL_EXECUTION_FAILED => ErrorSeverity::ERROR,
self::TOOL_NOT_FOUND,
self::INVALID_TOOL_ARGUMENTS,
self::RESOURCE_NOT_FOUND => ErrorSeverity::WARNING,
};
}
public function getDescription(): string
{
return match($this) {
self::TOOL_NOT_FOUND => 'MCP tool not found',
self::TOOL_EXECUTION_FAILED => 'MCP tool execution failed',
self::INVALID_TOOL_ARGUMENTS => 'Invalid arguments provided to MCP tool',
self::RESOURCE_NOT_FOUND => 'MCP resource not found',
self::PROTOCOL_ERROR => 'MCP protocol communication error',
self::SERVER_UNAVAILABLE => 'MCP server is unavailable',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::TOOL_NOT_FOUND => 'Check tool name and available MCP tools',
self::TOOL_EXECUTION_FAILED => 'Review tool logs and fix underlying issue',
self::INVALID_TOOL_ARGUMENTS => 'Provide valid arguments according to tool schema',
self::RESOURCE_NOT_FOUND => 'Verify resource URI and existence',
self::PROTOCOL_ERROR => 'Check MCP protocol version and message format',
self::SERVER_UNAVAILABLE => 'Start MCP server or check server configuration',
};
}
public function isRecoverable(): bool
{
return match($this) {
self::TOOL_NOT_FOUND,
self::RESOURCE_NOT_FOUND => false,
default => true,
};
}
public function getRetryAfterSeconds(): ?int
{
return match($this) {
self::SERVER_UNAVAILABLE => 30,
default => null,
};
}
}

View File

@@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Payment error codes
*
* Covers payment processing, gateway communication, and transaction failures
*/
enum PaymentErrorCode: string implements ErrorCode
{
case PAYMENT_DECLINED = 'PAY001';
case INSUFFICIENT_FUNDS = 'PAY002';
case GATEWAY_ERROR = 'PAY003';
case INVALID_CARD = 'PAY004';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'PAY';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::GATEWAY_ERROR => ErrorSeverity::CRITICAL,
self::PAYMENT_DECLINED,
self::INSUFFICIENT_FUNDS,
self::INVALID_CARD => ErrorSeverity::ERROR,
};
}
public function getDescription(): string
{
return match($this) {
self::PAYMENT_DECLINED => 'Payment was declined',
self::INSUFFICIENT_FUNDS => 'Insufficient funds for transaction',
self::GATEWAY_ERROR => 'Payment gateway communication error',
self::INVALID_CARD => 'Invalid or expired card information',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::PAYMENT_DECLINED => 'Contact card issuer or try different payment method',
self::INSUFFICIENT_FUNDS => 'Add funds or use different payment method',
self::GATEWAY_ERROR => 'Retry transaction or contact support',
self::INVALID_CARD => 'Verify card details or use different card',
};
}
public function isRecoverable(): bool
{
return true;
}
public function getRetryAfterSeconds(): ?int
{
return match($this) {
self::GATEWAY_ERROR => 60,
default => null,
};
}
}

View File

@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Performance error codes
*
* Covers performance degradation, resource exhaustion, and optimization issues
*/
enum PerformanceErrorCode: string implements ErrorCode
{
case MEMORY_LIMIT_EXCEEDED = 'PERF001';
case EXECUTION_TIME_EXCEEDED = 'PERF002';
case SLOW_QUERY_DETECTED = 'PERF003';
case HIGH_CPU_USAGE = 'PERF004';
case RESOURCE_EXHAUSTION = 'PERF005';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'PERF';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::MEMORY_LIMIT_EXCEEDED,
self::RESOURCE_EXHAUSTION => ErrorSeverity::CRITICAL,
self::EXECUTION_TIME_EXCEEDED,
self::HIGH_CPU_USAGE => ErrorSeverity::ERROR,
self::SLOW_QUERY_DETECTED => ErrorSeverity::WARNING,
};
}
public function getDescription(): string
{
return match($this) {
self::MEMORY_LIMIT_EXCEEDED => 'Memory limit exceeded',
self::EXECUTION_TIME_EXCEEDED => 'Execution time limit exceeded',
self::SLOW_QUERY_DETECTED => 'Slow database query detected',
self::HIGH_CPU_USAGE => 'High CPU usage detected',
self::RESOURCE_EXHAUSTION => 'System resources exhausted',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::MEMORY_LIMIT_EXCEEDED => 'Increase memory limit or optimize memory usage',
self::EXECUTION_TIME_EXCEEDED => 'Increase timeout or optimize execution',
self::SLOW_QUERY_DETECTED => 'Optimize query or add database indices',
self::HIGH_CPU_USAGE => 'Optimize algorithms or increase CPU resources',
self::RESOURCE_EXHAUSTION => 'Free up resources or increase system capacity',
};
}
public function isRecoverable(): bool
{
return match($this) {
self::MEMORY_LIMIT_EXCEEDED => false,
default => true,
};
}
public function getRetryAfterSeconds(): ?int
{
return match($this) {
self::RESOURCE_EXHAUSTION => 60,
default => null,
};
}
}

View File

@@ -0,0 +1,109 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Queue error codes
*
* Covers queue operations, job processing, and worker management
*/
enum QueueErrorCode: string implements ErrorCode
{
case QUEUE_FULL = 'QUEUE001';
case JOB_FAILED = 'QUEUE002';
case JOB_TIMEOUT = 'QUEUE003';
case WORKER_UNAVAILABLE = 'QUEUE004';
case SERIALIZATION_FAILED = 'QUEUE005';
case DEAD_LETTER_QUEUE_FULL = 'QUEUE006';
case JOB_NOT_FOUND = 'QUEUE007';
case CHAIN_NOT_FOUND = 'QUEUE008';
case INVALID_STATE = 'QUEUE009';
case CIRCULAR_DEPENDENCY = 'QUEUE010';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'QUEUE';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::QUEUE_FULL,
self::DEAD_LETTER_QUEUE_FULL => ErrorSeverity::CRITICAL,
self::JOB_FAILED,
self::WORKER_UNAVAILABLE,
self::SERIALIZATION_FAILED,
self::JOB_NOT_FOUND,
self::CHAIN_NOT_FOUND,
self::CIRCULAR_DEPENDENCY => ErrorSeverity::ERROR,
self::JOB_TIMEOUT,
self::INVALID_STATE => ErrorSeverity::WARNING,
};
}
public function getDescription(): string
{
return match($this) {
self::QUEUE_FULL => 'Queue is full and cannot accept new jobs',
self::JOB_FAILED => 'Job execution failed',
self::JOB_TIMEOUT => 'Job execution timed out',
self::WORKER_UNAVAILABLE => 'No workers available to process job',
self::SERIALIZATION_FAILED => 'Job serialization or deserialization failed',
self::DEAD_LETTER_QUEUE_FULL => 'Dead letter queue is full',
self::JOB_NOT_FOUND => 'Job not found in queue or persistence layer',
self::CHAIN_NOT_FOUND => 'Job chain not found',
self::INVALID_STATE => 'Operation not allowed in current state',
self::CIRCULAR_DEPENDENCY => 'Circular dependency detected in job chain',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::QUEUE_FULL => 'Increase queue capacity or process existing jobs',
self::JOB_FAILED => 'Review job logs and fix underlying issue',
self::JOB_TIMEOUT => 'Increase job timeout or optimize job execution',
self::WORKER_UNAVAILABLE => 'Start more workers or wait for availability',
self::SERIALIZATION_FAILED => 'Check job payload and serialization format',
self::DEAD_LETTER_QUEUE_FULL => 'Process or clear dead letter queue',
self::JOB_NOT_FOUND => 'Verify job ID and check if job was already processed or expired',
self::CHAIN_NOT_FOUND => 'Verify chain ID and check chain existence',
self::INVALID_STATE => 'Check chain/job status and verify operation prerequisites',
self::CIRCULAR_DEPENDENCY => 'Review job dependencies and remove circular references',
};
}
public function isRecoverable(): bool
{
return match($this) {
self::SERIALIZATION_FAILED => false,
default => true,
};
}
public function getRetryAfterSeconds(): ?int
{
return match($this) {
self::QUEUE_FULL => 30,
self::WORKER_UNAVAILABLE => 60,
self::JOB_FAILED => 120,
default => null,
};
}
}

View File

@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Search error codes
*
* Covers search engine operations, indexing, and query processing
*/
enum SearchErrorCode: string implements ErrorCode
{
case INDEX_NOT_FOUND = 'SEARCH001';
case QUERY_FAILED = 'SEARCH002';
case INDEXING_FAILED = 'SEARCH003';
case ENGINE_UNAVAILABLE = 'SEARCH004';
case INVALID_QUERY_SYNTAX = 'SEARCH005';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'SEARCH';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::ENGINE_UNAVAILABLE => ErrorSeverity::CRITICAL,
self::QUERY_FAILED,
self::INDEXING_FAILED => ErrorSeverity::ERROR,
self::INDEX_NOT_FOUND,
self::INVALID_QUERY_SYNTAX => ErrorSeverity::WARNING,
};
}
public function getDescription(): string
{
return match($this) {
self::INDEX_NOT_FOUND => 'Search index not found',
self::QUERY_FAILED => 'Search query execution failed',
self::INDEXING_FAILED => 'Document indexing failed',
self::ENGINE_UNAVAILABLE => 'Search engine is unavailable',
self::INVALID_QUERY_SYNTAX => 'Search query syntax is invalid',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::INDEX_NOT_FOUND => 'Create or rebuild search index',
self::QUERY_FAILED => 'Review query syntax and retry',
self::INDEXING_FAILED => 'Check document format and retry indexing',
self::ENGINE_UNAVAILABLE => 'Check search engine status and configuration',
self::INVALID_QUERY_SYNTAX => 'Correct query syntax according to documentation',
};
}
public function isRecoverable(): bool
{
return true;
}
public function getRetryAfterSeconds(): ?int
{
return match($this) {
self::ENGINE_UNAVAILABLE => 30,
default => null,
};
}
}

View File

@@ -0,0 +1,110 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Security error codes
*
* Covers security violations, access control, encryption, and threat detection
*/
enum SecurityErrorCode: string implements ErrorCode
{
case ACCESS_DENIED = 'SEC001';
case CSRF_TOKEN_INVALID = 'SEC002';
case ENCRYPTION_FAILED = 'SEC003';
case DECRYPTION_FAILED = 'SEC004';
case SIGNATURE_INVALID = 'SEC005';
case SQL_INJECTION_DETECTED = 'SEC006';
case XSS_DETECTED = 'SEC007';
case PATH_TRAVERSAL_DETECTED = 'SEC008';
case RATE_LIMIT_ABUSE = 'SEC009';
case SUSPICIOUS_ACTIVITY = 'SEC010';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'SEC';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::SQL_INJECTION_DETECTED,
self::XSS_DETECTED,
self::PATH_TRAVERSAL_DETECTED,
self::ENCRYPTION_FAILED => ErrorSeverity::CRITICAL,
self::ACCESS_DENIED,
self::CSRF_TOKEN_INVALID,
self::SIGNATURE_INVALID,
self::RATE_LIMIT_ABUSE,
self::SUSPICIOUS_ACTIVITY => ErrorSeverity::ERROR,
self::DECRYPTION_FAILED => ErrorSeverity::WARNING,
};
}
public function getDescription(): string
{
return match($this) {
self::ACCESS_DENIED => 'Access to resource denied',
self::CSRF_TOKEN_INVALID => 'CSRF token is invalid or expired',
self::ENCRYPTION_FAILED => 'Data encryption operation failed',
self::DECRYPTION_FAILED => 'Data decryption operation failed',
self::SIGNATURE_INVALID => 'Digital signature verification failed',
self::SQL_INJECTION_DETECTED => 'SQL injection attempt detected',
self::XSS_DETECTED => 'Cross-site scripting attempt detected',
self::PATH_TRAVERSAL_DETECTED => 'Path traversal attempt detected',
self::RATE_LIMIT_ABUSE => 'Rate limit abuse detected',
self::SUSPICIOUS_ACTIVITY => 'Suspicious activity pattern detected',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::ACCESS_DENIED => 'Request proper authorization or permissions',
self::CSRF_TOKEN_INVALID => 'Refresh page to obtain new CSRF token',
self::ENCRYPTION_FAILED => 'Check encryption configuration and keys',
self::DECRYPTION_FAILED => 'Verify encryption key and data integrity',
self::SIGNATURE_INVALID => 'Verify signature and signing key',
self::SQL_INJECTION_DETECTED => 'Input sanitization required - contact support',
self::XSS_DETECTED => 'Input sanitization required - contact support',
self::PATH_TRAVERSAL_DETECTED => 'Use valid resource paths only',
self::RATE_LIMIT_ABUSE => 'Reduce request frequency significantly',
self::SUSPICIOUS_ACTIVITY => 'Contact support if you believe this is an error',
};
}
public function isRecoverable(): bool
{
return match($this) {
self::SQL_INJECTION_DETECTED,
self::XSS_DETECTED,
self::PATH_TRAVERSAL_DETECTED => false,
default => true,
};
}
public function getRetryAfterSeconds(): ?int
{
return match($this) {
self::CSRF_TOKEN_INVALID => 0,
self::RATE_LIMIT_ABUSE => 300,
default => null,
};
}
}

View File

@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Service error codes
*
* Covers service layer failures, business logic errors, and service communication
*/
enum ServiceErrorCode: string implements ErrorCode
{
case SERVICE_UNAVAILABLE = 'SVC001';
case OPERATION_FAILED = 'SVC002';
case TIMEOUT = 'SVC003';
case CIRCUIT_BREAKER_OPEN = 'SVC004';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'SVC';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::SERVICE_UNAVAILABLE => ErrorSeverity::CRITICAL,
self::OPERATION_FAILED,
self::TIMEOUT,
self::CIRCUIT_BREAKER_OPEN => ErrorSeverity::ERROR,
};
}
public function getDescription(): string
{
return match($this) {
self::SERVICE_UNAVAILABLE => 'Service is unavailable',
self::OPERATION_FAILED => 'Service operation failed',
self::TIMEOUT => 'Service operation timed out',
self::CIRCUIT_BREAKER_OPEN => 'Circuit breaker is open for this service',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::SERVICE_UNAVAILABLE => 'Check service health and retry later',
self::OPERATION_FAILED => 'Review operation parameters and retry',
self::TIMEOUT => 'Increase timeout or optimize operation',
self::CIRCUIT_BREAKER_OPEN => 'Wait for circuit breaker reset or use fallback',
};
}
public function isRecoverable(): bool
{
return true;
}
public function getRetryAfterSeconds(): ?int
{
return match($this) {
self::SERVICE_UNAVAILABLE => 60,
self::TIMEOUT => 30,
self::CIRCUIT_BREAKER_OPEN => 120,
default => null,
};
}
}

View File

@@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* System-level error codes
*
* Covers system configuration, dependencies, and initialization
*/
enum SystemErrorCode: string implements ErrorCode
{
case CONFIG_MISSING = 'SYS001';
case CONFIG_INVALID = 'SYS002';
case DEPENDENCY_MISSING = 'SYS003';
case RESOURCE_EXHAUSTED = 'SYS004';
case INITIALIZATION_FAILED = 'SYS005';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'SYS';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::RESOURCE_EXHAUSTED,
self::INITIALIZATION_FAILED => ErrorSeverity::CRITICAL,
self::CONFIG_MISSING,
self::CONFIG_INVALID,
self::DEPENDENCY_MISSING => ErrorSeverity::ERROR,
};
}
public function getDescription(): string
{
return match($this) {
self::CONFIG_MISSING => 'Required configuration is missing',
self::CONFIG_INVALID => 'Configuration contains invalid values',
self::DEPENDENCY_MISSING => 'Required system dependency is not available',
self::RESOURCE_EXHAUSTED => 'System resources are exhausted',
self::INITIALIZATION_FAILED => 'System initialization failed',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::CONFIG_MISSING => 'Check configuration files and environment variables',
self::CONFIG_INVALID => 'Review configuration values and fix invalid entries',
self::DEPENDENCY_MISSING => 'Install missing dependencies or check system requirements',
self::RESOURCE_EXHAUSTED => 'Free up system resources or increase limits',
self::INITIALIZATION_FAILED => 'Check system startup logs and fix initialization issues',
};
}
public function isRecoverable(): bool
{
return match($this) {
self::CONFIG_MISSING,
self::CONFIG_INVALID,
self::DEPENDENCY_MISSING => false,
default => true,
};
}
public function getRetryAfterSeconds(): ?int
{
return match($this) {
self::RESOURCE_EXHAUSTED => 60,
default => null,
};
}
}

View File

@@ -0,0 +1,107 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Template error codes
*
* Covers template rendering, processing, and view operations
*/
enum TemplateErrorCode: string implements ErrorCode
{
case TEMPLATE_NOT_FOUND = 'TPL001';
case RENDERING_FAILED = 'TPL002';
case SYNTAX_ERROR = 'TPL003';
case PROCESSOR_FAILED = 'TPL004';
case COMPONENT_NOT_FOUND = 'TPL005';
case INVALID_PLACEHOLDER = 'TPL006';
case SLOT_NOT_FOUND = 'TPL007';
case LAYOUT_NOT_FOUND = 'TPL008';
case CIRCULAR_INCLUSION = 'TPL009';
case DATA_BINDING_FAILED = 'TPL010';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'TPL';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::RENDERING_FAILED,
self::CIRCULAR_INCLUSION => ErrorSeverity::CRITICAL,
self::SYNTAX_ERROR,
self::PROCESSOR_FAILED,
self::DATA_BINDING_FAILED => ErrorSeverity::ERROR,
self::TEMPLATE_NOT_FOUND,
self::COMPONENT_NOT_FOUND,
self::INVALID_PLACEHOLDER,
self::SLOT_NOT_FOUND,
self::LAYOUT_NOT_FOUND => ErrorSeverity::WARNING,
};
}
public function getDescription(): string
{
return match($this) {
self::TEMPLATE_NOT_FOUND => 'Template file not found',
self::RENDERING_FAILED => 'Template rendering failed',
self::SYNTAX_ERROR => 'Template syntax error detected',
self::PROCESSOR_FAILED => 'Template processor execution failed',
self::COMPONENT_NOT_FOUND => 'Template component not found',
self::INVALID_PLACEHOLDER => 'Invalid placeholder syntax or reference',
self::SLOT_NOT_FOUND => 'Template slot not found',
self::LAYOUT_NOT_FOUND => 'Layout template not found',
self::CIRCULAR_INCLUSION => 'Circular template inclusion detected',
self::DATA_BINDING_FAILED => 'Template data binding failed',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::TEMPLATE_NOT_FOUND => 'Check template path and file existence',
self::RENDERING_FAILED => 'Review template syntax and data structure',
self::SYNTAX_ERROR => 'Fix template syntax according to documentation',
self::PROCESSOR_FAILED => 'Check processor logs and template content',
self::COMPONENT_NOT_FOUND => 'Verify component name and registration',
self::INVALID_PLACEHOLDER => 'Use correct placeholder syntax {variable}',
self::SLOT_NOT_FOUND => 'Define required slots in template',
self::LAYOUT_NOT_FOUND => 'Create layout template or fix layout path',
self::CIRCULAR_INCLUSION => 'Remove circular template inclusion chain',
self::DATA_BINDING_FAILED => 'Ensure all required data is provided',
};
}
public function isRecoverable(): bool
{
return match($this) {
self::TEMPLATE_NOT_FOUND,
self::COMPONENT_NOT_FOUND,
self::LAYOUT_NOT_FOUND,
self::CIRCULAR_INCLUSION => false,
default => true,
};
}
public function getRetryAfterSeconds(): ?int
{
return null;
}
}

View File

@@ -0,0 +1,92 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Validation error codes
*
* Covers input validation, business rule violations, and data integrity checks
*/
enum ValidationErrorCode: string implements ErrorCode
{
case INVALID_INPUT = 'VAL001';
case REQUIRED_FIELD_MISSING = 'VAL002';
case INVALID_FORMAT = 'VAL003';
case VALUE_OUT_OF_RANGE = 'VAL004';
case DUPLICATE_VALUE = 'VAL005';
case BUSINESS_RULE_VIOLATION = 'VAL006';
case INVALID_STATE_TRANSITION = 'VAL007';
case VALIDATION_FAILED = 'VAL008';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'VAL';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::BUSINESS_RULE_VIOLATION,
self::INVALID_STATE_TRANSITION => ErrorSeverity::ERROR,
self::INVALID_INPUT,
self::REQUIRED_FIELD_MISSING,
self::INVALID_FORMAT,
self::VALUE_OUT_OF_RANGE,
self::DUPLICATE_VALUE,
self::VALIDATION_FAILED => ErrorSeverity::WARNING,
};
}
public function getDescription(): string
{
return match($this) {
self::INVALID_INPUT => 'Input data is invalid',
self::REQUIRED_FIELD_MISSING => 'Required field is missing',
self::INVALID_FORMAT => 'Data format is invalid',
self::VALUE_OUT_OF_RANGE => 'Value is outside acceptable range',
self::DUPLICATE_VALUE => 'Duplicate value detected',
self::BUSINESS_RULE_VIOLATION => 'Business rule violation occurred',
self::INVALID_STATE_TRANSITION => 'Invalid state transition attempted',
self::VALIDATION_FAILED => 'Validation check failed',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::INVALID_INPUT => 'Check input data format and values',
self::REQUIRED_FIELD_MISSING => 'Provide all required fields',
self::INVALID_FORMAT => 'Correct data format according to specification',
self::VALUE_OUT_OF_RANGE => 'Provide value within acceptable range',
self::DUPLICATE_VALUE => 'Use unique value or update existing entry',
self::BUSINESS_RULE_VIOLATION => 'Review business rules and adjust input',
self::INVALID_STATE_TRANSITION => 'Check current state and allowed transitions',
self::VALIDATION_FAILED => 'Review validation errors and correct input',
};
}
public function isRecoverable(): bool
{
return true;
}
public function getRetryAfterSeconds(): ?int
{
return null;
}
}

View File

@@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace App\Framework\Exception\Core;
use App\Framework\Exception\ErrorCode;
/**
* Value Object error codes
*
* Covers value object validation, construction, and transformation
*/
enum ValueObjectErrorCode: string implements ErrorCode
{
case INVALID_VALUE = 'VO001';
case VALIDATION_FAILED = 'VO002';
case TRANSFORMATION_FAILED = 'VO003';
case IMMUTABILITY_VIOLATION = 'VO004';
public function getValue(): string
{
return $this->value;
}
public function getCategory(): string
{
return 'VO';
}
public function getNumericCode(): int
{
return (int) substr($this->value, -3);
}
public function getSeverity(): ErrorSeverity
{
return match($this) {
self::IMMUTABILITY_VIOLATION => ErrorSeverity::CRITICAL,
self::INVALID_VALUE,
self::VALIDATION_FAILED,
self::TRANSFORMATION_FAILED => ErrorSeverity::ERROR,
};
}
public function getDescription(): string
{
return match($this) {
self::INVALID_VALUE => 'Value object contains invalid value',
self::VALIDATION_FAILED => 'Value object validation failed',
self::TRANSFORMATION_FAILED => 'Value object transformation failed',
self::IMMUTABILITY_VIOLATION => 'Attempt to modify immutable value object',
};
}
public function getRecoveryHint(): string
{
return match($this) {
self::INVALID_VALUE => 'Provide valid value according to value object constraints',
self::VALIDATION_FAILED => 'Review validation rules and correct input',
self::TRANSFORMATION_FAILED => 'Check transformation logic and input format',
self::IMMUTABILITY_VIOLATION => 'Create new value object instance instead of modifying',
};
}
public function isRecoverable(): bool
{
return match($this) {
self::IMMUTABILITY_VIOLATION => false,
default => true,
};
}
public function getRetryAfterSeconds(): ?int
{
return null;
}
}