Utility methods for PHP sub-processes.
@package PHPUnit @subpackage Util @author Sebastian Bergmann <sebastian@phpunit.de> @copyright 2001-2013 Sebastian Bergmann <sebastian@phpunit.de> @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License @link http://www.phpunit.de/ @since Class available since Release 3.4.0
Expanded class hierarchy of PHPUnit_Util_PHP
abstract class PHPUnit_Util_PHP {
/**
* @var string $phpBinary
*/
protected $phpBinary;
/**
* Returns the path to a PHP interpreter.
*
* PHPUnit_Util_PHP::$phpBinary contains the path to the PHP
* interpreter.
*
* When not set, the following assumptions will be made:
*
* 1. When PHPUnit is run using the CLI SAPI and the $_SERVER['_']
* variable does not contain the string "PHPUnit", $_SERVER['_']
* is assumed to contain the path to the current PHP interpreter
* and that will be used.
*
* 2. When PHPUnit is run using the CLI SAPI and the $_SERVER['_']
* variable contains the string "PHPUnit", the file that $_SERVER['_']
* points to is assumed to be the PHPUnit TextUI CLI wrapper script
* "phpunit" and the binary set up using #! on that file's first
* line of code is assumed to contain the path to the current PHP
* interpreter and that will be used.
*
* 3. When the PHP CLI/CGI binary configured with the PEAR Installer
* (php_bin configuration value) is readable, it will be used.
*
* 4. The current PHP interpreter is assumed to be in the $PATH and
* to be invokable through "php".
*
* @return string
*/
protected function getPhpBinary() {
if ($this->phpBinary === NULL) {
if (defined("PHP_BINARY")) {
$this->phpBinary = PHP_BINARY;
}
else {
if (PHP_SAPI == 'cli' && isset($_SERVER['_'])) {
if (strpos($_SERVER['_'], 'phpunit') !== FALSE) {
$file = file($_SERVER['_']);
if (strpos($file[0], ' ') !== FALSE) {
$tmp = explode(' ', $file[0]);
$this->phpBinary = trim($tmp[1]);
}
else {
$this->phpBinary = ltrim(trim($file[0]), '#!');
}
}
else {
if (strpos(basename($_SERVER['_']), 'php') !== FALSE) {
$this->phpBinary = $_SERVER['_'];
}
}
}
}
if ($this->phpBinary === NULL) {
$possibleBinaryLocations = array(
PHP_BINDIR . '/php',
PHP_BINDIR . '/php-cli.exe',
PHP_BINDIR . '/php.exe',
'@php_bin@',
);
foreach ($possibleBinaryLocations as $binary) {
if (is_readable($binary)) {
$this->phpBinary = $binary;
break;
}
}
}
if (!is_readable($this->phpBinary)) {
$this->phpBinary = 'php';
}
else {
$this->phpBinary = escapeshellarg($this->phpBinary);
}
}
return $this->phpBinary;
}
/**
* @return PHPUnit_Util_PHP
* @since Method available since Release 3.5.12
*/
public static function factory() {
if (DIRECTORY_SEPARATOR == '\\') {
return new PHPUnit_Util_PHP_Windows();
}
return new PHPUnit_Util_PHP_Default();
}
/**
* Runs a single job (PHP code) using a separate PHP process.
*
* @param string $job
* @param PHPUnit_Framework_TestCase $test
* @param PHPUnit_Framework_TestResult $result
* @return array|null
* @throws PHPUnit_Framework_Exception
*/
public function runJob($job, PHPUnit_Framework_Test $test = NULL, PHPUnit_Framework_TestResult $result = NULL) {
$process = proc_open($this
->getPhpBinary(), array(
0 => array(
'pipe',
'r',
),
1 => array(
'pipe',
'w',
),
2 => array(
'pipe',
'w',
),
), $pipes);
if (!is_resource($process)) {
throw new PHPUnit_Framework_Exception('Unable to create process for process isolation.');
}
if ($result !== NULL) {
$result
->startTest($test);
}
$this
->process($pipes[0], $job);
fclose($pipes[0]);
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
proc_close($process);
$this
->cleanup();
if ($result !== NULL) {
$this
->processChildResult($test, $result, $stdout, $stderr);
}
else {
return array(
'stdout' => $stdout,
'stderr' => $stderr,
);
}
}
/**
* @param resource $pipe
* @param string $job
* @since Method available since Release 3.5.12
*/
protected abstract function process($pipe, $job);
/**
* @since Method available since Release 3.5.12
*/
protected function cleanup() {
}
/**
* Processes the TestResult object from an isolated process.
*
* @param PHPUnit_Framework_TestCase $test
* @param PHPUnit_Framework_TestResult $result
* @param string $stdout
* @param string $stderr
* @since Method available since Release 3.5.0
*/
protected function processChildResult(PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result, $stdout, $stderr) {
$time = 0;
if (!empty($stderr)) {
$result
->addError($test, new PHPUnit_Framework_Exception(trim($stderr)), $time);
}
else {
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
throw new ErrorException($errstr, $errno, $errno, $errfile, $errline);
});
try {
$childResult = unserialize($stdout);
restore_error_handler();
} catch (ErrorException $e) {
restore_error_handler();
$childResult = FALSE;
$result
->addError($test, new PHPUnit_Framework_Exception(trim($stdout), 0, $e), $time);
}
if ($childResult !== FALSE) {
if (!empty($childResult['output'])) {
print $childResult['output'];
}
$test
->setResult($childResult['testResult']);
$test
->addToAssertionCount($childResult['numAssertions']);
$childResult = $childResult['result'];
if ($result
->getCollectCodeCoverageInformation()) {
$result
->getCodeCoverage()
->merge($childResult
->getCodeCoverage());
}
$time = $childResult
->time();
$notImplemented = $childResult
->notImplemented();
$skipped = $childResult
->skipped();
$errors = $childResult
->errors();
$failures = $childResult
->failures();
if (!empty($notImplemented)) {
$result
->addError($test, $this
->getException($notImplemented[0]), $time);
}
else {
if (!empty($skipped)) {
$result
->addError($test, $this
->getException($skipped[0]), $time);
}
else {
if (!empty($errors)) {
$result
->addError($test, $this
->getException($errors[0]), $time);
}
else {
if (!empty($failures)) {
$result
->addFailure($test, $this
->getException($failures[0]), $time);
}
}
}
}
}
}
$result
->endTest($test, $time);
}
/**
* Gets the thrown exception from a PHPUnit_Framework_TestFailure.
*
* @param PHPUnit_Framework_TestFailure $error
* @since Method available since Release 3.6.0
* @see https://github.com/sebastianbergmann/phpunit/issues/74
*/
protected function getException(PHPUnit_Framework_TestFailure $error) {
$exception = $error
->thrownException();
if ($exception instanceof __PHP_Incomplete_Class) {
$exceptionArray = array();
foreach ((array) $exception as $key => $value) {
$key = substr($key, strrpos($key, "\0") + 1);
$exceptionArray[$key] = $value;
}
$exception = new PHPUnit_Framework_SyntheticError(sprintf('%s: %s', $exceptionArray['_PHP_Incomplete_Class_Name'], $exceptionArray['message']), $exceptionArray['code'], $exceptionArray['file'], $exceptionArray['line'], $exceptionArray['trace']);
}
return $exception;
}
}
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
PHPUnit_Util_PHP:: |
protected | property | ||
PHPUnit_Util_PHP:: |
protected | function | @since Method available since Release 3.5.12 | 1 |
PHPUnit_Util_PHP:: |
public static | function | @since Method available since Release 3.5.12 | |
PHPUnit_Util_PHP:: |
protected | function | Gets the thrown exception from a PHPUnit_Framework_TestFailure. | |
PHPUnit_Util_PHP:: |
protected | function | Returns the path to a PHP interpreter. | |
PHPUnit_Util_PHP:: |
abstract protected | function | @since Method available since Release 3.5.12 | 2 |
PHPUnit_Util_PHP:: |
protected | function | Processes the TestResult object from an isolated process. | |
PHPUnit_Util_PHP:: |
public | function | Runs a single job (PHP code) using a separate PHP process. |