diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 000000000..58a423ea3 --- /dev/null +++ b/Caddyfile @@ -0,0 +1,8 @@ +{ + frankenphp { + worker { + file /app/public/index.php + watch + } + } +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..b69d5a5b0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM dunglas/frankenphp + +RUN install-php-extensions \ + pdo_mysql \ + gd \ + intl \ + zip \ + opcache diff --git a/packages/router/src/Commands/ServeCommand.php b/packages/router/src/Commands/ServeCommand.php index 192d0baef..6891b6ec7 100644 --- a/packages/router/src/Commands/ServeCommand.php +++ b/packages/router/src/Commands/ServeCommand.php @@ -5,19 +5,26 @@ namespace Tempest\Router\Commands; use Tempest\Console\ConsoleCommand; +use Tempest\Console\HasConsole; use Tempest\Intl\Number; use Tempest\Support\Str; +use function Tempest\Support\path; final readonly class ServeCommand { + use HasConsole; + #[ConsoleCommand( name: 'serve', description: 'Starts a PHP development server', )] - public function __invoke(string $host = '127.0.0.1', int $port = 8000, string $publicDir = 'public/'): void - { - $routerFile = __DIR__ . '/router.php'; - + public function __invoke( + string $host = 'localhost', + int $port = 8000, + int $httpsPort = 4433, + string $publicDir = './public/', + bool $worker = false, + ): void { if (Str\contains($host, ':')) { [$host, $overriddenPort] = explode(':', $host, limit: 2); @@ -26,6 +33,39 @@ public function __invoke(string $host = '127.0.0.1', int $port = 8000, string $p $port = Number\parse($overriddenPort, default: $port); } + if ($worker) { + $this->worker($host, $port, $httpsPort, $publicDir); + } else { + $this->serve($host, $port, $publicDir); + } + } + + private function worker(string $host, int $port, int $httpsPort, string $publicDir): void + { + $command = sprintf(<<<'SH' + docker run \ + -e FRANKENPHP_CONFIG="worker %s" \ + -v $PWD:/app \ + -p %d:80 -p %d:443 -p %d:443/udp \ + tempest + SH, + path($publicDir, 'index.php')->toString(), + $port, + $httpsPort, + $httpsPort + ); + + $this->info('Listening on http://' . $host . ':' . $port . ', https://' . $host . ':' . $httpsPort); + + $this->info($command); + + passthru($command); + } + + private function serve(string $host, int $port, string $publicDir): void + { + $routerFile = __DIR__ . '/router.php'; + passthru("php -S {$host}:{$port} -t {$publicDir} {$routerFile}"); } } diff --git a/packages/router/src/WorkerApplication.php b/packages/router/src/WorkerApplication.php new file mode 100644 index 000000000..1b00e43ad --- /dev/null +++ b/packages/router/src/WorkerApplication.php @@ -0,0 +1,66 @@ +get(WorkerApplication::class); + + // Application-specific setup + $logConfig = $container->get(LogConfig::class); + + if ($logConfig->debugLogPath === null && $logConfig->serverLogPath === null && $logConfig->channels === []) { + $logConfig->debugLogPath = path($container->get(Kernel::class)->root, '/log/debug.log')->toString(); + $logConfig->serverLogPath = env('SERVER_LOG'); + $logConfig->channels[] = new AppendLogChannel(path($root, '/log/tempest.log')->toString()); + } + + return $application; + } + + public function run(): void + { + $router = $this->container->get(Router::class); + + $psrRequest = $this->container->get(RequestFactory::class)->make(); + + $responseSender = $this->container->get(ResponseSender::class); + + $responseSender->send( + $router->dispatch($psrRequest), + ); + + $this->container->get(Session::class)->cleanup(); + + $this->container->invoke(FinishDeferredTasks::class); + } +} diff --git a/public/index.php b/public/index.php index 29a2f351f..8aecfffd8 100644 --- a/public/index.php +++ b/public/index.php @@ -1,14 +1,37 @@ run(); +]; + +if (function_exists('frankenphp_handle_request')) { + ignore_user_abort(true); + + $application = WorkerApplication::boot(__DIR__ . '/../', discoveryLocations: $discoveryLocations); + + $handler = static function () use ($application) { + $application->run(); + }; + + $maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); + + for ($nbRequests = 0; ! $maxRequests || $nbRequests < $maxRequests; ++$nbRequests) { + $keepRunning = frankenphp_handle_request($handler); + + gc_collect_cycles(); + + if (! $keepRunning) break; + } +} else { + HttpApplication::boot(__DIR__ . '/../', discoveryLocations: $discoveryLocations)->run(); + + exit(); + +} -exit();