PHP compatibility and project lifecycle
The supplied sources do not state a supported PHP version matrix or support policy for the CLI. The Docker page documents a php:<version>-cli tag pattern and shows only 8.2 examples. Whether the 8.4 cli tag exists is unconfirmed and must be checked. PHP 8.5 compatibility is unknown.
Official sources checked 2026-10-22. Browser lessons on this site run PHP 8.5.
What this project is
PHP CLI is the command line Server API (SAPI) of PHP itself. It is not a framework, a CMS, an async library, an adapter or an alternative interpreter. It is the same PHP language core, started from a shell instead of from a web server. The official manual says the main focus of the CLI SAPI is developing shell applications with PHP, and that it differs in several ways from other SAPIs. It also notes that CLI and CGI are different SAPIs even though they share many behaviors.
This guide is based only on the two supplied sources: the PHP manual chapter on using PHP from the command line, and the Docker Hub page for the official php image. Lifecycle and support status are marked unknown, because neither source states a support policy or a supported-version matrix for the CLI.
Classification and what it is not
Keep these categories separate when you read about PHP runtimes:
- PHP core, with its SAPIs (CLI, CGI, FPM, Apache module), is the language runtime. The CLI is one entry point into it.
- Frameworks and CMS products, such as Drupal, are applications written in PHP. Framework components are libraries. A CMS is a product built on top of PHP. None of them replaces the interpreter, and this page does not cover them.
- Async libraries and framework adapters run on top of PHP. They are not standalone runtimes. Swoole and Open Swoole are distinct projects and neither is PHP CLI, so do not assume that documentation for one applies to the other.
- Historical alternative interpreters are not the CLI. HHVM targets Hack and should not be treated as a modern PHP-compatible runtime.
Architecture and execution model
The manual explains that the CLI SAPI is enabled by default with the configure option for CLI and can be turned off at build time. The name, location and existence of the CLI and CGI binaries depend on how PHP was installed. In a source build both are named php, and what make install places at the prefix bin path depends on the configure line. On Windows the CLI binary is php.exe. A php-win.exe variant exists if PHP is configured for it, and it produces no console.
To see which SAPI you have, run php -v. Inside a script you can check php_sapi_name() or the PHP_SAPI constant.
The default execution model is one process per invocation. The process starts, runs your script, writes to its output streams and ends with an exit status. Shell tools can then act on that status: zero conventionally means success and non-zero means failure. The manual's table of contents also lists options, usage, I/O streams, an interactive shell, a built-in web server and INI settings. The supplied excerpt does not reproduce those sections, so check the manual for exact details.
Classic request lifetime versus persistent workers
In a classic web request, PHP builds state, answers one request and discards that state. A CLI script has the same shape: everything is released when the process exits. A script that loops forever, such as a queue consumer, behaves like a persistent worker. Static properties, globals, singletons, caches, open files and database handles then survive from one unit of work to the next.
That causes state leakage: data from job A can influence job B. Typical leaks are a user identity left in a static property, an in-memory cache that grows without limit, or a half-finished transaction on a reused connection. Practical cleanup habits, which are general engineering guidance rather than something from the supplied sources:
- Keep per-job data in objects created for that job, and avoid mutable statics.
- Reset or close connections and buffers at the end of each job.
- Bound memory and restart the worker process after a fixed amount of work.
- Log and test two jobs run back to back in one process.
The example below is deliberately short-lived, so it does not have this problem.
Practical use cases
Use the CLI for maintenance scripts, data import and export, filters that read stdin and write stdout, scheduled jobs, and tooling run in CI. Exit codes let a shell, scheduler or pipeline decide what to do next. Writing diagnostics to the error stream keeps normal output clean for piping.
Installation approach
There are two routes the sources support:
- Build from source, where the CLI is on by default. Where the binary lands depends on your configure line, as described above.
- Use the official Docker image. The Docker Hub page says every variant of the php image contains the PHP CLI at /usr/local/bin/php. The cli variant is designed both as a throwaway container, where you mount your code and start it, and as a base for your own images. The page is maintained by the Docker Community and is not the PHP project's own distribution page.
The page's examples use the 2.x-style pattern of a version-specific cli tag and show only an 8.2 cli tag. It documents the general form of php, a version, then cli. This guide's learning track is PHP 8.4, so the example uses the 8.4 cli tag by following that pattern. Whether that exact tag exists, and which patch version it carries, is not confirmed by the packet, so the integration check must verify it.
For extra extensions, the image provides the helper scripts docker-php-ext-configure, docker-php-ext-install and docker-php-ext-enable. It also supports PECL installs, and recommends an explicit version number in each pecl install. Run php -m to see what is already compiled in. The image ships php.ini-development and php.ini-production, and recommends the production configuration for production images.
Strengths
- It is the same language and extensions you already know, with no extra framework.
- It has simple process semantics: arguments in, stdin and stdout, an exit status out.
- A disposable container makes it easy to try scripts without installing PHP on the host.
Limitations
- Stated version support, deprecations and performance characteristics are unknown from the supplied sources, and this guide makes no speed claims.
- The CLI is not a web server. The manual lists a built-in web server section, but its details are not in the packet. Treat it as a development convenience, and bind any development port to 127.0.0.1.
- Long-running scripts need explicit memory and state management.
- Docker Hub's page is image documentation, not a PHP language specification.
Deployment notes
For scheduled or batch work, build a small image from the cli variant, copy in your script and run it as a disposable container. Use the production ini file where appropriate. Do not use the FPM variant for CLI jobs. The Docker page warns that FastCGI is inherently trusting and should not be exposed beyond a private container network, so never publish an FPM port with the publish flag. This guide's example publishes no ports at all.
Example summary
The example builds a small image, reads text from stdin, prints line and word counts, and returns distinct exit codes. Because it needs Docker and a network to pull the base image, it is not a browser exercise.
Practical examples
These examples have not been run in a live environment yet. Commands and versions follow the official documentation linked on this page; check them before relying on them, and report a problem if one does not work.
PHP 8.4 CLI stdin filter with exit codes in Docker
A small CLI script that reads stdin, prints line and word counts, optionally uppercases the text, and returns exit code 0 on success, 2 for empty stdin and 64 for an unknown option. It is built into an image using the php:<version>-cli pattern from the Docker Hub page. The 8.4 tag is assumed from that pattern and must be verified.
Dockerfile
FROM php:8.4-cli
WORKDIR /usr/src/myapp
COPY bin/ ./bin/
ENTRYPOINT ["php", "./bin/wordcount.php"]
bin/wordcount.php
declare(strict_types=1);
if (PHP_SAPI !== 'cli') {
echo "This script must run from the command line.\n";
exit(1);
}
$upper = false;
foreach (array_slice($argv, 1) as $arg) {
if ($arg === '--upper') {
$upper = true;
continue;
}
fwrite(STDERR, "Unknown option: {$arg}\n");
exit(64);
}
$input = stream_get_contents(STDIN);
if ($input === false || trim($input) === '') {
fwrite(STDERR, "No input received on stdin.\n");
exit(2);
}
$text = trim($input);
$lines = count(preg_split('/\R/', $text));
$words = str_word_count($text);
echo 'SAPI: ' . PHP_SAPI . "\n";
echo 'PHP version: ' . PHP_VERSION . "\n";
echo "Lines: {$lines}\n";
echo "Words: {$words}\n";
if ($upper) {
echo strtoupper($text) . "\n";
}
exit(0);
Install and run
docker build -t learnphp-cli-demo .
docker run --rm --entrypoint php learnphp-cli-demo -v
printf 'hello php\nsecond line\n' | docker run -i --rm learnphp-cli-demo
printf 'hello php\n' | docker run -i --rm learnphp-cli-demo --upper
docker run -i --rm learnphp-cli-demo < /dev/null; echo $?
printf 'x\n' | docker run -i --rm learnphp-cli-demo --bogus; echo $?Expected behavior
The version command reports the PHP CLI build. The first pipe prints SAPI: cli, the PHP version, Lines: 2 and Words: 4. The --upper run also prints HELLO PHP. Empty stdin prints an error on stderr and the shell shows 2. An unknown option shows 64.
Requires Docker, network access to pull the base image, and a php:8.4-cli tag that must be verified because the source packet shows only 8.2 examples. It is not a browser exercise. It is a short-lived process, so it does not demonstrate persistent worker state. The exit codes 2 and 64 are this script's own convention, not PHP requirements.