|
7 | 7 |
|
8 | 8 | namespace Tester\Runner; |
9 | 9 |
|
| 10 | +use Tester\Helpers; |
10 | 11 |
|
11 | | -interface PhpInterpreter |
| 12 | + |
| 13 | +/** |
| 14 | + * PHP command-line executable. |
| 15 | + */ |
| 16 | +class PhpInterpreter |
12 | 17 | { |
| 18 | + /** @var string */ |
| 19 | + private $commandLine; |
| 20 | + |
| 21 | + /** @var bool is CGI? */ |
| 22 | + private $cgi; |
| 23 | + |
| 24 | + /** @var \stdClass created by info.php */ |
| 25 | + private $info; |
| 26 | + |
| 27 | + /** @var string */ |
| 28 | + private $error; |
| 29 | + |
| 30 | + |
| 31 | + public function __construct($path, array $args = []) |
| 32 | + { |
| 33 | + $this->commandLine = Helpers::escapeArg($path); |
| 34 | + $proc = @proc_open( // @ is escalated to exception |
| 35 | + $this->commandLine . ' --version', |
| 36 | + [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']], |
| 37 | + $pipes, |
| 38 | + NULL, |
| 39 | + NULL, |
| 40 | + ['bypass_shell' => TRUE] |
| 41 | + ); |
| 42 | + if ($proc === FALSE) { |
| 43 | + throw new \Exception("Cannot run PHP interpreter $path. Use -p option."); |
| 44 | + } |
| 45 | + $output = stream_get_contents($pipes[1]); |
| 46 | + proc_close($proc); |
| 47 | + |
| 48 | + $args = ' -n ' . implode(' ', array_map(['Tester\Helpers', 'escapeArg'], $args)); |
| 49 | + if (preg_match('#HipHop VM#', $output)) { |
| 50 | + $args = ' --php' . $args . ' -d hhvm.log.always_log_unhandled_exceptions=false'; // HHVM issue #3019 |
| 51 | + } elseif (strpos($output, 'phpdbg') !== FALSE) { |
| 52 | + $args = ' -qrrb -S cli' . $args; |
| 53 | + } |
| 54 | + $this->commandLine .= $args; |
| 55 | + |
| 56 | + $proc = proc_open( |
| 57 | + $this->commandLine . ' ' . Helpers::escapeArg(__DIR__ . '/info.php') . ' serialized', |
| 58 | + [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']], |
| 59 | + $pipes, |
| 60 | + NULL, |
| 61 | + NULL, |
| 62 | + ['bypass_shell' => TRUE] |
| 63 | + ); |
| 64 | + $output = stream_get_contents($pipes[1]); |
| 65 | + $this->error = trim(stream_get_contents($pipes[2])); |
| 66 | + if (proc_close($proc)) { |
| 67 | + throw new \Exception("Unable to run $path: " . preg_replace('#[\r\n ]+#', ' ', $this->error)); |
| 68 | + } |
| 69 | + |
| 70 | + $parts = explode("\r\n\r\n", $output, 2); |
| 71 | + $this->cgi = count($parts) === 2; |
| 72 | + if (!($this->info = @unserialize($parts[$this->cgi]))) { |
| 73 | + throw new \Exception("Unable to detect PHP version (output: $output)."); |
| 74 | + |
| 75 | + } elseif ($this->info->hhvmVersion && version_compare($this->info->hhvmVersion, '3.3.0', '<')) { |
| 76 | + throw new \Exception('HHVM below version 3.3.0 is not supported.'); |
| 77 | + |
| 78 | + } elseif ($this->info->phpDbgVersion && version_compare($this->info->version, '7.0.0', '<')) { |
| 79 | + throw new \Exception('Unable to use phpdbg on PHP < 7.0.0.'); |
| 80 | + |
| 81 | + } elseif ($this->cgi && $this->error) { |
| 82 | + $this->error .= "\n(note that PHP CLI generates better error messages)"; |
| 83 | + } |
| 84 | + } |
| 85 | + |
| 86 | + |
| 87 | + /** |
| 88 | + * @param string |
| 89 | + * @param string |
| 90 | + */ |
| 91 | + public function addPhpIniOption($name, $value = NULL) |
| 92 | + { |
| 93 | + $this->commandLine .= ' -d ' . Helpers::escapeArg($name . ($value === NULL ? '' : "=$value")); |
| 94 | + } |
| 95 | + |
13 | 96 |
|
14 | 97 | /** |
15 | 98 | * @return string |
16 | 99 | */ |
17 | | - function getCommandLine(); |
| 100 | + public function getCommandLine() |
| 101 | + { |
| 102 | + return $this->commandLine; |
| 103 | + } |
| 104 | + |
18 | 105 |
|
19 | 106 | /** |
20 | 107 | * @return string |
21 | 108 | */ |
22 | | - function getVersion(); |
| 109 | + public function getVersion() |
| 110 | + { |
| 111 | + return $this->info->version; |
| 112 | + } |
| 113 | + |
23 | 114 |
|
24 | 115 | /** |
25 | 116 | * @return bool |
26 | 117 | */ |
27 | | - function hasXdebug(); |
| 118 | + public function canMeasureCodeCoverage() |
| 119 | + { |
| 120 | + return $this->info->canMeasureCodeCoverage; |
| 121 | + } |
| 122 | + |
28 | 123 |
|
29 | 124 | /** |
30 | 125 | * @return bool |
31 | 126 | */ |
32 | | - function isCgi(); |
| 127 | + public function isCgi() |
| 128 | + { |
| 129 | + return $this->cgi; |
| 130 | + } |
| 131 | + |
| 132 | + |
| 133 | + /** |
| 134 | + * @return string |
| 135 | + */ |
| 136 | + public function getStartupError() |
| 137 | + { |
| 138 | + return $this->error; |
| 139 | + } |
| 140 | + |
33 | 141 |
|
34 | 142 | /** |
35 | 143 | * @return string |
36 | 144 | */ |
37 | | - function getErrorOutput(); |
| 145 | + public function getShortInfo() |
| 146 | + { |
| 147 | + return "PHP {$this->info->version} ({$this->info->sapi})" |
| 148 | + . ($this->info->phpDbgVersion ? "; PHPDBG {$this->info->phpDbgVersion}" : '') |
| 149 | + . ($this->info->hhvmVersion ? "; HHVM {$this->info->hhvmVersion}" : ''); |
| 150 | + } |
| 151 | + |
| 152 | + |
| 153 | + /** |
| 154 | + * @param string |
| 155 | + * @return bool |
| 156 | + */ |
| 157 | + public function hasExtension($name) |
| 158 | + { |
| 159 | + return in_array(strtolower($name), array_map('strtolower', $this->info->extensions), TRUE); |
| 160 | + } |
38 | 161 |
|
39 | 162 | } |
0 commit comments