# 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
