> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/betoalien/Lyger-PHP-Framework/llms.txt
> Use this file to discover all available pages before exploring further.

# Always-Alive Server

> Understanding Lygers persistent PHP worker architecture for lightning-fast request handling

## The Problem with Traditional PHP

Traditional PHP follows a "shared-nothing" architecture:

```
Request → PHP Boots → Load Files → Execute → Shutdown → Response
  ↓
Next Request → PHP Boots → Load Files → Execute → Shutdown → Response
```

Every request:

* Starts a new PHP process
* Loads the framework from disk
* Parses and compiles PHP files
* Initializes dependencies
* Finally executes your code
* Destroys everything

<Warning>
  Traditional PHP spends 50-80% of request time on startup overhead, not your actual code.
</Warning>

## The Always-Alive Solution

Lyger's Always-Alive architecture keeps PHP running in memory:

```
┌──────────────────────────────────────┐
│   Rust HTTP Server (Port 8000)      │
│   - Ultra-fast request parsing       │
│   - Connection handling              │
│   - Static file serving              │
└──────────────────────────────────────┘
               ↓ FFI Callback
┌──────────────────────────────────────┐
│   PHP Worker (Persistent Process)   │
│   - Framework pre-loaded             │
│   - Classes in memory                │
│   - Connections pooled               │
│   - Routes pre-compiled              │
└──────────────────────────────────────┘
```

<Info>
  With Always-Alive, only your application code executes per request. The framework overhead is paid once at startup.
</Info>

## How It Works

### Server Startup

```php theme={null}
// From your application's entry point
use Lyger\Core\Engine;

// Define your router
$router = function(string $uri, string $method, array $data) {
    // Your routing logic here
    return Router::handle($uri, $method);
};

// Start the Always-Alive server
Engine::startServer($router, 8000);
```

### Behind the Scenes

```php theme={null}
// From Lyger/Core/Engine.php
public static function startServer(callable $routerHandler, int $port = 8000): void
{
    echo "\n";
    echo "========================================\n";
    echo "   LYGER v0.1 - Always-Alive Server\n";
    echo "========================================\n\n";

    // Preload framework into memory
    echo "Loading framework into memory...\n";
    ServerManager::start($routerHandler);

    // Start Rust HTTP server if FFI available
    $instance = self::getInstance();

    if ($instance->ffi !== null) {
        try {
            echo "Starting Rust HTTP server on port {$port}...\n";
            $instance->ffi->lyger_start_server($port);
            self::$serverRunning = true;
        } catch (\Throwable $e) {
            echo "Note: Using PHP built-in server (FFI start_server not available)\n";
        }
    } else {
        echo "Note: Using PHP built-in server (FFI not available)\n";
    }

    // Keep the PHP worker alive
    while (self::$serverRunning) {
        sleep(1);
    }
}
```

## ServerManager

The `ServerManager` class orchestrates the persistent PHP worker:

```php theme={null}
// From Lyger/Core/ServerManager.php
class ServerManager
{
    private static bool $running = false;
    private static $router = null;
    private static array $loadedClasses = [];

    public static function start(callable $router): void
    {
        self::$running = true;
        self::$router = $router;

        // Preload all framework classes into memory
        self::preloadFramework();

        echo "\n";
        echo "========================================\n";
        echo "   LYGER SERVER v0.1\n";
        echo "   Always-Alive Mode\n";
        echo "========================================\n\n";
        echo "✓ Framework loaded in memory\n";
        echo "✓ Waiting for requests...\n";
        echo "   Ctrl+C to stop\n\n";

        // Keep PHP alive - waiting for FFI callbacks from Rust
        while (self::$running) {
            sleep(1);

            if (!self::$running) {
                break;
            }
        }
    }
}
```

### Framework Preloading

```php theme={null}
// From Lyger/Core/ServerManager.php
private static function preloadFramework(): void
{
    $classes = [
        // Core
        'Lyger\Core\Engine',
        'Lyger\Routing\Router',
        'Lyger\Http\Request',
        'Lyger\Http\Response',

        // Container
        'Lyger\Container\Container',

        // Database
        'Lyger\Database\QueryBuilder',
        'Lyger\Database\Schema',
        'Lyger\Database\Model',
        'Lyger\Database\Collection',

        // Middleware
        'Lyger\Middleware\Middleware',
        'Lyger\Middleware\CorsMiddleware',
        'Lyger\Middleware\RateLimitMiddleware',
        'Lyger\Middleware\AuthMiddleware',

        // Validation
        'Lyger\Validation\Validator',

        // Cache
        'Lyger\Cache\Cache',

        // Foundation
        'Lyger\Foundation\Env',
        'Lyger\Foundation\Path',
    ];

    foreach ($classes as $class) {
        if (class_exists($class)) {
            self::$loadedClasses[] = $class;
        }
    }
}
```

<Note>
  PHP's class autoloader caches parsed classes. Once loaded, subsequent instantiations are nearly instant.
</Note>

## Request Handling

When a request arrives:

1. **Rust receives HTTP request** (port 8000)
2. **Rust parses request** (method, URI, headers, body)
3. **Rust calls PHP via FFI** with request data
4. **PHP executes router** (already in memory)
5. **PHP returns response** to Rust
6. **Rust sends HTTP response** to client

```php theme={null}
// From Lyger/Core/ServerManager.php
public static function handleRequest(string $uri, string $method, array $data = []): string
{
    if (self::$router === null) {
        return json_encode(['error' => 'No router configured']);
    }

    try {
        // Execute the router - instant since everything is in memory!
        $response = (self::$router)($uri, $method, $data);
        return $response;
    } catch (\Throwable $e) {
        return json_encode([
            'error' => $e->getMessage(),
            'file' => $e->getFile(),
            'line' => $e->getLine()
        ]);
    }
}
```

## Performance Benefits

### Startup Time Comparison

| Scenario           | Traditional PHP | Always-Alive      | Improvement |
| ------------------ | --------------- | ----------------- | ----------- |
| Framework boot     | \~50ms          | \~0ms (preloaded) | ∞           |
| Class loading      | \~20ms          | \~0ms (cached)    | ∞           |
| Route compilation  | \~10ms          | \~0ms (compiled)  | ∞           |
| DB connection      | \~5ms           | \~1ms (pooled)    | 5x          |
| **Total overhead** | **\~85ms**      | **\~1ms**         | **85x**     |

<Info>
  Your actual application code execution time remains the same. The improvement comes from eliminating startup overhead.
</Info>

### Real-World Impact

```
Traditional PHP:
85ms overhead + 15ms app code = 100ms total
Only 15% of time spent on your code!

Always-Alive:
1ms overhead + 15ms app code = 16ms total
94% of time spent on your code!

Result: 6.25x faster response time
```

## Memory Considerations

### Memory Usage

```
Traditional PHP per request:
- Process: ~8MB
- Framework: ~10MB
- Application: ~5MB
Total per concurrent request: ~23MB

Always-Alive:
- Single Process: ~8MB
- Framework: ~10MB (shared)
- Application: ~5MB (shared)
Total for all requests: ~23MB
```

<Warning>
  Always-Alive uses less total memory because the framework is loaded once, not per request. However, memory leaks are permanent until server restart.
</Warning>

### Memory Leaks

In traditional PHP, memory leaks are cleared after each request. With Always-Alive, you must be careful:

```php theme={null}
// ❌ Bad: Memory leak
class Controller {
    private static array $cache = [];
    
    public function handle($request) {
        self::$cache[] = $request; // Grows forever!
    }
}

// ✅ Good: Bounded memory
class Controller {
    public function handle($request) {
        $cache = $this->getCache(); // Instance variable, GC'd after request
        $cache->set('key', $value);
    }
}
```

<Accordion title="Best practices for Always-Alive">
  1. **Avoid static arrays that grow** - Use proper cache implementations
  2. **Close resources** - Database connections, file handles, etc.
  3. **Unset large variables** - Help PHP's garbage collector
  4. **Use dependency injection** - Avoid globals and static state
  5. **Monitor memory usage** - Set up alerts for memory growth
</Accordion>

## Starting the Server

### Command Line

```bash theme={null}
php rawr serve
```

This starts the server on port 8000 by default.

### Custom Port

```bash theme={null}
php rawr serve --port=8080
```

### Programmatic

```php theme={null}
use Lyger\Core\Engine;
use Lyger\Routing\Router;

$router = function($uri, $method, $data) {
    return Router::handle($uri, $method);
};

Engine::startServer($router, 8000);
```

## Stopping the Server

Press `Ctrl+C` or send SIGTERM:

```bash theme={null}
kill -TERM <pid>
```

Programmatically:

```php theme={null}
use Lyger\Core\Engine;

Engine::stopServer();
```

## Development vs Production

### Development Mode

In development, you'll want to reload code changes:

```bash theme={null}
# Restart on file changes (requires external tool)
ls *.php | entr -r php rawr serve
```

<Note>
  During development, restart the server when you change code. Use file watchers to automate this.
</Note>

### Production Mode

In production, the Always-Alive server shines:

```bash theme={null}
# Run as a service (systemd example)
[Unit]
Description=Lyger Always-Alive Server
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/app
ExecStart=/usr/bin/php rawr serve
Restart=always

[Install]
WantedBy=multi-user.target
```

## Comparing to Other Solutions

| Solution               | Startup Cost  | Memory     | Complexity |
| ---------------------- | ------------- | ---------- | ---------- |
| Traditional PHP        | Every request | Low        | Simple     |
| PHP-FPM                | Per process   | Medium     | Medium     |
| RoadRunner             | Once          | High       | Complex    |
| Swoole                 | Once          | High       | Complex    |
| **Lyger Always-Alive** | **Once**      | **Medium** | **Simple** |

<Info>
  Lyger provides RoadRunner/Swoole-like performance with much simpler architecture thanks to Rust FFI.
</Info>

## Troubleshooting

### Server Won't Start

```php theme={null}
// Check FFI
if (!extension_loaded('ffi')) {
    echo "FFI extension not loaded\n";
}

// Check library
$engine = Engine::getInstance();
if ($engine->ffi === null) {
    echo "Rust library not found\n";
}
```

### Port Already in Use

```bash theme={null}
# Find process using port
lsof -i :8000

# Kill it
kill <pid>

# Or use a different port
php rawr serve --port=8080
```

### Memory Growth

Monitor memory usage:

```php theme={null}
echo "Memory: " . memory_get_usage(true) / 1024 / 1024 . " MB\n";
```

Set memory limit:

```ini theme={null}
; php.ini
memory_limit = 256M
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Architecture Overview" icon="sitemap" href="/core/architecture">
    Understand the full system architecture
  </Card>

  <Card title="Rust FFI Integration" icon="link" href="/core/rust-ffi">
    Learn how PHP talks to Rust
  </Card>

  <Card title="Zero-Copy Database" icon="database" href="/core/zero-copy-database">
    Optimize database operations
  </Card>

  <Card title="Routing" icon="route" href="/routing/basic-routing">
    Build your application routes
  </Card>
</CardGroup>
