php runtime and server environment
SAPIs: CLI, FPM, Apache Module
SAPI means Server API. It is the interface PHP uses to run in a particular environment.
The same PHP code can run under different SAPIs: cli for terminal commands, fpm-fcgi for PHP-FPM behind a web server, apache2handler for Apache's PHP module, and cli-server for PHP's built-in development server.
Knowing the SAPI matters because each one can load different configuration, run as a different user, expose different inputs, and write errors to different places.
Checking The Current SAPI
PHP_SAPI tells you how the current process is running PHP.
<?php
declare(strict_types=1);
echo 'Current SAPI: ' . PHP_SAPI . PHP_EOL;
if (PHP_SAPI === 'cli') {
echo 'This code is running from the command line.' . PHP_EOL;
}
// Example CLI output:
// Current SAPI: cli
// This code is running from the command line.
This is a diagnostic. It helps you prove the runtime before debugging a configuration issue.
CLI
CLI is used for terminal commands:
php script.php
php -v
composer test
php worker.php
CLI has:
$argvand$argcSTDIN,STDOUT, andSTDERR- shell environment variables
- exit codes
- no browser request by default
CLI is used by Composer, tests, migrations, cron jobs, queue workers, import scripts, and framework console commands.
PHP-FPM
PHP-FPM is a process manager for web requests. Nginx, Apache, Caddy, or another server receives the HTTP request and passes PHP work to FPM.
Typical flow:
browser -> Nginx/Caddy/Apache -> PHP-FPM -> public/index.php
FPM has:
- pools of worker processes
- pool users and permissions
- FPM-specific configuration
- FastCGI parameters from the web server
- logs and slow logs
FPM often reports as:
fpm-fcgi
FPM is common in production because it separates the web server from the PHP process manager.
Apache Module
The Apache module embeds PHP inside Apache. Its SAPI often reports as:
apache2handler
This setup can be simple for local stacks, but many modern production deployments prefer PHP-FPM because it separates concerns and works well with Nginx, Apache, Caddy, and process pools.
If a project uses an Apache module locally but PHP-FPM in production, local behavior may differ.
Built-In Server
PHP's built-in development server reports as:
cli-server
Start it with:
php -S 127.0.0.1:8000 -t public
It is useful for local development, but it is not PHP-FPM and not a production server.
Different SAPIs Can Load Different Configuration
A common debugging trap:
CLI memory_limit: -1
FPM memory_limit: 128M
Or:
CLI has ext-intl loaded
FPM does not
Check CLI:
php --ini
php -m
Check web runtime with a temporary diagnostic:
<?php
declare(strict_types=1);
header('Content-Type: text/plain');
echo 'PHP: ' . PHP_VERSION . PHP_EOL;
echo 'SAPI: ' . PHP_SAPI . PHP_EOL;
echo 'Loaded ini: ' . (php_ini_loaded_file() ?: 'none') . PHP_EOL;
echo 'intl: ' . (extension_loaded('intl') ? 'loaded' : 'missing') . PHP_EOL;
Remove or protect diagnostics before production.
SAPI-Specific Inputs
Do not write code that assumes every SAPI has the same inputs.
<?php
declare(strict_types=1);
if (PHP_SAPI === 'cli') {
echo 'Argument count: ' . $_SERVER['argc'] . PHP_EOL;
} else {
echo 'Request method: ' . ($_SERVER['REQUEST_METHOD'] ?? 'unknown') . PHP_EOL;
}
CLI scripts should use CLI inputs. Web controllers should use HTTP inputs. Shared business logic should usually receive normal parameters instead of reading directly from either boundary.
What You Should Be Able To Do
After this lesson, you should be able to explain what a SAPI is, recognise cli, cli-server, fpm-fcgi, and apache2handler, and debug cases where CLI and web PHP behave differently.
For junior PHP work, this matters because many setup issues are SAPI issues. The right question is often not "why is PHP broken?" but "which PHP interface is running this code?"
Practice
Practice: Compare CLI And Web SAPIs
Create a small diagnostic that can be run from both CLI and a browser.
Task
Build a PHP file that prints:
PHP_VERSIONPHP_SAPI- loaded
php.ini - whether
intlis loaded - CLI argument count when running in CLI
- request method when running through HTTP
Use strict types. Keep example CLI and web output inside the PHP code block as comments.
Afterward, add a short note explaining why CLI and web results can differ.
Show solution
This diagnostic branches only at the runtime boundary and keeps the shared checks the same.
<?php
declare(strict_types=1);
if (PHP_SAPI !== 'cli') {
header('Content-Type: text/plain');
}
echo 'PHP: ' . PHP_VERSION . PHP_EOL;
echo 'SAPI: ' . PHP_SAPI . PHP_EOL;
echo 'Loaded ini: ' . (php_ini_loaded_file() ?: 'none') . PHP_EOL;
echo 'intl: ' . (extension_loaded('intl') ? 'loaded' : 'missing') . PHP_EOL;
if (PHP_SAPI === 'cli') {
echo 'Argument count: ' . ($_SERVER['argc'] ?? 0) . PHP_EOL;
} else {
echo 'Request method: ' . ($_SERVER['REQUEST_METHOD'] ?? 'unknown') . PHP_EOL;
}
// Example CLI output:
// PHP: 8.5.6
// SAPI: cli
// Loaded ini: /etc/php.ini
// intl: loaded
// Argument count: 1
//
// Example web output:
// PHP: 8.5.6
// SAPI: fpm-fcgi
// Loaded ini: /etc/php.ini
// intl: loaded
// Request method: GET
CLI and web results can differ because they may use different SAPIs, loaded configuration files, enabled extensions, process users, environment variables, and working directories.