diff --git a/NEWS b/NEWS index 328acc0a524..29305565784 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.0alpha4 +- CLI: + . Implement GH-10024 (support linting multiple files at once using php -l). + (nielsdos) 06 Jul 2023, PHP 8.3.0alpha3 diff --git a/UPGRADING b/UPGRADING index b709ccacd51..53dfe04237c 100644 --- a/UPGRADING +++ b/UPGRADING @@ -87,6 +87,9 @@ PHP 8.3 UPGRADE NOTES in a parent class or implemented interface. RFC: https://wiki.php.net/rfc/marking_overriden_methods +- CLI + . It is now possible to lint multiple files. + - Posix . posix_getrlimit() now takes an optional $res parameter to allow fetching a single resource limit. diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c index bf0f233118b..156599f6f9d 100644 --- a/sapi/cgi/cgi_main.c +++ b/sapi/cgi/cgi_main.c @@ -2372,6 +2372,7 @@ parent_loop_end: } } +do_repeat: if (script_file) { /* override path_translated if -f on command line */ if (SG(request_info).path_translated) efree(SG(request_info).path_translated); @@ -2512,7 +2513,6 @@ parent_loop_end: PG(during_request_startup) = 0; if (php_lint_script(&file_handle) == SUCCESS) { zend_printf("No syntax errors detected in %s\n", ZSTR_VAL(file_handle.filename)); - exit_status = 0; } else { zend_printf("Errors parsing %s\n", ZSTR_VAL(file_handle.filename)); exit_status = -1; @@ -2581,6 +2581,11 @@ fastcgi_request_done: } } } + if (behavior == PHP_MODE_LINT && argc - 1 > php_optind) { + php_optind++; + script_file = NULL; + goto do_repeat; + } break; } diff --git a/sapi/cgi/tests/012.phpt b/sapi/cgi/tests/012.phpt new file mode 100644 index 00000000000..7f981b7da8d --- /dev/null +++ b/sapi/cgi/tests/012.phpt @@ -0,0 +1,117 @@ +--TEST-- +multiple files syntax check +--SKIPIF-- + +--INI-- +display_errors=stdout +--FILE-- +/dev/null"; + } + exec($cmd, $output, $exit_code); + print_r($output); + // Normalize Windows vs Linux exit codes. On Windows exit code -1 is actually -1 instead of 255. + if ($exit_code < 0) { + $exit_code += 256; + } + var_dump($exit_code); +} + +$php = get_cgi_path(); +reset_env_vars(); + +$filename_good = __DIR__."/012_good.test.php"; +$filename_good_escaped = escapeshellarg($filename_good); +$filename_bad = __DIR__."/012_bad.test.php"; +$filename_bad_escaped = escapeshellarg($filename_bad); + +$code = ' + +'; + +file_put_contents($filename_bad, $code); + +run_and_output("$php -n -l $filename_good_escaped $filename_good_escaped"); +run_and_output("$php -n -l $filename_good_escaped some.unknown $filename_good_escaped"); +run_and_output("$php -n -l $filename_good_escaped $filename_bad_escaped $filename_good_escaped"); +run_and_output("$php -n -l $filename_bad_escaped $filename_bad_escaped"); +run_and_output("$php -n -l $filename_bad_escaped some.unknown $filename_bad_escaped"); +run_and_output("$php -n -l $filename_bad_escaped $filename_bad_escaped some.unknown"); + +echo "Done\n"; +?> +--CLEAN-- + +--EXPECTF-- +Array +( + [0] => No syntax errors detected in %s012_good.test.php + [1] => No syntax errors detected in %s012_good.test.php +) +int(0) +Array +( + [0] => No syntax errors detected in %s012_good.test.php + [1] => No input file specified. +) +int(255) +Array +( + [0] => No syntax errors detected in %s012_good.test.php + [1] =>
+ [2] => Parse error: syntax error, unexpected token "private", expecting "{" in %s012_bad.test.php on line 5
+ [3] => Errors parsing %s012_bad.test.php + [4] => No syntax errors detected in %s012_good.test.php +) +int(255) +Array +( + [0] =>
+ [1] => Parse error: syntax error, unexpected token "private", expecting "{" in %s012_bad.test.php on line 5
+ [2] => Errors parsing %s012_bad.test.php + [3] =>
+ [4] => Parse error: syntax error, unexpected token "private", expecting "{" in %s012_bad.test.php on line 5
+ [5] => Errors parsing %s012_bad.test.php +) +int(255) +Array +( + [0] =>
+ [1] => Parse error: syntax error, unexpected token "private", expecting "{" in %s012_bad.test.php on line 5
+ [2] => Errors parsing %s012_bad.test.php + [3] => No input file specified. +) +int(255) +Array +( + [0] =>
+ [1] => Parse error: syntax error, unexpected token "private", expecting "{" in %s012_bad.test.php on line 5
+ [2] => Errors parsing %s012_bad.test.php + [3] =>
+ [4] => Parse error: syntax error, unexpected token "private", expecting "{" in %s012_bad.test.php on line 5
+ [5] => Errors parsing %s012_bad.test.php + [6] => No input file specified. +) +int(255) +Done diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index c3598003058..546c4ba997d 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -734,6 +734,10 @@ static int do_cli(int argc, char **argv) /* {{{ */ break; } behavior=PHP_MODE_LINT; + /* We want to set the error exit status if at least one lint failed. + * If all were successful we set the exit status to 0. + * We already set EG(exit_status) here such that only failures set the exit status. */ + EG(exit_status) = 0; break; case 'q': /* do not generate HTTP headers */ @@ -962,7 +966,6 @@ do_repeat: case PHP_MODE_LINT: if (php_lint_script(&file_handle) == SUCCESS) { zend_printf("No syntax errors detected in %s\n", php_self); - EG(exit_status) = 0; } else { zend_printf("Errors parsing %s\n", php_self); EG(exit_status) = 255; @@ -1128,9 +1131,15 @@ out: } if (request_started) { php_request_shutdown((void *) 0); + request_started = 0; } if (translated_path) { free(translated_path); + translated_path = NULL; + } + if (behavior == PHP_MODE_LINT && argc > php_optind && strcmp(argv[php_optind],"--")) { + script_file = NULL; + goto do_repeat; } /* Don't repeat fork()ed processes. */ if (--num_repeats && pid == getpid()) { diff --git a/sapi/cli/tests/024.phpt b/sapi/cli/tests/024.phpt new file mode 100644 index 00000000000..bfd24679fd3 --- /dev/null +++ b/sapi/cli/tests/024.phpt @@ -0,0 +1,110 @@ +--TEST-- +multiple files syntax check +--SKIPIF-- + +--FILE-- + +'; + +file_put_contents($filename_bad, $code); + +run_and_output("$php -n -l $filename_good_escaped $filename_good_escaped 2>&1"); +run_and_output("$php -n -l $filename_good_escaped some.unknown $filename_good_escaped 2>&1"); +run_and_output("$php -n -l $filename_good_escaped $filename_bad_escaped $filename_good_escaped 2>&1"); +run_and_output("$php -n -l $filename_bad_escaped $filename_bad_escaped 2>&1"); +run_and_output("$php -n -l $filename_bad_escaped some.unknown $filename_bad_escaped 2>&1"); +run_and_output("$php -n -l $filename_bad_escaped $filename_bad_escaped some.unknown 2>&1"); + +echo "Done\n"; +?> +--CLEAN-- + +--EXPECTF-- +Array +( + [0] => No syntax errors detected in %s024_good.test.php + [1] => No syntax errors detected in %s024_good.test.php +) +int(0) +Array +( + [0] => No syntax errors detected in %s024_good.test.php + [1] => Could not open input file: some.unknown + [2] => No syntax errors detected in %s024_good.test.php +) +int(1) +Array +( + [0] => No syntax errors detected in %s024_good.test.php + [1] => + [2] => Parse error: syntax error, unexpected token "private", expecting "{" in %s on line %d + [3] => Errors parsing %s024_bad.test.php + [4] => No syntax errors detected in %s024_good.test.php +) +int(255) +Array +( + [0] => + [1] => Parse error: syntax error, unexpected token "private", expecting "{" in %s on line %d + [2] => Errors parsing %s024_bad.test.php + [3] => + [4] => Parse error: syntax error, unexpected token "private", expecting "{" in %s on line %d + [5] => Errors parsing %s024_bad.test.php +) +int(255) +Array +( + [0] => + [1] => Parse error: syntax error, unexpected token "private", expecting "{" in %s on line %d + [2] => Errors parsing %s024_bad.test.php + [3] => Could not open input file: some.unknown + [4] => + [5] => Parse error: syntax error, unexpected token "private", expecting "{" in %s on line %d + [6] => Errors parsing %s024_bad.test.php +) +int(255) +Array +( + [0] => + [1] => Parse error: syntax error, unexpected token "private", expecting "{" in %s on line %d + [2] => Errors parsing %s024_bad.test.php + [3] => + [4] => Parse error: syntax error, unexpected token "private", expecting "{" in %s on line %d + [5] => Errors parsing %s024_bad.test.php + [6] => Could not open input file: some.unknown +) +int(1) +Done