Skip to content

/commands/php/php-l-check-syntax

php -l file.php: check a PHP file for syntax errors (lint)

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash)updated
tool
PHP
versions
PHP 5.x – 8.x
os
linux, macos, windows

php -l file.php parses a PHP file without running it and reports syntax errors. A valid file prints No syntax errors detected in file.php and exits with status 0; a broken one prints the parse error with its line number, Errors parsing file.php, and exits with status 255. -l is short for --syntax-check.

Valid and invalid file

The example uses PHP 8.4 compiled to WebAssembly (php-wasm, from the WordPress Playground project) and calls its CLI with the same arguments as php in a terminal:

bash
npm init -y >/dev/null
npm install --silent --no-audit --no-fund @php-wasm/node@3.1.56 @php-wasm/universal@3.1.56 >/dev/null 2>&1
cat > php.mjs <<'EOF'
// Minimal "php" command: copies *.php files into the virtual file system and runs the CLI
import fs from "node:fs";
import { PHP } from "@php-wasm/universal";
import { loadNodeRuntime } from "@php-wasm/node";
const php = new PHP(await loadNodeRuntime("8.4", { emscriptenOptions: { processId: 1 } }));
php.mkdir("/app");
for (const f of fs.readdirSync(".").filter((f) => f.endsWith(".php"))) php.writeFile(`/app/${f}`, fs.readFileSync(f));
php.chdir("/app");
const r = await php.cli(["php", ...process.argv.slice(2)]);
process.stdout.write(await r.stdoutText);
process.stdout.write(await r.stderrText);
process.exit(await r.exitCode);
EOF
printf '<?php\n$items = [1, 2, 3];\necho count($items), "\\n";\n' > good.php
printf '<?php\n$total = 0\nforeach ([1, 2] as $n) { $total += $n; }\n' > bad.php
node php.mjs -l good.php
node php.mjs -l bad.php

Output · exit 255

No syntax errors detected in good.php

Parse error: syntax error, unexpected token "foreach" in bad.php on line 3
Errors parsing bad.php
PHP Parse error:  syntax error, unexpected token "foreach" in bad.php on line 3

The missing semicolon on line 2 is reported on line 3, where the parser meets the unexpected foreach. The error appears twice: once on standard output (display_errors) and once on standard error with the PHP prefix (log_errors, which writes to stderr when no error_log file is set). With a different php.ini, you may see only one of them.

Lint a whole project

bash
# every .php file below src/, stop on the first failure
find src -name '*.php' -print0 | xargs -0 -n1 php -l

# PHP 8.3+: several files in one call
php -l src/a.php src/b.php

-n1 runs one php -l per file, which also works on PHP versions that accept only one file per call.

Options

CommandWhat it does
php -l file.phpSyntax check only; nothing is executed
php --syntax-check file.phpLong form of -l
php -l -d display_errors=stderr file.phpSend the message to stderr only
php -n -l file.phpIgnore php.ini (no extensions or ini settings loaded)
php -vShow which PHP version does the checking
php -r "code"Run code instead of checking it, see php -r

What it does not catch

php -l only finds what the parser rejects: missing semicolons or brackets, unclosed strings, invalid tokens, syntax that is too new for the interpreter. It does not run the file, so Call to undefined function, undefined variables, wrong argument types and runtime warnings such as headers already sent are not reported.

Confirm it worked

Check the exit status: php -l file.php; echo $? prints 0 for a valid file and 255 for a parse error, which is what CI jobs and Git pre-commit hooks test.

FAQ

Does php -l find undefined functions or wrong types?

No. It only parses the file. A call to a function that does not exist, a missing class or a type error appears only when the code runs, as Call to undefined function or a TypeError. Use a static analyser such as PHPStan or Psalm for those.

Can php -l check several files at once?

Since PHP 8.3 php -l accepts several file names. On older versions it checks only the first file, so loop with find ... -exec php -l {} \; or xargs -n1.

Which PHP version does php -l check against?

The version of the php binary you run. Code that uses newer syntax, such as enums or readonly properties, fails on an older interpreter; lint with the version you deploy.

Sources