8889841cPK [ z README.mdnu W+A
# PHPUnit
[](https://packagist.org/packages/phpunit/phpunit)
[](https://github.com/sebastianbergmann/phpunit/actions)
[](https://shepherd.dev/github/sebastianbergmann/phpunit)
[](https://codecov.io/gh/sebastianbergmann/phpunit)
PHPUnit is a programmer-oriented testing framework for PHP. It is an instance of the xUnit architecture for unit testing frameworks.
## Installation
We distribute a [PHP Archive (PHAR)](https://php.net/phar) that has all required (as well as some optional) dependencies of PHPUnit bundled in a single file:
```bash
$ wget https://phar.phpunit.de/phpunit-X.Y.phar
$ php phpunit-X.Y.phar --version
```
Please replace `X.Y` with the version of PHPUnit you are interested in.
Alternatively, you may use [Composer](https://getcomposer.org/) to download and install PHPUnit as well as its dependencies. Please refer to the "[Getting Started](https://phpunit.de/getting-started-with-phpunit.html)" guide for details on how to install PHPUnit.
## Contribute
Please refer to [CONTRIBUTING.md](https://github.com/sebastianbergmann/phpunit/blob/master/.github/CONTRIBUTING.md) for information on how to contribute to PHPUnit and its related projects.
## List of Contributors
Thanks to everyone who has contributed to PHPUnit! You can find a detailed list of contributors on every PHPUnit related package on GitHub. This list shows only the major components:
* [PHPUnit](https://github.com/sebastianbergmann/phpunit/graphs/contributors)
* [php-code-coverage](https://github.com/sebastianbergmann/php-code-coverage/graphs/contributors)
A very special thanks to everyone who has contributed to the [documentation](https://github.com/sebastianbergmann/phpunit-documentation-english/graphs/contributors).
PK [O .phpstorm.meta.phpnu W+A "$0"])
);
override(
\PHPUnit\Framework\TestCase::createStub(0),
map([""=>"$0"])
);
override(
\PHPUnit\Framework\TestCase::createConfiguredMock(0),
map([""=>"$0"])
);
override(
\PHPUnit\Framework\TestCase::createPartialMock(0),
map([""=>"$0"])
);
override(
\PHPUnit\Framework\TestCase::createTestProxy(0),
map([""=>"$0"])
);
override(
\PHPUnit\Framework\TestCase::getMockForAbstractClass(0),
map([""=>"$0"])
);
}
PK [ˊʜ
phpunitnu W+A #!/usr/bin/env php
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (!version_compare(PHP_VERSION, PHP_VERSION, '=')) {
fwrite(
STDERR,
sprintf(
'%s declares an invalid value for PHP_VERSION.' . PHP_EOL .
'This breaks fundamental functionality such as version_compare().' . PHP_EOL .
'Please use a different PHP interpreter.' . PHP_EOL,
PHP_BINARY
)
);
die(1);
}
if (version_compare('7.3.0', PHP_VERSION, '>')) {
fwrite(
STDERR,
sprintf(
'This version of PHPUnit requires PHP >= 7.3.' . PHP_EOL .
'You are using PHP %s (%s).' . PHP_EOL,
PHP_VERSION,
PHP_BINARY
)
);
die(1);
}
$requiredExtensions = ['dom', 'json', 'libxml', 'mbstring', 'tokenizer', 'xml', 'xmlwriter'];
$unavailableExtensions = array_filter(
$requiredExtensions,
static function ($extension) {
return !extension_loaded($extension);
}
);
if ([] !== $unavailableExtensions) {
fwrite(
STDERR,
sprintf(
'PHPUnit requires the "%s" extensions, but the "%s" %s not available.' . PHP_EOL,
count($unavailableExtensions) === 1 ? 'extension is' : 'extensions are',
implode('", "', $requiredExtensions),
implode('", "', $unavailableExtensions)
)
);
die(1);
}
unset($requiredExtensions, $unavailableExtensions);
if (!ini_get('date.timezone')) {
ini_set('date.timezone', 'UTC');
}
if (isset($GLOBALS['_composer_autoload_path'])) {
define('PHPUNIT_COMPOSER_INSTALL', $GLOBALS['_composer_autoload_path']);
unset($GLOBALS['_composer_autoload_path']);
} else {
foreach (array(__DIR__ . '/../../autoload.php', __DIR__ . '/../vendor/autoload.php', __DIR__ . '/vendor/autoload.php') as $file) {
if (file_exists($file)) {
define('PHPUNIT_COMPOSER_INSTALL', $file);
break;
}
}
unset($file);
}
if (!defined('PHPUNIT_COMPOSER_INSTALL')) {
fwrite(
STDERR,
'You need to set up the project dependencies using Composer:' . PHP_EOL . PHP_EOL .
' composer install' . PHP_EOL . PHP_EOL .
'You can learn all about Composer on https://getcomposer.org/.' . PHP_EOL
);
die(1);
}
$options = getopt('', array('prepend:'));
if (isset($options['prepend'])) {
require $options['prepend'];
}
unset($options);
require PHPUNIT_COMPOSER_INSTALL;
PHPUnit\TextUI\Command::main();
PK []r8
src/Util/Printer.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use const ENT_COMPAT;
use const ENT_SUBSTITUTE;
use const PHP_SAPI;
use function assert;
use function count;
use function dirname;
use function explode;
use function fclose;
use function fopen;
use function fsockopen;
use function fwrite;
use function htmlspecialchars;
use function is_resource;
use function is_string;
use function sprintf;
use function str_replace;
use function strncmp;
use function strpos;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
class Printer
{
/**
* @psalm-var closed-resource|resource
*/
private $stream;
/**
* @var bool
*/
private $isPhpStream;
/**
* @param null|resource|string $out
*
* @throws Exception
*/
public function __construct($out = null)
{
if (is_resource($out)) {
$this->stream = $out;
return;
}
if (!is_string($out)) {
return;
}
if (strpos($out, 'socket://') === 0) {
$tmp = explode(':', str_replace('socket://', '', $out));
if (count($tmp) !== 2) {
throw new Exception(
sprintf(
'"%s" does not match "socket://hostname:port" format',
$out
)
);
}
$this->stream = fsockopen($tmp[0], (int) $tmp[1]);
return;
}
if (strpos($out, 'php://') === false && !Filesystem::createDirectory(dirname($out))) {
throw new Exception(
sprintf(
'Directory "%s" was not created',
dirname($out)
)
);
}
$this->stream = fopen($out, 'wb');
$this->isPhpStream = strncmp($out, 'php://', 6) !== 0;
}
public function write(string $buffer): void
{
if ($this->stream) {
assert(is_resource($this->stream));
fwrite($this->stream, $buffer);
} else {
if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
$buffer = htmlspecialchars($buffer, ENT_COMPAT | ENT_SUBSTITUTE);
}
print $buffer;
}
}
public function flush(): void
{
if ($this->stream && $this->isPhpStream) {
assert(is_resource($this->stream));
fclose($this->stream);
}
}
}
PK [T src/Util/ErrorHandler.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use const E_DEPRECATED;
use const E_NOTICE;
use const E_STRICT;
use const E_USER_DEPRECATED;
use const E_USER_NOTICE;
use const E_USER_WARNING;
use const E_WARNING;
use function error_reporting;
use function restore_error_handler;
use function set_error_handler;
use PHPUnit\Framework\Error\Deprecated;
use PHPUnit\Framework\Error\Error;
use PHPUnit\Framework\Error\Notice;
use PHPUnit\Framework\Error\Warning;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ErrorHandler
{
/**
* @var bool
*/
private $convertDeprecationsToExceptions;
/**
* @var bool
*/
private $convertErrorsToExceptions;
/**
* @var bool
*/
private $convertNoticesToExceptions;
/**
* @var bool
*/
private $convertWarningsToExceptions;
/**
* @var bool
*/
private $registered = false;
public static function invokeIgnoringWarnings(callable $callable)
{
set_error_handler(
static function ($errorNumber, $errorString)
{
if ($errorNumber === E_WARNING) {
return;
}
return false;
}
);
$result = $callable();
restore_error_handler();
return $result;
}
public function __construct(bool $convertDeprecationsToExceptions, bool $convertErrorsToExceptions, bool $convertNoticesToExceptions, bool $convertWarningsToExceptions)
{
$this->convertDeprecationsToExceptions = $convertDeprecationsToExceptions;
$this->convertErrorsToExceptions = $convertErrorsToExceptions;
$this->convertNoticesToExceptions = $convertNoticesToExceptions;
$this->convertWarningsToExceptions = $convertWarningsToExceptions;
}
public function __invoke(int $errorNumber, string $errorString, string $errorFile, int $errorLine): bool
{
/*
* Do not raise an exception when the error suppression operator (@) was used.
*
* @see https://github.com/sebastianbergmann/phpunit/issues/3739
*/
if (!($errorNumber & error_reporting())) {
return false;
}
switch ($errorNumber) {
case E_NOTICE:
case E_USER_NOTICE:
case E_STRICT:
if (!$this->convertNoticesToExceptions) {
return false;
}
throw new Notice($errorString, $errorNumber, $errorFile, $errorLine);
case E_WARNING:
case E_USER_WARNING:
if (!$this->convertWarningsToExceptions) {
return false;
}
throw new Warning($errorString, $errorNumber, $errorFile, $errorLine);
case E_DEPRECATED:
case E_USER_DEPRECATED:
if (!$this->convertDeprecationsToExceptions) {
return false;
}
throw new Deprecated($errorString, $errorNumber, $errorFile, $errorLine);
default:
if (!$this->convertErrorsToExceptions) {
return false;
}
throw new Error($errorString, $errorNumber, $errorFile, $errorLine);
}
}
public function register(): void
{
if ($this->registered) {
return;
}
$oldErrorHandler = set_error_handler($this);
if ($oldErrorHandler !== null) {
restore_error_handler();
return;
}
$this->registered = true;
}
public function unregister(): void
{
if (!$this->registered) {
return;
}
restore_error_handler();
}
}
PK [q src/Util/Type.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Type
{
public static function isType(string $type): bool
{
switch ($type) {
case 'numeric':
case 'integer':
case 'int':
case 'iterable':
case 'float':
case 'string':
case 'boolean':
case 'bool':
case 'null':
case 'array':
case 'object':
case 'resource':
case 'scalar':
return true;
default:
return false;
}
}
}
PK [If f src/Util/Json.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use const JSON_PRETTY_PRINT;
use const JSON_UNESCAPED_SLASHES;
use const JSON_UNESCAPED_UNICODE;
use function count;
use function is_array;
use function is_object;
use function json_decode;
use function json_encode;
use function json_last_error;
use function ksort;
use PHPUnit\Framework\Exception;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Json
{
/**
* Prettify json string.
*
* @throws \PHPUnit\Framework\Exception
*/
public static function prettify(string $json): string
{
$decodedJson = json_decode($json, false);
if (json_last_error()) {
throw new Exception(
'Cannot prettify invalid json'
);
}
return json_encode($decodedJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
/**
* To allow comparison of JSON strings, first process them into a consistent
* format so that they can be compared as strings.
*
* @return array ($error, $canonicalized_json) The $error parameter is used
* to indicate an error decoding the json. This is used to avoid ambiguity
* with JSON strings consisting entirely of 'null' or 'false'.
*/
public static function canonicalize(string $json): array
{
$decodedJson = json_decode($json);
if (json_last_error()) {
return [true, null];
}
self::recursiveSort($decodedJson);
$reencodedJson = json_encode($decodedJson);
return [false, $reencodedJson];
}
/**
* JSON object keys are unordered while PHP array keys are ordered.
*
* Sort all array keys to ensure both the expected and actual values have
* their keys in the same order.
*/
private static function recursiveSort(&$json): void
{
if (!is_array($json)) {
// If the object is not empty, change it to an associative array
// so we can sort the keys (and we will still re-encode it
// correctly, since PHP encodes associative arrays as JSON objects.)
// But EMPTY objects MUST remain empty objects. (Otherwise we will
// re-encode it as a JSON array rather than a JSON object.)
// See #2919.
if (is_object($json) && count((array) $json) > 0) {
$json = (array) $json;
} else {
return;
}
}
ksort($json);
foreach ($json as $key => &$value) {
self::recursiveSort($value);
}
}
}
PK [tqHH src/Util/Color.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use const DIRECTORY_SEPARATOR;
use function array_keys;
use function array_map;
use function array_values;
use function count;
use function explode;
use function implode;
use function min;
use function preg_replace;
use function preg_replace_callback;
use function sprintf;
use function strtr;
use function trim;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Color
{
/**
* @var array
*/
private const WHITESPACE_MAP = [
' ' => '·',
"\t" => '⇥',
];
/**
* @var array
*/
private const WHITESPACE_EOL_MAP = [
' ' => '·',
"\t" => '⇥',
"\n" => '↵',
"\r" => '⟵',
];
/**
* @var array
*/
private static $ansiCodes = [
'reset' => '0',
'bold' => '1',
'dim' => '2',
'dim-reset' => '22',
'underlined' => '4',
'fg-default' => '39',
'fg-black' => '30',
'fg-red' => '31',
'fg-green' => '32',
'fg-yellow' => '33',
'fg-blue' => '34',
'fg-magenta' => '35',
'fg-cyan' => '36',
'fg-white' => '37',
'bg-default' => '49',
'bg-black' => '40',
'bg-red' => '41',
'bg-green' => '42',
'bg-yellow' => '43',
'bg-blue' => '44',
'bg-magenta' => '45',
'bg-cyan' => '46',
'bg-white' => '47',
];
public static function colorize(string $color, string $buffer): string
{
if (trim($buffer) === '') {
return $buffer;
}
$codes = array_map('\trim', explode(',', $color));
$styles = [];
foreach ($codes as $code) {
if (isset(self::$ansiCodes[$code])) {
$styles[] = self::$ansiCodes[$code] ?? '';
}
}
if (empty($styles)) {
return $buffer;
}
return self::optimizeColor(sprintf("\x1b[%sm", implode(';', $styles)) . $buffer . "\x1b[0m");
}
public static function colorizePath(string $path, ?string $prevPath = null, bool $colorizeFilename = false): string
{
if ($prevPath === null) {
$prevPath = '';
}
$path = explode(DIRECTORY_SEPARATOR, $path);
$prevPath = explode(DIRECTORY_SEPARATOR, $prevPath);
for ($i = 0; $i < min(count($path), count($prevPath)); $i++) {
if ($path[$i] == $prevPath[$i]) {
$path[$i] = self::dim($path[$i]);
}
}
if ($colorizeFilename) {
$last = count($path) - 1;
$path[$last] = preg_replace_callback(
'/([\-_\.]+|phpt$)/',
static function ($matches)
{
return self::dim($matches[0]);
},
$path[$last]
);
}
return self::optimizeColor(implode(self::dim(DIRECTORY_SEPARATOR), $path));
}
public static function dim(string $buffer): string
{
if (trim($buffer) === '') {
return $buffer;
}
return "\e[2m{$buffer}\e[22m";
}
public static function visualizeWhitespace(string $buffer, bool $visualizeEOL = false): string
{
$replaceMap = $visualizeEOL ? self::WHITESPACE_EOL_MAP : self::WHITESPACE_MAP;
return preg_replace_callback('/\s+/', static function ($matches) use ($replaceMap)
{
return self::dim(strtr($matches[0], $replaceMap));
}, $buffer);
}
private static function optimizeColor(string $buffer): string
{
$patterns = [
"/\e\\[22m\e\\[2m/" => '',
"/\e\\[([^m]*)m\e\\[([1-9][0-9;]*)m/" => "\e[$1;$2m",
"/(\e\\[[^m]*m)+(\e\\[0m)/" => '$2',
];
return preg_replace(array_keys($patterns), array_values($patterns), $buffer);
}
}
PK [̹}-_
_
src/Util/Filter.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use function array_unshift;
use function defined;
use function in_array;
use function is_file;
use function realpath;
use function sprintf;
use function strpos;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\SyntheticError;
use Throwable;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Filter
{
/**
* @throws Exception
*/
public static function getFilteredStacktrace(Throwable $t): string
{
$filteredStacktrace = '';
if ($t instanceof SyntheticError) {
$eTrace = $t->getSyntheticTrace();
$eFile = $t->getSyntheticFile();
$eLine = $t->getSyntheticLine();
} elseif ($t instanceof Exception) {
$eTrace = $t->getSerializableTrace();
$eFile = $t->getFile();
$eLine = $t->getLine();
} else {
if ($t->getPrevious()) {
$t = $t->getPrevious();
}
$eTrace = $t->getTrace();
$eFile = $t->getFile();
$eLine = $t->getLine();
}
if (!self::frameExists($eTrace, $eFile, $eLine)) {
array_unshift(
$eTrace,
['file' => $eFile, 'line' => $eLine]
);
}
$prefix = defined('__PHPUNIT_PHAR_ROOT__') ? __PHPUNIT_PHAR_ROOT__ : false;
$excludeList = new ExcludeList;
foreach ($eTrace as $frame) {
if (self::shouldPrintFrame($frame, $prefix, $excludeList)) {
$filteredStacktrace .= sprintf(
"%s:%s\n",
$frame['file'],
$frame['line'] ?? '?'
);
}
}
return $filteredStacktrace;
}
private static function shouldPrintFrame(array $frame, $prefix, ExcludeList $excludeList): bool
{
if (!isset($frame['file'])) {
return false;
}
$file = $frame['file'];
$fileIsNotPrefixed = $prefix === false || strpos($file, $prefix) !== 0;
// @see https://github.com/sebastianbergmann/phpunit/issues/4033
if (isset($GLOBALS['_SERVER']['SCRIPT_NAME'])) {
$script = realpath($GLOBALS['_SERVER']['SCRIPT_NAME']);
} else {
$script = '';
}
return is_file($file) &&
self::fileIsExcluded($file, $excludeList) &&
$fileIsNotPrefixed &&
$file !== $script;
}
private static function fileIsExcluded(string $file, ExcludeList $excludeList): bool
{
return (empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) ||
!in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], true)) &&
!$excludeList->isExcluded($file);
}
private static function frameExists(array $trace, string $file, int $line): bool
{
foreach ($trace as $frame) {
if (isset($frame['file'], $frame['line']) && $frame['file'] === $file && $frame['line'] === $line) {
return true;
}
}
return false;
}
}
PK [i &