# Troubleshooting Comprehensive guide for diagnosing and fixing common issues in the Custom PHP Framework. ## Common Errors ### Class Not Found Errors **Symptom**: `Class 'App\...' not found` **Causes**: - Autoloader not regenerated after creating new classes - Incorrect namespace - File not saved in correct directory **Solutions**: ```bash # Regenerate autoloader composer reload # Or in Docker make reload # Check namespace matches directory structure # src/Domain/User/Services/UserService.php namespace App\Domain\User\Services; ``` **Prevention**: - Always run `composer reload` after creating new classes - Use PSR-4 autoloading standards - Namespace must match directory structure --- ### Container Binding Missing **Symptom**: `No binding found for interface X` **Causes**: - Missing `#[Initializer]` attribute - Initializer not discovered (cache issue) - Circular dependency **Solutions**: ```php // 1. Add Initializer attribute final readonly class ServiceInitializer { #[Initializer] public function initialize(Container $container): void { $container->singleton( UserRepository::class, new DatabaseUserRepository($container->get(Connection::class)) ); } } // 2. Clear discovery cache rm -rf storage/cache/discovery_* // 3. Check for circular dependencies in constructor // ❌ Bad: A depends on B, B depends on A final readonly class ServiceA { public function __construct(private readonly ServiceB $b) {} } final readonly class ServiceB { public function __construct(private readonly ServiceA $a) {} // Circular! } ``` **Prevention**: - Use `#[Initializer]` for all service registrations - Avoid circular dependencies through interface abstraction - Use method injection instead of constructor injection when needed --- ### Route Not Found (404) **Symptom**: Route returns 404 even though controller exists **Causes**: - Missing `#[Route]` attribute - Route cache out of date - HTTP method mismatch - Middleware blocking request **Solutions**: ```bash # 1. Check route is registered docker exec php php console.php routes:list # 2. Clear route cache rm -rf storage/cache/routes_* # 3. Verify Route attribute #[Route(path: '/api/users', method: Method::GET)] public function getUsers(): JsonResult # 4. Check middleware chain docker exec php php -r " use App\Framework\Discovery\UnifiedDiscoveryService; \$discovery = new UnifiedDiscoveryService(); \$routes = \$discovery->discoverRoutes(); print_r(\$routes); " ``` **Common Mistakes**: ```php // ❌ Wrong: Method mismatch #[Route(path: '/api/users', method: Method::GET)] // Request: POST /api/users → 404 // ✅ Correct: Match HTTP method #[Route(path: '/api/users', method: Method::POST)] ``` --- ### HTTPS Required Error **Symptom**: `HTTPS is required for this operation` **Causes**: - Accessing framework via HTTP instead of HTTPS - Missing SSL certificates in development - Reverse proxy not forwarding HTTPS headers **Solutions**: ```bash # Development: Use HTTPS https://localhost/ # ✅ Correct http://localhost/ # ❌ Wrong - will be rejected # Check SSL certificates exist ls -la docker/ssl/ # Should see: localhost.crt, localhost.key # Regenerate if missing cd docker/ssl ./generate-ssl.sh ``` **Production Fix**: ```nginx # Nginx: Forward HTTPS headers proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Ssl on; ``` --- ### User Agent Required Error **Symptom**: `Valid User-Agent header is required` **Causes**: - Missing User-Agent in cURL/API requests - Bot/Scanner detection blocking legitimate requests **Solutions**: ```bash # Add User-Agent to cURL requests curl -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)" \ https://localhost/api/endpoint # In API client code $client = new GuzzleHttp\Client([ 'headers' => [ 'User-Agent' => 'MyApp/1.0' ] ]); ``` --- ## Container/DI Issues ### Cannot Resolve Dependency **Symptom**: `Cannot resolve parameter 'X' for class Y` **Causes**: - Constructor parameter has no type hint - Scalar parameter without default value - Interface not bound in container **Solutions**: ```php // ❌ Problem: No type hint public function __construct($logger) {} // ✅ Solution: Add type hint public function __construct(private readonly Logger $logger) {} // ❌ Problem: Scalar without default public function __construct(string $apiKey) {} // ✅ Solution: Use Value Object or default public function __construct( private readonly ApiKey $apiKey ) {} // OR provide default public function __construct( private readonly string $apiKey = '' ) {} ``` **Interface Binding**: ```php // Bind interface to implementation #[Initializer] public function initialize(Container $container): void { $container->bind( LoggerInterface::class, fn() => new FileLogger('/var/log/app.log') ); } ``` --- ### Circular Dependency Detected **Symptom**: `Circular dependency detected: A -> B -> A` **Solutions**: ```php // ❌ Problem: Direct circular dependency final readonly class OrderService { public function __construct( private readonly InvoiceService $invoices ) {} } final readonly class InvoiceService { public function __construct( private readonly OrderService $orders // Circular! ) {} } // ✅ Solution 1: Extract shared logic to third service final readonly class OrderService { public function __construct( private readonly PriceCalculator $calculator ) {} } final readonly class InvoiceService { public function __construct( private readonly PriceCalculator $calculator ) {} } // ✅ Solution 2: Use events for decoupling final readonly class OrderService { public function __construct( private readonly EventDispatcher $events ) {} public function completeOrder(Order $order): void { // Process order $this->events->dispatch(new OrderCompletedEvent($order)); } } final readonly class InvoiceService { #[EventHandler] public function onOrderCompleted(OrderCompletedEvent $event): void { // Generate invoice without direct dependency $this->generateInvoice($event->order); } } ``` --- ### Service Not Singleton **Symptom**: Multiple instances created when singleton expected **Solutions**: ```php // ❌ Wrong: Regular binding creates new instance each time $container->bind( DatabaseConnection::class, fn() => new DatabaseConnection($config) ); // ✅ Correct: Singleton creates one instance $container->singleton( DatabaseConnection::class, new DatabaseConnection($config) ); // ✅ Alternative: Lazy singleton $container->singleton( DatabaseConnection::class, fn() => new DatabaseConnection($config) ); ``` --- ## Routing Problems ### Route Conflicts **Symptom**: Wrong controller handling request **Causes**: - Multiple routes match same pattern - Route priority issues - Catch-all route defined too early **Solutions**: ```bash # List all routes to find conflicts docker exec php php console.php routes:list | grep '/api/users' # Check route priority order docker exec php php console.php routes:list --sort=priority ``` **Fix Route Conflicts**: ```php // ❌ Problem: Routes conflict #[Route(path: '/api/users/{id}', method: Method::GET)] public function getUser(string $id): JsonResult {} #[Route(path: '/api/users/me', method: Method::GET)] public function getCurrentUser(): JsonResult {} // Never matched! // ✅ Solution: Most specific routes first #[Route(path: '/api/users/me', method: Method::GET)] public function getCurrentUser(): JsonResult {} #[Route(path: '/api/users/{id}', method: Method::GET)] public function getUser(string $id): JsonResult {} ``` --- ### Route Parameters Not Working **Symptom**: Route parameter is null or empty **Solutions**: ```php // ✅ Parameter name must match route #[Route(path: '/api/users/{userId}', method: Method::GET)] public function getUser(string $userId): JsonResult // Parameter name matches! { return new JsonResult(['userId' => $userId]); } // ❌ Wrong: Parameter name mismatch #[Route(path: '/api/users/{userId}', method: Method::GET)] public function getUser(string $id): JsonResult // Mismatch! { // $id will be null } ``` --- ### Middleware Not Executing **Symptom**: Middleware bypassed or not running **Causes**: - Missing `#[MiddlewarePriority]` attribute - Wrong middleware registration - Middleware exception not caught **Solutions**: ```php // Add priority attribute #[MiddlewarePriority(100)] final readonly class AuthMiddleware implements Middleware { public function process(Request $request, callable $next): Response { // Auth logic return $next($request); } } // Check middleware is discovered docker exec php php console.php debug:middleware ``` **Middleware Chain Debug**: ```php // Add logging to trace execution final readonly class DebugMiddleware implements Middleware { public function process(Request $request, callable $next): Response { Logger::debug('[Middleware] Before: ' . get_class($this)); $response = $next($request); Logger::debug('[Middleware] After: ' . get_class($this)); return $response; } } ``` --- ## Performance Issues ### Slow Page Load Times **Diagnosis Steps**: ```bash # 1. Enable performance profiling docker exec php php console.php performance:enable # 2. Check slow query log docker exec php tail -f /var/log/mysql/slow-queries.log # 3. Profile specific endpoint docker exec php php console.php performance:profile /api/users # 4. Check N+1 queries docker exec php php console.php db:explain ``` **Common Causes & Fixes**: #### N+1 Query Problem ```php // ❌ Problem: N+1 queries $users = $this->userRepository->findAll(); foreach ($users as $user) { // Triggers separate query for each user! $profile = $user->getProfile(); } // ✅ Solution: Eager loading $users = $this->userRepository->findAllWithProfiles(); // Single query with JOIN ``` #### Missing Indexes ```bash # Check missing indexes docker exec php php console.php db:analyze-indexes # Add index via migration $table->index('email'); // Single column $table->index(['user_id', 'created_at']); // Composite ``` #### Cache Not Used ```php // ✅ Cache expensive operations use App\Framework\Cache\CacheKey; use App\Framework\Core\ValueObjects\Duration; $cacheKey = CacheKey::fromString('users_list'); $users = $this->cache->remember( $cacheKey, fn() => $this->repository->findAll(), Duration::fromMinutes(10) ); ``` --- ### Memory Exhaustion **Symptom**: `Allowed memory size exhausted` **Causes**: - Large result sets loaded into memory - Memory leaks in loops - Circular references **Solutions**: ```php // ❌ Problem: Loading entire table $allUsers = $this->connection->query('SELECT * FROM users'); // 1 million rows = memory exhausted // ✅ Solution: Batch processing $batchSize = 1000; $offset = 0; do { $batch = $this->connection->query( "SELECT * FROM users LIMIT {$batchSize} OFFSET {$offset}" ); foreach ($batch as $user) { $this->processUser($user); } $offset += $batchSize; // Force garbage collection gc_collect_cycles(); } while (count($batch) === $batchSize); ``` **Memory Leak Prevention**: ```php // ✅ Clear references in loops foreach ($largeDataset as $item) { $result = $this->process($item); // Clear large objects unset($result, $item); if ($iteration % 100 === 0) { gc_collect_cycles(); } } ``` --- ## Database Connection Issues ### Connection Refused **Symptom**: `SQLSTATE[HY000] [2002] Connection refused` **Solutions**: ```bash # 1. Check MySQL container is running docker ps | grep mysql # 2. Check database credentials in .env DB_HOST=mysql # Use service name, not 'localhost' DB_PORT=3306 DB_NAME=your_database DB_USER=root DB_PASS=your_password # 3. Test connection directly docker exec mysql mysql -u root -p your_database # 4. Check MySQL logs docker logs mysql ``` **Common Mistakes**: ```bash # ❌ Wrong: localhost (from container perspective) DB_HOST=localhost # ✅ Correct: Docker service name DB_HOST=mysql ``` --- ### Too Many Connections **Symptom**: `SQLSTATE[HY000] [1040] Too many connections` **Causes**: - Connection leaks (not closing connections) - Too many concurrent requests - Connection pool exhausted **Solutions**: ```php // ✅ Always use try-finally for connections $connection = $this->connectionPool->getConnection(); try { $connection->beginTransaction(); // Database operations $connection->commit(); } catch (\Exception $e) { $connection->rollback(); throw $e; } finally { // Always release connection $this->connectionPool->releaseConnection($connection); } ``` **Increase Connection Limit**: ```ini # docker/mysql/my.cnf [mysqld] max_connections = 200 ``` --- ### Deadlock Detected **Symptom**: `SQLSTATE[40001] Deadlock found when trying to get lock` **Causes**: - Transactions accessing tables in different order - Long-running transactions - Missing indexes causing lock escalation **Solutions**: ```php // ✅ Always access tables in consistent order final readonly class PaymentService { public function transfer(Account $from, Account $to, Money $amount): void { $this->connection->beginTransaction(); try { // ALWAYS lock in same order (e.g., by ID) $accounts = [$from, $to]; usort($accounts, fn($a, $b) => $a->id <=> $b->id); foreach ($accounts as $account) { $this->lockAccount($account); } // Process transfer $from->debit($amount); $to->credit($amount); $this->connection->commit(); } catch (\Exception $e) { $this->connection->rollback(); throw $e; } } } ``` **Retry Strategy**: ```php // ✅ Retry on deadlock public function executeWithRetry(callable $operation, int $maxAttempts = 3): mixed { $attempt = 0; while ($attempt < $maxAttempts) { try { return $operation(); } catch (DeadlockException $e) { $attempt++; if ($attempt >= $maxAttempts) { throw $e; } // Exponential backoff usleep(pow(2, $attempt) * 100000); // 200ms, 400ms, 800ms } } } ``` --- ## Cache Problems ### Cache Not Working **Symptom**: Data not cached, fetched every time **Diagnosis**: ```bash # 1. Check cache driver in .env CACHE_DRIVER=redis # or file, array, memcached # 2. Test cache directly docker exec php php -r " \$cache = new App\Framework\Cache\SmartCache(); \$cache->set('test', 'value', 60); var_dump(\$cache->get('test')); " # 3. Check Redis connection (if using Redis) docker exec redis redis-cli PING # Should return: PONG ``` **Common Issues**: ```php // ❌ Problem: TTL too short $cache->set($key, $value, 1); // 1 second - expires immediately // ✅ Solution: Appropriate TTL use App\Framework\Core\ValueObjects\Duration; $cache->set($key, $value, Duration::fromMinutes(10)->toSeconds()); // ✅ Better: Use CacheItem with Duration $cacheItem = CacheItem::forSetting( key: $key, value: $value, ttl: Duration::fromHours(1) ); $cache->set($cacheItem); ``` --- ### Cache Stampede **Symptom**: Multiple processes regenerating same cached value simultaneously **Solutions**: ```php // ✅ Use cache locking use App\Framework\Cache\CacheLock; public function getExpensiveData(string $key): array { $cacheKey = CacheKey::fromString($key); // Try to get from cache $cached = $this->cache->get($cacheKey); if ($cached !== null) { return $cached; } // Acquire lock to prevent stampede $lock = $this->cache->lock($cacheKey, Duration::fromSeconds(10)); if (!$lock->acquire()) { // Another process is regenerating, wait for it sleep(1); return $this->cache->get($cacheKey) ?? []; } try { // Regenerate data $data = $this->expensiveOperation(); // Cache it $this->cache->set( CacheItem::forSetting($cacheKey, $data, Duration::fromMinutes(10)) ); return $data; } finally { $lock->release(); } } ``` --- ### Cache Invalidation Issues **Symptom**: Stale data served from cache **Solutions**: ```php // ✅ Tag-based invalidation use App\Framework\Cache\CacheTag; // Cache with tags $userTag = CacheTag::fromString("user_{$userId}"); $teamTag = CacheTag::fromString("team_{$teamId}"); $cacheItem = CacheItem::forSetting( key: CacheKey::fromString("user_profile_{$userId}"), value: $userProfile, ttl: Duration::fromHours(1), tags: [$userTag, $teamTag] ); $this->cache->set($cacheItem); // Invalidate all user-related caches $this->cache->forget($userTag); // Invalidate all team-related caches $this->cache->forget($teamTag); ``` **Event-Based Invalidation**: ```php // ✅ Automatic invalidation via events final readonly class CacheInvalidationListener { #[EventHandler] public function onUserUpdated(UserUpdatedEvent $event): void { $userTag = CacheTag::fromString("user_{$event->userId}"); $this->cache->forget($userTag); } } ``` --- ## Debug Tools ### Framework Debug Mode ```bash # Enable debug mode in .env APP_ENV=development APP_DEBUG=true # Enhanced error pages with: # - Full stack traces # - Variable dumps # - SQL query logs # - Performance metrics ``` --- ### Performance Profiling ```bash # Enable performance collector docker exec php php console.php performance:enable # Profile specific request docker exec php php console.php performance:profile /api/users # View performance report docker exec php php console.php performance:report # Disable profiling docker exec php php console.php performance:disable ``` --- ### Database Query Logging ```php // Enable query logging in code use App\Framework\Database\QueryLogger; $queryLogger = new QueryLogger(); $connection->setQueryLogger($queryLogger); // Log queries $users = $connection->query('SELECT * FROM users'); // View logged queries foreach ($queryLogger->getQueries() as $query) { Logger::debug('[SQL]', [ 'query' => $query['sql'], 'bindings' => $query['bindings'], 'duration' => $query['duration'] ]); } ``` **Detect N+1 Queries**: ```bash # Run N+1 detection docker exec php php console.php db:detect-n-plus-one /api/users ``` --- ### MCP Server Debugging ```bash # Test MCP server connection echo '{"jsonrpc": "2.0", "method": "initialize", "params": {}}' | \ docker exec -i php php console.php mcp:server # Analyze routes via MCP docker exec php php console.php mcp:analyze routes # Analyze container bindings docker exec php php console.php mcp:analyze container # Check framework health docker exec php php console.php mcp:health ``` --- ### Request Debugging ```php // Log full request details use App\Framework\Http\Request; Logger::debug('[Request]', [ 'method' => $request->method->value, 'path' => $request->path, 'query' => $request->queryParameters ?? [], 'body' => $request->parsedBody ?? [], 'headers' => $request->headers->toArray(), 'server' => [ 'ip' => $request->server->getRemoteAddr(), 'user_agent' => $request->server->getUserAgent() ] ]); ``` --- ### Event System Debugging ```php // Log all dispatched events final readonly class EventDebugListener { #[EventHandler] public function onAnyEvent(object $event): void { Logger::debug('[Event]', [ 'type' => get_class($event), 'data' => $event ]); } } ``` --- ## Emergency Procedures ### Application Down **Quick Recovery Steps**: ```bash # 1. Check Docker containers docker ps -a # 2. Restart all services make down && make up # 3. Check logs make logs # 4. Verify database connection docker exec mysql mysql -u root -p # 5. Clear all caches rm -rf storage/cache/* composer reload # 6. Test health endpoint curl -k https://localhost/health ``` --- ### Database Corruption ```bash # 1. Stop application make down # 2. Backup database immediately docker exec mysql mysqldump -u root -p your_database > backup.sql # 3. Check and repair tables docker exec mysql mysqlcheck -u root -p --auto-repair your_database # 4. Restore from backup if needed docker exec -i mysql mysql -u root -p your_database < backup.sql # 5. Restart application make up ``` --- ### Cache Corruption ```bash # 1. Flush all caches docker exec redis redis-cli FLUSHALL # If using Redis rm -rf storage/cache/* # File cache # 2. Restart services make restart # 3. Warm up critical caches docker exec php php console.php cache:warm ``` --- ## Getting Help ### Framework Support Channels **Issues & Bug Reports**: - GitHub Issues: Report bugs with reproduction steps - Include: PHP version, Docker logs, error messages **Documentation**: - `/docs/claude/` - Complete framework documentation - CLAUDE.md - AI assistant integration guide **Debugging Resources**: - MCP Server - AI-powered framework analysis - Performance Profiler - Built-in performance monitoring - Query Logger - SQL debugging tool --- ## Prevention Best Practices ### Development Checklist - [ ] Run `composer reload` after creating new classes - [ ] Clear caches after config changes - [ ] Test with HTTPS in development - [ ] Add User-Agent to API requests - [ ] Use Value Objects instead of primitives - [ ] Implement proper error handling - [ ] Add logging to critical operations - [ ] Test with production-like data volumes - [ ] Profile performance before deployment - [ ] Review query execution plans ### Code Review Checklist - [ ] No circular dependencies - [ ] Proper exception handling - [ ] Database transactions properly closed - [ ] Cache keys properly namespaced - [ ] Routes don't conflict - [ ] Middleware properly ordered - [ ] Security best practices followed - [ ] Performance considerations addressed - [ ] Tests cover critical paths - [ ] Documentation updated --- ## Appendix: Common Error Codes | Error Code | Meaning | Common Cause | |------------|---------|--------------| | 403 | Forbidden | WAF blocking, missing Auth, IP restriction | | 404 | Not Found | Route not registered, wrong path | | 405 | Method Not Allowed | HTTP method mismatch in Route attribute | | 429 | Too Many Requests | Rate limit exceeded | | 500 | Internal Server Error | Unhandled exception, see logs | | 502 | Bad Gateway | PHP-FPM down, connection refused | | 503 | Service Unavailable | Database down, cache unavailable | --- **Last Updated**: 2025-01-28 **Framework Version**: 2.x