Commit 2b6075bddf9 for php.net
commit 2b6075bddf9b5963b79dd79192fd51bbaa45e5f5
Author: NickSdot <32384907+NickSdot@users.noreply.github.com>
Date: Mon Aug 3 23:17:27 2026 +0700
run-tests: run test subprocesses without a shell where possible (#22957)
diff --git a/NEWS b/NEWS
index 1af715a1bc7..1f3011b8e61 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,10 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.6.0beta1
+- Core:
+ . Changed run-tests.php to run test subprocesses without a shell where
+ possible. (NickSdot)
+
- GMP:
. Added optional $definitely_prime output parameter to gmp_prevprime().
(Weilin Du)
diff --git a/run-tests.php b/run-tests.php
index 998e7e24c33..703dd643ca7 100755
--- a/run-tests.php
+++ b/run-tests.php
@@ -148,7 +148,7 @@ function main(): void
$exts_skipped, $exts_tested, $exts_to_test, $failed_tests_file,
$ignored_by_ext, $ini_overwrites, $colorize,
$log_format, $no_clean, $no_file_cache,
- $pass_options, $php, $php_cgi, $preload,
+ $pass_option_args, $php, $php_cgi, $preload,
$result_tests_file, $slow_min_ms, $start_time,
$temp_source, $temp_target, $test_cnt,
$test_files, $test_idx, $test_results, $testfile,
@@ -306,7 +306,10 @@ function main(): void
'date.timezone=UTC',
];
- $no_file_cache = '-d opcache.file_cache= -d opcache.file_cache_only=0';
+ $no_file_cache = [
+ '-d', 'opcache.file_cache=',
+ '-d', 'opcache.file_cache_only=0',
+ ];
// Determine the tests to be run.
@@ -327,7 +330,7 @@ function main(): void
$result_tests_file = false;
$failed_tests_file = false;
$pass_option_n = false;
- $pass_options = '';
+ $pass_option_args = [];
$output_file = INIT_DIR . '/php_test_results_' . date('Ymd_Hi') . '.txt';
@@ -471,12 +474,12 @@ function main(): void
break;
case 'n':
if (!$pass_option_n) {
- $pass_options .= ' -n';
+ $pass_option_args[] = '-n';
}
$pass_option_n = true;
break;
case 'e':
- $pass_options .= ' -e';
+ $pass_option_args[] = '-e';
break;
case '--preload':
$preload = true;
@@ -681,9 +684,12 @@ function main(): void
if ($conf_passed !== null) {
if (IS_WINDOWS) {
- $pass_options .= " -c " . escapeshellarg($conf_passed);
+ $pass_option_args[] = '-c';
+ $pass_option_args[] = $conf_passed;
} else {
- $pass_options .= " -c '" . realpath($conf_passed) . "'";
+ $configuration_file = realpath($conf_passed);
+ $pass_option_args[] = '-c';
+ $pass_option_args[] = (string) $configuration_file;
}
}
@@ -816,8 +822,10 @@ function verify_config(string $php): void
*/
function write_information(array $user_tests, $phpdbg): void
{
- global $php, $php_cgi, $php_info, $ini_overwrites, $pass_options, $exts_to_test, $valgrind, $no_file_cache;
+ global $php, $php_cgi, $php_info, $ini_overwrites, $pass_option_args, $exts_to_test, $valgrind, $no_file_cache;
$php_escaped = escapeshellarg($php);
+ $escaped_pass_options = escaped_shell_string_from($pass_option_args);
+ $escaped_no_file_cache = escaped_shell_string_from($no_file_cache);
// Get info from php
$info_file = __DIR__ . '/run-test-info.php';
@@ -833,12 +841,12 @@ function write_information(array $user_tests, $phpdbg): void
$info_params = [];
settings2array($ini_overwrites, $info_params);
$info_params = settings2params($info_params);
- $php_info = shell_exec("$php_escaped $pass_options $info_params $no_file_cache \"$info_file\"");
+ $php_info = shell_exec("$php_escaped $escaped_pass_options $info_params $escaped_no_file_cache \"$info_file\"");
define('TESTED_PHP_VERSION', shell_exec("$php_escaped -n -r \"echo PHP_VERSION;\""));
if ($php_cgi && $php != $php_cgi) {
$php_cgi_escaped = escapeshellarg($php_cgi);
- $php_info_cgi = shell_exec("$php_cgi_escaped $pass_options $info_params $no_file_cache -q \"$info_file\"");
+ $php_info_cgi = shell_exec("$php_cgi_escaped $escaped_pass_options $info_params $escaped_no_file_cache -q \"$info_file\"");
$php_info_sep = "\n---------------------------------------------------------------------";
$php_cgi_info = "$php_info_sep\nPHP : $php_cgi $php_info_cgi$php_info_sep";
} else {
@@ -847,7 +855,7 @@ function write_information(array $user_tests, $phpdbg): void
if ($phpdbg) {
$phpdbg_escaped = escapeshellarg($phpdbg);
- $phpdbg_info = shell_exec("$phpdbg_escaped $pass_options $info_params $no_file_cache -qrr \"$info_file\"");
+ $phpdbg_info = shell_exec("$phpdbg_escaped $escaped_pass_options $info_params $escaped_no_file_cache -qrr \"$info_file\"");
$php_info_sep = "\n---------------------------------------------------------------------";
$phpdbg_info = "$php_info_sep\nPHP : $phpdbg $phpdbg_info$php_info_sep";
} else {
@@ -873,7 +881,7 @@ function write_information(array $user_tests, $phpdbg): void
}
echo implode(',', $exts);
PHP);
- $extensionsNames = explode(',', shell_exec("$php_escaped $pass_options $info_params $no_file_cache \"$info_file\""));
+ $extensionsNames = explode(',', shell_exec("$php_escaped $escaped_pass_options $info_params $escaped_no_file_cache \"$info_file\""));
$exts_to_test = array_unique(remap_loaded_extensions_names($extensionsNames));
// check for extensions that need special handling and regenerate
$info_params_ex = [
@@ -1173,20 +1181,22 @@ function error_report(string $testname, string $logname, string $tested): void
* @return false|string
*/
function system_with_timeout(
- string $commandline,
+ array|string $command,
?array $env = null,
?string $stdin = null,
+ ?string $stdinFile = null,
bool $captureStdIn = true,
bool $captureStdOut = true,
- bool $captureStdErr = true
+ bool $captureStdErr = true,
+ bool $mergeStdErr = false
) {
global $valgrind;
// when proc_open cmd is passed as a string (without bypass_shell=true option) the cmd goes thru shell
// and on Windows quotes are discarded, this is a fix to honor the quotes and allow values containing
// spaces like '"C:\Program Files\PHP\php.exe"' to be passed as 1 argument correctly
- if (IS_WINDOWS) {
- $commandline = 'start "" /b /wait ' . $commandline . ' & exit';
+ if (IS_WINDOWS && is_string($command)) {
+ $command = 'start "" /b /wait ' . $command . ' & exit';
}
$data = '';
@@ -1197,22 +1207,26 @@ function system_with_timeout(
}
$descriptorspec = [];
- if ($captureStdIn) {
+ if ($stdinFile !== null) {
+ $descriptorspec[0] = ['file', $stdinFile, 'r'];
+ } elseif ($captureStdIn) {
$descriptorspec[0] = ['pipe', 'r'];
}
if ($captureStdOut) {
$descriptorspec[1] = ['pipe', 'w'];
}
if ($captureStdErr) {
- $descriptorspec[2] = ['pipe', 'w'];
+ $descriptorspec[2] = $mergeStdErr
+ ? ['redirect', 1]
+ : ['pipe', 'w'];
}
- $proc = proc_open($commandline, $descriptorspec, $pipes, TEST_PHP_SRCDIR, $bin_env, ['suppress_errors' => true]);
+ $proc = proc_open($command, $descriptorspec, $pipes, TEST_PHP_SRCDIR, $bin_env, ['suppress_errors' => true]);
if (!$proc) {
return false;
}
- if ($captureStdIn) {
+ if (isset($pipes[0])) {
if (!is_null($stdin)) {
fwrite($pipes[0], $stdin);
}
@@ -1277,6 +1291,36 @@ function system_with_timeout(
return $data;
}
+function create_test_command(
+ string $php,
+ array $optionArgs,
+ array $iniSettings,
+ string $testFile,
+ int $numRepeats
+): array {
+ $command = [$php, ...$optionArgs];
+ if ($numRepeats > 1) {
+ $command[] = '--repeat';
+ $command[] = (string) $numRepeats;
+ }
+
+ return [
+ ...$command,
+ ...settings2arguments($iniSettings),
+ '-f',
+ $testFile,
+ ];
+}
+
+function create_shell_invocation(string $command): array|string
+{
+ if (IS_WINDOWS) {
+ return $command;
+ }
+
+ return ['/bin/sh', '-c', "exec $command"];
+}
+
function run_all_tests(array $test_files, array $env, ?string $redir_tested = null): void
{
global $test_results, $failed_tests_file, $result_tests_file, $php, $test_idx, $file_cache, $shuffle;
@@ -1837,7 +1881,7 @@ function skip_test(string $tested, string $tested_file, string $shortname, strin
function run_test(string $php, $file, array $env): string
{
global $log_format, $ini_overwrites, $PHP_FAILED_TESTS;
- global $pass_options, $DETAILED, $IN_REDIRECT, $test_cnt, $test_idx;
+ global $pass_option_args, $DETAILED, $IN_REDIRECT, $test_cnt, $test_idx;
global $valgrind, $temp_source, $temp_target, $cfg, $environment;
global $no_clean;
global $SHOW_ONLY_GROUPS;
@@ -1859,8 +1903,8 @@ function run_test(string $php, $file, array $env): string
$skipCache = new SkipCache($enableSkipCache, $cfg['keep']['skip']);
}
- $php = escapeshellarg($php);
- $orig_php = $php;
+ $php_path = $php;
+ $sapi_option_args = [];
$retried = false;
retry:
@@ -1918,19 +1962,14 @@ function run_test(string $php, $file, array $env): string
$captureStdOut = true;
$captureStdErr = true;
}
- if ($captureStdOut && $captureStdErr) {
- $cmdRedirect = ' 2>&1';
- } else {
- $cmdRedirect = '';
- }
-
/* For GET/POST/PUT tests, check if cgi sapi is available and if it is, use it. */
$uses_cgi = false;
if ($test->isCGI()) {
if (!$php_cgi) {
return skip_test($tested, $tested_file, $shortname, 'CGI not available');
}
- $php = escapeshellarg($php_cgi) . ' -C ';
+ $php_path = $php_cgi;
+ $sapi_option_args[] = '-C';
$uses_cgi = true;
if ($num_repeats > 1) {
return skip_test($tested, $tested_file, $shortname, 'CGI does not support --repeat');
@@ -1938,14 +1977,15 @@ function run_test(string $php, $file, array $env): string
}
/* For phpdbg tests, check if phpdbg sapi is available and if it is, use it. */
- $extra_options = '';
+ $extra_option_args = [];
if ($test->hasSection('PHPDBG')) {
if (isset($phpdbg)) {
- $php = escapeshellarg($phpdbg) . ' -qIb';
+ $php_path = $phpdbg;
+ $sapi_option_args = ['-qIb'];
// Additional phpdbg command line options for sections that need to
// be run straight away. For example, EXTENSIONS, SKIPIF, CLEAN.
- $extra_options = '-rr';
+ $extra_option_args[] = '-rr';
} else {
return skip_test($tested, $tested_file, $shortname, 'phpdbg not available');
}
@@ -2070,8 +2110,16 @@ function run_test(string $php, $file, array $env): string
if ($extensions != []) {
$ext_params = [];
settings2array($ini_overwrites, $ext_params);
- $ext_params = settings2params($ext_params);
- [$ext_dir, $loaded] = $skipCache->getExtensions("$orig_php $pass_options $extra_options $ext_params $no_file_cache");
+ $ext_params = settings2arguments($ext_params);
+
+ [$ext_dir, $loaded] = $skipCache->getExtensions([
+ $php,
+ ...$pass_option_args,
+ ...$extra_option_args,
+ ...$ext_params,
+ ...$no_file_cache,
+ ]);
+
$ext_prefix = IS_WINDOWS ? "php_" : "";
$missing = [];
foreach ($extensions as $req_ext) {
@@ -2099,8 +2147,7 @@ function run_test(string $php, $file, array $env): string
//$ini_overwrites[] = 'setting=value';
settings2array($ini_overwrites, $ini_settings);
- $orig_ini_settings = settings2params($ini_settings);
-
+ $orig_ini_settings_args = settings2arguments($ini_settings);
if ($file_cache !== null) {
$ini_settings['opcache.file_cache'] = get_file_cache_dir();
// Make sure warnings still show up on the second run.
@@ -2146,9 +2193,11 @@ function run_test(string $php, $file, array $env): string
}
}
+ $test_ini_settings = $ini_settings;
$ini_settings = settings2params($ini_settings);
- $env['TEST_PHP_EXTRA_ARGS'] = $pass_options . ' ' . $ini_settings;
+ $escaped_pass_options = escaped_shell_string_from($pass_option_args);
+ $env['TEST_PHP_EXTRA_ARGS'] = $escaped_pass_options . ' ' . $ini_settings;
// Check if test should be skipped.
$info = '';
@@ -2156,19 +2205,41 @@ function run_test(string $php, $file, array $env): string
if ($test->sectionNotEmpty('SKIPIF')) {
show_file_block('skip', $test->getSection('SKIPIF'));
- $extra = !IS_WINDOWS ?
- "unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;" : "";
+ $skip_env = $env;
+ if (!IS_WINDOWS) {
+ unset(
+ $skip_env['REQUEST_METHOD'],
+ $skip_env['QUERY_STRING'],
+ $skip_env['PATH_TRANSLATED'],
+ $skip_env['SCRIPT_FILENAME'],
+ );
+ }
if ($valgrind) {
- $env['USE_ZEND_ALLOC'] = '0';
- $env['ZEND_DONT_UNLOAD_MODULES'] = 1;
+ $skip_env['USE_ZEND_ALLOC'] = '0';
+ $skip_env['ZEND_DONT_UNLOAD_MODULES'] = 1;
}
$junit->startTimer($shortname);
$startTime = microtime(true);
- $commandLine = "$extra $php $pass_options $extra_options -q $orig_ini_settings $no_file_cache -d display_errors=1 -d display_startup_errors=0";
- $output = $skipCache->checkSkip($commandLine, $test->getSection('SKIPIF'), $test_skipif, $temp_skipif, $env);
+ $commandLine = [
+ $php_path,
+ ...$sapi_option_args,
+ ...$pass_option_args,
+ ...$extra_option_args,
+ '-q',
+ ...$orig_ini_settings_args,
+ '-d',
+ 'opcache.file_cache=',
+ '-d',
+ 'opcache.file_cache_only=0',
+ '-d',
+ 'display_errors=1',
+ '-d',
+ 'display_startup_errors=0',
+ ];
+ $output = $skipCache->checkSkip($commandLine, $test->getSection('SKIPIF'), $test_skipif, $temp_skipif, $skip_env);
$time = microtime(true) - $startTime;
$junit->stopTimer($shortname);
@@ -2338,16 +2409,18 @@ function run_test(string $php, $file, array $env): string
$env['HTTP_COOKIE'] = '';
}
- $args = $test->hasSection('ARGS') ? ' -- ' . $test->getSection('ARGS') : '';
-
+ $test_option_args = [
+ ...$sapi_option_args,
+ ...$pass_option_args,
+ ];
if ($preload && !empty($test_file)) {
save_text($preload_filename, "<?php opcache_compile_file('$test_file');");
- $local_pass_options = $pass_options;
- unset($pass_options);
- $pass_options = $local_pass_options;
- $pass_options .= " -d opcache.preload=" . $preload_filename;
+ $test_option_args[] = '-d';
+ $test_option_args[] = "opcache.preload=$preload_filename";
}
+ $stdin = $test->hasSection('STDIN') ? $test->getSection('STDIN') : null;
+ $request = null;
if ($test->sectionNotEmpty('POST_RAW')) {
$post = trim($test->getSection('POST_RAW'));
$raw_lines = explode("\n", $post);
@@ -2378,9 +2451,6 @@ function run_test(string $php, $file, array $env): string
$junit->markTestAs('BORK', $shortname, $tested, null, 'empty $request');
return 'BORKED';
}
-
- save_text($tmp_post, $request);
- $cmd = "$php $pass_options $ini_settings -f \"$test_file\"$cmdRedirect < \"$tmp_post\"";
} elseif ($test->sectionNotEmpty('PUT')) {
$post = trim($test->getSection('PUT'));
$raw_lines = explode("\n", $post);
@@ -2409,13 +2479,9 @@ function run_test(string $php, $file, array $env): string
$junit->markTestAs('BORK', $shortname, $tested, null, 'empty $request');
return 'BORKED';
}
-
- save_text($tmp_post, $request);
- $cmd = "$php $pass_options $ini_settings -f \"$test_file\"$cmdRedirect < \"$tmp_post\"";
} elseif ($test->sectionNotEmpty('POST')) {
- $post = trim($test->getSection('POST'));
- $content_length = strlen($post);
- save_text($tmp_post, $post);
+ $request = trim($test->getSection('POST'));
+ $content_length = strlen($request);
$env['REQUEST_METHOD'] = 'POST';
if (empty($env['CONTENT_TYPE'])) {
@@ -2425,48 +2491,67 @@ function run_test(string $php, $file, array $env): string
if (empty($env['CONTENT_LENGTH'])) {
$env['CONTENT_LENGTH'] = $content_length;
}
-
- $cmd = "$php $pass_options $ini_settings -f \"$test_file\"$cmdRedirect < \"$tmp_post\"";
} elseif ($test->sectionNotEmpty('GZIP_POST')) {
- $post = trim($test->getSection('GZIP_POST'));
- $post = gzencode($post, 9, FORCE_GZIP);
+ $request = trim($test->getSection('GZIP_POST'));
+ $request = gzencode($request, 9, FORCE_GZIP);
$env['HTTP_CONTENT_ENCODING'] = 'gzip';
- save_text($tmp_post, $post);
- $content_length = strlen($post);
+ $content_length = strlen($request);
$env['REQUEST_METHOD'] = 'POST';
$env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
$env['CONTENT_LENGTH'] = $content_length;
-
- $cmd = "$php $pass_options $ini_settings -f \"$test_file\"$cmdRedirect < \"$tmp_post\"";
} elseif ($test->sectionNotEmpty('DEFLATE_POST')) {
- $post = trim($test->getSection('DEFLATE_POST'));
- $post = gzcompress($post, 9);
+ $request = trim($test->getSection('DEFLATE_POST'));
+ $request = gzcompress($request, 9);
$env['HTTP_CONTENT_ENCODING'] = 'deflate';
- save_text($tmp_post, $post);
- $content_length = strlen($post);
+ $content_length = strlen($request);
$env['REQUEST_METHOD'] = 'POST';
$env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
$env['CONTENT_LENGTH'] = $content_length;
-
- $cmd = "$php $pass_options $ini_settings -f \"$test_file\"$cmdRedirect < \"$tmp_post\"";
} else {
$env['REQUEST_METHOD'] = 'GET';
$env['CONTENT_TYPE'] = '';
$env['CONTENT_LENGTH'] = '';
+ }
- $repeat_option = $num_repeats > 1 ? "--repeat $num_repeats" : "";
- $cmd = "$php $pass_options $repeat_option $ini_settings -f \"$test_file\" $args$cmdRedirect";
+ $request_file = null;
+ if ($request !== null) {
+ // A file descriptor avoids blocking while writing large request bodies to a pipe.
+ save_text($tmp_post, $request);
+ $request_file = $tmp_post;
+ }
+
+ $test_command = create_test_command(
+ $php_path,
+ $test_option_args,
+ $test_ini_settings,
+ $test_file,
+ $num_repeats,
+ );
+ $orig_cmd = escaped_shell_string_from($test_command);
+ if ($test->hasSection('ARGS')) {
+ // Preserve the existing shell parsing of the raw ARGS section.
+ $orig_cmd .= ' -- ' . $test->getSection('ARGS');
+ $test_command = create_shell_invocation($orig_cmd);
+ }
+ if ($request_file !== null) {
+ $orig_cmd .= ' < ' . escapeshellarg($request_file);
+ }
+ if ($captureStdOut && $captureStdErr) {
+ $orig_cmd .= ' 2>&1';
}
- $orig_cmd = $cmd;
if ($valgrind) {
$env['USE_ZEND_ALLOC'] = '0';
$env['ZEND_DONT_UNLOAD_MODULES'] = 1;
- $cmd = $valgrind->wrapCommand($cmd, $memcheck_filename, strpos($test_file, "pcre") !== false);
+ $test_command = $valgrind->wrapCommand(
+ $test_command,
+ $memcheck_filename,
+ strpos($test_file, "pcre") !== false,
+ );
}
if ($test->hasSection('XLEAK')) {
@@ -2489,7 +2574,7 @@ function run_test(string $php, $file, array $env): string
REQUEST_METHOD = " . $env['REQUEST_METHOD'] . "
SCRIPT_FILENAME = " . $env['SCRIPT_FILENAME'] . "
HTTP_COOKIE = " . $env['HTTP_COOKIE'] . "
-COMMAND $cmd
+COMMAND $orig_cmd
";
}
@@ -2497,8 +2582,16 @@ function run_test(string $php, $file, array $env): string
$hrtime = hrtime();
$startTime = $hrtime[0] * 1000000000 + $hrtime[1];
- $stdin = $test->hasSection('STDIN') ? $test->getSection('STDIN') : null;
- $out = system_with_timeout($cmd, $env, $stdin, $captureStdIn, $captureStdOut, $captureStdErr);
+ $out = system_with_timeout(
+ $test_command,
+ $env,
+ $stdin,
+ $request_file,
+ $captureStdIn,
+ $captureStdOut,
+ $captureStdErr,
+ $captureStdOut && $captureStdErr,
+ );
$junit->stopTimer($shortname);
$hrtime = hrtime();
@@ -2520,9 +2613,27 @@ function run_test(string $php, $file, array $env): string
save_text($test_clean, trim($test->getSection('CLEAN')), $temp_clean);
if (!$no_clean) {
- $extra = !IS_WINDOWS ?
- "unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;" : "";
- $clean_output = system_with_timeout("$extra $orig_php $pass_options -q $orig_ini_settings $no_file_cache \"$test_clean\"", $env);
+ $clean_command = [
+ $php,
+ ...$pass_option_args,
+ '-q',
+ ...$orig_ini_settings_args,
+ '-d',
+ 'opcache.file_cache=',
+ '-d',
+ 'opcache.file_cache_only=0',
+ $test_clean,
+ ];
+ $clean_env = $env;
+ if (!IS_WINDOWS) {
+ unset(
+ $clean_env['REQUEST_METHOD'],
+ $clean_env['QUERY_STRING'],
+ $clean_env['PATH_TRANSLATED'],
+ $clean_env['SCRIPT_FILENAME'],
+ );
+ }
+ $clean_output = system_with_timeout($clean_command, $clean_env);
}
if (!$cfg['keep']['clean']) {
@@ -3033,6 +3144,25 @@ function settings2params(array $ini_settings): string
return $settings;
}
+function settings2arguments(array $ini_settings): array
+{
+ $arguments = [];
+
+ foreach ($ini_settings as $name => $value) {
+ foreach ((array) $value as $item) {
+ $arguments[] = '-d';
+ $arguments[] = "$name=$item";
+ }
+ }
+
+ return $arguments;
+}
+
+function escaped_shell_string_from(array $arguments): string
+{
+ return implode(' ', array_map(escapeshellarg(...), $arguments));
+}
+
function compute_summary(): void
{
global $n_total, $test_results, $ignored_by_ext, $sum_results, $percent_results;
@@ -3612,12 +3742,12 @@ public function __construct(bool $enable, bool $keepFile)
$this->keepFile = $keepFile;
}
- public function checkSkip(string $php, string $code, string $checkFile, string $tempFile, array $env): string
+ public function checkSkip(array $command, string $code, string $checkFile, string $tempFile, array $env): string
{
// Extension tests frequently use something like <?php require 'skipif.inc';
// for skip checks. This forces us to cache per directory to avoid pollution.
$dir = dirname($checkFile);
- $key = "$php => $dir";
+ $key = implode("\0", $command) . " => $dir";
if (isset($this->skips[$key][$code])) {
$this->hits++;
@@ -3628,7 +3758,8 @@ public function checkSkip(string $php, string $code, string $checkFile, string $
}
save_text($checkFile, $code, $tempFile);
- $result = trim(system_with_timeout("$php \"$checkFile\"", $env));
+ $command[] = $checkFile;
+ $result = trim(system_with_timeout($command, $env));
if (strpos($result, 'nocache') === 0) {
$result = '';
} else if ($this->enable) {
@@ -3643,19 +3774,37 @@ public function checkSkip(string $php, string $code, string $checkFile, string $
return $result;
}
- public function getExtensions(string $php): array
+ public function getExtensions(array $command): array
{
- if (isset($this->extensions[$php])) {
+ $key = implode("\0", $command);
+ if (isset($this->extensions[$key])) {
$this->extHits++;
- return $this->extensions[$php];
+ return $this->extensions[$key];
+ }
+
+ $probeCommand = escaped_shell_string_from([
+ ...$command,
+ '-d',
+ 'display_errors=0',
+ '-r',
+ ]);
+
+ $output = shell_exec(
+ $probeCommand
+ . ' "echo ini_get(\'extension_dir\'), chr(0), implode(\',\', get_loaded_extensions());"'
+ );
+
+ if (!is_string($output) || !str_contains($output, "\0")) {
+ error("Unable to query loaded PHP extensions.");
}
- $extDir = shell_exec("$php -d display_errors=0 -r \"echo ini_get('extension_dir');\"");
- $extensionsNames = explode(",", shell_exec("$php -d display_errors=0 -r \"echo implode(',', get_loaded_extensions());\""));
+ [$extDir, $extensionsNames] = explode("\0", $output, 2);
+
+ $extensionsNames = explode(",", $extensionsNames);
$extensions = remap_loaded_extensions_names($extensionsNames);
$result = [$extDir, $extensions];
- $this->extensions[$php] = $result;
+ $this->extensions[$key] = $result;
$this->extMisses++;
return $result;
@@ -3676,7 +3825,11 @@ public function getHeader(): string
public function __construct(array $environment, string $tool = 'memcheck')
{
$this->tool = $tool;
- $header = system_with_timeout("valgrind --tool={$this->tool} --version", $environment);
+ $header = system_with_timeout([
+ 'valgrind',
+ "--tool={$this->tool}",
+ '--version',
+ ], $environment);
if (!$header) {
error("Valgrind returned no version info for {$this->tool}, cannot proceed.\n".
"Please check if Valgrind is installed and the tool is named correctly.");
@@ -3690,18 +3843,28 @@ public function __construct(array $environment, string $tool = 'memcheck')
$this->version_3_8_0 = version_compare($version, '3.8.0', '>=');
}
- public function wrapCommand(string $cmd, string $memcheck_filename, bool $check_all): string
+ public function wrapCommand(array $command, string $memcheck_filename, bool $check_all): array
{
- $vcmd = "valgrind -q --tool={$this->tool} --trace-children=yes";
+ $valgrind_arguments = [
+ 'valgrind',
+ '-q',
+ "--tool={$this->tool}",
+ '--trace-children=yes',
+ ];
+
if ($check_all) {
- $vcmd .= ' --smc-check=all';
+ $valgrind_arguments[] = '--smc-check=all';
}
- /* --vex-iropt-register-updates=allregs-at-mem-access is necessary for phpdbg watchpoint tests */
- if ($this->version_3_8_0) {
- return "$vcmd --vex-iropt-register-updates=allregs-at-mem-access --log-file=$memcheck_filename $cmd";
- }
- return "$vcmd --vex-iropt-precise-memory-exns=yes --log-file=$memcheck_filename $cmd";
+ $valgrind_arguments[] = $this->version_3_8_0
+ ? '--vex-iropt-register-updates=allregs-at-mem-access' // necessary for phpdbg watchpoint tests
+ : '--vex-iropt-precise-memory-exns=yes';
+
+ return [
+ ...$valgrind_arguments,
+ "--log-file=$memcheck_filename",
+ ...$command,
+ ];
}
}
diff --git a/tests/basic/req60524-win.phpt b/tests/basic/req60524-win.phpt
index 26fa9d9c5c7..d75eb8c7dcf 100644
--- a/tests/basic/req60524-win.phpt
+++ b/tests/basic/req60524-win.phpt
@@ -10,4 +10,4 @@
--FILE--
<?php echo sys_get_temp_dir(); ?>
--EXPECT--
-C:\\Windows
+C:\Windows
diff --git a/tests/run-test/clean_environment.phpt b/tests/run-test/clean_environment.phpt
new file mode 100644
index 00000000000..cf2d3c4b4f5
--- /dev/null
+++ b/tests/run-test/clean_environment.phpt
@@ -0,0 +1,26 @@
+--TEST--
+SKIPIF and CLEAN do not inherit request environment variables on POSIX
+--SKIPIF--
+<?php
+if (PHP_OS_FAMILY !== 'Windows') {
+ foreach (['REQUEST_METHOD', 'QUERY_STRING', 'PATH_TRANSLATED', 'SCRIPT_FILENAME'] as $name) {
+ if (getenv($name) !== false) {
+ die("$name was inherited\n");
+ }
+ }
+}
+?>
+--FILE--
+<?php
+?>
+--CLEAN--
+<?php
+if (PHP_OS_FAMILY !== 'Windows') {
+ foreach (['REQUEST_METHOD', 'QUERY_STRING', 'PATH_TRANSLATED', 'SCRIPT_FILENAME'] as $name) {
+ if (getenv($name) !== false) {
+ echo "$name was inherited\n";
+ }
+ }
+}
+?>
+--EXPECT--