5 * Copyright (c) 2002-2011, Sebastian Bergmann <sebastian@phpunit.de>.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
20 * * Neither the name of Sebastian Bergmann nor the names of his
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
39 * @author Sebastian Bergmann <sebastian@phpunit.de>
40 * @copyright 2002-2011 Sebastian Bergmann <sebastian@phpunit.de>
41 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
42 * @link http://www.phpunit.de/
43 * @since File available since Release 3.4.0
47 * Utility methods for PHP sub-processes.
51 * @author Sebastian Bergmann <sebastian@phpunit.de>
52 * @copyright 2002-2011 Sebastian Bergmann <sebastian@phpunit.de>
53 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
54 * @version Release: 3.5.14
55 * @link http://www.phpunit.de/
56 * @since Class available since Release 3.4.0
58 abstract class PHPUnit_Util_PHP
61 * Path to the PHP interpreter that is to be used.
63 * @var string $phpBinary
65 protected static $phpBinary = NULL;
68 * Returns the path to a PHP interpreter.
70 * PHPUnit_Util_PHP::$phpBinary contains the path to the PHP
73 * When not set, the following assumptions will be made:
75 * 1. When the PHP CLI/CGI binary configured with the PEAR Installer
76 * (php_bin configuration value) is readable, it will be used.
78 * 2. When PHPUnit is run using the CLI SAPI and the $_SERVER['_']
79 * variable does not contain the string "PHPUnit", $_SERVER['_']
80 * is assumed to contain the path to the current PHP interpreter
81 * and that will be used.
83 * 3. When PHPUnit is run using the CLI SAPI and the $_SERVER['_']
84 * variable contains the string "PHPUnit", the file that $_SERVER['_']
85 * points to is assumed to be the PHPUnit TextUI CLI wrapper script
86 * "phpunit" and the binary set up using #! on that file's first
87 * line of code is assumed to contain the path to the current PHP
88 * interpreter and that will be used.
90 * 4. The current PHP interpreter is assumed to be in the $PATH and
91 * to be invokable through "php".
95 public static function getPhpBinary()
97 if (self::$phpBinary === NULL) {
98 if (is_readable('@php_bin@')) {
99 self::$phpBinary = '@php_bin@';
102 else if (PHP_SAPI == 'cli' && isset($_SERVER['_']) &&
103 strpos($_SERVER['_'], 'phpunit') !== FALSE) {
104 $file = file($_SERVER['_']);
105 $tmp = explode(' ', $file[0]);
106 self::$phpBinary = trim($tmp[1]);
109 if (!is_readable(self::$phpBinary)) {
110 self::$phpBinary = 'php';
112 self::$phpBinary = escapeshellarg(self::$phpBinary);
116 return self::$phpBinary;
120 * @return PHPUnit_Util_PHP
121 * @since Method available since Release 3.5.12
123 public static function factory()
125 if (DIRECTORY_SEPARATOR == '\\') {
126 return new PHPUnit_Util_PHP_Windows;
129 return new PHPUnit_Util_PHP_Default;
133 * Runs a single job (PHP code) using a separate PHP process.
136 * @param PHPUnit_Framework_TestCase $test
137 * @param PHPUnit_Framework_TestResult $result
139 * @throws PHPUnit_Framework_Exception
141 public function runJob($job, PHPUnit_Framework_Test $test = NULL, PHPUnit_Framework_TestResult $result = NULL)
143 $process = proc_open(
144 self::getPhpBinary(),
146 0 => array('pipe', 'r'),
147 1 => array('pipe', 'w'),
148 2 => array('pipe', 'w')
153 if (!is_resource($process)) {
154 throw new PHPUnit_Framework_Exception(
155 'Unable to create process for process isolation.'
159 if ($result !== NULL) {
160 $result->startTest($test);
163 $this->process($pipes[0], $job);
166 $stdout = stream_get_contents($pipes[1]);
169 $stderr = stream_get_contents($pipes[2]);
172 proc_close($process);
175 if ($result !== NULL) {
176 $this->processChildResult($test, $result, $stdout, $stderr);
178 return array('stdout' => $stdout, 'stderr' => $stderr);
183 * @param resource $pipe
185 * @since Method available since Release 3.5.12
187 abstract protected function process($pipe, $job);
190 * @since Method available since Release 3.5.12
192 protected function cleanup()
197 * Processes the TestResult object from an isolated process.
199 * @param PHPUnit_Framework_TestCase $test
200 * @param PHPUnit_Framework_TestResult $result
201 * @param string $stdout
202 * @param string $stderr
203 * @since Method available since Release 3.5.0
205 protected function processChildResult(PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result, $stdout, $stderr)
207 if (!empty($stderr)) {
211 new RuntimeException(trim($stderr)), $time
214 $childResult = @unserialize($stdout);
216 if ($childResult !== FALSE) {
217 if (!empty($childResult['output'])) {
218 print $childResult['output'];
221 $test->setResult($childResult['testResult']);
222 $test->addToAssertionCount($childResult['numAssertions']);
224 $childResult = $childResult['result'];
226 if ($result->getCollectCodeCoverageInformation()) {
227 $codeCoverageInformation = $childResult->getRawCodeCoverageInformation();
229 if (isset($codeCoverageInformation[0]) &&
230 is_array($codeCoverageInformation[0])) {
231 $result->getCodeCoverage()->append(
232 $codeCoverageInformation[0], $test
237 $time = $childResult->time();
238 $notImplemented = $childResult->notImplemented();
239 $skipped = $childResult->skipped();
240 $errors = $childResult->errors();
241 $failures = $childResult->failures();
243 if (!empty($notImplemented)) {
245 $test, $notImplemented[0]->thrownException(), $time
249 else if (!empty($skipped)) {
251 $test, $skipped[0]->thrownException(), $time
255 else if (!empty($errors)) {
257 $test, $errors[0]->thrownException(), $time
261 else if (!empty($failures)) {
263 $test, $failures[0]->thrownException(), $time
270 $test, new RuntimeException(trim($stdout)), $time
275 $result->endTest($test, $time);