From 17f7fb76053cabe20225472debe4d1801221ef9f Mon Sep 17 00:00:00 2001
From: Nikita Popov <nikita.ppv@gmail.com>
Date: Mon, 1 Jul 2019 13:07:30 +0200
Subject: [PATCH] Switch to using shell-less proc_open() in various server
 tests

---
 ext/curl/tests/server.inc               | 31 ++++--------------
 ext/opcache/tests/php_cli_server.inc    | 27 +++++-----------
 ext/soap/tests/bug73037.phpt            |  3 +-
 ext/soap/tests/custom_content_type.phpt |  5 +--
 sapi/cli/tests/php_cli_server.inc       | 43 +++++++++----------------
 sapi/cli/tests/upload_2G.phpt           |  4 +--
 sapi/fpm/tests/main-global-prefix.phpt  |  2 +-
 sapi/fpm/tests/tester.inc               | 24 ++++++--------
 tests/basic/bug67198.phpt               |  4 ++-
 9 files changed, 51 insertions(+), 92 deletions(-)

diff --git a/ext/curl/tests/server.inc b/ext/curl/tests/server.inc
index 68a5bde8a8..00eeb5ff76 100644
--- a/ext/curl/tests/server.inc
+++ b/ext/curl/tests/server.inc
@@ -12,30 +12,13 @@ function curl_cli_server_start() {
     $php_executable = getenv('TEST_PHP_EXECUTABLE');
     $doc_root = __DIR__;
     $router = "responder/get.inc";
-
-    if (substr(PHP_OS, 0, 3) == 'WIN') {
-        $descriptorspec = array(
-            0 => STDIN,
-            1 => STDOUT,
-            2 => array("pipe", "w"),
-        );
-
-        $cmd = "{$php_executable} -t {$doc_root} -n -S " . PHP_CURL_SERVER_ADDRESS;
-        $cmd .= " {$router}";
-        $handle = proc_open(addslashes($cmd), $descriptorspec, $pipes, $doc_root, NULL, array("bypass_shell" => true,  "suppress_errors" => true));
-    } else {
-        $descriptorspec = array(
-            0 => STDIN,
-            1 => STDOUT,
-            2 => STDERR,
-        );
-
-        $cmd = "exec {$php_executable} -t {$doc_root} -n -S " . PHP_CURL_SERVER_ADDRESS;
-        $cmd .= " {$router}";
-        $cmd .= " 2>/dev/null";
-
-        $handle = proc_open($cmd, $descriptorspec, $pipes, $doc_root);
-    }
+    $cmd = [$php_executable, '-t', $doc_root, '-n', '-S', PHP_CURL_SERVER_ADDRESS, $router];
+    $descriptorspec = array(
+        0 => STDIN,
+        1 => STDOUT,
+        2 => array("null"),
+    );
+    $handle = proc_open($cmd, $descriptorspec, $pipes, $doc_root, null, array("suppress_errors" => true));
 
     // note: even when server prints 'Listening on localhost:8964...Press Ctrl-C to quit.'
     //       it might not be listening yet...need to wait until fsockopen() call returns
diff --git a/ext/opcache/tests/php_cli_server.inc b/ext/opcache/tests/php_cli_server.inc
index 70ef14e585..8367715728 100644
--- a/ext/opcache/tests/php_cli_server.inc
+++ b/ext/opcache/tests/php_cli_server.inc
@@ -7,25 +7,14 @@ function php_cli_server_start($ini = "") {
 	$php_executable = getenv('TEST_PHP_EXECUTABLE');
 	$doc_root = __DIR__;
 
-	if (substr(PHP_OS, 0, 3) == 'WIN') {
-		$descriptorspec = array(
-			0 => STDIN,
-			1 => STDOUT,
-			2 => array("pipe", "w"),
-		);
-
-		$cmd = "{$php_executable} -t {$doc_root} $ini -S " . PHP_CLI_SERVER_ADDRESS;
-		$handle = proc_open(addslashes($cmd), $descriptorspec, $pipes, $doc_root, NULL, array("bypass_shell" => true,  "suppress_errors" => true));
-	} else {
-		$descriptorspec = array(
-			0 => STDIN,
-			1 => STDOUT,
-			2 => STDERR,
-		);
-
-		$cmd = "exec {$php_executable} -t {$doc_root} $ini -S " . PHP_CLI_SERVER_ADDRESS . " 2>/dev/null";
-		$handle = proc_open($cmd, $descriptorspec, $pipes, $doc_root);
-	}
+	$ini_array = preg_split('/\s+/', trim($ini));
+	$cmd = [$php_executable, '-t', $doc_root, '-n', ...$ini_array, '-S', PHP_CLI_SERVER_ADDRESS];
+	$descriptorspec = array(
+		0 => STDIN,
+		1 => STDOUT,
+		2 => array("null"),
+	);
+	$handle = proc_open($cmd, $descriptorspec, $pipes, $doc_root, null, array("suppress_errors" => true));
 
 	// note: even when server prints 'Listening on localhost:8964...Press Ctrl-C to quit.'
 	//       it might not be listening yet...need to wait until fsockopen() call returns
diff --git a/ext/soap/tests/bug73037.phpt b/ext/soap/tests/bug73037.phpt
index 7f49a843f5..897aec89d0 100644
--- a/ext/soap/tests/bug73037.phpt
+++ b/ext/soap/tests/bug73037.phpt
@@ -63,7 +63,8 @@ function get_data($max)
 }
 
 $router = "bug73037_server.php";
-$args = substr(PHP_OS, 0, 3) == 'WIN' ? "-d extension_dir=" . ini_get("extension_dir") . " -d extension=php_soap.dll" : "";
+$args = substr(PHP_OS, 0, 3) == 'WIN'
+    ? ["-d", "extension_dir=" . ini_get("extension_dir"), "-d", "extension=php_soap.dll"] : [];
 $code = <<<'PHP'
 $s = new SoapServer(NULL, array('uri' => 'http://here'));
 $s->setObject(new stdclass());
diff --git a/ext/soap/tests/custom_content_type.phpt b/ext/soap/tests/custom_content_type.phpt
index a0ee164053..ef95be4ffc 100644
--- a/ext/soap/tests/custom_content_type.phpt
+++ b/ext/soap/tests/custom_content_type.phpt
@@ -15,13 +15,14 @@ server
 
 include __DIR__ . "/../../../sapi/cli/tests/php_cli_server.inc";
 
-$args = substr(PHP_OS, 0, 3) == 'WIN' ? "-d extension_dir=" . ini_get("extension_dir") . " -d extension=php_soap.dll" : "";
+$args = substr(PHP_OS, 0, 3) == 'WIN'
+    ? ["-d", "extension_dir=" . ini_get("extension_dir"), "-d", "extension=php_soap.dll"] : [];
 $code = <<<'PHP'
 /* Receive */
 $content = trim(file_get_contents("php://input")) . PHP_EOL;
 PHP;
 
-php_cli_server_start($code, false, $args);
+php_cli_server_start($code, null, $args);
 
 $client = new soapclient(NULL, [
   'location' => 'http://' . PHP_CLI_SERVER_ADDRESS,
diff --git a/sapi/cli/tests/php_cli_server.inc b/sapi/cli/tests/php_cli_server.inc
index 6421978a37..679a55eed4 100644
--- a/sapi/cli/tests/php_cli_server.inc
+++ b/sapi/cli/tests/php_cli_server.inc
@@ -3,7 +3,11 @@ define ("PHP_CLI_SERVER_HOSTNAME", "localhost");
 define ("PHP_CLI_SERVER_PORT", 8964);
 define ("PHP_CLI_SERVER_ADDRESS", PHP_CLI_SERVER_HOSTNAME.":".PHP_CLI_SERVER_PORT);
 
-function php_cli_server_start($code = 'echo "Hello world";', $router = 'index.php', $cmd_args = null) {
+function php_cli_server_start(
+	?string $code = 'echo "Hello world";',
+	?string $router = 'index.php',
+	array $cmd_args = []
+) {
 	$php_executable = getenv('TEST_PHP_EXECUTABLE');
 	$doc_root = __DIR__;
     $error = null;
@@ -12,35 +16,18 @@ function php_cli_server_start($code = 'echo "Hello world";', $router = 'index.ph
 		file_put_contents($doc_root . '/' . ($router ?: 'index.php'), '<?php ' . $code . ' ?>');
 	}
 
-	if (substr(PHP_OS, 0, 3) == 'WIN') {
-		$descriptorspec = array(
-			0 => STDIN,
-			1 => STDOUT,
-			2 => array("pipe", "w"),
-		);
-
-		$cmd = "{$php_executable} -t {$doc_root} -n {$cmd_args} -S " . PHP_CLI_SERVER_ADDRESS;
-		if (!is_null($router)) {
-			$cmd .= " {$router}";
-		}
-
-		$handle = proc_open(addslashes($cmd), $descriptorspec, $pipes, $doc_root, NULL, array("bypass_shell" => true,  "suppress_errors" => true));
-	} else {
-		$descriptorspec = array(
-			0 => STDIN,
-			1 => STDOUT,
-			2 => STDERR,
-		);
-
-		$cmd = "exec {$php_executable} -t {$doc_root} -n {$cmd_args} -S " . PHP_CLI_SERVER_ADDRESS;
-		if (!is_null($router)) {
-			$cmd .= " {$router}";
-		}
-		$cmd .= " 2>/dev/null";
-
-		$handle = proc_open($cmd, $descriptorspec, $pipes, $doc_root);
+	$cmd = [$php_executable, '-t', $doc_root, '-n', ...$cmd_args, '-S', PHP_CLI_SERVER_ADDRESS];
+	if (!is_null($router)) {
+		$cmd[] = $router;
 	}
 
+	$descriptorspec = array(
+		0 => STDIN,
+		1 => STDOUT,
+		2 => array("null"),
+	);
+	$handle = proc_open($cmd, $descriptorspec, $pipes, $doc_root, null, array("suppress_errors" => true));
+
     // note: here we check the process is running
     for ($i=0; $i < 120; $i++) {
         $status = proc_get_status($handle);
diff --git a/sapi/cli/tests/upload_2G.phpt b/sapi/cli/tests/upload_2G.phpt
index a4bcc6860f..44d2da4a81 100644
--- a/sapi/cli/tests/upload_2G.phpt
+++ b/sapi/cli/tests/upload_2G.phpt
@@ -37,8 +37,8 @@ echo "Test\n";
 
 include "php_cli_server.inc";
 
-php_cli_server_start("var_dump(\$_FILES);", false,
-	"-d post_max_size=3G -d upload_max_filesize=3G");
+php_cli_server_start("var_dump(\$_FILES);", null,
+	["-d", "post_max_size=3G", "-d", "upload_max_filesize=3G"]);
 
 list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
 $port = intval($port)?:80;
diff --git a/sapi/fpm/tests/main-global-prefix.phpt b/sapi/fpm/tests/main-global-prefix.phpt
index 710e688c40..44486e4ed3 100644
--- a/sapi/fpm/tests/main-global-prefix.phpt
+++ b/sapi/fpm/tests/main-global-prefix.phpt
@@ -27,7 +27,7 @@ EOT;
 
 $prefix = __DIR__;
 $tester = new FPM\Tester($cfg);
-$tester->start('--prefix ' . $prefix);
+$tester->start(['--prefix', $prefix]);
 $tester->expectLogStartNotices();
 $tester->expectFile(FPM\Tester::FILE_EXT_LOG_ACC, $prefix);
 $tester->expectFile(FPM\Tester::FILE_EXT_LOG_ERR, $prefix);
diff --git a/sapi/fpm/tests/tester.inc b/sapi/fpm/tests/tester.inc
index 70c03ad70f..12fe3dc2de 100644
--- a/sapi/fpm/tests/tester.inc
+++ b/sapi/fpm/tests/tester.inc
@@ -320,25 +320,21 @@ class Tester
     /**
      * Start PHP-FPM master process
      *
-     * @param string $extraArgs
+     * @param array $extraArgs
      * @return bool
      * @throws \Exception
      */
-    public function start(string $extraArgs = '')
+    public function start(array $extraArgs = [])
     {
         $configFile = $this->createConfig();
-        $desc = $this->outDesc ? [] : [1 => array('pipe', 'w')];
-        $asRoot = getenv('TEST_FPM_RUN_AS_ROOT') ? '--allow-to-run-as-root' : '';
-        $cmd = self::findExecutable() . " $asRoot -F -O -y $configFile $extraArgs";
-        /* Since it's not possible to spawn a process under linux without using a
-         * shell in php (why?!?) we need a little shell trickery, so that we can
-         * actually kill php-fpm */
-        $this->masterProcess = proc_open(
-            "killit () { kill \$child 2> /dev/null; }; " .
-                "trap killit TERM; $cmd 2>&1 & child=\$!; wait",
-            $desc,
-            $pipes
-        );
+        $desc = $this->outDesc ? [] : [1 => array('pipe', 'w'), 2 => array('redirect', 1)];
+        $cmd = [self::findExecutable(), '-F', '-O', '-y', $configFile];
+        if (getenv('TEST_FPM_RUN_AS_ROOT')) {
+            $cmd[] = '--allow-to-run-as-root';
+        }
+        $cmd = array_merge($cmd, $extraArgs);
+
+        $this->masterProcess = proc_open($cmd, $desc, $pipes);
         register_shutdown_function(
             function($masterProcess) use($configFile) {
                 @unlink($configFile);
diff --git a/tests/basic/bug67198.phpt b/tests/basic/bug67198.phpt
index 184916197b..3f5e2a348c 100644
--- a/tests/basic/bug67198.phpt
+++ b/tests/basic/bug67198.phpt
@@ -35,7 +35,9 @@ $opts = array('http' =>
 
 $context  = stream_context_create($opts);
 
-php_cli_server_start("exit(file_get_contents('php://input'));", false, "-d enable_post_data_reading=Off");
+php_cli_server_start(
+    "exit(file_get_contents('php://input'));", null,
+    ["-d", "enable_post_data_reading=Off"]);
 
 var_dump(file_get_contents("http://" . PHP_CLI_SERVER_ADDRESS, false, $context));
 var_dump(file_get_contents("http://" . PHP_CLI_SERVER_ADDRESS, false, $context));
-- 
2.40.0