celema / server
Celema development server commands
Requires
- php: ^8.5
- celema/console: ^0.5.3
Requires (Dev)
- carthage-software/mago: ~1.47.0
- celema/dev: ^6.0
- ernst/coverlyzer: ^0.3
- phpunit/phpunit: ^13.0
- vimeo/psalm: ~6.16.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Development server commands for PHP applications, built on celema/console, with request logging and live reload.
Installation
composer require --dev celema/server
It requires PHP 8.5. The FrankenPHP command additionally needs the frankenphp executable on PATH; FrankenPHP embeds its own PHP runtime, extensions, and configuration rather than using the PHP CLI that starts the command. Live reload needs no Node.js or other external tools.
Usage
The package provides two console commands:
Celema\Server\Server(server) runs the application with the PHP CLI's built-in server.Celema\Server\FrankenPhp(frankenphp) runs it with FrankenPHP in classic mode, not worker mode.
Register them with celema/console:
#!/usr/bin/env php <?php use Celema\Console\Commands; use Celema\Console\Runner; use Celema\Server\FrankenPhp; use Celema\Server\Server; require __DIR__ . '/vendor/autoload.php'; $docroot = __DIR__ . '/public'; $watch = ['src/**/*.{php,css,js}', 'views/**/*.php']; $commands = new Commands([ new Server($docroot, port: 1973, watch: $watch), new FrankenPhp($docroot, port: 1973, watch: $watch), ]); exit(new Runner($commands)->run());
Then start one of them, for example php run server --watch.
Constructor arguments
Both commands take the same arguments:
| Argument | Default | Description |
|---|---|---|
docroot |
required | The public directory. |
port |
1983 |
The default port. |
routePrefix |
'' |
A path prefix stripped from request paths, for applications mounted below a path. |
watch |
'**/*.{php,js,css}' |
Watch patterns for live reload, as a list or a comma-separated string. See Live reload. |
executable |
'php' or 'frankenphp' |
The executable to run the backend with. |
Options
| Option | Description |
|---|---|
-h, --host=<host> |
Host to bind to. Defaults to localhost. |
-p, --port=<port> |
Port to listen on. Defaults to the port argument. |
-f, --filter=<regex> |
Hides request log lines whose URL matches the regex, for example --filter='#^/assets/#'. |
-d, --debug |
server: sets XDEBUG_SESSION, so Xdebug debugs every request. frankenphp: enables verbose Caddy logs. |
-q, --quiet |
Reduces output: runs the PHP server with -q, hides FrankenPHP's startup banner, and hides live reload lines while pages are connected. |
-o, --open |
Opens the application in the default browser once it responds. |
-w, --watch[=<glob>] |
Enables live reload. Given patterns replace the watch argument; repeat the option or separate patterns with commas. |
--reload-port=<port> |
Port for the live reload endpoint. Defaults to ten times the port, or the next free port above. |
Routing
With the built-in PHP server, requests for existing files in the public directory are handled by the server directly: PHP files run, others are served as they are, and a directory with an index.html serves that file. Every other request goes to index.php in the public directory, the front controller. FrankenPHP routes requests with its own PHP server defaults. Both commands strip the routePrefix from request paths.
Live reload
With --watch, the command polls the watched files and tells open pages to reload when they change. Changed stylesheets are swapped in place without a full reload.
Patterns are relative to the working directory. ** matches across directories, * and ? within one path segment, and braces list alternatives, like *.{php,js}. A pattern starting with ! excludes matching files, for example ['src/**/*.php', '!src/cache/**']; a negated pattern that covers a whole directory, like !src/cache/** or !src/cache/, skips it while scanning. Directories named node_modules, vendor, or starting with a dot are skipped, unless a pattern's fixed path already points into them, like vendor/acme/lib/**/*.php. Symlinked directories are followed. At startup, the command prints how many files it watches, or warns when the patterns match none.
Pages opt in by including the live reload script. The command serves it on a separate port and passes its URL to the application as the CELEMA_LIVE_RELOAD environment variable. It is only set while --watch runs, so the snippet renders nothing in production. Add it to your layout, before </body>:
<?php if ($liveReload = getenv('CELEMA_LIVE_RELOAD')): ?> <script src="<?= htmlspecialchars($liveReload) ?>" defer></script> <?php endif ?>
The URL uses the --host address, with localhost for wildcard addresses, which suits a browser on the same machine. For other devices, virtual machines, or containers, bind a wildcard such as --host=0.0.0.0 and replace the URL's host with the host the page was requested under. With localhost, the script is served on both loopback addresses, so local host names resolving to either work too. Pages served over HTTPS cannot load the script, because it is only served over HTTP.
Open pages also reload once when they reconnect after the command restarts. If a watched file changes while no page is connected, the command says so, which usually means the snippet is missing.
Request log protocol
The served application reports each handled request to the parent command as a structured celema-request line on stderr, which the command renders as a request log line. With the built-in PHP server, the log shows the status of the PSR-7 response the front controller returns, or otherwise the status the script set.
Applications can additionally report handled exceptions through Celema\Server\Console, which is inert unless the CELEMA_CLI_SERVER environment variable set by the dev server is present. celema/core's error handler does this automatically when this package is installed.
Platform support
The commands are developed and tested on macOS and Linux. Windows has basic support, like finding executables with where, but is untested.
License
This project is licensed under the MIT license.