exit code, 'stdout' => ..., 'stderr' => ..., * 'log' => what the code wrote to the PHP error log). */ public static function run(string $code, array $env = array(), array $ini = array()): array { // One line, so that line numbers in $code stay the same; the code runs // in publish/, as the unit tests do return self::runPlain("require " . var_export(XXX_ROOT . '/tests/php/bootstrap.php', TRUE) . "; chdir(XXX_ROOT . '/publish');\n" . $code, $env, $ini); } // The same with only the test environment, for code that includes a site library itself public static function runBare(string $code, array $env = array(), array $ini = array()): array { return self::runPlain("require " . var_export(XXX_ROOT . '/tests/php/environment.php', TRUE) . ";\n" . $code, $env, $ini); } // The same without the bootstrap: the code loads what it needs itself public static function runPlain(string $code, array $env = array(), array $ini = array()): array { $script = tempnam(sys_get_temp_dir(), 'xxx-php-'); $log = $script . '.log'; file_put_contents($script, " $value) array_push($command, '-d', "$name=$value"); $command[] = $script; $env = array_merge(getenv(), array('XXX_TEST_LOG' => $log), $env); $process = proc_open($command, array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $pipes, XXX_ROOT, $env); $stdout = stream_get_contents($pipes[1]); $stderr = stream_get_contents($pipes[2]); fclose($pipes[1]); fclose($pipes[2]); $status = proc_close($process); $logged = is_file($log) ? file_get_contents($log) : ''; @unlink($script); @unlink($log); return array('status' => $status, 'stdout' => $stdout, 'stderr' => $stderr, 'log' => $logged); } }