skip to content

In a PHP CLI script, what do $argv and $argc contain, and why can a function not see $argv directly?

level: juniorimportance: must knowfreq 50%

answer

  1. index 0 is the script name
  2. $argc counts the script name too
  3. every value is a string
  4. globals, not superglobals
  5. $_SERVER['argv'] works anywhere

basics

~20 s

$argv is an array of the command-line arguments with the script name at index 0, and $argc is count($argv). Both are ordinary global variables, not superglobals, so inside a function use $_SERVER['argv'] or pass the array in.

solid answer

~40 s

When you run `php import.php users.csv 100`, `$argv` is `['import.php', 'users.csv', '100']` and `$argc` is `3` — the count includes the script name. Every element is a `string`, so `'100'` needs converting and validating before use as a number. `$argv` and `$argc` are **global variables**, not superglobals: inside a function or method they are undefined unless you write `global $argv;`, so real code reads `$_SERVER['argv']` or, better, passes the array into the entry point. Arguments that start with `-` can be taken by the PHP binary itself; put `--` before them so they reach the script. To test whether a script runs from the command line, check `PHP_SAPI === 'cli'` or `php_sapi_name()`, not whether `$argv` is set.

code

php · 19 lines
php
<?php
declare(strict_types=1);

/** @param list<string> $argv */
function main(array $argv): int
{
    if (count($argv) < 2) {
        fwrite(STDERR, "usage: {$argv[0]} <file.csv>\n");
        return 2;
    }
    $file = $argv[1];
    // ... import $file
    return 0;
}

if (PHP_SAPI !== 'cli') {
    exit(1);
}
exit(main($argv));

go deeper

for a junior

Recall that $argv[0] is the script name, that $argc counts it, and that every argument is a string.

for a middle

Explain why functions cannot see $argv, the options $_SERVER['argv'] and dependency passing, and how -- keeps dash-prefixed arguments away from the PHP binary.

for a senior

Structure commands so the entry point receives argv as a parameter, validates every value, and refuses to run outside the CLI SAPI.

for a principal

Decide when hand-rolled argv handling stops being acceptable and a shared console library should own argument parsing across the codebase.

## What the CLI gives a script When a PHP script runs from a terminal, cron or a CI job, the words after the script name are its **arguments**. PHP's command-line SAPI exposes them in two variables: | Variable | Content for `php import.php users.csv 100` | |---|---| | `$argv` | `[0 => 'import.php', 1 => 'users.csv', 2 => '100']` | | `$argc` | `3` | | `$_SERVER['argv']` | the same array as `$argv` | | `$_SERVER['argc']` | the same number as `$argc` | Key points: - `$argv[0]` is always the name used to run the script, exactly as typed (`import.php`, `./bin/import`, or a full path). - `$argc` is the number of elements in `$argv`, so it is **one more** than the number of real arguments. - Every element is a **string**. `'100'` must be converted and validated — for example with `filter_var($argv[2], FILTER_VALIDATE_INT)` — before being used as a limit. With inline code (`php -r '...'`) or code piped on standard input, `$argv[0]` is the string `"Standard input code"` rather than a file name. ## Why functions do not see `$argv` `$argv` and `$argc` are registered as ordinary **global variables** in the script's top-level scope. PHP functions have their own local scope and do not see globals unless told to. Only a fixed set of **superglobals** (`$_SERVER`, `$_GET`, `$GLOBALS` and the like) are visible everywhere. So: ```php <?php function main(): int { var_dump($argv); // Warning: Undefined variable $argv, then NULL return 0; } exit(main()); ``` Three fixes, from worst to best: 1. `global $argv;` inside the function — works, but hides the dependency. 2. `$_SERVER['argv']` — a superglobal, so visible anywhere. 3. **Pass it in**: `exit(main($argv));` with `function main(array $argv): int`. The function becomes testable with any array, which is how command classes in frameworks receive input. ## When `$argv` is missing The variables exist only when `register_argc_argv` is enabled, which the CLI SAPI always does. Under a web SAPI they are normally absent. That is why the manual recommends testing the SAPI rather than the variable: - `PHP_SAPI === 'cli'` or `php_sapi_name() === 'cli'` answers "am I on the command line?"; - `isset($argv)` can be fooled by configuration and says nothing reliable. A command meant only for the terminal often starts with `if (PHP_SAPI !== 'cli') { exit(1); }` so it can never be triggered through a web request. ## Arguments that start with a dash The `php` binary parses its own options (`-r`, `-d`, `-n` and so on) before starting the script. An argument placed after the code or file that looks like a PHP option can be taken by the binary instead of the script. The argument separator `--` ends PHP's own option parsing: - `php -r 'var_dump($argv);' -h` prints PHP's usage text; - `php -r 'var_dump($argv);' -- -h` passes `-h` to the code. With a script file, arguments after the file name normally go to the script, but `--` is a harmless habit in wrapper scripts. ## Beyond positional arguments Positional access (`$argv[1]`, `$argv[2]`) is fine for one or two required values. As soon as a command grows flags such as `--dry-run` or `--limit=100`, parse them with `getopt()` or a console library instead of scanning `$argv` by hand — manual parsing breaks on `--limit 100` versus `--limit=100`, repeated flags, and flags placed in unexpected positions.

  • What is $argc for php import.php with no arguments?
    `1`. `$argc` counts the elements of `$argv`, and `$argv[0]` is always the script name, so a run with no arguments still has one element. A check for "no arguments" is therefore `$argc < 2`, not `$argc === 0`.
  • Why does the manual recommend php_sapi_name() over isset($argv) to detect the CLI?
    `$argv` exists only when `register_argc_argv` is on, which is a configuration detail rather than a statement about the SAPI. `php_sapi_name()` (or the `PHP_SAPI` constant) returns `'cli'` exactly when the command-line SAPI is running, so it is the reliable test.

saying these in an interview costs you the question

  • $argv[0] holds the first argument after the script name
  • $argc is the number of arguments excluding the script name
  • $argv is a superglobal, visible inside every function
  • Numeric arguments arrive in $argv as int
  • isset($argv) is the reliable way to detect the CLI