Running PHP from the command line for scripts, queue workers, cron jobs, and interactive tools — a distinct execution context from web requests.
Explanation
PHP CLI runs without a web server, with no request timeout, unlimited execution time by default, and access to stdin/stdout/stderr via STDIN, STDOUT, and STDERR constants. Arguments are available via $argv/$argc. Key differences from web PHP: no $_GET/$_POST/$_SERVER HTTP keys, different error output (stderr), and no output buffering by default. Use PHP CLI for: queue workers (long-running), scheduled tasks (cron), database migrations, and build scripts. Symfony Console and Laravel Artisan provide command frameworks with argument parsing, progress bars, and table output. Always handle signals (pcntl_signal) in long-running workers for graceful shutdown. Long-running CLI processes must actively manage memory — objects and references persist across iterations unlike web requests.
Common Misconception
✗ PHP CLI scripts share the same configuration as the web server PHP process. PHP CLI uses a separate php.ini — memory_limit, max_execution_time, and extension settings often differ. CLI also does not reset state between iterations like web requests, which means memory usage and static variables persist in long-running scripts.
Why It Matters
PHP CLI enables running scripts, cron jobs, and build tools without a web server — it has different php.ini defaults, no request timeout, and access to stdin/stdout/stderr making it ideal for automation. Misunderstanding CLI behaviour leads to memory leaks, silent failures in CI, and unstable workers.
Common Mistakes
Not setting the correct shebang (#!/usr/bin/env php) for executable PHP scripts.
Using $_SERVER['REQUEST_URI'] or other HTTP superglobals in CLI scripts — they are not populated.
Not handling exit codes — a CLI script that exits with 0 on error confuses CI pipelines.
Ignoring that CLI has no memory or time limits by default — add them explicitly for long-running tasks.
Running long loops without freeing memory (e.g. ORM entities, large arrays) — leads to memory leaks and crashes.
Code Examples
✗ Vulnerable
// CLI script with no exit code handling — CI can't detect failure:
<?php
try {
runImport();
} catch (Exception $e) {
echo $e->getMessage(); // Prints error but exits with 0 — CI thinks success
}
// Fix: exit(1) on failure
✓ Fixed
#!/usr/bin/env php
<?php
// Guard: must not run via web
if (php_sapi_name() !== 'cli') {
fwrite(STDERR, "CLI only.\n"); exit(1);
}
// Parse arguments
\$opts = getopt('u:v', ['user:', 'verbose']);
\$userId = \$opts['u'] ?? \$opts['user'] ?? null;
\$verbose = isset(\$opts['v']) || isset(\$opts['verbose']);
if (!\$userId) {
fwrite(STDERR, "Usage: php script.php -u <user_id>\n"); exit(1);
}
fwrite(STDOUT, "Processing user \$userId\n");
// Exit codes: 0 = success, non-zero = error
exit(0);
Use STDIN/STDOUT/STDERR for IO, always return proper exit codes (0 success, non-zero error), explicitly set memory_limit and time limits for long-running scripts, and handle SIGTERM/SIGINT with pcntl_signal() to allow graceful shutdown