diff --git a/.gitattributes b/.gitattributes index 8507199..5f34008 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,5 +6,5 @@ tools/ export-ignore /.gitignore export-ignore /phpunit.xml export-ignore /phive.xml export-ignore -/php_cs.dist export-ignore +.php-cs-fixer.dist.php export-ignore /psalm.xml export-ignore diff --git a/.github/workflows/analyzers.yaml b/.github/workflows/analyzers.yaml index d24c53e..1902284 100644 --- a/.github/workflows/analyzers.yaml +++ b/.github/workflows/analyzers.yaml @@ -7,7 +7,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-versions: ['7.4'] + php-versions: ['7.4', '8.0', '8.1'] fail-fast: false name: PHP ${{ matrix.php-versions }} @ ${{ matrix.operating-system }} steps: diff --git a/.github/workflows/code-style.yaml b/.github/workflows/code-style.yaml index 6c2a0d1..9ec2876 100644 --- a/.github/workflows/code-style.yaml +++ b/.github/workflows/code-style.yaml @@ -7,7 +7,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-versions: ['7.4'] + php-versions: ['7.4', '8.0', '8.1'] fail-fast: false name: PHP ${{ matrix.php-versions }} @ ${{ matrix.operating-system }} steps: diff --git a/.github/workflows/functional.yaml b/.github/workflows/functional.yaml index d80307e..5ae17eb 100644 --- a/.github/workflows/functional.yaml +++ b/.github/workflows/functional.yaml @@ -7,7 +7,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest, windows-latest, macos-latest] - php-versions: ['7.4', '8.0'] + php-versions: ['7.4', '8.0', '8.1'] fail-fast: false name: PHP ${{ matrix.php-versions }} @ ${{ matrix.operating-system }} steps: diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 72f343a..49f207a 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -7,7 +7,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-versions: ['7.4', '8.0'] + php-versions: ['7.4', '8.0', '8.1'] fail-fast: false name: PHP ${{ matrix.php-versions }} @ ${{ matrix.operating-system }} steps: diff --git a/.gitignore b/.gitignore index 2593e1d..39e2b27 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ /vendor/ .phpunit.cache -.php_cs.cache +.php-cs-fixer.cache composer.lock infection.log \ No newline at end of file diff --git a/.phive/phars.xml b/.phive/phars.xml new file mode 100644 index 0000000..cd0e142 --- /dev/null +++ b/.phive/phars.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/.php_cs.dist b/.php-cs-fixer.dist.php similarity index 88% rename from .php_cs.dist rename to .php-cs-fixer.dist.php index 46f916c..5a15f67 100644 --- a/.php_cs.dist +++ b/.php-cs-fixer.dist.php @@ -1,6 +1,6 @@ setFinder( \Symfony\Component\Finder\Finder::create() ->in([ @@ -16,7 +16,9 @@ 'array_indentation' => true, 'array_syntax' => ['syntax' => 'short'], 'backtick_to_shell_exec' => true, - 'blank_line_before_return' => true, + 'blank_line_before_statement' => [ + 'statements' => ['return'] + ], 'class_keyword_remove' => false, 'combine_consecutive_issets' => true, 'combine_consecutive_unsets' => true, @@ -48,7 +50,9 @@ 'no_blank_lines_before_namespace' => false, 'no_null_property_initialization' => true, 'no_php4_constructor' => true, - 'no_short_echo_tag' => false, + 'echo_tag_syntax' => [ + 'format' => 'long' + ], 'no_superfluous_elseif' => true, 'no_unreachable_default_argument_value' => true, 'no_useless_else' => true, @@ -62,7 +66,12 @@ 'php_unit_mock' => false, 'php_unit_namespaced' => false, 'php_unit_no_expectation_annotation' => false, - 'php_unit_ordered_covers' => true, + 'phpdoc_order_by_value' => [ + 'annotations' => ['covers'] + ], + 'php_unit_method_casing' => [ + 'case' => 'snake_case', + ], 'php_unit_set_up_tear_down_visibility' => true, 'php_unit_strict' => false, 'php_unit_test_annotation' => false, @@ -71,7 +80,7 @@ 'phpdoc_order' => true, 'phpdoc_types_order' => ['null_adjustment' => 'always_last'], 'pow_to_exponentiation' => true, - 'psr0' => true, + 'psr_autoloading' => true, 'random_api_migration' => false, 'simplified_null_return' => true, 'static_lambda' => false, diff --git a/composer.json b/composer.json index efc6838..607db1b 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ } ], "require": { - "php": "^7.4 || ^8.0", + "php": "^7.4 || ^8.0.0 || ^8.1.0", "composer-plugin-api": "~2.0" }, "require-dev": { @@ -35,7 +35,7 @@ "scripts": { "parallel": "ComposerRunParallel\\Scripts\\ParallelScript::initializeAndRun", "cs": "@fix-cs --dry-run", - "fix-cs": "./tools/php-cs-fixer.phar fix", + "fix-cs": "PHP_CS_FIXER_IGNORE_ENV=1 ./tools/php-cs-fixer.phar fix", "psalm": "./tools/psalm.phar", "tests": "./tools/phpunit.phar", "coverage": "@php ./tools/full-coverage-check.php .phpunit.cache/clover/clover.xml", diff --git a/phive.xml b/phive.xml deleted file mode 100644 index 15e812b..0000000 --- a/phive.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/psalm.xml b/psalm.xml index 2a8fa87..e8a8b3a 100644 --- a/psalm.xml +++ b/psalm.xml @@ -4,7 +4,6 @@ forbidEcho="true" strictBinaryOperands="true" phpVersion="7.4" - allowPhpStormGenerics="true" allowStringToStandInForClass="true" rememberPropertyAssignmentsAfterCall="false" skipChecksOnUnresolvableIncludes="false" diff --git a/src/Finder/PhpExecutableFinder.php b/src/Finder/PhpExecutableFinder.php index e9badaa..971b88d 100644 --- a/src/Finder/PhpExecutableFinder.php +++ b/src/Finder/PhpExecutableFinder.php @@ -36,6 +36,7 @@ public function __invoke(): string throw ParallelException::phpBinaryNotFound(); } + /** @var list $phpArgs */ $phpArgs = $this->finder->findArguments(); $phpArgs = $phpArgs ? ' '.implode(' ', $phpArgs) : ''; $allowUrlFOpenFlag = ' -d allow_url_fopen='.ProcessExecutor::escape(ini_get('allow_url_fopen')); diff --git a/src/Result/ResultMap.php b/src/Result/ResultMap.php index 66a19ac..4a4677f 100644 --- a/src/Result/ResultMap.php +++ b/src/Result/ResultMap.php @@ -81,7 +81,7 @@ public function getResultCode(): int { return array_reduce( $this->map, - fn (int $highest, int $value): int => (int) max($highest, $value), + fn (int $highest, int $value): int => max($highest, $value), 0 ); } diff --git a/tools/infection.phar b/tools/infection.phar index 4d1c20c..9c3a365 100755 Binary files a/tools/infection.phar and b/tools/infection.phar differ diff --git a/tools/php-cs-fixer.phar b/tools/php-cs-fixer.phar index 859a4c3..55d1ba0 100755 Binary files a/tools/php-cs-fixer.phar and b/tools/php-cs-fixer.phar differ diff --git a/tools/phpunit.phar b/tools/phpunit.phar index 04e4a0d..42eaa5b 100755 --- a/tools/phpunit.phar +++ b/tools/phpunit.phar @@ -1,11 +1,26 @@ #!/usr/bin/env php ')) { fwrite( STDERR, sprintf( - 'PHPUnit 9.4.4 by Sebastian Bergmann and contributors.' . PHP_EOL . PHP_EOL . - 'This version of PHPUnit is supported on PHP 7.3 and PHP 7.4.' . PHP_EOL . + 'PHPUnit 9.5.10 by Sebastian Bergmann and contributors.' . PHP_EOL . PHP_EOL . + 'This version of PHPUnit requires PHP >= 7.3.' . PHP_EOL . 'You are using PHP %s (%s).' . PHP_EOL, PHP_VERSION, PHP_BINARY @@ -15,6 +30,22 @@ if (version_compare('7.3.0', PHP_VERSION, '>')) { die(1); } +foreach (['dom', 'json', 'libxml', 'mbstring', 'tokenizer', 'xml', 'xmlwriter'] as $extension) { + if (extension_loaded($extension)) { + continue; + } + + fwrite( + STDERR, + sprintf( + 'PHPUnit requires the "%s" extension.' . PHP_EOL, + $extension + ) + ); + + die(1); +} + if (__FILE__ === realpath($_SERVER['SCRIPT_NAME'])) { $execute = true; } else { @@ -34,1049 +65,2208 @@ if (isset($options['manifest'])) { unset($options); define('__PHPUNIT_PHAR__', str_replace(DIRECTORY_SEPARATOR, '/', __FILE__)); -define('__PHPUNIT_PHAR_ROOT__', 'phar://phpunit-9.4.4.phar'); - -Phar::mapPhar('phpunit-9.4.4.phar'); - -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/DeepCopy.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/Exception/CloneException.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/Exception/PropertyException.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Filter.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/Filter/KeepFilter.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/Matcher.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php'; -require 'phar://phpunit-9.4.4.phar' . '/myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php'; -require 'phar://phpunit-9.4.4.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php'; -require 'phar://phpunit-9.4.4.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.php'; -require 'phar://phpunit-9.4.4.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php'; -require 'phar://phpunit-9.4.4.phar' . '/doctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php'; -require 'phar://phpunit-9.4.4.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Instantiator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/ActualValueIsNotAnObjectException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Assert.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/SelfDescribing.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/AssertionFailedError.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/CodeCoverageException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/ComparisonMethodDoesNotDeclareParameterTypeException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/ComparisonMethodDoesNotExistException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Constraint.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Traversable/ArrayHasKey.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Operator/Operator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Operator/BinaryOperator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Callback.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Object/ClassHasAttribute.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Object/ClassHasStaticAttribute.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Cardinality/Count.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Filesystem/DirectoryExists.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Exception/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Exception/ExceptionCode.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Exception/ExceptionMessage.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Exception/ExceptionMessageRegularExpression.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Filesystem/FileExists.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Cardinality/GreaterThan.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/IsAnything.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Cardinality/IsEmpty.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Equality/IsEqual.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Equality/IsEqualCanonicalizing.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Equality/IsEqualIgnoringCase.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Equality/IsEqualWithDelta.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Boolean/IsFalse.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Math/IsFinite.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/IsIdentical.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Math/IsInfinite.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Type/IsInstanceOf.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/String/IsJson.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Math/IsNan.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Type/IsNull.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Filesystem/IsReadable.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Boolean/IsTrue.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Type/IsType.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Filesystem/IsWritable.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/JsonMatches.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/JsonMatchesErrorMessageProvider.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Cardinality/LessThan.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Operator/LogicalAnd.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Operator/UnaryOperator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Operator/LogicalNot.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Operator/LogicalOr.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Operator/LogicalXor.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Object/ObjectEquals.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Object/ObjectHasAttribute.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/String/RegularExpression.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Cardinality/SameSize.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/String/StringContains.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/String/StringEndsWith.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/String/StringMatchesFormatDescription.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/String/StringStartsWith.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Traversable/TraversableContains.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Traversable/TraversableContainsEqual.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Traversable/TraversableContainsIdentical.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Constraint/Traversable/TraversableContainsOnly.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/RiskyTestError.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/CoveredCodeNotExecutedException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Reorderable.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Test.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/TestSuite.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/DataProviderTestSuite.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Error/Error.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Error/Deprecated.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Error/Notice.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Error/Warning.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/ExceptionWrapper.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/ExecutionOrderDependency.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/ExpectationFailedException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/IncompleteTest.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/TestCase.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/IncompleteTestCase.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/IncompleteTestError.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/InvalidArgumentException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/InvalidCoversTargetException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/InvalidDataProviderException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/InvalidParameterGroupException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/MissingCoversAnnotationException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Api/Api.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Exception/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Exception/BadMethodCallException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Builder/Identity.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Builder/InvocationStubber.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Builder/Stub.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Builder/ParametersMatch.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Builder/MethodNameMatch.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Builder/InvocationMocker.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/ConfigurableMethod.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Generator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Exception/IncompatibleReturnValueException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Invocation.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/InvocationHandler.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Matcher.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Api/Method.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/MethodNameConstraint.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/MockBuilder.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/MockType.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/MockClass.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/MockMethod.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/MockMethodSet.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Stub.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/MockObject.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/MockTrait.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Api/MockedCloneMethod.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Verifiable.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Rule/InvocationOrder.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Rule/AnyInvokedCount.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Rule/ParametersRule.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Rule/AnyParameters.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Rule/ConsecutiveParameters.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Rule/InvokedAtIndex.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Rule/InvokedAtLeastCount.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Rule/InvokedAtLeastOnce.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Rule/InvokedAtMostCount.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Rule/InvokedCount.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Rule/MethodName.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Rule/Parameters.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Exception/RuntimeException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Stub/Stub.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Stub/ConsecutiveCalls.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Stub/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Stub/ReturnArgument.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Stub/ReturnCallback.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Stub/ReturnReference.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Stub/ReturnSelf.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Stub/ReturnStub.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Stub/ReturnValueMap.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/MockObject/Api/UnmockedCloneMethod.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/NoChildTestSuiteException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/OutputError.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/SyntheticError.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/PHPTAssertionFailedError.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/SkippedTest.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/SkippedTestCase.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/SkippedTestError.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/SkippedTestSuiteError.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/SyntheticSkippedError.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/TestBuilder.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/TestFailure.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/TestListener.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/TestListenerDefaultImplementation.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/TestResult.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/TestSuiteIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/UnintentionallyCoveredCodeError.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/Exception/Warning.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Framework/WarningTestCase.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/Type.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/Application.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/ApplicationName.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/Author.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/AuthorCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/AuthorCollectionIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/xml/ManifestElement.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/xml/AuthorElement.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/xml/ElementCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/xml/AuthorElementCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/BundledComponent.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/BundledComponentCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/BundledComponentCollectionIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/xml/BundlesElement.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/xml/ComponentElement.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/xml/ComponentElementCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/xml/ContainsElement.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/xml/CopyrightElement.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/CopyrightInformation.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/exceptions/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/exceptions/ElementCollectionException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/Email.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/xml/ExtElement.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/xml/ExtElementCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/Extension.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/xml/ExtensionElement.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/exceptions/InvalidApplicationNameException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/exceptions/InvalidEmailException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/exceptions/InvalidUrlException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/Library.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/License.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/xml/LicenseElement.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/Manifest.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/xml/ManifestDocument.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/exceptions/ManifestDocumentException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/exceptions/ManifestDocumentLoadingException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/ManifestDocumentMapper.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/exceptions/ManifestDocumentMapperException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/exceptions/ManifestElementException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/ManifestLoader.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/exceptions/ManifestLoaderException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/ManifestSerializer.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/xml/PhpElement.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/Requirement.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/PhpExtensionRequirement.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/PhpVersionRequirement.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/RequirementCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/RequirementCollectionIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/xml/RequiresElement.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-manifest/values/Url.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/constraints/VersionConstraint.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/constraints/AbstractVersionConstraint.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/constraints/AndVersionConstraintGroup.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/constraints/AnyVersionConstraint.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/constraints/ExactVersionConstraint.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/exceptions/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/exceptions/InvalidPreReleaseSuffixException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/exceptions/InvalidVersionException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/constraints/OrVersionConstraintGroup.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/PreReleaseSuffix.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/constraints/SpecificMajorVersionConstraint.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/exceptions/UnsupportedVersionConstraintException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/Version.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/VersionConstraintParser.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/VersionConstraintValue.php'; -require 'phar://phpunit-9.4.4.phar' . '/phar-io-version/VersionNumber.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Builder.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/BuilderFactory.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/BuilderHelpers.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Builder/Declaration.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Builder/Class_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Builder/FunctionLike.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Builder/Function_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Builder/Interface_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Builder/Method.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Builder/Namespace_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Builder/Param.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Builder/Property.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Builder/TraitUse.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Builder/TraitUseAdaptation.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Builder/Trait_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Builder/Use_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Comment.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Comment/Doc.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/ConstExprEvaluationException.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/ConstExprEvaluator.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Error.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/ErrorHandler.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/ErrorHandler/Collecting.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/ErrorHandler/Throwing.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Internal/DiffElem.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Internal/Differ.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/NodeAbstract.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Internal/PrintableNewAnonClassNode.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Internal/TokenStream.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/JsonDecoder.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Lexer.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Lexer/Emulative.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/TokenEmulator.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/NameContext.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/NodeDumper.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/NodeFinder.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/NodeTraverserInterface.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/NodeTraverser.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/NodeVisitor.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/NodeVisitorAbstract.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/NodeVisitor/CloningVisitor.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/NodeVisitor/FindingVisitor.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/NodeVisitor/FirstFindingVisitor.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/NodeVisitor/NameResolver.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/NodeVisitor/NodeConnectingVisitor.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/NodeVisitor/ParentConnectingVisitor.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Arg.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Attribute.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/AttributeGroup.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Const_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/ArrayDimFetch.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/ArrayItem.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Array_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/FunctionLike.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/ArrowFunction.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Assign.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/AssignOp.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseOr.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseXor.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Coalesce.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Concat.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Div.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Minus.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mod.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mul.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Plus.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Pow.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftLeft.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftRight.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/AssignRef.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseOr.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseXor.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanAnd.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanOr.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Coalesce.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Concat.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Div.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Equal.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Greater.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Identical.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalAnd.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalOr.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalXor.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Minus.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mod.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mul.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotEqual.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotIdentical.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Plus.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Pow.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftLeft.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftRight.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Smaller.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Spaceship.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BitwiseNot.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/BooleanNot.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Cast.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Cast/Array_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Cast/Bool_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Cast/Double.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Cast/Int_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Cast/Object_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Cast/String_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Cast/Unset_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/ClassConstFetch.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Clone_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Closure.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/ClosureUse.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/ConstFetch.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Empty_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Error.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/ErrorSuppress.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Eval_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Exit_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/FuncCall.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Include_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Instanceof_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Isset_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/List_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Match_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/MethodCall.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/New_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/NullsafeMethodCall.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/NullsafePropertyFetch.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/PostDec.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/PostInc.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/PreDec.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/PreInc.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Print_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/PropertyFetch.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/ShellExec.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/StaticCall.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/StaticPropertyFetch.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Ternary.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Throw_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/UnaryMinus.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/UnaryPlus.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Variable.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/YieldFrom.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Expr/Yield_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Identifier.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/MatchArm.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Name.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Name/FullyQualified.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Name/Relative.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/NullableType.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Param.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Scalar.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Scalar/DNumber.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Scalar/Encapsed.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Scalar/EncapsedStringPart.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Scalar/LNumber.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Class_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Dir.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/File.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Function_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Line.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Method.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Namespace_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Trait_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Scalar/String_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Break_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Case_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Catch_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/ClassConst.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/ClassLike.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/ClassMethod.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Class_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Const_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Continue_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/DeclareDeclare.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Declare_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Do_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Echo_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/ElseIf_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Else_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Expression.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Finally_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/For_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Foreach_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Function_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Global_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Goto_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/GroupUse.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/HaltCompiler.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/If_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/InlineHTML.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Interface_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Label.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Namespace_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Nop.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Property.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/PropertyProperty.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Return_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/StaticVar.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Static_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Switch_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Throw_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/TraitUse.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Trait_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/TryCatch.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Unset_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/UseUse.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/Use_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/Stmt/While_.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/UnionType.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Node/VarLikeIdentifier.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Parser.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/ParserAbstract.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/ParserFactory.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Parser/Multiple.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Parser/Php5.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Parser/Php7.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/Parser/Tokens.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/PrettyPrinterAbstract.php'; -require 'phar://phpunit-9.4.4.phar' . '/nikic-php-parser/PhpParser/PrettyPrinter/Standard.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Hook/Hook.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Hook/TestHook.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Hook/AfterIncompleteTestHook.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Hook/AfterLastTestHook.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Hook/AfterRiskyTestHook.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Hook/AfterSkippedTestHook.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Hook/AfterSuccessfulTestHook.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Hook/AfterTestErrorHook.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Hook/AfterTestFailureHook.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Hook/AfterTestHook.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Hook/AfterTestWarningHook.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/BaseTestRunner.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Hook/BeforeFirstTestHook.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Hook/BeforeTestHook.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/TestResultCache.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/DefaultTestResultCache.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Filter/GroupFilterIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Filter/ExcludeGroupFilterIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Filter/Factory.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Filter/IncludeGroupFilterIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Filter/NameFilterIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/NullTestResultCache.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/PhptTestCase.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/ResultCacheExtension.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/TestSuiteLoader.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/StandardTestSuiteLoader.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Hook/TestListenerAdapter.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/TestSuiteSorter.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Runner/Version.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-cli-parser/exceptions/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-cli-parser/exceptions/AmbiguousOptionException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-cli-parser/exceptions/OptionDoesNotAllowArgumentException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-cli-parser/Parser.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-cli-parser/exceptions/RequiredOptionArgumentMissingException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-cli-parser/exceptions/UnknownOptionException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/BranchAndPathCoverageNotSupportedException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/CodeCoverage.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/CrapIndex.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/DeadCodeDetectionNotSupportedException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Directory.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/DirectoryCouldNotBeCreatedException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Driver/Driver.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/PathExistsButIsNotDirectoryException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Driver/PcovDriver.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/PcovNotAvailableException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Driver/PhpdbgDriver.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/PhpdbgNotAvailableException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Driver/Selector.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/WriteOperationFailedException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/WrongXdebugVersionException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Driver/Xdebug2Driver.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/Xdebug2NotEnabledException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Driver/Xdebug3Driver.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/Xdebug3NotEnabledException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/XdebugNotAvailableException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Filter.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/InvalidArgumentException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/NoCodeCoverageDriverAvailableException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Node/AbstractNode.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Node/Builder.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Node/Directory.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Node/File.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Node/Iterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/ParserException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Percentage.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/ProcessedCodeCoverageData.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/RawCodeCoverageData.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/ReflectionException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/ReportAlreadyFinalizedException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Clover.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Cobertura.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Crap4j.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Html/Renderer.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Html/Renderer/Dashboard.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Html/Renderer/Directory.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Html/Facade.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Html/Renderer/File.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/PHP.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Text.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Xml/BuildInformation.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Xml/Coverage.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Xml/Node.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Xml/Directory.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Xml/Facade.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Xml/File.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Xml/Method.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Xml/Project.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Xml/Report.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Xml/Source.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Xml/Tests.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Xml/Totals.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Report/Xml/Unit.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/StaticAnalysisCacheNotConfiguredException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/StaticAnalysis/Cache.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/StaticAnalysis/CacheWarmer.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/StaticAnalysis/CoveredFileAnalyser.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/StaticAnalysis/CachingCoveredFileAnalyser.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/StaticAnalysis/UncoveredFileAnalyser.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/StaticAnalysis/CachingUncoveredFileAnalyser.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/StaticAnalysis/CodeUnitFindingVisitor.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/StaticAnalysis/ExecutableLinesFindingVisitor.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/StaticAnalysis/IgnoredLinesFindingVisitor.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/StaticAnalysis/ParsingCoveredFileAnalyser.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/StaticAnalysis/ParsingUncoveredFileAnalyser.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/TestIdMissingException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/UnintentionallyCoveredCodeException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Version.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-code-coverage/Exception/XmlException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-code-unit-reverse-lookup/Wizard.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-code-unit/CodeUnit.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-code-unit/ClassMethodUnit.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-code-unit/ClassUnit.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-code-unit/CodeUnitCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-code-unit/CodeUnitCollectionIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-code-unit/exceptions/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-code-unit/FunctionUnit.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-code-unit/InterfaceMethodUnit.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-code-unit/InterfaceUnit.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-code-unit/exceptions/InvalidCodeUnitException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-code-unit/Mapper.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-code-unit/exceptions/NoTraitException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-code-unit/exceptions/ReflectionException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-code-unit/TraitMethodUnit.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-code-unit/TraitUnit.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-comparator/Comparator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-comparator/ArrayComparator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-comparator/ComparisonFailure.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-comparator/ObjectComparator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-comparator/DOMNodeComparator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-comparator/DateTimeComparator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-comparator/ScalarComparator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-comparator/NumericComparator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-comparator/DoubleComparator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-comparator/exceptions/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-comparator/ExceptionComparator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-comparator/Factory.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-comparator/MockObjectComparator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-comparator/ResourceComparator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-comparator/exceptions/RuntimeException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-comparator/SplObjectStorageComparator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-comparator/TypeComparator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-complexity/Calculator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-complexity/Complexity/Complexity.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-complexity/Visitor/ComplexityCalculatingVisitor.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-complexity/Complexity/ComplexityCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-complexity/Complexity/ComplexityCollectionIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-complexity/Visitor/CyclomaticComplexityCalculatingVisitor.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-complexity/Exception/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-complexity/Exception/RuntimeException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-diff/Chunk.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-diff/Exception/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-diff/Exception/InvalidArgumentException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-diff/Exception/ConfigurationException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-diff/Diff.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-diff/Differ.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-diff/Line.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-diff/LongestCommonSubsequenceCalculator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-diff/Output/DiffOutputBuilderInterface.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-diff/Output/AbstractChunkOutputBuilder.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-diff/Output/DiffOnlyOutputBuilder.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-diff/Output/UnifiedDiffOutputBuilder.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-diff/Parser.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-environment/Console.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-environment/OperatingSystem.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-environment/Runtime.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-exporter/Exporter.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-file-iterator/Facade.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-file-iterator/Factory.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-file-iterator/Iterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-global-state/CodeExporter.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-global-state/exceptions/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-global-state/ExcludeList.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-global-state/Restorer.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-global-state/exceptions/RuntimeException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-global-state/Snapshot.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-invoker/exceptions/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-invoker/Invoker.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-invoker/exceptions/ProcessControlExtensionNotLoadedException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-invoker/exceptions/TimeoutException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-lines-of-code/Counter.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-lines-of-code/Exception/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-lines-of-code/Exception/IllogicalValuesException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-lines-of-code/LineCountingVisitor.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-lines-of-code/LinesOfCode.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-lines-of-code/Exception/NegativeValueException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-lines-of-code/Exception/RuntimeException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-object-enumerator/Enumerator.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-object-enumerator/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-object-enumerator/InvalidArgumentException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-object-reflector/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-object-reflector/InvalidArgumentException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-object-reflector/ObjectReflector.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-recursion-context/Context.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-recursion-context/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-recursion-context/InvalidArgumentException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-resource-operations/ResourceOperations.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-text-template/exceptions/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-text-template/exceptions/InvalidArgumentException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-text-template/exceptions/RuntimeException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-text-template/Template.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-timer/Duration.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-timer/exceptions/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-timer/exceptions/NoActiveTimerException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-timer/ResourceUsageFormatter.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-timer/exceptions/TimeSinceStartOfRequestNotAvailableException.php'; -require 'phar://phpunit-9.4.4.phar' . '/php-timer/Timer.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-type/Type.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-type/CallableType.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-type/exception/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-type/GenericObjectType.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-type/IterableType.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-type/MixedType.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-type/NullType.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-type/ObjectType.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-type/ReflectionMapper.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-type/exception/RuntimeException.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-type/SimpleType.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-type/StaticType.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-type/TypeName.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-type/UnionType.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-type/UnknownType.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-type/VoidType.php'; -require 'phar://phpunit-9.4.4.phar' . '/sebastian-version/Version.php'; -require 'phar://phpunit-9.4.4.phar' . '/symfony-polyfill-ctype/Ctype.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/CliArguments/Builder.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/CliArguments/Configuration.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/CliArguments/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/CliArguments/Mapper.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/Command.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/ResultPrinter.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Printer.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/DefaultResultPrinter.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/Help.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/TestRunner.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/CodeCoverage/FilterMapper.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/Directory.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Clover.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Html.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Php.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Text.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Xml.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Configuration.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/PHP/Constant.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/PHP/ConstantCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/PHP/ConstantCollectionIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/Migration.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/ConvertLogTypes.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/LogToReportMigration.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCloverToReport.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCrap4jToReport.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageHtmlToReport.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoveragePhpToReport.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageTextToReport.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageXmlToReport.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Filesystem/Directory.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollectionIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/PHPUnit/Extension.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollectionIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionHandler.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Filesystem/File.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Filesystem/FileCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Filesystem/FileCollectionIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Generator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Group/Group.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Group/GroupCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Group/GroupCollectionIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Group/Groups.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/PHP/IniSetting.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/PHP/IniSettingCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/PHP/IniSettingCollectionIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/IntroduceCoverageElement.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Loader.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Logging/Junit.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Logging/Logging.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Logging/TeamCity.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Logging/TestDox/Html.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Logging/TestDox/Text.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Logging/TestDox/Xml.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Logging/Text.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/MigrationBuilder.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/MigrationBuilderException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/MigrationException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistDirectoriesToCoverage.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistExcludesToCoverage.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/PHPUnit/PHPUnit.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/PHP/Php.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/PHP/PhpHandler.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveCacheTokensAttribute.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveEmptyFilter.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveLogTypes.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectory.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollectionIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/TestSuite/TestFile.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollectionIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/TestSuite/TestSuite.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollectionIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteMapper.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/UpdateSchemaLocationTo93.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/PHP/Variable.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/PHP/VariableCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.php'; -require 'phar://phpunit-9.4.4.phar' . '/theseer-tokenizer/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/theseer-tokenizer/NamespaceUri.php'; -require 'phar://phpunit-9.4.4.phar' . '/theseer-tokenizer/NamespaceUriException.php'; -require 'phar://phpunit-9.4.4.phar' . '/theseer-tokenizer/Token.php'; -require 'phar://phpunit-9.4.4.phar' . '/theseer-tokenizer/TokenCollection.php'; -require 'phar://phpunit-9.4.4.phar' . '/theseer-tokenizer/TokenCollectionException.php'; -require 'phar://phpunit-9.4.4.phar' . '/theseer-tokenizer/Tokenizer.php'; -require 'phar://phpunit-9.4.4.phar' . '/theseer-tokenizer/XMLSerializer.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Annotation/DocBlock.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Annotation/Registry.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Blacklist.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Color.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/ErrorHandler.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/ExcludeList.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/FileLoader.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Filesystem.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Filter.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/GlobalState.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/InvalidDataSetException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Json.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Log/JUnit.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Log/TeamCity.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/PHP/AbstractPhpProcess.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/PHP/DefaultPhpProcess.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/PHP/WindowsPhpProcess.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/RegularExpression.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Test.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/TestDox/TestDoxPrinter.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/TestDox/CliTestDoxPrinter.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/TestDox/ResultPrinter.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/TestDox/HtmlResultPrinter.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/TestDox/NamePrettifier.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/TestDox/TextResultPrinter.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/TestDox/XmlResultPrinter.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/TextTestListRenderer.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Type.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/VersionComparisonOperator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/XdebugFilterScriptGenerator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Xml.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/XmlTestListRenderer.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Xml/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Xml/SchemaDetectionResult.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Xml/FailedSchemaDetectionResult.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Xml/Loader.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Xml/SchemaDetector.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Xml/SchemaFinder.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Xml/SnapshotNodeList.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Xml/SuccessfulSchemaDetectionResult.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Xml/ValidationResult.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpunit/Util/Xml/Validator.php'; -require 'phar://phpunit-9.4.4.phar' . '/webmozart-assert/Assert.php'; -require 'phar://phpunit-9.4.4.phar' . '/webmozart-assert/Mixin.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlockFactory.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Description.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Serializer.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/TagFactory.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tag.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Example.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/InvalidTag.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Link.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/TagWithType.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/See.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Since.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Source.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-common/Element.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/Exception/PcreException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-common/File.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-common/Fqsen.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/FqsenResolver.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-common/Location.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-common/Project.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-common/ProjectFactory.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Type.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/PseudoType.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Boolean.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/PseudoTypes/False_.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/PseudoTypes/True_.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/TypeResolver.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/AbstractList.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/AggregatedType.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Array_.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Callable_.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/ClassString.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Collection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Compound.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Context.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/ContextFactory.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Expression.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Float_.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Integer.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Intersection.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Iterable_.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Mixed_.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Null_.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Nullable.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Object_.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Parent_.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Resource_.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Scalar.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Self_.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Static_.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/String_.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/This.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-type-resolver/Types/Void_.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpdocumentor-reflection-docblock/Utils.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/InArrayToken.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/NotInArrayToken.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/TypeToken.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Call/Call.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Call/CallCenter.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Comparator/ClosureComparator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Comparator/Factory.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/Doubler.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/CachedDoubler.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ThrowablePatch.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/DoubleInterface.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCreator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/TypeNodeAbstract.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentTypeNode.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ReturnTypeNode.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/TypeHintReference.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/LazyDouble.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Doubler/NameGenerator.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Exception.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassTagRetriever.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/LegacyClassTagRetriever.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Prediction/PredictionInterface.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Prediction/CallPrediction.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Promise/PromiseInterface.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Promise/CallbackPromise.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Promise/ReturnPromise.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Promise/ThrowPromise.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Prophecy/Revealer.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Prophet.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Util/ExportUtil.php'; -require 'phar://phpunit-9.4.4.phar' . '/phpspec-prophecy/Prophecy/Util/StringUtil.php'; +define('__PHPUNIT_PHAR_ROOT__', 'phar://phpunit-9.5.10.phar'); + +Phar::mapPhar('phpunit-9.5.10.phar'); + +spl_autoload_register( + function ($class) { + static $classes = null; + + if ($classes === null) { + $classes = ['PHPUnit\\DeepCopy\\DeepCopy' => '/myclabs-deep-copy/DeepCopy/DeepCopy.php', + 'PHPUnit\\DeepCopy\\Exception\\CloneException' => '/myclabs-deep-copy/DeepCopy/Exception/CloneException.php', + 'PHPUnit\\DeepCopy\\Exception\\PropertyException' => '/myclabs-deep-copy/DeepCopy/Exception/PropertyException.php', + 'PHPUnit\\DeepCopy\\Filter\\Doctrine\\DoctrineCollectionFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php', + 'PHPUnit\\DeepCopy\\Filter\\Doctrine\\DoctrineEmptyCollectionFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php', + 'PHPUnit\\DeepCopy\\Filter\\Doctrine\\DoctrineProxyFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php', + 'PHPUnit\\DeepCopy\\Filter\\Filter' => '/myclabs-deep-copy/DeepCopy/Filter/Filter.php', + 'PHPUnit\\DeepCopy\\Filter\\KeepFilter' => '/myclabs-deep-copy/DeepCopy/Filter/KeepFilter.php', + 'PHPUnit\\DeepCopy\\Filter\\ReplaceFilter' => '/myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.php', + 'PHPUnit\\DeepCopy\\Filter\\SetNullFilter' => '/myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.php', + 'PHPUnit\\DeepCopy\\Matcher\\Doctrine\\DoctrineProxyMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php', + 'PHPUnit\\DeepCopy\\Matcher\\Matcher' => '/myclabs-deep-copy/DeepCopy/Matcher/Matcher.php', + 'PHPUnit\\DeepCopy\\Matcher\\PropertyMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.php', + 'PHPUnit\\DeepCopy\\Matcher\\PropertyNameMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.php', + 'PHPUnit\\DeepCopy\\Matcher\\PropertyTypeMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php', + 'PHPUnit\\DeepCopy\\Reflection\\ReflectionHelper' => '/myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php', + 'PHPUnit\\DeepCopy\\TypeFilter\\Date\\DateIntervalFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.php', + 'PHPUnit\\DeepCopy\\TypeFilter\\ReplaceFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.php', + 'PHPUnit\\DeepCopy\\TypeFilter\\ShallowCopyFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.php', + 'PHPUnit\\DeepCopy\\TypeFilter\\Spl\\ArrayObjectFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php', + 'PHPUnit\\DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedList' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php', + 'PHPUnit\\DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedListFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php', + 'PHPUnit\\DeepCopy\\TypeFilter\\TypeFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php', + 'PHPUnit\\DeepCopy\\TypeMatcher\\TypeMatcher' => '/myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php', + 'PHPUnit\\Doctrine\\Instantiator\\Exception\\ExceptionInterface' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php', + 'PHPUnit\\Doctrine\\Instantiator\\Exception\\InvalidArgumentException' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.php', + 'PHPUnit\\Doctrine\\Instantiator\\Exception\\UnexpectedValueException' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php', + 'PHPUnit\\Doctrine\\Instantiator\\Instantiator' => '/doctrine-instantiator/Doctrine/Instantiator/Instantiator.php', + 'PHPUnit\\Doctrine\\Instantiator\\InstantiatorInterface' => '/doctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php', + 'PHPUnit\\Exception' => '/phpunit/Exception.php', + 'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => '/phpunit/Framework/Exception/ActualValueIsNotAnObjectException.php', + 'PHPUnit\\Framework\\Assert' => '/phpunit/Framework/Assert.php', + 'PHPUnit\\Framework\\AssertionFailedError' => '/phpunit/Framework/Exception/AssertionFailedError.php', + 'PHPUnit\\Framework\\CodeCoverageException' => '/phpunit/Framework/Exception/CodeCoverageException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotAcceptParameterTypeException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareBoolReturnTypeException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareExactlyOneParameterException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareParameterTypeException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotDeclareParameterTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotExistException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotExistException.php', + 'PHPUnit\\Framework\\Constraint\\ArrayHasKey' => '/phpunit/Framework/Constraint/Traversable/ArrayHasKey.php', + 'PHPUnit\\Framework\\Constraint\\BinaryOperator' => '/phpunit/Framework/Constraint/Operator/BinaryOperator.php', + 'PHPUnit\\Framework\\Constraint\\Callback' => '/phpunit/Framework/Constraint/Callback.php', + 'PHPUnit\\Framework\\Constraint\\ClassHasAttribute' => '/phpunit/Framework/Constraint/Object/ClassHasAttribute.php', + 'PHPUnit\\Framework\\Constraint\\ClassHasStaticAttribute' => '/phpunit/Framework/Constraint/Object/ClassHasStaticAttribute.php', + 'PHPUnit\\Framework\\Constraint\\Constraint' => '/phpunit/Framework/Constraint/Constraint.php', + 'PHPUnit\\Framework\\Constraint\\Count' => '/phpunit/Framework/Constraint/Cardinality/Count.php', + 'PHPUnit\\Framework\\Constraint\\DirectoryExists' => '/phpunit/Framework/Constraint/Filesystem/DirectoryExists.php', + 'PHPUnit\\Framework\\Constraint\\Exception' => '/phpunit/Framework/Constraint/Exception/Exception.php', + 'PHPUnit\\Framework\\Constraint\\ExceptionCode' => '/phpunit/Framework/Constraint/Exception/ExceptionCode.php', + 'PHPUnit\\Framework\\Constraint\\ExceptionMessage' => '/phpunit/Framework/Constraint/Exception/ExceptionMessage.php', + 'PHPUnit\\Framework\\Constraint\\ExceptionMessageRegularExpression' => '/phpunit/Framework/Constraint/Exception/ExceptionMessageRegularExpression.php', + 'PHPUnit\\Framework\\Constraint\\FileExists' => '/phpunit/Framework/Constraint/Filesystem/FileExists.php', + 'PHPUnit\\Framework\\Constraint\\GreaterThan' => '/phpunit/Framework/Constraint/Cardinality/GreaterThan.php', + 'PHPUnit\\Framework\\Constraint\\IsAnything' => '/phpunit/Framework/Constraint/IsAnything.php', + 'PHPUnit\\Framework\\Constraint\\IsEmpty' => '/phpunit/Framework/Constraint/Cardinality/IsEmpty.php', + 'PHPUnit\\Framework\\Constraint\\IsEqual' => '/phpunit/Framework/Constraint/Equality/IsEqual.php', + 'PHPUnit\\Framework\\Constraint\\IsEqualCanonicalizing' => '/phpunit/Framework/Constraint/Equality/IsEqualCanonicalizing.php', + 'PHPUnit\\Framework\\Constraint\\IsEqualIgnoringCase' => '/phpunit/Framework/Constraint/Equality/IsEqualIgnoringCase.php', + 'PHPUnit\\Framework\\Constraint\\IsEqualWithDelta' => '/phpunit/Framework/Constraint/Equality/IsEqualWithDelta.php', + 'PHPUnit\\Framework\\Constraint\\IsFalse' => '/phpunit/Framework/Constraint/Boolean/IsFalse.php', + 'PHPUnit\\Framework\\Constraint\\IsFinite' => '/phpunit/Framework/Constraint/Math/IsFinite.php', + 'PHPUnit\\Framework\\Constraint\\IsIdentical' => '/phpunit/Framework/Constraint/IsIdentical.php', + 'PHPUnit\\Framework\\Constraint\\IsInfinite' => '/phpunit/Framework/Constraint/Math/IsInfinite.php', + 'PHPUnit\\Framework\\Constraint\\IsInstanceOf' => '/phpunit/Framework/Constraint/Type/IsInstanceOf.php', + 'PHPUnit\\Framework\\Constraint\\IsJson' => '/phpunit/Framework/Constraint/String/IsJson.php', + 'PHPUnit\\Framework\\Constraint\\IsNan' => '/phpunit/Framework/Constraint/Math/IsNan.php', + 'PHPUnit\\Framework\\Constraint\\IsNull' => '/phpunit/Framework/Constraint/Type/IsNull.php', + 'PHPUnit\\Framework\\Constraint\\IsReadable' => '/phpunit/Framework/Constraint/Filesystem/IsReadable.php', + 'PHPUnit\\Framework\\Constraint\\IsTrue' => '/phpunit/Framework/Constraint/Boolean/IsTrue.php', + 'PHPUnit\\Framework\\Constraint\\IsType' => '/phpunit/Framework/Constraint/Type/IsType.php', + 'PHPUnit\\Framework\\Constraint\\IsWritable' => '/phpunit/Framework/Constraint/Filesystem/IsWritable.php', + 'PHPUnit\\Framework\\Constraint\\JsonMatches' => '/phpunit/Framework/Constraint/JsonMatches.php', + 'PHPUnit\\Framework\\Constraint\\JsonMatchesErrorMessageProvider' => '/phpunit/Framework/Constraint/JsonMatchesErrorMessageProvider.php', + 'PHPUnit\\Framework\\Constraint\\LessThan' => '/phpunit/Framework/Constraint/Cardinality/LessThan.php', + 'PHPUnit\\Framework\\Constraint\\LogicalAnd' => '/phpunit/Framework/Constraint/Operator/LogicalAnd.php', + 'PHPUnit\\Framework\\Constraint\\LogicalNot' => '/phpunit/Framework/Constraint/Operator/LogicalNot.php', + 'PHPUnit\\Framework\\Constraint\\LogicalOr' => '/phpunit/Framework/Constraint/Operator/LogicalOr.php', + 'PHPUnit\\Framework\\Constraint\\LogicalXor' => '/phpunit/Framework/Constraint/Operator/LogicalXor.php', + 'PHPUnit\\Framework\\Constraint\\ObjectEquals' => '/phpunit/Framework/Constraint/Object/ObjectEquals.php', + 'PHPUnit\\Framework\\Constraint\\ObjectHasAttribute' => '/phpunit/Framework/Constraint/Object/ObjectHasAttribute.php', + 'PHPUnit\\Framework\\Constraint\\Operator' => '/phpunit/Framework/Constraint/Operator/Operator.php', + 'PHPUnit\\Framework\\Constraint\\RegularExpression' => '/phpunit/Framework/Constraint/String/RegularExpression.php', + 'PHPUnit\\Framework\\Constraint\\SameSize' => '/phpunit/Framework/Constraint/Cardinality/SameSize.php', + 'PHPUnit\\Framework\\Constraint\\StringContains' => '/phpunit/Framework/Constraint/String/StringContains.php', + 'PHPUnit\\Framework\\Constraint\\StringEndsWith' => '/phpunit/Framework/Constraint/String/StringEndsWith.php', + 'PHPUnit\\Framework\\Constraint\\StringMatchesFormatDescription' => '/phpunit/Framework/Constraint/String/StringMatchesFormatDescription.php', + 'PHPUnit\\Framework\\Constraint\\StringStartsWith' => '/phpunit/Framework/Constraint/String/StringStartsWith.php', + 'PHPUnit\\Framework\\Constraint\\TraversableContains' => '/phpunit/Framework/Constraint/Traversable/TraversableContains.php', + 'PHPUnit\\Framework\\Constraint\\TraversableContainsEqual' => '/phpunit/Framework/Constraint/Traversable/TraversableContainsEqual.php', + 'PHPUnit\\Framework\\Constraint\\TraversableContainsIdentical' => '/phpunit/Framework/Constraint/Traversable/TraversableContainsIdentical.php', + 'PHPUnit\\Framework\\Constraint\\TraversableContainsOnly' => '/phpunit/Framework/Constraint/Traversable/TraversableContainsOnly.php', + 'PHPUnit\\Framework\\Constraint\\UnaryOperator' => '/phpunit/Framework/Constraint/Operator/UnaryOperator.php', + 'PHPUnit\\Framework\\CoveredCodeNotExecutedException' => '/phpunit/Framework/Exception/CoveredCodeNotExecutedException.php', + 'PHPUnit\\Framework\\DataProviderTestSuite' => '/phpunit/Framework/DataProviderTestSuite.php', + 'PHPUnit\\Framework\\Error' => '/phpunit/Framework/Exception/Error.php', + 'PHPUnit\\Framework\\ErrorTestCase' => '/phpunit/Framework/ErrorTestCase.php', + 'PHPUnit\\Framework\\Error\\Deprecated' => '/phpunit/Framework/Error/Deprecated.php', + 'PHPUnit\\Framework\\Error\\Error' => '/phpunit/Framework/Error/Error.php', + 'PHPUnit\\Framework\\Error\\Notice' => '/phpunit/Framework/Error/Notice.php', + 'PHPUnit\\Framework\\Error\\Warning' => '/phpunit/Framework/Error/Warning.php', + 'PHPUnit\\Framework\\Exception' => '/phpunit/Framework/Exception/Exception.php', + 'PHPUnit\\Framework\\ExceptionWrapper' => '/phpunit/Framework/ExceptionWrapper.php', + 'PHPUnit\\Framework\\ExecutionOrderDependency' => '/phpunit/Framework/ExecutionOrderDependency.php', + 'PHPUnit\\Framework\\ExpectationFailedException' => '/phpunit/Framework/Exception/ExpectationFailedException.php', + 'PHPUnit\\Framework\\IncompleteTest' => '/phpunit/Framework/IncompleteTest.php', + 'PHPUnit\\Framework\\IncompleteTestCase' => '/phpunit/Framework/IncompleteTestCase.php', + 'PHPUnit\\Framework\\IncompleteTestError' => '/phpunit/Framework/Exception/IncompleteTestError.php', + 'PHPUnit\\Framework\\InvalidArgumentException' => '/phpunit/Framework/Exception/InvalidArgumentException.php', + 'PHPUnit\\Framework\\InvalidCoversTargetException' => '/phpunit/Framework/Exception/InvalidCoversTargetException.php', + 'PHPUnit\\Framework\\InvalidDataProviderException' => '/phpunit/Framework/Exception/InvalidDataProviderException.php', + 'PHPUnit\\Framework\\InvalidParameterGroupException' => '/phpunit/Framework/InvalidParameterGroupException.php', + 'PHPUnit\\Framework\\MissingCoversAnnotationException' => '/phpunit/Framework/Exception/MissingCoversAnnotationException.php', + 'PHPUnit\\Framework\\MockObject\\Api' => '/phpunit/Framework/MockObject/Api/Api.php', + 'PHPUnit\\Framework\\MockObject\\BadMethodCallException' => '/phpunit/Framework/MockObject/Exception/BadMethodCallException.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\Identity' => '/phpunit/Framework/MockObject/Builder/Identity.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationMocker' => '/phpunit/Framework/MockObject/Builder/InvocationMocker.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationStubber' => '/phpunit/Framework/MockObject/Builder/InvocationStubber.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\MethodNameMatch' => '/phpunit/Framework/MockObject/Builder/MethodNameMatch.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\ParametersMatch' => '/phpunit/Framework/MockObject/Builder/ParametersMatch.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\Stub' => '/phpunit/Framework/MockObject/Builder/Stub.php', + 'PHPUnit\\Framework\\MockObject\\CannotUseAddMethodsException' => '/phpunit/Framework/MockObject/Exception/CannotUseAddMethodsException.php', + 'PHPUnit\\Framework\\MockObject\\CannotUseOnlyMethodsException' => '/phpunit/Framework/MockObject/Exception/CannotUseOnlyMethodsException.php', + 'PHPUnit\\Framework\\MockObject\\ClassAlreadyExistsException' => '/phpunit/Framework/MockObject/Exception/ClassAlreadyExistsException.php', + 'PHPUnit\\Framework\\MockObject\\ClassIsFinalException' => '/phpunit/Framework/MockObject/Exception/ClassIsFinalException.php', + 'PHPUnit\\Framework\\MockObject\\ConfigurableMethod' => '/phpunit/Framework/MockObject/ConfigurableMethod.php', + 'PHPUnit\\Framework\\MockObject\\ConfigurableMethodsAlreadyInitializedException' => '/phpunit/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php', + 'PHPUnit\\Framework\\MockObject\\DuplicateMethodException' => '/phpunit/Framework/MockObject/Exception/DuplicateMethodException.php', + 'PHPUnit\\Framework\\MockObject\\Exception' => '/phpunit/Framework/MockObject/Exception/Exception.php', + 'PHPUnit\\Framework\\MockObject\\Generator' => '/phpunit/Framework/MockObject/Generator.php', + 'PHPUnit\\Framework\\MockObject\\IncompatibleReturnValueException' => '/phpunit/Framework/MockObject/Exception/IncompatibleReturnValueException.php', + 'PHPUnit\\Framework\\MockObject\\InvalidMethodNameException' => '/phpunit/Framework/MockObject/Exception/InvalidMethodNameException.php', + 'PHPUnit\\Framework\\MockObject\\Invocation' => '/phpunit/Framework/MockObject/Invocation.php', + 'PHPUnit\\Framework\\MockObject\\InvocationHandler' => '/phpunit/Framework/MockObject/InvocationHandler.php', + 'PHPUnit\\Framework\\MockObject\\MatchBuilderNotFoundException' => '/phpunit/Framework/MockObject/Exception/MatchBuilderNotFoundException.php', + 'PHPUnit\\Framework\\MockObject\\Matcher' => '/phpunit/Framework/MockObject/Matcher.php', + 'PHPUnit\\Framework\\MockObject\\MatcherAlreadyRegisteredException' => '/phpunit/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.php', + 'PHPUnit\\Framework\\MockObject\\Method' => '/phpunit/Framework/MockObject/Api/Method.php', + 'PHPUnit\\Framework\\MockObject\\MethodCannotBeConfiguredException' => '/phpunit/Framework/MockObject/Exception/MethodCannotBeConfiguredException.php', + 'PHPUnit\\Framework\\MockObject\\MethodNameAlreadyConfiguredException' => '/phpunit/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.php', + 'PHPUnit\\Framework\\MockObject\\MethodNameConstraint' => '/phpunit/Framework/MockObject/MethodNameConstraint.php', + 'PHPUnit\\Framework\\MockObject\\MethodNameNotConfiguredException' => '/phpunit/Framework/MockObject/Exception/MethodNameNotConfiguredException.php', + 'PHPUnit\\Framework\\MockObject\\MethodParametersAlreadyConfiguredException' => '/phpunit/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.php', + 'PHPUnit\\Framework\\MockObject\\MockBuilder' => '/phpunit/Framework/MockObject/MockBuilder.php', + 'PHPUnit\\Framework\\MockObject\\MockClass' => '/phpunit/Framework/MockObject/MockClass.php', + 'PHPUnit\\Framework\\MockObject\\MockMethod' => '/phpunit/Framework/MockObject/MockMethod.php', + 'PHPUnit\\Framework\\MockObject\\MockMethodSet' => '/phpunit/Framework/MockObject/MockMethodSet.php', + 'PHPUnit\\Framework\\MockObject\\MockObject' => '/phpunit/Framework/MockObject/MockObject.php', + 'PHPUnit\\Framework\\MockObject\\MockTrait' => '/phpunit/Framework/MockObject/MockTrait.php', + 'PHPUnit\\Framework\\MockObject\\MockType' => '/phpunit/Framework/MockObject/MockType.php', + 'PHPUnit\\Framework\\MockObject\\MockedCloneMethod' => '/phpunit/Framework/MockObject/Api/MockedCloneMethod.php', + 'PHPUnit\\Framework\\MockObject\\OriginalConstructorInvocationRequiredException' => '/phpunit/Framework/MockObject/Exception/OriginalConstructorInvocationRequiredException.php', + 'PHPUnit\\Framework\\MockObject\\ReflectionException' => '/phpunit/Framework/MockObject/Exception/ReflectionException.php', + 'PHPUnit\\Framework\\MockObject\\ReturnValueNotConfiguredException' => '/phpunit/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\AnyInvokedCount' => '/phpunit/Framework/MockObject/Rule/AnyInvokedCount.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\AnyParameters' => '/phpunit/Framework/MockObject/Rule/AnyParameters.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\ConsecutiveParameters' => '/phpunit/Framework/MockObject/Rule/ConsecutiveParameters.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvocationOrder' => '/phpunit/Framework/MockObject/Rule/InvocationOrder.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtIndex' => '/phpunit/Framework/MockObject/Rule/InvokedAtIndex.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtLeastCount' => '/phpunit/Framework/MockObject/Rule/InvokedAtLeastCount.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtLeastOnce' => '/phpunit/Framework/MockObject/Rule/InvokedAtLeastOnce.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtMostCount' => '/phpunit/Framework/MockObject/Rule/InvokedAtMostCount.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedCount' => '/phpunit/Framework/MockObject/Rule/InvokedCount.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\MethodName' => '/phpunit/Framework/MockObject/Rule/MethodName.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\Parameters' => '/phpunit/Framework/MockObject/Rule/Parameters.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\ParametersRule' => '/phpunit/Framework/MockObject/Rule/ParametersRule.php', + 'PHPUnit\\Framework\\MockObject\\RuntimeException' => '/phpunit/Framework/MockObject/Exception/RuntimeException.php', + 'PHPUnit\\Framework\\MockObject\\SoapExtensionNotAvailableException' => '/phpunit/Framework/MockObject/Exception/SoapExtensionNotAvailableException.php', + 'PHPUnit\\Framework\\MockObject\\Stub' => '/phpunit/Framework/MockObject/Stub.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ConsecutiveCalls' => '/phpunit/Framework/MockObject/Stub/ConsecutiveCalls.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\Exception' => '/phpunit/Framework/MockObject/Stub/Exception.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnArgument' => '/phpunit/Framework/MockObject/Stub/ReturnArgument.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnCallback' => '/phpunit/Framework/MockObject/Stub/ReturnCallback.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnReference' => '/phpunit/Framework/MockObject/Stub/ReturnReference.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnSelf' => '/phpunit/Framework/MockObject/Stub/ReturnSelf.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnStub' => '/phpunit/Framework/MockObject/Stub/ReturnStub.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnValueMap' => '/phpunit/Framework/MockObject/Stub/ReturnValueMap.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\Stub' => '/phpunit/Framework/MockObject/Stub/Stub.php', + 'PHPUnit\\Framework\\MockObject\\UnknownClassException' => '/phpunit/Framework/MockObject/Exception/UnknownClassException.php', + 'PHPUnit\\Framework\\MockObject\\UnknownTraitException' => '/phpunit/Framework/MockObject/Exception/UnknownTraitException.php', + 'PHPUnit\\Framework\\MockObject\\UnknownTypeException' => '/phpunit/Framework/MockObject/Exception/UnknownTypeException.php', + 'PHPUnit\\Framework\\MockObject\\UnmockedCloneMethod' => '/phpunit/Framework/MockObject/Api/UnmockedCloneMethod.php', + 'PHPUnit\\Framework\\MockObject\\Verifiable' => '/phpunit/Framework/MockObject/Verifiable.php', + 'PHPUnit\\Framework\\NoChildTestSuiteException' => '/phpunit/Framework/Exception/NoChildTestSuiteException.php', + 'PHPUnit\\Framework\\OutputError' => '/phpunit/Framework/Exception/OutputError.php', + 'PHPUnit\\Framework\\PHPTAssertionFailedError' => '/phpunit/Framework/Exception/PHPTAssertionFailedError.php', + 'PHPUnit\\Framework\\Reorderable' => '/phpunit/Framework/Reorderable.php', + 'PHPUnit\\Framework\\RiskyTestError' => '/phpunit/Framework/Exception/RiskyTestError.php', + 'PHPUnit\\Framework\\SelfDescribing' => '/phpunit/Framework/SelfDescribing.php', + 'PHPUnit\\Framework\\SkippedTest' => '/phpunit/Framework/SkippedTest.php', + 'PHPUnit\\Framework\\SkippedTestCase' => '/phpunit/Framework/SkippedTestCase.php', + 'PHPUnit\\Framework\\SkippedTestError' => '/phpunit/Framework/Exception/SkippedTestError.php', + 'PHPUnit\\Framework\\SkippedTestSuiteError' => '/phpunit/Framework/Exception/SkippedTestSuiteError.php', + 'PHPUnit\\Framework\\SyntheticError' => '/phpunit/Framework/Exception/SyntheticError.php', + 'PHPUnit\\Framework\\SyntheticSkippedError' => '/phpunit/Framework/Exception/SyntheticSkippedError.php', + 'PHPUnit\\Framework\\Test' => '/phpunit/Framework/Test.php', + 'PHPUnit\\Framework\\TestBuilder' => '/phpunit/Framework/TestBuilder.php', + 'PHPUnit\\Framework\\TestCase' => '/phpunit/Framework/TestCase.php', + 'PHPUnit\\Framework\\TestFailure' => '/phpunit/Framework/TestFailure.php', + 'PHPUnit\\Framework\\TestListener' => '/phpunit/Framework/TestListener.php', + 'PHPUnit\\Framework\\TestListenerDefaultImplementation' => '/phpunit/Framework/TestListenerDefaultImplementation.php', + 'PHPUnit\\Framework\\TestResult' => '/phpunit/Framework/TestResult.php', + 'PHPUnit\\Framework\\TestSuite' => '/phpunit/Framework/TestSuite.php', + 'PHPUnit\\Framework\\TestSuiteIterator' => '/phpunit/Framework/TestSuiteIterator.php', + 'PHPUnit\\Framework\\UnintentionallyCoveredCodeError' => '/phpunit/Framework/Exception/UnintentionallyCoveredCodeError.php', + 'PHPUnit\\Framework\\Warning' => '/phpunit/Framework/Exception/Warning.php', + 'PHPUnit\\Framework\\WarningTestCase' => '/phpunit/Framework/WarningTestCase.php', + 'PHPUnit\\PharIo\\Manifest\\Application' => '/phar-io-manifest/values/Application.php', + 'PHPUnit\\PharIo\\Manifest\\ApplicationName' => '/phar-io-manifest/values/ApplicationName.php', + 'PHPUnit\\PharIo\\Manifest\\Author' => '/phar-io-manifest/values/Author.php', + 'PHPUnit\\PharIo\\Manifest\\AuthorCollection' => '/phar-io-manifest/values/AuthorCollection.php', + 'PHPUnit\\PharIo\\Manifest\\AuthorCollectionIterator' => '/phar-io-manifest/values/AuthorCollectionIterator.php', + 'PHPUnit\\PharIo\\Manifest\\AuthorElement' => '/phar-io-manifest/xml/AuthorElement.php', + 'PHPUnit\\PharIo\\Manifest\\AuthorElementCollection' => '/phar-io-manifest/xml/AuthorElementCollection.php', + 'PHPUnit\\PharIo\\Manifest\\BundledComponent' => '/phar-io-manifest/values/BundledComponent.php', + 'PHPUnit\\PharIo\\Manifest\\BundledComponentCollection' => '/phar-io-manifest/values/BundledComponentCollection.php', + 'PHPUnit\\PharIo\\Manifest\\BundledComponentCollectionIterator' => '/phar-io-manifest/values/BundledComponentCollectionIterator.php', + 'PHPUnit\\PharIo\\Manifest\\BundlesElement' => '/phar-io-manifest/xml/BundlesElement.php', + 'PHPUnit\\PharIo\\Manifest\\ComponentElement' => '/phar-io-manifest/xml/ComponentElement.php', + 'PHPUnit\\PharIo\\Manifest\\ComponentElementCollection' => '/phar-io-manifest/xml/ComponentElementCollection.php', + 'PHPUnit\\PharIo\\Manifest\\ContainsElement' => '/phar-io-manifest/xml/ContainsElement.php', + 'PHPUnit\\PharIo\\Manifest\\CopyrightElement' => '/phar-io-manifest/xml/CopyrightElement.php', + 'PHPUnit\\PharIo\\Manifest\\CopyrightInformation' => '/phar-io-manifest/values/CopyrightInformation.php', + 'PHPUnit\\PharIo\\Manifest\\ElementCollection' => '/phar-io-manifest/xml/ElementCollection.php', + 'PHPUnit\\PharIo\\Manifest\\ElementCollectionException' => '/phar-io-manifest/exceptions/ElementCollectionException.php', + 'PHPUnit\\PharIo\\Manifest\\Email' => '/phar-io-manifest/values/Email.php', + 'PHPUnit\\PharIo\\Manifest\\Exception' => '/phar-io-manifest/exceptions/Exception.php', + 'PHPUnit\\PharIo\\Manifest\\ExtElement' => '/phar-io-manifest/xml/ExtElement.php', + 'PHPUnit\\PharIo\\Manifest\\ExtElementCollection' => '/phar-io-manifest/xml/ExtElementCollection.php', + 'PHPUnit\\PharIo\\Manifest\\Extension' => '/phar-io-manifest/values/Extension.php', + 'PHPUnit\\PharIo\\Manifest\\ExtensionElement' => '/phar-io-manifest/xml/ExtensionElement.php', + 'PHPUnit\\PharIo\\Manifest\\InvalidApplicationNameException' => '/phar-io-manifest/exceptions/InvalidApplicationNameException.php', + 'PHPUnit\\PharIo\\Manifest\\InvalidEmailException' => '/phar-io-manifest/exceptions/InvalidEmailException.php', + 'PHPUnit\\PharIo\\Manifest\\InvalidUrlException' => '/phar-io-manifest/exceptions/InvalidUrlException.php', + 'PHPUnit\\PharIo\\Manifest\\Library' => '/phar-io-manifest/values/Library.php', + 'PHPUnit\\PharIo\\Manifest\\License' => '/phar-io-manifest/values/License.php', + 'PHPUnit\\PharIo\\Manifest\\LicenseElement' => '/phar-io-manifest/xml/LicenseElement.php', + 'PHPUnit\\PharIo\\Manifest\\Manifest' => '/phar-io-manifest/values/Manifest.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestDocument' => '/phar-io-manifest/xml/ManifestDocument.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestDocumentException' => '/phar-io-manifest/exceptions/ManifestDocumentException.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestDocumentLoadingException' => '/phar-io-manifest/exceptions/ManifestDocumentLoadingException.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestDocumentMapper' => '/phar-io-manifest/ManifestDocumentMapper.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestDocumentMapperException' => '/phar-io-manifest/exceptions/ManifestDocumentMapperException.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestElement' => '/phar-io-manifest/xml/ManifestElement.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestElementException' => '/phar-io-manifest/exceptions/ManifestElementException.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestLoader' => '/phar-io-manifest/ManifestLoader.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestLoaderException' => '/phar-io-manifest/exceptions/ManifestLoaderException.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestSerializer' => '/phar-io-manifest/ManifestSerializer.php', + 'PHPUnit\\PharIo\\Manifest\\PhpElement' => '/phar-io-manifest/xml/PhpElement.php', + 'PHPUnit\\PharIo\\Manifest\\PhpExtensionRequirement' => '/phar-io-manifest/values/PhpExtensionRequirement.php', + 'PHPUnit\\PharIo\\Manifest\\PhpVersionRequirement' => '/phar-io-manifest/values/PhpVersionRequirement.php', + 'PHPUnit\\PharIo\\Manifest\\Requirement' => '/phar-io-manifest/values/Requirement.php', + 'PHPUnit\\PharIo\\Manifest\\RequirementCollection' => '/phar-io-manifest/values/RequirementCollection.php', + 'PHPUnit\\PharIo\\Manifest\\RequirementCollectionIterator' => '/phar-io-manifest/values/RequirementCollectionIterator.php', + 'PHPUnit\\PharIo\\Manifest\\RequiresElement' => '/phar-io-manifest/xml/RequiresElement.php', + 'PHPUnit\\PharIo\\Manifest\\Type' => '/phar-io-manifest/values/Type.php', + 'PHPUnit\\PharIo\\Manifest\\Url' => '/phar-io-manifest/values/Url.php', + 'PHPUnit\\PharIo\\Version\\AbstractVersionConstraint' => '/phar-io-version/constraints/AbstractVersionConstraint.php', + 'PHPUnit\\PharIo\\Version\\AndVersionConstraintGroup' => '/phar-io-version/constraints/AndVersionConstraintGroup.php', + 'PHPUnit\\PharIo\\Version\\AnyVersionConstraint' => '/phar-io-version/constraints/AnyVersionConstraint.php', + 'PHPUnit\\PharIo\\Version\\ExactVersionConstraint' => '/phar-io-version/constraints/ExactVersionConstraint.php', + 'PHPUnit\\PharIo\\Version\\Exception' => '/phar-io-version/exceptions/Exception.php', + 'PHPUnit\\PharIo\\Version\\GreaterThanOrEqualToVersionConstraint' => '/phar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.php', + 'PHPUnit\\PharIo\\Version\\InvalidPreReleaseSuffixException' => '/phar-io-version/exceptions/InvalidPreReleaseSuffixException.php', + 'PHPUnit\\PharIo\\Version\\InvalidVersionException' => '/phar-io-version/exceptions/InvalidVersionException.php', + 'PHPUnit\\PharIo\\Version\\NoPreReleaseSuffixException' => '/phar-io-version/exceptions/NoPreReleaseSuffixException.php', + 'PHPUnit\\PharIo\\Version\\OrVersionConstraintGroup' => '/phar-io-version/constraints/OrVersionConstraintGroup.php', + 'PHPUnit\\PharIo\\Version\\PreReleaseSuffix' => '/phar-io-version/PreReleaseSuffix.php', + 'PHPUnit\\PharIo\\Version\\SpecificMajorAndMinorVersionConstraint' => '/phar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.php', + 'PHPUnit\\PharIo\\Version\\SpecificMajorVersionConstraint' => '/phar-io-version/constraints/SpecificMajorVersionConstraint.php', + 'PHPUnit\\PharIo\\Version\\UnsupportedVersionConstraintException' => '/phar-io-version/exceptions/UnsupportedVersionConstraintException.php', + 'PHPUnit\\PharIo\\Version\\Version' => '/phar-io-version/Version.php', + 'PHPUnit\\PharIo\\Version\\VersionConstraint' => '/phar-io-version/constraints/VersionConstraint.php', + 'PHPUnit\\PharIo\\Version\\VersionConstraintParser' => '/phar-io-version/VersionConstraintParser.php', + 'PHPUnit\\PharIo\\Version\\VersionConstraintValue' => '/phar-io-version/VersionConstraintValue.php', + 'PHPUnit\\PharIo\\Version\\VersionNumber' => '/phar-io-version/VersionNumber.php', + 'PHPUnit\\PhpParser\\Builder' => '/nikic-php-parser/PhpParser/Builder.php', + 'PHPUnit\\PhpParser\\BuilderFactory' => '/nikic-php-parser/PhpParser/BuilderFactory.php', + 'PHPUnit\\PhpParser\\BuilderHelpers' => '/nikic-php-parser/PhpParser/BuilderHelpers.php', + 'PHPUnit\\PhpParser\\Builder\\ClassConst' => '/nikic-php-parser/PhpParser/Builder/ClassConst.php', + 'PHPUnit\\PhpParser\\Builder\\Class_' => '/nikic-php-parser/PhpParser/Builder/Class_.php', + 'PHPUnit\\PhpParser\\Builder\\Declaration' => '/nikic-php-parser/PhpParser/Builder/Declaration.php', + 'PHPUnit\\PhpParser\\Builder\\FunctionLike' => '/nikic-php-parser/PhpParser/Builder/FunctionLike.php', + 'PHPUnit\\PhpParser\\Builder\\Function_' => '/nikic-php-parser/PhpParser/Builder/Function_.php', + 'PHPUnit\\PhpParser\\Builder\\Interface_' => '/nikic-php-parser/PhpParser/Builder/Interface_.php', + 'PHPUnit\\PhpParser\\Builder\\Method' => '/nikic-php-parser/PhpParser/Builder/Method.php', + 'PHPUnit\\PhpParser\\Builder\\Namespace_' => '/nikic-php-parser/PhpParser/Builder/Namespace_.php', + 'PHPUnit\\PhpParser\\Builder\\Param' => '/nikic-php-parser/PhpParser/Builder/Param.php', + 'PHPUnit\\PhpParser\\Builder\\Property' => '/nikic-php-parser/PhpParser/Builder/Property.php', + 'PHPUnit\\PhpParser\\Builder\\TraitUse' => '/nikic-php-parser/PhpParser/Builder/TraitUse.php', + 'PHPUnit\\PhpParser\\Builder\\TraitUseAdaptation' => '/nikic-php-parser/PhpParser/Builder/TraitUseAdaptation.php', + 'PHPUnit\\PhpParser\\Builder\\Trait_' => '/nikic-php-parser/PhpParser/Builder/Trait_.php', + 'PHPUnit\\PhpParser\\Builder\\Use_' => '/nikic-php-parser/PhpParser/Builder/Use_.php', + 'PHPUnit\\PhpParser\\Comment' => '/nikic-php-parser/PhpParser/Comment.php', + 'PHPUnit\\PhpParser\\Comment\\Doc' => '/nikic-php-parser/PhpParser/Comment/Doc.php', + 'PHPUnit\\PhpParser\\ConstExprEvaluationException' => '/nikic-php-parser/PhpParser/ConstExprEvaluationException.php', + 'PHPUnit\\PhpParser\\ConstExprEvaluator' => '/nikic-php-parser/PhpParser/ConstExprEvaluator.php', + 'PHPUnit\\PhpParser\\Error' => '/nikic-php-parser/PhpParser/Error.php', + 'PHPUnit\\PhpParser\\ErrorHandler' => '/nikic-php-parser/PhpParser/ErrorHandler.php', + 'PHPUnit\\PhpParser\\ErrorHandler\\Collecting' => '/nikic-php-parser/PhpParser/ErrorHandler/Collecting.php', + 'PHPUnit\\PhpParser\\ErrorHandler\\Throwing' => '/nikic-php-parser/PhpParser/ErrorHandler/Throwing.php', + 'PHPUnit\\PhpParser\\Internal\\DiffElem' => '/nikic-php-parser/PhpParser/Internal/DiffElem.php', + 'PHPUnit\\PhpParser\\Internal\\Differ' => '/nikic-php-parser/PhpParser/Internal/Differ.php', + 'PHPUnit\\PhpParser\\Internal\\PrintableNewAnonClassNode' => '/nikic-php-parser/PhpParser/Internal/PrintableNewAnonClassNode.php', + 'PHPUnit\\PhpParser\\Internal\\TokenStream' => '/nikic-php-parser/PhpParser/Internal/TokenStream.php', + 'PHPUnit\\PhpParser\\JsonDecoder' => '/nikic-php-parser/PhpParser/JsonDecoder.php', + 'PHPUnit\\PhpParser\\Lexer' => '/nikic-php-parser/PhpParser/Lexer.php', + 'PHPUnit\\PhpParser\\Lexer\\Emulative' => '/nikic-php-parser/PhpParser/Lexer/Emulative.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\AttributeEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\CoaleseEqualTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\EnumTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\ExplicitOctalEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\FlexibleDocStringEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\FnTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\KeywordEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\MatchTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\NullsafeTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\NumericLiteralSeparatorEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\ReadonlyTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\ReverseEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\TokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/TokenEmulator.php', + 'PHPUnit\\PhpParser\\NameContext' => '/nikic-php-parser/PhpParser/NameContext.php', + 'PHPUnit\\PhpParser\\Node' => '/nikic-php-parser/PhpParser/Node.php', + 'PHPUnit\\PhpParser\\NodeAbstract' => '/nikic-php-parser/PhpParser/NodeAbstract.php', + 'PHPUnit\\PhpParser\\NodeDumper' => '/nikic-php-parser/PhpParser/NodeDumper.php', + 'PHPUnit\\PhpParser\\NodeFinder' => '/nikic-php-parser/PhpParser/NodeFinder.php', + 'PHPUnit\\PhpParser\\NodeTraverser' => '/nikic-php-parser/PhpParser/NodeTraverser.php', + 'PHPUnit\\PhpParser\\NodeTraverserInterface' => '/nikic-php-parser/PhpParser/NodeTraverserInterface.php', + 'PHPUnit\\PhpParser\\NodeVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor.php', + 'PHPUnit\\PhpParser\\NodeVisitorAbstract' => '/nikic-php-parser/PhpParser/NodeVisitorAbstract.php', + 'PHPUnit\\PhpParser\\NodeVisitor\\CloningVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/CloningVisitor.php', + 'PHPUnit\\PhpParser\\NodeVisitor\\FindingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/FindingVisitor.php', + 'PHPUnit\\PhpParser\\NodeVisitor\\FirstFindingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/FirstFindingVisitor.php', + 'PHPUnit\\PhpParser\\NodeVisitor\\NameResolver' => '/nikic-php-parser/PhpParser/NodeVisitor/NameResolver.php', + 'PHPUnit\\PhpParser\\NodeVisitor\\NodeConnectingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/NodeConnectingVisitor.php', + 'PHPUnit\\PhpParser\\NodeVisitor\\ParentConnectingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/ParentConnectingVisitor.php', + 'PHPUnit\\PhpParser\\Node\\Arg' => '/nikic-php-parser/PhpParser/Node/Arg.php', + 'PHPUnit\\PhpParser\\Node\\Attribute' => '/nikic-php-parser/PhpParser/Node/Attribute.php', + 'PHPUnit\\PhpParser\\Node\\AttributeGroup' => '/nikic-php-parser/PhpParser/Node/AttributeGroup.php', + 'PHPUnit\\PhpParser\\Node\\ComplexType' => '/nikic-php-parser/PhpParser/Node/ComplexType.php', + 'PHPUnit\\PhpParser\\Node\\Const_' => '/nikic-php-parser/PhpParser/Node/Const_.php', + 'PHPUnit\\PhpParser\\Node\\Expr' => '/nikic-php-parser/PhpParser/Node/Expr.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\ArrayDimFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ArrayDimFetch.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\ArrayItem' => '/nikic-php-parser/PhpParser/Node/Expr/ArrayItem.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Array_' => '/nikic-php-parser/PhpParser/Node/Expr/Array_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\ArrowFunction' => '/nikic-php-parser/PhpParser/Node/Expr/ArrowFunction.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Assign' => '/nikic-php-parser/PhpParser/Node/Expr/Assign.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseAnd' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseOr' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseOr.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseXor' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseXor.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Coalesce' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Coalesce.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Concat' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Concat.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Div' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Div.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Minus' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Minus.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Mod' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mod.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Mul' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mul.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Plus' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Plus.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Pow' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Pow.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\ShiftLeft' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftLeft.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\ShiftRight' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftRight.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignRef' => '/nikic-php-parser/PhpParser/Node/Expr/AssignRef.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseOr.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseXor' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseXor.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BooleanAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanAnd.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BooleanOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanOr.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Coalesce' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Coalesce.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Concat' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Concat.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Div' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Div.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Equal' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Equal.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Greater' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Greater.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\GreaterOrEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Identical' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Identical.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalAnd.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalOr.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalXor' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalXor.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Minus' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Minus.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Mod' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mod.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Mul' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mul.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\NotEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotEqual.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\NotIdentical' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotIdentical.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Plus' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Plus.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Pow' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Pow.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\ShiftLeft' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftLeft.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\ShiftRight' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftRight.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Smaller' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Smaller.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\SmallerOrEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Spaceship' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Spaceship.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BitwiseNot' => '/nikic-php-parser/PhpParser/Node/Expr/BitwiseNot.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BooleanNot' => '/nikic-php-parser/PhpParser/Node/Expr/BooleanNot.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\CallLike' => '/nikic-php-parser/PhpParser/Node/Expr/CallLike.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Cast' => '/nikic-php-parser/PhpParser/Node/Expr/Cast.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Array_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Array_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Bool_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Bool_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Double' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Double.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Int_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Int_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Object_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Object_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\String_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/String_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Unset_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Unset_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\ClassConstFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ClassConstFetch.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Clone_' => '/nikic-php-parser/PhpParser/Node/Expr/Clone_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Closure' => '/nikic-php-parser/PhpParser/Node/Expr/Closure.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\ClosureUse' => '/nikic-php-parser/PhpParser/Node/Expr/ClosureUse.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\ConstFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ConstFetch.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Empty_' => '/nikic-php-parser/PhpParser/Node/Expr/Empty_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Error' => '/nikic-php-parser/PhpParser/Node/Expr/Error.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\ErrorSuppress' => '/nikic-php-parser/PhpParser/Node/Expr/ErrorSuppress.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Eval_' => '/nikic-php-parser/PhpParser/Node/Expr/Eval_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Exit_' => '/nikic-php-parser/PhpParser/Node/Expr/Exit_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\FuncCall' => '/nikic-php-parser/PhpParser/Node/Expr/FuncCall.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Include_' => '/nikic-php-parser/PhpParser/Node/Expr/Include_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Instanceof_' => '/nikic-php-parser/PhpParser/Node/Expr/Instanceof_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Isset_' => '/nikic-php-parser/PhpParser/Node/Expr/Isset_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\List_' => '/nikic-php-parser/PhpParser/Node/Expr/List_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Match_' => '/nikic-php-parser/PhpParser/Node/Expr/Match_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\MethodCall' => '/nikic-php-parser/PhpParser/Node/Expr/MethodCall.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\New_' => '/nikic-php-parser/PhpParser/Node/Expr/New_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\NullsafeMethodCall' => '/nikic-php-parser/PhpParser/Node/Expr/NullsafeMethodCall.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\NullsafePropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/NullsafePropertyFetch.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\PostDec' => '/nikic-php-parser/PhpParser/Node/Expr/PostDec.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\PostInc' => '/nikic-php-parser/PhpParser/Node/Expr/PostInc.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\PreDec' => '/nikic-php-parser/PhpParser/Node/Expr/PreDec.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\PreInc' => '/nikic-php-parser/PhpParser/Node/Expr/PreInc.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Print_' => '/nikic-php-parser/PhpParser/Node/Expr/Print_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\PropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/PropertyFetch.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\ShellExec' => '/nikic-php-parser/PhpParser/Node/Expr/ShellExec.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\StaticCall' => '/nikic-php-parser/PhpParser/Node/Expr/StaticCall.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\StaticPropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/StaticPropertyFetch.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Ternary' => '/nikic-php-parser/PhpParser/Node/Expr/Ternary.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Throw_' => '/nikic-php-parser/PhpParser/Node/Expr/Throw_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\UnaryMinus' => '/nikic-php-parser/PhpParser/Node/Expr/UnaryMinus.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\UnaryPlus' => '/nikic-php-parser/PhpParser/Node/Expr/UnaryPlus.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Variable' => '/nikic-php-parser/PhpParser/Node/Expr/Variable.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\YieldFrom' => '/nikic-php-parser/PhpParser/Node/Expr/YieldFrom.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Yield_' => '/nikic-php-parser/PhpParser/Node/Expr/Yield_.php', + 'PHPUnit\\PhpParser\\Node\\FunctionLike' => '/nikic-php-parser/PhpParser/Node/FunctionLike.php', + 'PHPUnit\\PhpParser\\Node\\Identifier' => '/nikic-php-parser/PhpParser/Node/Identifier.php', + 'PHPUnit\\PhpParser\\Node\\IntersectionType' => '/nikic-php-parser/PhpParser/Node/IntersectionType.php', + 'PHPUnit\\PhpParser\\Node\\MatchArm' => '/nikic-php-parser/PhpParser/Node/MatchArm.php', + 'PHPUnit\\PhpParser\\Node\\Name' => '/nikic-php-parser/PhpParser/Node/Name.php', + 'PHPUnit\\PhpParser\\Node\\Name\\FullyQualified' => '/nikic-php-parser/PhpParser/Node/Name/FullyQualified.php', + 'PHPUnit\\PhpParser\\Node\\Name\\Relative' => '/nikic-php-parser/PhpParser/Node/Name/Relative.php', + 'PHPUnit\\PhpParser\\Node\\NullableType' => '/nikic-php-parser/PhpParser/Node/NullableType.php', + 'PHPUnit\\PhpParser\\Node\\Param' => '/nikic-php-parser/PhpParser/Node/Param.php', + 'PHPUnit\\PhpParser\\Node\\Scalar' => '/nikic-php-parser/PhpParser/Node/Scalar.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\DNumber' => '/nikic-php-parser/PhpParser/Node/Scalar/DNumber.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\Encapsed' => '/nikic-php-parser/PhpParser/Node/Scalar/Encapsed.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\EncapsedStringPart' => '/nikic-php-parser/PhpParser/Node/Scalar/EncapsedStringPart.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\LNumber' => '/nikic-php-parser/PhpParser/Node/Scalar/LNumber.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Class_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Class_.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Dir' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Dir.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\File' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/File.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Function_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Function_.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Line' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Line.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Method' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Method.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Namespace_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Namespace_.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Trait_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Trait_.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\String_' => '/nikic-php-parser/PhpParser/Node/Scalar/String_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt' => '/nikic-php-parser/PhpParser/Node/Stmt.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Break_' => '/nikic-php-parser/PhpParser/Node/Stmt/Break_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Case_' => '/nikic-php-parser/PhpParser/Node/Stmt/Case_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Catch_' => '/nikic-php-parser/PhpParser/Node/Stmt/Catch_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\ClassConst' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassConst.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\ClassLike' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassLike.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\ClassMethod' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassMethod.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Class_' => '/nikic-php-parser/PhpParser/Node/Stmt/Class_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Const_' => '/nikic-php-parser/PhpParser/Node/Stmt/Const_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Continue_' => '/nikic-php-parser/PhpParser/Node/Stmt/Continue_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\DeclareDeclare' => '/nikic-php-parser/PhpParser/Node/Stmt/DeclareDeclare.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Declare_' => '/nikic-php-parser/PhpParser/Node/Stmt/Declare_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Do_' => '/nikic-php-parser/PhpParser/Node/Stmt/Do_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Echo_' => '/nikic-php-parser/PhpParser/Node/Stmt/Echo_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\ElseIf_' => '/nikic-php-parser/PhpParser/Node/Stmt/ElseIf_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Else_' => '/nikic-php-parser/PhpParser/Node/Stmt/Else_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\EnumCase' => '/nikic-php-parser/PhpParser/Node/Stmt/EnumCase.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Enum_' => '/nikic-php-parser/PhpParser/Node/Stmt/Enum_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Expression' => '/nikic-php-parser/PhpParser/Node/Stmt/Expression.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Finally_' => '/nikic-php-parser/PhpParser/Node/Stmt/Finally_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\For_' => '/nikic-php-parser/PhpParser/Node/Stmt/For_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Foreach_' => '/nikic-php-parser/PhpParser/Node/Stmt/Foreach_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Function_' => '/nikic-php-parser/PhpParser/Node/Stmt/Function_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Global_' => '/nikic-php-parser/PhpParser/Node/Stmt/Global_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Goto_' => '/nikic-php-parser/PhpParser/Node/Stmt/Goto_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\GroupUse' => '/nikic-php-parser/PhpParser/Node/Stmt/GroupUse.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\HaltCompiler' => '/nikic-php-parser/PhpParser/Node/Stmt/HaltCompiler.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\If_' => '/nikic-php-parser/PhpParser/Node/Stmt/If_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\InlineHTML' => '/nikic-php-parser/PhpParser/Node/Stmt/InlineHTML.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Interface_' => '/nikic-php-parser/PhpParser/Node/Stmt/Interface_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Label' => '/nikic-php-parser/PhpParser/Node/Stmt/Label.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Namespace_' => '/nikic-php-parser/PhpParser/Node/Stmt/Namespace_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Nop' => '/nikic-php-parser/PhpParser/Node/Stmt/Nop.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Property' => '/nikic-php-parser/PhpParser/Node/Stmt/Property.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\PropertyProperty' => '/nikic-php-parser/PhpParser/Node/Stmt/PropertyProperty.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Return_' => '/nikic-php-parser/PhpParser/Node/Stmt/Return_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\StaticVar' => '/nikic-php-parser/PhpParser/Node/Stmt/StaticVar.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Static_' => '/nikic-php-parser/PhpParser/Node/Stmt/Static_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Switch_' => '/nikic-php-parser/PhpParser/Node/Stmt/Switch_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Throw_' => '/nikic-php-parser/PhpParser/Node/Stmt/Throw_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\TraitUse' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUse.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\TraitUseAdaptation' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Alias' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Precedence' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Trait_' => '/nikic-php-parser/PhpParser/Node/Stmt/Trait_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\TryCatch' => '/nikic-php-parser/PhpParser/Node/Stmt/TryCatch.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Unset_' => '/nikic-php-parser/PhpParser/Node/Stmt/Unset_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\UseUse' => '/nikic-php-parser/PhpParser/Node/Stmt/UseUse.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Use_' => '/nikic-php-parser/PhpParser/Node/Stmt/Use_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\While_' => '/nikic-php-parser/PhpParser/Node/Stmt/While_.php', + 'PHPUnit\\PhpParser\\Node\\UnionType' => '/nikic-php-parser/PhpParser/Node/UnionType.php', + 'PHPUnit\\PhpParser\\Node\\VarLikeIdentifier' => '/nikic-php-parser/PhpParser/Node/VarLikeIdentifier.php', + 'PHPUnit\\PhpParser\\Node\\VariadicPlaceholder' => '/nikic-php-parser/PhpParser/Node/VariadicPlaceholder.php', + 'PHPUnit\\PhpParser\\Parser' => '/nikic-php-parser/PhpParser/Parser.php', + 'PHPUnit\\PhpParser\\ParserAbstract' => '/nikic-php-parser/PhpParser/ParserAbstract.php', + 'PHPUnit\\PhpParser\\ParserFactory' => '/nikic-php-parser/PhpParser/ParserFactory.php', + 'PHPUnit\\PhpParser\\Parser\\Multiple' => '/nikic-php-parser/PhpParser/Parser/Multiple.php', + 'PHPUnit\\PhpParser\\Parser\\Php5' => '/nikic-php-parser/PhpParser/Parser/Php5.php', + 'PHPUnit\\PhpParser\\Parser\\Php7' => '/nikic-php-parser/PhpParser/Parser/Php7.php', + 'PHPUnit\\PhpParser\\Parser\\Tokens' => '/nikic-php-parser/PhpParser/Parser/Tokens.php', + 'PHPUnit\\PhpParser\\PrettyPrinterAbstract' => '/nikic-php-parser/PhpParser/PrettyPrinterAbstract.php', + 'PHPUnit\\PhpParser\\PrettyPrinter\\Standard' => '/nikic-php-parser/PhpParser/PrettyPrinter/Standard.php', + 'PHPUnit\\Runner\\AfterIncompleteTestHook' => '/phpunit/Runner/Hook/AfterIncompleteTestHook.php', + 'PHPUnit\\Runner\\AfterLastTestHook' => '/phpunit/Runner/Hook/AfterLastTestHook.php', + 'PHPUnit\\Runner\\AfterRiskyTestHook' => '/phpunit/Runner/Hook/AfterRiskyTestHook.php', + 'PHPUnit\\Runner\\AfterSkippedTestHook' => '/phpunit/Runner/Hook/AfterSkippedTestHook.php', + 'PHPUnit\\Runner\\AfterSuccessfulTestHook' => '/phpunit/Runner/Hook/AfterSuccessfulTestHook.php', + 'PHPUnit\\Runner\\AfterTestErrorHook' => '/phpunit/Runner/Hook/AfterTestErrorHook.php', + 'PHPUnit\\Runner\\AfterTestFailureHook' => '/phpunit/Runner/Hook/AfterTestFailureHook.php', + 'PHPUnit\\Runner\\AfterTestHook' => '/phpunit/Runner/Hook/AfterTestHook.php', + 'PHPUnit\\Runner\\AfterTestWarningHook' => '/phpunit/Runner/Hook/AfterTestWarningHook.php', + 'PHPUnit\\Runner\\BaseTestRunner' => '/phpunit/Runner/BaseTestRunner.php', + 'PHPUnit\\Runner\\BeforeFirstTestHook' => '/phpunit/Runner/Hook/BeforeFirstTestHook.php', + 'PHPUnit\\Runner\\BeforeTestHook' => '/phpunit/Runner/Hook/BeforeTestHook.php', + 'PHPUnit\\Runner\\DefaultTestResultCache' => '/phpunit/Runner/DefaultTestResultCache.php', + 'PHPUnit\\Runner\\Exception' => '/phpunit/Runner/Exception.php', + 'PHPUnit\\Runner\\Extension\\ExtensionHandler' => '/phpunit/Runner/Extension/ExtensionHandler.php', + 'PHPUnit\\Runner\\Extension\\PharLoader' => '/phpunit/Runner/Extension/PharLoader.php', + 'PHPUnit\\Runner\\Filter\\ExcludeGroupFilterIterator' => '/phpunit/Runner/Filter/ExcludeGroupFilterIterator.php', + 'PHPUnit\\Runner\\Filter\\Factory' => '/phpunit/Runner/Filter/Factory.php', + 'PHPUnit\\Runner\\Filter\\GroupFilterIterator' => '/phpunit/Runner/Filter/GroupFilterIterator.php', + 'PHPUnit\\Runner\\Filter\\IncludeGroupFilterIterator' => '/phpunit/Runner/Filter/IncludeGroupFilterIterator.php', + 'PHPUnit\\Runner\\Filter\\NameFilterIterator' => '/phpunit/Runner/Filter/NameFilterIterator.php', + 'PHPUnit\\Runner\\Hook' => '/phpunit/Runner/Hook/Hook.php', + 'PHPUnit\\Runner\\NullTestResultCache' => '/phpunit/Runner/NullTestResultCache.php', + 'PHPUnit\\Runner\\PhptTestCase' => '/phpunit/Runner/PhptTestCase.php', + 'PHPUnit\\Runner\\ResultCacheExtension' => '/phpunit/Runner/ResultCacheExtension.php', + 'PHPUnit\\Runner\\StandardTestSuiteLoader' => '/phpunit/Runner/StandardTestSuiteLoader.php', + 'PHPUnit\\Runner\\TestHook' => '/phpunit/Runner/Hook/TestHook.php', + 'PHPUnit\\Runner\\TestListenerAdapter' => '/phpunit/Runner/Hook/TestListenerAdapter.php', + 'PHPUnit\\Runner\\TestResultCache' => '/phpunit/Runner/TestResultCache.php', + 'PHPUnit\\Runner\\TestSuiteLoader' => '/phpunit/Runner/TestSuiteLoader.php', + 'PHPUnit\\Runner\\TestSuiteSorter' => '/phpunit/Runner/TestSuiteSorter.php', + 'PHPUnit\\Runner\\Version' => '/phpunit/Runner/Version.php', + 'PHPUnit\\SebastianBergmann\\CliParser\\AmbiguousOptionException' => '/sebastian-cli-parser/exceptions/AmbiguousOptionException.php', + 'PHPUnit\\SebastianBergmann\\CliParser\\Exception' => '/sebastian-cli-parser/exceptions/Exception.php', + 'PHPUnit\\SebastianBergmann\\CliParser\\OptionDoesNotAllowArgumentException' => '/sebastian-cli-parser/exceptions/OptionDoesNotAllowArgumentException.php', + 'PHPUnit\\SebastianBergmann\\CliParser\\Parser' => '/sebastian-cli-parser/Parser.php', + 'PHPUnit\\SebastianBergmann\\CliParser\\RequiredOptionArgumentMissingException' => '/sebastian-cli-parser/exceptions/RequiredOptionArgumentMissingException.php', + 'PHPUnit\\SebastianBergmann\\CliParser\\UnknownOptionException' => '/sebastian-cli-parser/exceptions/UnknownOptionException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\BranchAndPathCoverageNotSupportedException' => '/php-code-coverage/Exception/BranchAndPathCoverageNotSupportedException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\CodeCoverage' => '/php-code-coverage/CodeCoverage.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\CrapIndex' => '/php-code-coverage/CrapIndex.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\DeadCodeDetectionNotSupportedException' => '/php-code-coverage/Exception/DeadCodeDetectionNotSupportedException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Directory' => '/php-code-coverage/Directory.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\DirectoryCouldNotBeCreatedException' => '/php-code-coverage/Exception/DirectoryCouldNotBeCreatedException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Driver' => '/php-code-coverage/Driver/Driver.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PathExistsButIsNotDirectoryException' => '/php-code-coverage/Exception/PathExistsButIsNotDirectoryException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PcovDriver' => '/php-code-coverage/Driver/PcovDriver.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PcovNotAvailableException' => '/php-code-coverage/Exception/PcovNotAvailableException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgDriver' => '/php-code-coverage/Driver/PhpdbgDriver.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgNotAvailableException' => '/php-code-coverage/Exception/PhpdbgNotAvailableException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Selector' => '/php-code-coverage/Driver/Selector.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\WriteOperationFailedException' => '/php-code-coverage/Exception/WriteOperationFailedException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\WrongXdebugVersionException' => '/php-code-coverage/Exception/WrongXdebugVersionException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2Driver' => '/php-code-coverage/Driver/Xdebug2Driver.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2NotEnabledException' => '/php-code-coverage/Exception/Xdebug2NotEnabledException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3Driver' => '/php-code-coverage/Driver/Xdebug3Driver.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3NotEnabledException' => '/php-code-coverage/Exception/Xdebug3NotEnabledException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\XdebugNotAvailableException' => '/php-code-coverage/Exception/XdebugNotAvailableException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Exception' => '/php-code-coverage/Exception/Exception.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Filter' => '/php-code-coverage/Filter.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\InvalidArgumentException' => '/php-code-coverage/Exception/InvalidArgumentException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverAvailableException' => '/php-code-coverage/Exception/NoCodeCoverageDriverAvailableException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverWithPathCoverageSupportAvailableException' => '/php-code-coverage/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\AbstractNode' => '/php-code-coverage/Node/AbstractNode.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\Builder' => '/php-code-coverage/Node/Builder.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\Directory' => '/php-code-coverage/Node/Directory.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\File' => '/php-code-coverage/Node/File.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\Iterator' => '/php-code-coverage/Node/Iterator.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\ParserException' => '/php-code-coverage/Exception/ParserException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Percentage' => '/php-code-coverage/Percentage.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\ProcessedCodeCoverageData' => '/php-code-coverage/ProcessedCodeCoverageData.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\RawCodeCoverageData' => '/php-code-coverage/RawCodeCoverageData.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\ReflectionException' => '/php-code-coverage/Exception/ReflectionException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\ReportAlreadyFinalizedException' => '/php-code-coverage/Exception/ReportAlreadyFinalizedException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Clover' => '/php-code-coverage/Report/Clover.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Cobertura' => '/php-code-coverage/Report/Cobertura.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Crap4j' => '/php-code-coverage/Report/Crap4j.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Dashboard' => '/php-code-coverage/Report/Html/Renderer/Dashboard.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Directory' => '/php-code-coverage/Report/Html/Renderer/Directory.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Facade' => '/php-code-coverage/Report/Html/Facade.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\File' => '/php-code-coverage/Report/Html/Renderer/File.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Renderer' => '/php-code-coverage/Report/Html/Renderer.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\PHP' => '/php-code-coverage/Report/PHP.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Text' => '/php-code-coverage/Report/Text.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\BuildInformation' => '/php-code-coverage/Report/Xml/BuildInformation.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Coverage' => '/php-code-coverage/Report/Xml/Coverage.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Directory' => '/php-code-coverage/Report/Xml/Directory.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Facade' => '/php-code-coverage/Report/Xml/Facade.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\File' => '/php-code-coverage/Report/Xml/File.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Method' => '/php-code-coverage/Report/Xml/Method.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Node' => '/php-code-coverage/Report/Xml/Node.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Project' => '/php-code-coverage/Report/Xml/Project.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Report' => '/php-code-coverage/Report/Xml/Report.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Source' => '/php-code-coverage/Report/Xml/Source.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Tests' => '/php-code-coverage/Report/Xml/Tests.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Totals' => '/php-code-coverage/Report/Xml/Totals.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Unit' => '/php-code-coverage/Report/Xml/Unit.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysisCacheNotConfiguredException' => '/php-code-coverage/Exception/StaticAnalysisCacheNotConfiguredException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\Cache' => '/php-code-coverage/StaticAnalysis/Cache.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CacheWarmer' => '/php-code-coverage/StaticAnalysis/CacheWarmer.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CachingCoveredFileAnalyser' => '/php-code-coverage/StaticAnalysis/CachingCoveredFileAnalyser.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CachingUncoveredFileAnalyser' => '/php-code-coverage/StaticAnalysis/CachingUncoveredFileAnalyser.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CodeUnitFindingVisitor' => '/php-code-coverage/StaticAnalysis/CodeUnitFindingVisitor.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CoveredFileAnalyser' => '/php-code-coverage/StaticAnalysis/CoveredFileAnalyser.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ExecutableLinesFindingVisitor' => '/php-code-coverage/StaticAnalysis/ExecutableLinesFindingVisitor.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\IgnoredLinesFindingVisitor' => '/php-code-coverage/StaticAnalysis/IgnoredLinesFindingVisitor.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingCoveredFileAnalyser' => '/php-code-coverage/StaticAnalysis/ParsingCoveredFileAnalyser.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingUncoveredFileAnalyser' => '/php-code-coverage/StaticAnalysis/ParsingUncoveredFileAnalyser.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\UncoveredFileAnalyser' => '/php-code-coverage/StaticAnalysis/UncoveredFileAnalyser.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\TestIdMissingException' => '/php-code-coverage/Exception/TestIdMissingException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => '/php-code-coverage/Exception/UnintentionallyCoveredCodeException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Version' => '/php-code-coverage/Version.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\XmlException' => '/php-code-coverage/Exception/XmlException.php', + 'PHPUnit\\SebastianBergmann\\CodeUnitReverseLookup\\Wizard' => '/sebastian-code-unit-reverse-lookup/Wizard.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\ClassMethodUnit' => '/sebastian-code-unit/ClassMethodUnit.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\ClassUnit' => '/sebastian-code-unit/ClassUnit.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\CodeUnit' => '/sebastian-code-unit/CodeUnit.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\CodeUnitCollection' => '/sebastian-code-unit/CodeUnitCollection.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\CodeUnitCollectionIterator' => '/sebastian-code-unit/CodeUnitCollectionIterator.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\Exception' => '/sebastian-code-unit/exceptions/Exception.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\FunctionUnit' => '/sebastian-code-unit/FunctionUnit.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\InterfaceMethodUnit' => '/sebastian-code-unit/InterfaceMethodUnit.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\InterfaceUnit' => '/sebastian-code-unit/InterfaceUnit.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\InvalidCodeUnitException' => '/sebastian-code-unit/exceptions/InvalidCodeUnitException.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\Mapper' => '/sebastian-code-unit/Mapper.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\NoTraitException' => '/sebastian-code-unit/exceptions/NoTraitException.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\ReflectionException' => '/sebastian-code-unit/exceptions/ReflectionException.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\TraitMethodUnit' => '/sebastian-code-unit/TraitMethodUnit.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\TraitUnit' => '/sebastian-code-unit/TraitUnit.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\ArrayComparator' => '/sebastian-comparator/ArrayComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\Comparator' => '/sebastian-comparator/Comparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\ComparisonFailure' => '/sebastian-comparator/ComparisonFailure.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\DOMNodeComparator' => '/sebastian-comparator/DOMNodeComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\DateTimeComparator' => '/sebastian-comparator/DateTimeComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\DoubleComparator' => '/sebastian-comparator/DoubleComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\Exception' => '/sebastian-comparator/exceptions/Exception.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\ExceptionComparator' => '/sebastian-comparator/ExceptionComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\Factory' => '/sebastian-comparator/Factory.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\MockObjectComparator' => '/sebastian-comparator/MockObjectComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\NumericComparator' => '/sebastian-comparator/NumericComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\ObjectComparator' => '/sebastian-comparator/ObjectComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\ResourceComparator' => '/sebastian-comparator/ResourceComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\RuntimeException' => '/sebastian-comparator/exceptions/RuntimeException.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\ScalarComparator' => '/sebastian-comparator/ScalarComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\SplObjectStorageComparator' => '/sebastian-comparator/SplObjectStorageComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\TypeComparator' => '/sebastian-comparator/TypeComparator.php', + 'PHPUnit\\SebastianBergmann\\Complexity\\Calculator' => '/sebastian-complexity/Calculator.php', + 'PHPUnit\\SebastianBergmann\\Complexity\\Complexity' => '/sebastian-complexity/Complexity/Complexity.php', + 'PHPUnit\\SebastianBergmann\\Complexity\\ComplexityCalculatingVisitor' => '/sebastian-complexity/Visitor/ComplexityCalculatingVisitor.php', + 'PHPUnit\\SebastianBergmann\\Complexity\\ComplexityCollection' => '/sebastian-complexity/Complexity/ComplexityCollection.php', + 'PHPUnit\\SebastianBergmann\\Complexity\\ComplexityCollectionIterator' => '/sebastian-complexity/Complexity/ComplexityCollectionIterator.php', + 'PHPUnit\\SebastianBergmann\\Complexity\\CyclomaticComplexityCalculatingVisitor' => '/sebastian-complexity/Visitor/CyclomaticComplexityCalculatingVisitor.php', + 'PHPUnit\\SebastianBergmann\\Complexity\\Exception' => '/sebastian-complexity/Exception/Exception.php', + 'PHPUnit\\SebastianBergmann\\Complexity\\RuntimeException' => '/sebastian-complexity/Exception/RuntimeException.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Chunk' => '/sebastian-diff/Chunk.php', + 'PHPUnit\\SebastianBergmann\\Diff\\ConfigurationException' => '/sebastian-diff/Exception/ConfigurationException.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Diff' => '/sebastian-diff/Diff.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Differ' => '/sebastian-diff/Differ.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Exception' => '/sebastian-diff/Exception/Exception.php', + 'PHPUnit\\SebastianBergmann\\Diff\\InvalidArgumentException' => '/sebastian-diff/Exception/InvalidArgumentException.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Line' => '/sebastian-diff/Line.php', + 'PHPUnit\\SebastianBergmann\\Diff\\LongestCommonSubsequenceCalculator' => '/sebastian-diff/LongestCommonSubsequenceCalculator.php', + 'PHPUnit\\SebastianBergmann\\Diff\\MemoryEfficientLongestCommonSubsequenceCalculator' => '/sebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Output\\AbstractChunkOutputBuilder' => '/sebastian-diff/Output/AbstractChunkOutputBuilder.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Output\\DiffOnlyOutputBuilder' => '/sebastian-diff/Output/DiffOnlyOutputBuilder.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Output\\DiffOutputBuilderInterface' => '/sebastian-diff/Output/DiffOutputBuilderInterface.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Output\\StrictUnifiedDiffOutputBuilder' => '/sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Output\\UnifiedDiffOutputBuilder' => '/sebastian-diff/Output/UnifiedDiffOutputBuilder.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Parser' => '/sebastian-diff/Parser.php', + 'PHPUnit\\SebastianBergmann\\Diff\\TimeEfficientLongestCommonSubsequenceCalculator' => '/sebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php', + 'PHPUnit\\SebastianBergmann\\Environment\\Console' => '/sebastian-environment/Console.php', + 'PHPUnit\\SebastianBergmann\\Environment\\OperatingSystem' => '/sebastian-environment/OperatingSystem.php', + 'PHPUnit\\SebastianBergmann\\Environment\\Runtime' => '/sebastian-environment/Runtime.php', + 'PHPUnit\\SebastianBergmann\\Exporter\\Exporter' => '/sebastian-exporter/Exporter.php', + 'PHPUnit\\SebastianBergmann\\FileIterator\\Facade' => '/php-file-iterator/Facade.php', + 'PHPUnit\\SebastianBergmann\\FileIterator\\Factory' => '/php-file-iterator/Factory.php', + 'PHPUnit\\SebastianBergmann\\FileIterator\\Iterator' => '/php-file-iterator/Iterator.php', + 'PHPUnit\\SebastianBergmann\\GlobalState\\CodeExporter' => '/sebastian-global-state/CodeExporter.php', + 'PHPUnit\\SebastianBergmann\\GlobalState\\Exception' => '/sebastian-global-state/exceptions/Exception.php', + 'PHPUnit\\SebastianBergmann\\GlobalState\\ExcludeList' => '/sebastian-global-state/ExcludeList.php', + 'PHPUnit\\SebastianBergmann\\GlobalState\\Restorer' => '/sebastian-global-state/Restorer.php', + 'PHPUnit\\SebastianBergmann\\GlobalState\\RuntimeException' => '/sebastian-global-state/exceptions/RuntimeException.php', + 'PHPUnit\\SebastianBergmann\\GlobalState\\Snapshot' => '/sebastian-global-state/Snapshot.php', + 'PHPUnit\\SebastianBergmann\\Invoker\\Exception' => '/php-invoker/exceptions/Exception.php', + 'PHPUnit\\SebastianBergmann\\Invoker\\Invoker' => '/php-invoker/Invoker.php', + 'PHPUnit\\SebastianBergmann\\Invoker\\ProcessControlExtensionNotLoadedException' => '/php-invoker/exceptions/ProcessControlExtensionNotLoadedException.php', + 'PHPUnit\\SebastianBergmann\\Invoker\\TimeoutException' => '/php-invoker/exceptions/TimeoutException.php', + 'PHPUnit\\SebastianBergmann\\LinesOfCode\\Counter' => '/sebastian-lines-of-code/Counter.php', + 'PHPUnit\\SebastianBergmann\\LinesOfCode\\Exception' => '/sebastian-lines-of-code/Exception/Exception.php', + 'PHPUnit\\SebastianBergmann\\LinesOfCode\\IllogicalValuesException' => '/sebastian-lines-of-code/Exception/IllogicalValuesException.php', + 'PHPUnit\\SebastianBergmann\\LinesOfCode\\LineCountingVisitor' => '/sebastian-lines-of-code/LineCountingVisitor.php', + 'PHPUnit\\SebastianBergmann\\LinesOfCode\\LinesOfCode' => '/sebastian-lines-of-code/LinesOfCode.php', + 'PHPUnit\\SebastianBergmann\\LinesOfCode\\NegativeValueException' => '/sebastian-lines-of-code/Exception/NegativeValueException.php', + 'PHPUnit\\SebastianBergmann\\LinesOfCode\\RuntimeException' => '/sebastian-lines-of-code/Exception/RuntimeException.php', + 'PHPUnit\\SebastianBergmann\\ObjectEnumerator\\Enumerator' => '/sebastian-object-enumerator/Enumerator.php', + 'PHPUnit\\SebastianBergmann\\ObjectEnumerator\\Exception' => '/sebastian-object-enumerator/Exception.php', + 'PHPUnit\\SebastianBergmann\\ObjectEnumerator\\InvalidArgumentException' => '/sebastian-object-enumerator/InvalidArgumentException.php', + 'PHPUnit\\SebastianBergmann\\ObjectReflector\\Exception' => '/sebastian-object-reflector/Exception.php', + 'PHPUnit\\SebastianBergmann\\ObjectReflector\\InvalidArgumentException' => '/sebastian-object-reflector/InvalidArgumentException.php', + 'PHPUnit\\SebastianBergmann\\ObjectReflector\\ObjectReflector' => '/sebastian-object-reflector/ObjectReflector.php', + 'PHPUnit\\SebastianBergmann\\RecursionContext\\Context' => '/sebastian-recursion-context/Context.php', + 'PHPUnit\\SebastianBergmann\\RecursionContext\\Exception' => '/sebastian-recursion-context/Exception.php', + 'PHPUnit\\SebastianBergmann\\RecursionContext\\InvalidArgumentException' => '/sebastian-recursion-context/InvalidArgumentException.php', + 'PHPUnit\\SebastianBergmann\\ResourceOperations\\ResourceOperations' => '/sebastian-resource-operations/ResourceOperations.php', + 'PHPUnit\\SebastianBergmann\\Template\\Exception' => '/php-text-template/exceptions/Exception.php', + 'PHPUnit\\SebastianBergmann\\Template\\InvalidArgumentException' => '/php-text-template/exceptions/InvalidArgumentException.php', + 'PHPUnit\\SebastianBergmann\\Template\\RuntimeException' => '/php-text-template/exceptions/RuntimeException.php', + 'PHPUnit\\SebastianBergmann\\Template\\Template' => '/php-text-template/Template.php', + 'PHPUnit\\SebastianBergmann\\Timer\\Duration' => '/php-timer/Duration.php', + 'PHPUnit\\SebastianBergmann\\Timer\\Exception' => '/php-timer/exceptions/Exception.php', + 'PHPUnit\\SebastianBergmann\\Timer\\NoActiveTimerException' => '/php-timer/exceptions/NoActiveTimerException.php', + 'PHPUnit\\SebastianBergmann\\Timer\\ResourceUsageFormatter' => '/php-timer/ResourceUsageFormatter.php', + 'PHPUnit\\SebastianBergmann\\Timer\\TimeSinceStartOfRequestNotAvailableException' => '/php-timer/exceptions/TimeSinceStartOfRequestNotAvailableException.php', + 'PHPUnit\\SebastianBergmann\\Timer\\Timer' => '/php-timer/Timer.php', + 'PHPUnit\\SebastianBergmann\\Type\\CallableType' => '/sebastian-type/CallableType.php', + 'PHPUnit\\SebastianBergmann\\Type\\Exception' => '/sebastian-type/exception/Exception.php', + 'PHPUnit\\SebastianBergmann\\Type\\FalseType' => '/sebastian-type/FalseType.php', + 'PHPUnit\\SebastianBergmann\\Type\\GenericObjectType' => '/sebastian-type/GenericObjectType.php', + 'PHPUnit\\SebastianBergmann\\Type\\IterableType' => '/sebastian-type/IterableType.php', + 'PHPUnit\\SebastianBergmann\\Type\\LogicException' => '/sebastian-type/exception/LogicException.php', + 'PHPUnit\\SebastianBergmann\\Type\\MixedType' => '/sebastian-type/MixedType.php', + 'PHPUnit\\SebastianBergmann\\Type\\NullType' => '/sebastian-type/NullType.php', + 'PHPUnit\\SebastianBergmann\\Type\\ObjectType' => '/sebastian-type/ObjectType.php', + 'PHPUnit\\SebastianBergmann\\Type\\ReflectionMapper' => '/sebastian-type/ReflectionMapper.php', + 'PHPUnit\\SebastianBergmann\\Type\\RuntimeException' => '/sebastian-type/exception/RuntimeException.php', + 'PHPUnit\\SebastianBergmann\\Type\\SimpleType' => '/sebastian-type/SimpleType.php', + 'PHPUnit\\SebastianBergmann\\Type\\StaticType' => '/sebastian-type/StaticType.php', + 'PHPUnit\\SebastianBergmann\\Type\\Type' => '/sebastian-type/Type.php', + 'PHPUnit\\SebastianBergmann\\Type\\TypeName' => '/sebastian-type/TypeName.php', + 'PHPUnit\\SebastianBergmann\\Type\\UnionType' => '/sebastian-type/UnionType.php', + 'PHPUnit\\SebastianBergmann\\Type\\UnknownType' => '/sebastian-type/UnknownType.php', + 'PHPUnit\\SebastianBergmann\\Type\\VoidType' => '/sebastian-type/VoidType.php', + 'PHPUnit\\SebastianBergmann\\Version' => '/sebastian-version/Version.php', + 'PHPUnit\\Symfony\\Polyfill\\Ctype\\Ctype' => '/symfony-polyfill-ctype/Ctype.php', + 'PHPUnit\\TextUI\\CliArguments\\Builder' => '/phpunit/TextUI/CliArguments/Builder.php', + 'PHPUnit\\TextUI\\CliArguments\\Configuration' => '/phpunit/TextUI/CliArguments/Configuration.php', + 'PHPUnit\\TextUI\\CliArguments\\Exception' => '/phpunit/TextUI/CliArguments/Exception.php', + 'PHPUnit\\TextUI\\CliArguments\\Mapper' => '/phpunit/TextUI/CliArguments/Mapper.php', + 'PHPUnit\\TextUI\\Command' => '/phpunit/TextUI/Command.php', + 'PHPUnit\\TextUI\\DefaultResultPrinter' => '/phpunit/TextUI/DefaultResultPrinter.php', + 'PHPUnit\\TextUI\\Exception' => '/phpunit/TextUI/Exception/Exception.php', + 'PHPUnit\\TextUI\\Help' => '/phpunit/TextUI/Help.php', + 'PHPUnit\\TextUI\\ReflectionException' => '/phpunit/TextUI/Exception/ReflectionException.php', + 'PHPUnit\\TextUI\\ResultPrinter' => '/phpunit/TextUI/ResultPrinter.php', + 'PHPUnit\\TextUI\\RuntimeException' => '/phpunit/TextUI/Exception/RuntimeException.php', + 'PHPUnit\\TextUI\\TestDirectoryNotFoundException' => '/phpunit/TextUI/Exception/TestDirectoryNotFoundException.php', + 'PHPUnit\\TextUI\\TestFileNotFoundException' => '/phpunit/TextUI/Exception/TestFileNotFoundException.php', + 'PHPUnit\\TextUI\\TestRunner' => '/phpunit/TextUI/TestRunner.php', + 'PHPUnit\\TextUI\\TestSuiteMapper' => '/phpunit/TextUI/TestSuiteMapper.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\CodeCoverage' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\FilterMapper' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/FilterMapper.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\Directory' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/Directory.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\DirectoryCollection' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\DirectoryCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Clover' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Clover.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Cobertura' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Crap4j' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Html' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Html.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Php' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Php.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Text' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Text.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Xml' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Xml.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Configuration' => '/phpunit/TextUI/XmlConfiguration/Configuration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Constant' => '/phpunit/TextUI/XmlConfiguration/PHP/Constant.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ConstantCollection' => '/phpunit/TextUI/XmlConfiguration/PHP/ConstantCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ConstantCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHP/ConstantCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ConvertLogTypes' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/ConvertLogTypes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCloverToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCloverToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCrap4jToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCrap4jToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageHtmlToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageHtmlToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoveragePhpToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoveragePhpToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageTextToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageTextToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageXmlToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageXmlToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Directory' => '/phpunit/TextUI/XmlConfiguration/Filesystem/Directory.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\DirectoryCollection' => '/phpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\DirectoryCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Exception' => '/phpunit/TextUI/XmlConfiguration/Exception.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Extension' => '/phpunit/TextUI/XmlConfiguration/PHPUnit/Extension.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ExtensionCollection' => '/phpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ExtensionCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\File' => '/phpunit/TextUI/XmlConfiguration/Filesystem/File.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\FileCollection' => '/phpunit/TextUI/XmlConfiguration/Filesystem/FileCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\FileCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/Filesystem/FileCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Generator' => '/phpunit/TextUI/XmlConfiguration/Generator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Group' => '/phpunit/TextUI/XmlConfiguration/Group/Group.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\GroupCollection' => '/phpunit/TextUI/XmlConfiguration/Group/GroupCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\GroupCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/Group/GroupCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Groups' => '/phpunit/TextUI/XmlConfiguration/Group/Groups.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IniSetting' => '/phpunit/TextUI/XmlConfiguration/PHP/IniSetting.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IniSettingCollection' => '/phpunit/TextUI/XmlConfiguration/PHP/IniSettingCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IniSettingCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHP/IniSettingCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IntroduceCoverageElement' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/IntroduceCoverageElement.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Loader' => '/phpunit/TextUI/XmlConfiguration/Loader.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\LogToReportMigration' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/LogToReportMigration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Junit' => '/phpunit/TextUI/XmlConfiguration/Logging/Junit.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Logging' => '/phpunit/TextUI/XmlConfiguration/Logging/Logging.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TeamCity' => '/phpunit/TextUI/XmlConfiguration/Logging/TeamCity.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Html' => '/phpunit/TextUI/XmlConfiguration/Logging/TestDox/Html.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Text' => '/phpunit/TextUI/XmlConfiguration/Logging/TestDox/Text.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Xml' => '/phpunit/TextUI/XmlConfiguration/Logging/TestDox/Xml.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Text' => '/phpunit/TextUI/XmlConfiguration/Logging/Text.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Migration' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/Migration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilder' => '/phpunit/TextUI/XmlConfiguration/Migration/MigrationBuilder.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilderException' => '/phpunit/TextUI/XmlConfiguration/Migration/MigrationBuilderException.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationException' => '/phpunit/TextUI/XmlConfiguration/Migration/MigrationException.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Migrator' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromFilterWhitelistToCoverage' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromRootToCoverage' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistDirectoriesToCoverage' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistDirectoriesToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistExcludesToCoverage' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistExcludesToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\PHPUnit' => '/phpunit/TextUI/XmlConfiguration/PHPUnit/PHPUnit.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Php' => '/phpunit/TextUI/XmlConfiguration/PHP/Php.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\PhpHandler' => '/phpunit/TextUI/XmlConfiguration/PHP/PhpHandler.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveCacheTokensAttribute' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveCacheTokensAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveEmptyFilter' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveEmptyFilter.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveLogTypes' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveLogTypes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectory' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectory.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectoryCollection' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectoryCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestFile' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestFile.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestFileCollection' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestFileCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuite' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestSuite.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteCollection' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\UpdateSchemaLocationTo93' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/UpdateSchemaLocationTo93.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Variable' => '/phpunit/TextUI/XmlConfiguration/PHP/Variable.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollection' => '/phpunit/TextUI/XmlConfiguration/PHP/VariableCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.php', + 'PHPUnit\\TheSeer\\Tokenizer\\Exception' => '/theseer-tokenizer/Exception.php', + 'PHPUnit\\TheSeer\\Tokenizer\\NamespaceUri' => '/theseer-tokenizer/NamespaceUri.php', + 'PHPUnit\\TheSeer\\Tokenizer\\NamespaceUriException' => '/theseer-tokenizer/NamespaceUriException.php', + 'PHPUnit\\TheSeer\\Tokenizer\\Token' => '/theseer-tokenizer/Token.php', + 'PHPUnit\\TheSeer\\Tokenizer\\TokenCollection' => '/theseer-tokenizer/TokenCollection.php', + 'PHPUnit\\TheSeer\\Tokenizer\\TokenCollectionException' => '/theseer-tokenizer/TokenCollectionException.php', + 'PHPUnit\\TheSeer\\Tokenizer\\Tokenizer' => '/theseer-tokenizer/Tokenizer.php', + 'PHPUnit\\TheSeer\\Tokenizer\\XMLSerializer' => '/theseer-tokenizer/XMLSerializer.php', + 'PHPUnit\\Util\\Annotation\\DocBlock' => '/phpunit/Util/Annotation/DocBlock.php', + 'PHPUnit\\Util\\Annotation\\Registry' => '/phpunit/Util/Annotation/Registry.php', + 'PHPUnit\\Util\\Blacklist' => '/phpunit/Util/Blacklist.php', + 'PHPUnit\\Util\\Color' => '/phpunit/Util/Color.php', + 'PHPUnit\\Util\\ErrorHandler' => '/phpunit/Util/ErrorHandler.php', + 'PHPUnit\\Util\\Exception' => '/phpunit/Util/Exception.php', + 'PHPUnit\\Util\\ExcludeList' => '/phpunit/Util/ExcludeList.php', + 'PHPUnit\\Util\\FileLoader' => '/phpunit/Util/FileLoader.php', + 'PHPUnit\\Util\\Filesystem' => '/phpunit/Util/Filesystem.php', + 'PHPUnit\\Util\\Filter' => '/phpunit/Util/Filter.php', + 'PHPUnit\\Util\\GlobalState' => '/phpunit/Util/GlobalState.php', + 'PHPUnit\\Util\\InvalidDataSetException' => '/phpunit/Util/InvalidDataSetException.php', + 'PHPUnit\\Util\\Json' => '/phpunit/Util/Json.php', + 'PHPUnit\\Util\\Log\\JUnit' => '/phpunit/Util/Log/JUnit.php', + 'PHPUnit\\Util\\Log\\TeamCity' => '/phpunit/Util/Log/TeamCity.php', + 'PHPUnit\\Util\\PHP\\AbstractPhpProcess' => '/phpunit/Util/PHP/AbstractPhpProcess.php', + 'PHPUnit\\Util\\PHP\\DefaultPhpProcess' => '/phpunit/Util/PHP/DefaultPhpProcess.php', + 'PHPUnit\\Util\\PHP\\WindowsPhpProcess' => '/phpunit/Util/PHP/WindowsPhpProcess.php', + 'PHPUnit\\Util\\Printer' => '/phpunit/Util/Printer.php', + 'PHPUnit\\Util\\RegularExpression' => '/phpunit/Util/RegularExpression.php', + 'PHPUnit\\Util\\Test' => '/phpunit/Util/Test.php', + 'PHPUnit\\Util\\TestDox\\CliTestDoxPrinter' => '/phpunit/Util/TestDox/CliTestDoxPrinter.php', + 'PHPUnit\\Util\\TestDox\\HtmlResultPrinter' => '/phpunit/Util/TestDox/HtmlResultPrinter.php', + 'PHPUnit\\Util\\TestDox\\NamePrettifier' => '/phpunit/Util/TestDox/NamePrettifier.php', + 'PHPUnit\\Util\\TestDox\\ResultPrinter' => '/phpunit/Util/TestDox/ResultPrinter.php', + 'PHPUnit\\Util\\TestDox\\TestDoxPrinter' => '/phpunit/Util/TestDox/TestDoxPrinter.php', + 'PHPUnit\\Util\\TestDox\\TextResultPrinter' => '/phpunit/Util/TestDox/TextResultPrinter.php', + 'PHPUnit\\Util\\TestDox\\XmlResultPrinter' => '/phpunit/Util/TestDox/XmlResultPrinter.php', + 'PHPUnit\\Util\\TextTestListRenderer' => '/phpunit/Util/TextTestListRenderer.php', + 'PHPUnit\\Util\\Type' => '/phpunit/Util/Type.php', + 'PHPUnit\\Util\\VersionComparisonOperator' => '/phpunit/Util/VersionComparisonOperator.php', + 'PHPUnit\\Util\\XdebugFilterScriptGenerator' => '/phpunit/Util/XdebugFilterScriptGenerator.php', + 'PHPUnit\\Util\\Xml' => '/phpunit/Util/Xml.php', + 'PHPUnit\\Util\\XmlTestListRenderer' => '/phpunit/Util/XmlTestListRenderer.php', + 'PHPUnit\\Util\\Xml\\Exception' => '/phpunit/Util/Xml/Exception.php', + 'PHPUnit\\Util\\Xml\\FailedSchemaDetectionResult' => '/phpunit/Util/Xml/FailedSchemaDetectionResult.php', + 'PHPUnit\\Util\\Xml\\Loader' => '/phpunit/Util/Xml/Loader.php', + 'PHPUnit\\Util\\Xml\\SchemaDetectionResult' => '/phpunit/Util/Xml/SchemaDetectionResult.php', + 'PHPUnit\\Util\\Xml\\SchemaDetector' => '/phpunit/Util/Xml/SchemaDetector.php', + 'PHPUnit\\Util\\Xml\\SchemaFinder' => '/phpunit/Util/Xml/SchemaFinder.php', + 'PHPUnit\\Util\\Xml\\SnapshotNodeList' => '/phpunit/Util/Xml/SnapshotNodeList.php', + 'PHPUnit\\Util\\Xml\\SuccessfulSchemaDetectionResult' => '/phpunit/Util/Xml/SuccessfulSchemaDetectionResult.php', + 'PHPUnit\\Util\\Xml\\ValidationResult' => '/phpunit/Util/Xml/ValidationResult.php', + 'PHPUnit\\Util\\Xml\\Validator' => '/phpunit/Util/Xml/Validator.php', + 'PHPUnit\\Webmozart\\Assert\\Assert' => '/webmozart-assert/Assert.php', + 'PHPUnit\\Webmozart\\Assert\\InvalidArgumentException' => '/webmozart-assert/InvalidArgumentException.php', + 'PHPUnit\\Webmozart\\Assert\\Mixin' => '/webmozart-assert/Mixin.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock' => '/phpdocumentor-reflection-docblock/DocBlock.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlockFactory' => '/phpdocumentor-reflection-docblock/DocBlockFactory.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlockFactoryInterface' => '/phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Description' => '/phpdocumentor-reflection-docblock/DocBlock/Description.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\DescriptionFactory' => '/phpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\ExampleFinder' => '/phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Serializer' => '/phpdocumentor-reflection-docblock/DocBlock/Serializer.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\StandardTagFactory' => '/phpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tag' => '/phpdocumentor-reflection-docblock/DocBlock/Tag.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\TagFactory' => '/phpdocumentor-reflection-docblock/DocBlock/TagFactory.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Author' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\BaseTag' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Covers' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Deprecated' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Example' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Example.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Factory\\StaticMethod' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\AlignFormatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\PassthroughFormatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Generic' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\InvalidTag' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/InvalidTag.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Link' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Link.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Method' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Param' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Property' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyRead' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyWrite' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Fqsen' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Reference' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Url' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Return_' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\See' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/See.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Since' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Since.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Source' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Source.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\TagWithType' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/TagWithType.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Throws' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Uses' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Var_' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Version' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Element' => '/phpdocumentor-reflection-common/Element.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Exception\\PcreException' => '/phpdocumentor-reflection-docblock/Exception/PcreException.php', + 'PHPUnit\\phpDocumentor\\Reflection\\File' => '/phpdocumentor-reflection-common/File.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Fqsen' => '/phpdocumentor-reflection-common/Fqsen.php', + 'PHPUnit\\phpDocumentor\\Reflection\\FqsenResolver' => '/phpdocumentor-type-resolver/FqsenResolver.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Location' => '/phpdocumentor-reflection-common/Location.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Project' => '/phpdocumentor-reflection-common/Project.php', + 'PHPUnit\\phpDocumentor\\Reflection\\ProjectFactory' => '/phpdocumentor-reflection-common/ProjectFactory.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoType' => '/phpdocumentor-type-resolver/PseudoType.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\CallableString' => '/phpdocumentor-type-resolver/PseudoTypes/CallableString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\False_' => '/phpdocumentor-type-resolver/PseudoTypes/False_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\HtmlEscapedString' => '/phpdocumentor-type-resolver/PseudoTypes/HtmlEscapedString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\LowercaseString' => '/phpdocumentor-type-resolver/PseudoTypes/LowercaseString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyLowercaseString' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyLowercaseString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyString' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\NumericString' => '/phpdocumentor-type-resolver/PseudoTypes/NumericString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\PositiveInteger' => '/phpdocumentor-type-resolver/PseudoTypes/PositiveInteger.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\TraitString' => '/phpdocumentor-type-resolver/PseudoTypes/TraitString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\True_' => '/phpdocumentor-type-resolver/PseudoTypes/True_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Type' => '/phpdocumentor-type-resolver/Type.php', + 'PHPUnit\\phpDocumentor\\Reflection\\TypeResolver' => '/phpdocumentor-type-resolver/TypeResolver.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\AbstractList' => '/phpdocumentor-type-resolver/Types/AbstractList.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\AggregatedType' => '/phpdocumentor-type-resolver/Types/AggregatedType.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\ArrayKey' => '/phpdocumentor-type-resolver/Types/ArrayKey.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Array_' => '/phpdocumentor-type-resolver/Types/Array_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Boolean' => '/phpdocumentor-type-resolver/Types/Boolean.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Callable_' => '/phpdocumentor-type-resolver/Types/Callable_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\ClassString' => '/phpdocumentor-type-resolver/Types/ClassString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Collection' => '/phpdocumentor-type-resolver/Types/Collection.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Compound' => '/phpdocumentor-type-resolver/Types/Compound.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Context' => '/phpdocumentor-type-resolver/Types/Context.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\ContextFactory' => '/phpdocumentor-type-resolver/Types/ContextFactory.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Expression' => '/phpdocumentor-type-resolver/Types/Expression.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Float_' => '/phpdocumentor-type-resolver/Types/Float_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Integer' => '/phpdocumentor-type-resolver/Types/Integer.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\InterfaceString' => '/phpdocumentor-type-resolver/Types/InterfaceString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Intersection' => '/phpdocumentor-type-resolver/Types/Intersection.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Iterable_' => '/phpdocumentor-type-resolver/Types/Iterable_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Mixed_' => '/phpdocumentor-type-resolver/Types/Mixed_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Never_' => '/phpdocumentor-type-resolver/Types/Never_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Null_' => '/phpdocumentor-type-resolver/Types/Null_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Nullable' => '/phpdocumentor-type-resolver/Types/Nullable.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Object_' => '/phpdocumentor-type-resolver/Types/Object_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Parent_' => '/phpdocumentor-type-resolver/Types/Parent_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Resource_' => '/phpdocumentor-type-resolver/Types/Resource_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Scalar' => '/phpdocumentor-type-resolver/Types/Scalar.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Self_' => '/phpdocumentor-type-resolver/Types/Self_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Static_' => '/phpdocumentor-type-resolver/Types/Static_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\String_' => '/phpdocumentor-type-resolver/Types/String_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\This' => '/phpdocumentor-type-resolver/Types/This.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Void_' => '/phpdocumentor-type-resolver/Types/Void_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Utils' => '/phpdocumentor-reflection-docblock/Utils.php', + 'Prophecy\\Argument' => '/phpspec-prophecy/Prophecy/Argument.php', + 'Prophecy\\Argument\\ArgumentsWildcard' => '/phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php', + 'Prophecy\\Argument\\Token\\AnyValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.php', + 'Prophecy\\Argument\\Token\\AnyValuesToken' => '/phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.php', + 'Prophecy\\Argument\\Token\\ApproximateValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.php', + 'Prophecy\\Argument\\Token\\ArrayCountToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.php', + 'Prophecy\\Argument\\Token\\ArrayEntryToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.php', + 'Prophecy\\Argument\\Token\\ArrayEveryEntryToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.php', + 'Prophecy\\Argument\\Token\\CallbackToken' => '/phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.php', + 'Prophecy\\Argument\\Token\\ExactValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php', + 'Prophecy\\Argument\\Token\\IdenticalValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.php', + 'Prophecy\\Argument\\Token\\InArrayToken' => '/phpspec-prophecy/Prophecy/Argument/Token/InArrayToken.php', + 'Prophecy\\Argument\\Token\\LogicalAndToken' => '/phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.php', + 'Prophecy\\Argument\\Token\\LogicalNotToken' => '/phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.php', + 'Prophecy\\Argument\\Token\\NotInArrayToken' => '/phpspec-prophecy/Prophecy/Argument/Token/NotInArrayToken.php', + 'Prophecy\\Argument\\Token\\ObjectStateToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php', + 'Prophecy\\Argument\\Token\\StringContainsToken' => '/phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.php', + 'Prophecy\\Argument\\Token\\TokenInterface' => '/phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.php', + 'Prophecy\\Argument\\Token\\TypeToken' => '/phpspec-prophecy/Prophecy/Argument/Token/TypeToken.php', + 'Prophecy\\Call\\Call' => '/phpspec-prophecy/Prophecy/Call/Call.php', + 'Prophecy\\Call\\CallCenter' => '/phpspec-prophecy/Prophecy/Call/CallCenter.php', + 'Prophecy\\Comparator\\ClosureComparator' => '/phpspec-prophecy/Prophecy/Comparator/ClosureComparator.php', + 'Prophecy\\Comparator\\Factory' => '/phpspec-prophecy/Prophecy/Comparator/Factory.php', + 'Prophecy\\Comparator\\ProphecyComparator' => '/phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.php', + 'Prophecy\\Doubler\\CachedDoubler' => '/phpspec-prophecy/Prophecy/Doubler/CachedDoubler.php', + 'Prophecy\\Doubler\\ClassPatch\\ClassPatchInterface' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.php', + 'Prophecy\\Doubler\\ClassPatch\\DisableConstructorPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php', + 'Prophecy\\Doubler\\ClassPatch\\HhvmExceptionPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php', + 'Prophecy\\Doubler\\ClassPatch\\KeywordPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php', + 'Prophecy\\Doubler\\ClassPatch\\MagicCallPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.php', + 'Prophecy\\Doubler\\ClassPatch\\ProphecySubjectPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php', + 'Prophecy\\Doubler\\ClassPatch\\ReflectionClassNewInstancePatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php', + 'Prophecy\\Doubler\\ClassPatch\\SplFileInfoPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php', + 'Prophecy\\Doubler\\ClassPatch\\ThrowablePatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ThrowablePatch.php', + 'Prophecy\\Doubler\\ClassPatch\\TraversablePatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php', + 'Prophecy\\Doubler\\DoubleInterface' => '/phpspec-prophecy/Prophecy/Doubler/DoubleInterface.php', + 'Prophecy\\Doubler\\Doubler' => '/phpspec-prophecy/Prophecy/Doubler/Doubler.php', + 'Prophecy\\Doubler\\Generator\\ClassCodeGenerator' => '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php', + 'Prophecy\\Doubler\\Generator\\ClassCreator' => '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCreator.php', + 'Prophecy\\Doubler\\Generator\\ClassMirror' => '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.php', + 'Prophecy\\Doubler\\Generator\\Node\\ArgumentNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.php', + 'Prophecy\\Doubler\\Generator\\Node\\ArgumentTypeNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentTypeNode.php', + 'Prophecy\\Doubler\\Generator\\Node\\ClassNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.php', + 'Prophecy\\Doubler\\Generator\\Node\\MethodNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.php', + 'Prophecy\\Doubler\\Generator\\Node\\ReturnTypeNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ReturnTypeNode.php', + 'Prophecy\\Doubler\\Generator\\Node\\TypeNodeAbstract' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/TypeNodeAbstract.php', + 'Prophecy\\Doubler\\Generator\\ReflectionInterface' => '/phpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.php', + 'Prophecy\\Doubler\\Generator\\TypeHintReference' => '/phpspec-prophecy/Prophecy/Doubler/Generator/TypeHintReference.php', + 'Prophecy\\Doubler\\LazyDouble' => '/phpspec-prophecy/Prophecy/Doubler/LazyDouble.php', + 'Prophecy\\Doubler\\NameGenerator' => '/phpspec-prophecy/Prophecy/Doubler/NameGenerator.php', + 'Prophecy\\Exception\\Call\\UnexpectedCallException' => '/phpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.php', + 'Prophecy\\Exception\\Doubler\\ClassCreatorException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.php', + 'Prophecy\\Exception\\Doubler\\ClassMirrorException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.php', + 'Prophecy\\Exception\\Doubler\\ClassNotFoundException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.php', + 'Prophecy\\Exception\\Doubler\\DoubleException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.php', + 'Prophecy\\Exception\\Doubler\\DoublerException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.php', + 'Prophecy\\Exception\\Doubler\\InterfaceNotFoundException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.php', + 'Prophecy\\Exception\\Doubler\\MethodNotExtendableException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.php', + 'Prophecy\\Exception\\Doubler\\MethodNotFoundException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.php', + 'Prophecy\\Exception\\Doubler\\ReturnByReferenceException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.php', + 'Prophecy\\Exception\\Exception' => '/phpspec-prophecy/Prophecy/Exception/Exception.php', + 'Prophecy\\Exception\\InvalidArgumentException' => '/phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.php', + 'Prophecy\\Exception\\Prediction\\AggregateException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.php', + 'Prophecy\\Exception\\Prediction\\FailedPredictionException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.php', + 'Prophecy\\Exception\\Prediction\\NoCallsException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.php', + 'Prophecy\\Exception\\Prediction\\PredictionException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.php', + 'Prophecy\\Exception\\Prediction\\UnexpectedCallsCountException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php', + 'Prophecy\\Exception\\Prediction\\UnexpectedCallsException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.php', + 'Prophecy\\Exception\\Prophecy\\MethodProphecyException' => '/phpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.php', + 'Prophecy\\Exception\\Prophecy\\ObjectProphecyException' => '/phpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.php', + 'Prophecy\\Exception\\Prophecy\\ProphecyException' => '/phpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.php', + 'Prophecy\\PhpDocumentor\\ClassAndInterfaceTagRetriever' => '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php', + 'Prophecy\\PhpDocumentor\\ClassTagRetriever' => '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassTagRetriever.php', + 'Prophecy\\PhpDocumentor\\LegacyClassTagRetriever' => '/phpspec-prophecy/Prophecy/PhpDocumentor/LegacyClassTagRetriever.php', + 'Prophecy\\PhpDocumentor\\MethodTagRetrieverInterface' => '/phpspec-prophecy/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php', + 'Prophecy\\Prediction\\CallPrediction' => '/phpspec-prophecy/Prophecy/Prediction/CallPrediction.php', + 'Prophecy\\Prediction\\CallTimesPrediction' => '/phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.php', + 'Prophecy\\Prediction\\CallbackPrediction' => '/phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.php', + 'Prophecy\\Prediction\\NoCallsPrediction' => '/phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.php', + 'Prophecy\\Prediction\\PredictionInterface' => '/phpspec-prophecy/Prophecy/Prediction/PredictionInterface.php', + 'Prophecy\\Promise\\CallbackPromise' => '/phpspec-prophecy/Prophecy/Promise/CallbackPromise.php', + 'Prophecy\\Promise\\PromiseInterface' => '/phpspec-prophecy/Prophecy/Promise/PromiseInterface.php', + 'Prophecy\\Promise\\ReturnArgumentPromise' => '/phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.php', + 'Prophecy\\Promise\\ReturnPromise' => '/phpspec-prophecy/Prophecy/Promise/ReturnPromise.php', + 'Prophecy\\Promise\\ThrowPromise' => '/phpspec-prophecy/Prophecy/Promise/ThrowPromise.php', + 'Prophecy\\Prophecy\\MethodProphecy' => '/phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.php', + 'Prophecy\\Prophecy\\ObjectProphecy' => '/phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.php', + 'Prophecy\\Prophecy\\ProphecyInterface' => '/phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.php', + 'Prophecy\\Prophecy\\ProphecySubjectInterface' => '/phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.php', + 'Prophecy\\Prophecy\\Revealer' => '/phpspec-prophecy/Prophecy/Prophecy/Revealer.php', + 'Prophecy\\Prophecy\\RevealerInterface' => '/phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.php', + 'Prophecy\\Prophet' => '/phpspec-prophecy/Prophecy/Prophet.php', + 'Prophecy\\Util\\ExportUtil' => '/phpspec-prophecy/Prophecy/Util/ExportUtil.php', + 'Prophecy\\Util\\StringUtil' => '/phpspec-prophecy/Prophecy/Util/StringUtil.php']; + } + + if (isset($classes[$class])) { + require_once 'phar://phpunit-9.5.10.phar' . $classes[$class]; + } + }, + true, + false +); + +foreach (['PHPUnit\\DeepCopy\\DeepCopy' => '/myclabs-deep-copy/DeepCopy/DeepCopy.php', + 'PHPUnit\\DeepCopy\\Exception\\CloneException' => '/myclabs-deep-copy/DeepCopy/Exception/CloneException.php', + 'PHPUnit\\DeepCopy\\Exception\\PropertyException' => '/myclabs-deep-copy/DeepCopy/Exception/PropertyException.php', + 'PHPUnit\\DeepCopy\\Filter\\Doctrine\\DoctrineCollectionFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php', + 'PHPUnit\\DeepCopy\\Filter\\Doctrine\\DoctrineEmptyCollectionFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php', + 'PHPUnit\\DeepCopy\\Filter\\Doctrine\\DoctrineProxyFilter' => '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php', + 'PHPUnit\\DeepCopy\\Filter\\Filter' => '/myclabs-deep-copy/DeepCopy/Filter/Filter.php', + 'PHPUnit\\DeepCopy\\Filter\\KeepFilter' => '/myclabs-deep-copy/DeepCopy/Filter/KeepFilter.php', + 'PHPUnit\\DeepCopy\\Filter\\ReplaceFilter' => '/myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.php', + 'PHPUnit\\DeepCopy\\Filter\\SetNullFilter' => '/myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.php', + 'PHPUnit\\DeepCopy\\Matcher\\Doctrine\\DoctrineProxyMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php', + 'PHPUnit\\DeepCopy\\Matcher\\Matcher' => '/myclabs-deep-copy/DeepCopy/Matcher/Matcher.php', + 'PHPUnit\\DeepCopy\\Matcher\\PropertyMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.php', + 'PHPUnit\\DeepCopy\\Matcher\\PropertyNameMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.php', + 'PHPUnit\\DeepCopy\\Matcher\\PropertyTypeMatcher' => '/myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php', + 'PHPUnit\\DeepCopy\\Reflection\\ReflectionHelper' => '/myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php', + 'PHPUnit\\DeepCopy\\TypeFilter\\Date\\DateIntervalFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.php', + 'PHPUnit\\DeepCopy\\TypeFilter\\ReplaceFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.php', + 'PHPUnit\\DeepCopy\\TypeFilter\\ShallowCopyFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.php', + 'PHPUnit\\DeepCopy\\TypeFilter\\Spl\\ArrayObjectFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php', + 'PHPUnit\\DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedList' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php', + 'PHPUnit\\DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedListFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php', + 'PHPUnit\\DeepCopy\\TypeFilter\\TypeFilter' => '/myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php', + 'PHPUnit\\DeepCopy\\TypeMatcher\\TypeMatcher' => '/myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php', + 'PHPUnit\\Doctrine\\Instantiator\\Exception\\ExceptionInterface' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php', + 'PHPUnit\\Doctrine\\Instantiator\\Exception\\InvalidArgumentException' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.php', + 'PHPUnit\\Doctrine\\Instantiator\\Exception\\UnexpectedValueException' => '/doctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php', + 'PHPUnit\\Doctrine\\Instantiator\\Instantiator' => '/doctrine-instantiator/Doctrine/Instantiator/Instantiator.php', + 'PHPUnit\\Doctrine\\Instantiator\\InstantiatorInterface' => '/doctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php', + 'PHPUnit\\Exception' => '/phpunit/Exception.php', + 'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => '/phpunit/Framework/Exception/ActualValueIsNotAnObjectException.php', + 'PHPUnit\\Framework\\Assert' => '/phpunit/Framework/Assert.php', + 'PHPUnit\\Framework\\AssertionFailedError' => '/phpunit/Framework/Exception/AssertionFailedError.php', + 'PHPUnit\\Framework\\CodeCoverageException' => '/phpunit/Framework/Exception/CodeCoverageException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotAcceptParameterTypeException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareBoolReturnTypeException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareExactlyOneParameterException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareParameterTypeException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotDeclareParameterTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotExistException' => '/phpunit/Framework/Exception/ComparisonMethodDoesNotExistException.php', + 'PHPUnit\\Framework\\Constraint\\ArrayHasKey' => '/phpunit/Framework/Constraint/Traversable/ArrayHasKey.php', + 'PHPUnit\\Framework\\Constraint\\BinaryOperator' => '/phpunit/Framework/Constraint/Operator/BinaryOperator.php', + 'PHPUnit\\Framework\\Constraint\\Callback' => '/phpunit/Framework/Constraint/Callback.php', + 'PHPUnit\\Framework\\Constraint\\ClassHasAttribute' => '/phpunit/Framework/Constraint/Object/ClassHasAttribute.php', + 'PHPUnit\\Framework\\Constraint\\ClassHasStaticAttribute' => '/phpunit/Framework/Constraint/Object/ClassHasStaticAttribute.php', + 'PHPUnit\\Framework\\Constraint\\Constraint' => '/phpunit/Framework/Constraint/Constraint.php', + 'PHPUnit\\Framework\\Constraint\\Count' => '/phpunit/Framework/Constraint/Cardinality/Count.php', + 'PHPUnit\\Framework\\Constraint\\DirectoryExists' => '/phpunit/Framework/Constraint/Filesystem/DirectoryExists.php', + 'PHPUnit\\Framework\\Constraint\\Exception' => '/phpunit/Framework/Constraint/Exception/Exception.php', + 'PHPUnit\\Framework\\Constraint\\ExceptionCode' => '/phpunit/Framework/Constraint/Exception/ExceptionCode.php', + 'PHPUnit\\Framework\\Constraint\\ExceptionMessage' => '/phpunit/Framework/Constraint/Exception/ExceptionMessage.php', + 'PHPUnit\\Framework\\Constraint\\ExceptionMessageRegularExpression' => '/phpunit/Framework/Constraint/Exception/ExceptionMessageRegularExpression.php', + 'PHPUnit\\Framework\\Constraint\\FileExists' => '/phpunit/Framework/Constraint/Filesystem/FileExists.php', + 'PHPUnit\\Framework\\Constraint\\GreaterThan' => '/phpunit/Framework/Constraint/Cardinality/GreaterThan.php', + 'PHPUnit\\Framework\\Constraint\\IsAnything' => '/phpunit/Framework/Constraint/IsAnything.php', + 'PHPUnit\\Framework\\Constraint\\IsEmpty' => '/phpunit/Framework/Constraint/Cardinality/IsEmpty.php', + 'PHPUnit\\Framework\\Constraint\\IsEqual' => '/phpunit/Framework/Constraint/Equality/IsEqual.php', + 'PHPUnit\\Framework\\Constraint\\IsEqualCanonicalizing' => '/phpunit/Framework/Constraint/Equality/IsEqualCanonicalizing.php', + 'PHPUnit\\Framework\\Constraint\\IsEqualIgnoringCase' => '/phpunit/Framework/Constraint/Equality/IsEqualIgnoringCase.php', + 'PHPUnit\\Framework\\Constraint\\IsEqualWithDelta' => '/phpunit/Framework/Constraint/Equality/IsEqualWithDelta.php', + 'PHPUnit\\Framework\\Constraint\\IsFalse' => '/phpunit/Framework/Constraint/Boolean/IsFalse.php', + 'PHPUnit\\Framework\\Constraint\\IsFinite' => '/phpunit/Framework/Constraint/Math/IsFinite.php', + 'PHPUnit\\Framework\\Constraint\\IsIdentical' => '/phpunit/Framework/Constraint/IsIdentical.php', + 'PHPUnit\\Framework\\Constraint\\IsInfinite' => '/phpunit/Framework/Constraint/Math/IsInfinite.php', + 'PHPUnit\\Framework\\Constraint\\IsInstanceOf' => '/phpunit/Framework/Constraint/Type/IsInstanceOf.php', + 'PHPUnit\\Framework\\Constraint\\IsJson' => '/phpunit/Framework/Constraint/String/IsJson.php', + 'PHPUnit\\Framework\\Constraint\\IsNan' => '/phpunit/Framework/Constraint/Math/IsNan.php', + 'PHPUnit\\Framework\\Constraint\\IsNull' => '/phpunit/Framework/Constraint/Type/IsNull.php', + 'PHPUnit\\Framework\\Constraint\\IsReadable' => '/phpunit/Framework/Constraint/Filesystem/IsReadable.php', + 'PHPUnit\\Framework\\Constraint\\IsTrue' => '/phpunit/Framework/Constraint/Boolean/IsTrue.php', + 'PHPUnit\\Framework\\Constraint\\IsType' => '/phpunit/Framework/Constraint/Type/IsType.php', + 'PHPUnit\\Framework\\Constraint\\IsWritable' => '/phpunit/Framework/Constraint/Filesystem/IsWritable.php', + 'PHPUnit\\Framework\\Constraint\\JsonMatches' => '/phpunit/Framework/Constraint/JsonMatches.php', + 'PHPUnit\\Framework\\Constraint\\JsonMatchesErrorMessageProvider' => '/phpunit/Framework/Constraint/JsonMatchesErrorMessageProvider.php', + 'PHPUnit\\Framework\\Constraint\\LessThan' => '/phpunit/Framework/Constraint/Cardinality/LessThan.php', + 'PHPUnit\\Framework\\Constraint\\LogicalAnd' => '/phpunit/Framework/Constraint/Operator/LogicalAnd.php', + 'PHPUnit\\Framework\\Constraint\\LogicalNot' => '/phpunit/Framework/Constraint/Operator/LogicalNot.php', + 'PHPUnit\\Framework\\Constraint\\LogicalOr' => '/phpunit/Framework/Constraint/Operator/LogicalOr.php', + 'PHPUnit\\Framework\\Constraint\\LogicalXor' => '/phpunit/Framework/Constraint/Operator/LogicalXor.php', + 'PHPUnit\\Framework\\Constraint\\ObjectEquals' => '/phpunit/Framework/Constraint/Object/ObjectEquals.php', + 'PHPUnit\\Framework\\Constraint\\ObjectHasAttribute' => '/phpunit/Framework/Constraint/Object/ObjectHasAttribute.php', + 'PHPUnit\\Framework\\Constraint\\Operator' => '/phpunit/Framework/Constraint/Operator/Operator.php', + 'PHPUnit\\Framework\\Constraint\\RegularExpression' => '/phpunit/Framework/Constraint/String/RegularExpression.php', + 'PHPUnit\\Framework\\Constraint\\SameSize' => '/phpunit/Framework/Constraint/Cardinality/SameSize.php', + 'PHPUnit\\Framework\\Constraint\\StringContains' => '/phpunit/Framework/Constraint/String/StringContains.php', + 'PHPUnit\\Framework\\Constraint\\StringEndsWith' => '/phpunit/Framework/Constraint/String/StringEndsWith.php', + 'PHPUnit\\Framework\\Constraint\\StringMatchesFormatDescription' => '/phpunit/Framework/Constraint/String/StringMatchesFormatDescription.php', + 'PHPUnit\\Framework\\Constraint\\StringStartsWith' => '/phpunit/Framework/Constraint/String/StringStartsWith.php', + 'PHPUnit\\Framework\\Constraint\\TraversableContains' => '/phpunit/Framework/Constraint/Traversable/TraversableContains.php', + 'PHPUnit\\Framework\\Constraint\\TraversableContainsEqual' => '/phpunit/Framework/Constraint/Traversable/TraversableContainsEqual.php', + 'PHPUnit\\Framework\\Constraint\\TraversableContainsIdentical' => '/phpunit/Framework/Constraint/Traversable/TraversableContainsIdentical.php', + 'PHPUnit\\Framework\\Constraint\\TraversableContainsOnly' => '/phpunit/Framework/Constraint/Traversable/TraversableContainsOnly.php', + 'PHPUnit\\Framework\\Constraint\\UnaryOperator' => '/phpunit/Framework/Constraint/Operator/UnaryOperator.php', + 'PHPUnit\\Framework\\CoveredCodeNotExecutedException' => '/phpunit/Framework/Exception/CoveredCodeNotExecutedException.php', + 'PHPUnit\\Framework\\DataProviderTestSuite' => '/phpunit/Framework/DataProviderTestSuite.php', + 'PHPUnit\\Framework\\Error' => '/phpunit/Framework/Exception/Error.php', + 'PHPUnit\\Framework\\ErrorTestCase' => '/phpunit/Framework/ErrorTestCase.php', + 'PHPUnit\\Framework\\Error\\Deprecated' => '/phpunit/Framework/Error/Deprecated.php', + 'PHPUnit\\Framework\\Error\\Error' => '/phpunit/Framework/Error/Error.php', + 'PHPUnit\\Framework\\Error\\Notice' => '/phpunit/Framework/Error/Notice.php', + 'PHPUnit\\Framework\\Error\\Warning' => '/phpunit/Framework/Error/Warning.php', + 'PHPUnit\\Framework\\Exception' => '/phpunit/Framework/Exception/Exception.php', + 'PHPUnit\\Framework\\ExceptionWrapper' => '/phpunit/Framework/ExceptionWrapper.php', + 'PHPUnit\\Framework\\ExecutionOrderDependency' => '/phpunit/Framework/ExecutionOrderDependency.php', + 'PHPUnit\\Framework\\ExpectationFailedException' => '/phpunit/Framework/Exception/ExpectationFailedException.php', + 'PHPUnit\\Framework\\IncompleteTest' => '/phpunit/Framework/IncompleteTest.php', + 'PHPUnit\\Framework\\IncompleteTestCase' => '/phpunit/Framework/IncompleteTestCase.php', + 'PHPUnit\\Framework\\IncompleteTestError' => '/phpunit/Framework/Exception/IncompleteTestError.php', + 'PHPUnit\\Framework\\InvalidArgumentException' => '/phpunit/Framework/Exception/InvalidArgumentException.php', + 'PHPUnit\\Framework\\InvalidCoversTargetException' => '/phpunit/Framework/Exception/InvalidCoversTargetException.php', + 'PHPUnit\\Framework\\InvalidDataProviderException' => '/phpunit/Framework/Exception/InvalidDataProviderException.php', + 'PHPUnit\\Framework\\InvalidParameterGroupException' => '/phpunit/Framework/InvalidParameterGroupException.php', + 'PHPUnit\\Framework\\MissingCoversAnnotationException' => '/phpunit/Framework/Exception/MissingCoversAnnotationException.php', + 'PHPUnit\\Framework\\MockObject\\Api' => '/phpunit/Framework/MockObject/Api/Api.php', + 'PHPUnit\\Framework\\MockObject\\BadMethodCallException' => '/phpunit/Framework/MockObject/Exception/BadMethodCallException.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\Identity' => '/phpunit/Framework/MockObject/Builder/Identity.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationMocker' => '/phpunit/Framework/MockObject/Builder/InvocationMocker.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationStubber' => '/phpunit/Framework/MockObject/Builder/InvocationStubber.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\MethodNameMatch' => '/phpunit/Framework/MockObject/Builder/MethodNameMatch.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\ParametersMatch' => '/phpunit/Framework/MockObject/Builder/ParametersMatch.php', + 'PHPUnit\\Framework\\MockObject\\Builder\\Stub' => '/phpunit/Framework/MockObject/Builder/Stub.php', + 'PHPUnit\\Framework\\MockObject\\CannotUseAddMethodsException' => '/phpunit/Framework/MockObject/Exception/CannotUseAddMethodsException.php', + 'PHPUnit\\Framework\\MockObject\\CannotUseOnlyMethodsException' => '/phpunit/Framework/MockObject/Exception/CannotUseOnlyMethodsException.php', + 'PHPUnit\\Framework\\MockObject\\ClassAlreadyExistsException' => '/phpunit/Framework/MockObject/Exception/ClassAlreadyExistsException.php', + 'PHPUnit\\Framework\\MockObject\\ClassIsFinalException' => '/phpunit/Framework/MockObject/Exception/ClassIsFinalException.php', + 'PHPUnit\\Framework\\MockObject\\ConfigurableMethod' => '/phpunit/Framework/MockObject/ConfigurableMethod.php', + 'PHPUnit\\Framework\\MockObject\\ConfigurableMethodsAlreadyInitializedException' => '/phpunit/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php', + 'PHPUnit\\Framework\\MockObject\\DuplicateMethodException' => '/phpunit/Framework/MockObject/Exception/DuplicateMethodException.php', + 'PHPUnit\\Framework\\MockObject\\Exception' => '/phpunit/Framework/MockObject/Exception/Exception.php', + 'PHPUnit\\Framework\\MockObject\\Generator' => '/phpunit/Framework/MockObject/Generator.php', + 'PHPUnit\\Framework\\MockObject\\IncompatibleReturnValueException' => '/phpunit/Framework/MockObject/Exception/IncompatibleReturnValueException.php', + 'PHPUnit\\Framework\\MockObject\\InvalidMethodNameException' => '/phpunit/Framework/MockObject/Exception/InvalidMethodNameException.php', + 'PHPUnit\\Framework\\MockObject\\Invocation' => '/phpunit/Framework/MockObject/Invocation.php', + 'PHPUnit\\Framework\\MockObject\\InvocationHandler' => '/phpunit/Framework/MockObject/InvocationHandler.php', + 'PHPUnit\\Framework\\MockObject\\MatchBuilderNotFoundException' => '/phpunit/Framework/MockObject/Exception/MatchBuilderNotFoundException.php', + 'PHPUnit\\Framework\\MockObject\\Matcher' => '/phpunit/Framework/MockObject/Matcher.php', + 'PHPUnit\\Framework\\MockObject\\MatcherAlreadyRegisteredException' => '/phpunit/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.php', + 'PHPUnit\\Framework\\MockObject\\Method' => '/phpunit/Framework/MockObject/Api/Method.php', + 'PHPUnit\\Framework\\MockObject\\MethodCannotBeConfiguredException' => '/phpunit/Framework/MockObject/Exception/MethodCannotBeConfiguredException.php', + 'PHPUnit\\Framework\\MockObject\\MethodNameAlreadyConfiguredException' => '/phpunit/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.php', + 'PHPUnit\\Framework\\MockObject\\MethodNameConstraint' => '/phpunit/Framework/MockObject/MethodNameConstraint.php', + 'PHPUnit\\Framework\\MockObject\\MethodNameNotConfiguredException' => '/phpunit/Framework/MockObject/Exception/MethodNameNotConfiguredException.php', + 'PHPUnit\\Framework\\MockObject\\MethodParametersAlreadyConfiguredException' => '/phpunit/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.php', + 'PHPUnit\\Framework\\MockObject\\MockBuilder' => '/phpunit/Framework/MockObject/MockBuilder.php', + 'PHPUnit\\Framework\\MockObject\\MockClass' => '/phpunit/Framework/MockObject/MockClass.php', + 'PHPUnit\\Framework\\MockObject\\MockMethod' => '/phpunit/Framework/MockObject/MockMethod.php', + 'PHPUnit\\Framework\\MockObject\\MockMethodSet' => '/phpunit/Framework/MockObject/MockMethodSet.php', + 'PHPUnit\\Framework\\MockObject\\MockObject' => '/phpunit/Framework/MockObject/MockObject.php', + 'PHPUnit\\Framework\\MockObject\\MockTrait' => '/phpunit/Framework/MockObject/MockTrait.php', + 'PHPUnit\\Framework\\MockObject\\MockType' => '/phpunit/Framework/MockObject/MockType.php', + 'PHPUnit\\Framework\\MockObject\\MockedCloneMethod' => '/phpunit/Framework/MockObject/Api/MockedCloneMethod.php', + 'PHPUnit\\Framework\\MockObject\\OriginalConstructorInvocationRequiredException' => '/phpunit/Framework/MockObject/Exception/OriginalConstructorInvocationRequiredException.php', + 'PHPUnit\\Framework\\MockObject\\ReflectionException' => '/phpunit/Framework/MockObject/Exception/ReflectionException.php', + 'PHPUnit\\Framework\\MockObject\\ReturnValueNotConfiguredException' => '/phpunit/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\AnyInvokedCount' => '/phpunit/Framework/MockObject/Rule/AnyInvokedCount.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\AnyParameters' => '/phpunit/Framework/MockObject/Rule/AnyParameters.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\ConsecutiveParameters' => '/phpunit/Framework/MockObject/Rule/ConsecutiveParameters.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvocationOrder' => '/phpunit/Framework/MockObject/Rule/InvocationOrder.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtIndex' => '/phpunit/Framework/MockObject/Rule/InvokedAtIndex.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtLeastCount' => '/phpunit/Framework/MockObject/Rule/InvokedAtLeastCount.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtLeastOnce' => '/phpunit/Framework/MockObject/Rule/InvokedAtLeastOnce.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtMostCount' => '/phpunit/Framework/MockObject/Rule/InvokedAtMostCount.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedCount' => '/phpunit/Framework/MockObject/Rule/InvokedCount.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\MethodName' => '/phpunit/Framework/MockObject/Rule/MethodName.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\Parameters' => '/phpunit/Framework/MockObject/Rule/Parameters.php', + 'PHPUnit\\Framework\\MockObject\\Rule\\ParametersRule' => '/phpunit/Framework/MockObject/Rule/ParametersRule.php', + 'PHPUnit\\Framework\\MockObject\\RuntimeException' => '/phpunit/Framework/MockObject/Exception/RuntimeException.php', + 'PHPUnit\\Framework\\MockObject\\SoapExtensionNotAvailableException' => '/phpunit/Framework/MockObject/Exception/SoapExtensionNotAvailableException.php', + 'PHPUnit\\Framework\\MockObject\\Stub' => '/phpunit/Framework/MockObject/Stub.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ConsecutiveCalls' => '/phpunit/Framework/MockObject/Stub/ConsecutiveCalls.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\Exception' => '/phpunit/Framework/MockObject/Stub/Exception.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnArgument' => '/phpunit/Framework/MockObject/Stub/ReturnArgument.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnCallback' => '/phpunit/Framework/MockObject/Stub/ReturnCallback.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnReference' => '/phpunit/Framework/MockObject/Stub/ReturnReference.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnSelf' => '/phpunit/Framework/MockObject/Stub/ReturnSelf.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnStub' => '/phpunit/Framework/MockObject/Stub/ReturnStub.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnValueMap' => '/phpunit/Framework/MockObject/Stub/ReturnValueMap.php', + 'PHPUnit\\Framework\\MockObject\\Stub\\Stub' => '/phpunit/Framework/MockObject/Stub/Stub.php', + 'PHPUnit\\Framework\\MockObject\\UnknownClassException' => '/phpunit/Framework/MockObject/Exception/UnknownClassException.php', + 'PHPUnit\\Framework\\MockObject\\UnknownTraitException' => '/phpunit/Framework/MockObject/Exception/UnknownTraitException.php', + 'PHPUnit\\Framework\\MockObject\\UnknownTypeException' => '/phpunit/Framework/MockObject/Exception/UnknownTypeException.php', + 'PHPUnit\\Framework\\MockObject\\UnmockedCloneMethod' => '/phpunit/Framework/MockObject/Api/UnmockedCloneMethod.php', + 'PHPUnit\\Framework\\MockObject\\Verifiable' => '/phpunit/Framework/MockObject/Verifiable.php', + 'PHPUnit\\Framework\\NoChildTestSuiteException' => '/phpunit/Framework/Exception/NoChildTestSuiteException.php', + 'PHPUnit\\Framework\\OutputError' => '/phpunit/Framework/Exception/OutputError.php', + 'PHPUnit\\Framework\\PHPTAssertionFailedError' => '/phpunit/Framework/Exception/PHPTAssertionFailedError.php', + 'PHPUnit\\Framework\\Reorderable' => '/phpunit/Framework/Reorderable.php', + 'PHPUnit\\Framework\\RiskyTestError' => '/phpunit/Framework/Exception/RiskyTestError.php', + 'PHPUnit\\Framework\\SelfDescribing' => '/phpunit/Framework/SelfDescribing.php', + 'PHPUnit\\Framework\\SkippedTest' => '/phpunit/Framework/SkippedTest.php', + 'PHPUnit\\Framework\\SkippedTestCase' => '/phpunit/Framework/SkippedTestCase.php', + 'PHPUnit\\Framework\\SkippedTestError' => '/phpunit/Framework/Exception/SkippedTestError.php', + 'PHPUnit\\Framework\\SkippedTestSuiteError' => '/phpunit/Framework/Exception/SkippedTestSuiteError.php', + 'PHPUnit\\Framework\\SyntheticError' => '/phpunit/Framework/Exception/SyntheticError.php', + 'PHPUnit\\Framework\\SyntheticSkippedError' => '/phpunit/Framework/Exception/SyntheticSkippedError.php', + 'PHPUnit\\Framework\\Test' => '/phpunit/Framework/Test.php', + 'PHPUnit\\Framework\\TestBuilder' => '/phpunit/Framework/TestBuilder.php', + 'PHPUnit\\Framework\\TestCase' => '/phpunit/Framework/TestCase.php', + 'PHPUnit\\Framework\\TestFailure' => '/phpunit/Framework/TestFailure.php', + 'PHPUnit\\Framework\\TestListener' => '/phpunit/Framework/TestListener.php', + 'PHPUnit\\Framework\\TestListenerDefaultImplementation' => '/phpunit/Framework/TestListenerDefaultImplementation.php', + 'PHPUnit\\Framework\\TestResult' => '/phpunit/Framework/TestResult.php', + 'PHPUnit\\Framework\\TestSuite' => '/phpunit/Framework/TestSuite.php', + 'PHPUnit\\Framework\\TestSuiteIterator' => '/phpunit/Framework/TestSuiteIterator.php', + 'PHPUnit\\Framework\\UnintentionallyCoveredCodeError' => '/phpunit/Framework/Exception/UnintentionallyCoveredCodeError.php', + 'PHPUnit\\Framework\\Warning' => '/phpunit/Framework/Exception/Warning.php', + 'PHPUnit\\Framework\\WarningTestCase' => '/phpunit/Framework/WarningTestCase.php', + 'PHPUnit\\PharIo\\Manifest\\Application' => '/phar-io-manifest/values/Application.php', + 'PHPUnit\\PharIo\\Manifest\\ApplicationName' => '/phar-io-manifest/values/ApplicationName.php', + 'PHPUnit\\PharIo\\Manifest\\Author' => '/phar-io-manifest/values/Author.php', + 'PHPUnit\\PharIo\\Manifest\\AuthorCollection' => '/phar-io-manifest/values/AuthorCollection.php', + 'PHPUnit\\PharIo\\Manifest\\AuthorCollectionIterator' => '/phar-io-manifest/values/AuthorCollectionIterator.php', + 'PHPUnit\\PharIo\\Manifest\\AuthorElement' => '/phar-io-manifest/xml/AuthorElement.php', + 'PHPUnit\\PharIo\\Manifest\\AuthorElementCollection' => '/phar-io-manifest/xml/AuthorElementCollection.php', + 'PHPUnit\\PharIo\\Manifest\\BundledComponent' => '/phar-io-manifest/values/BundledComponent.php', + 'PHPUnit\\PharIo\\Manifest\\BundledComponentCollection' => '/phar-io-manifest/values/BundledComponentCollection.php', + 'PHPUnit\\PharIo\\Manifest\\BundledComponentCollectionIterator' => '/phar-io-manifest/values/BundledComponentCollectionIterator.php', + 'PHPUnit\\PharIo\\Manifest\\BundlesElement' => '/phar-io-manifest/xml/BundlesElement.php', + 'PHPUnit\\PharIo\\Manifest\\ComponentElement' => '/phar-io-manifest/xml/ComponentElement.php', + 'PHPUnit\\PharIo\\Manifest\\ComponentElementCollection' => '/phar-io-manifest/xml/ComponentElementCollection.php', + 'PHPUnit\\PharIo\\Manifest\\ContainsElement' => '/phar-io-manifest/xml/ContainsElement.php', + 'PHPUnit\\PharIo\\Manifest\\CopyrightElement' => '/phar-io-manifest/xml/CopyrightElement.php', + 'PHPUnit\\PharIo\\Manifest\\CopyrightInformation' => '/phar-io-manifest/values/CopyrightInformation.php', + 'PHPUnit\\PharIo\\Manifest\\ElementCollection' => '/phar-io-manifest/xml/ElementCollection.php', + 'PHPUnit\\PharIo\\Manifest\\ElementCollectionException' => '/phar-io-manifest/exceptions/ElementCollectionException.php', + 'PHPUnit\\PharIo\\Manifest\\Email' => '/phar-io-manifest/values/Email.php', + 'PHPUnit\\PharIo\\Manifest\\Exception' => '/phar-io-manifest/exceptions/Exception.php', + 'PHPUnit\\PharIo\\Manifest\\ExtElement' => '/phar-io-manifest/xml/ExtElement.php', + 'PHPUnit\\PharIo\\Manifest\\ExtElementCollection' => '/phar-io-manifest/xml/ExtElementCollection.php', + 'PHPUnit\\PharIo\\Manifest\\Extension' => '/phar-io-manifest/values/Extension.php', + 'PHPUnit\\PharIo\\Manifest\\ExtensionElement' => '/phar-io-manifest/xml/ExtensionElement.php', + 'PHPUnit\\PharIo\\Manifest\\InvalidApplicationNameException' => '/phar-io-manifest/exceptions/InvalidApplicationNameException.php', + 'PHPUnit\\PharIo\\Manifest\\InvalidEmailException' => '/phar-io-manifest/exceptions/InvalidEmailException.php', + 'PHPUnit\\PharIo\\Manifest\\InvalidUrlException' => '/phar-io-manifest/exceptions/InvalidUrlException.php', + 'PHPUnit\\PharIo\\Manifest\\Library' => '/phar-io-manifest/values/Library.php', + 'PHPUnit\\PharIo\\Manifest\\License' => '/phar-io-manifest/values/License.php', + 'PHPUnit\\PharIo\\Manifest\\LicenseElement' => '/phar-io-manifest/xml/LicenseElement.php', + 'PHPUnit\\PharIo\\Manifest\\Manifest' => '/phar-io-manifest/values/Manifest.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestDocument' => '/phar-io-manifest/xml/ManifestDocument.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestDocumentException' => '/phar-io-manifest/exceptions/ManifestDocumentException.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestDocumentLoadingException' => '/phar-io-manifest/exceptions/ManifestDocumentLoadingException.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestDocumentMapper' => '/phar-io-manifest/ManifestDocumentMapper.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestDocumentMapperException' => '/phar-io-manifest/exceptions/ManifestDocumentMapperException.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestElement' => '/phar-io-manifest/xml/ManifestElement.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestElementException' => '/phar-io-manifest/exceptions/ManifestElementException.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestLoader' => '/phar-io-manifest/ManifestLoader.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestLoaderException' => '/phar-io-manifest/exceptions/ManifestLoaderException.php', + 'PHPUnit\\PharIo\\Manifest\\ManifestSerializer' => '/phar-io-manifest/ManifestSerializer.php', + 'PHPUnit\\PharIo\\Manifest\\PhpElement' => '/phar-io-manifest/xml/PhpElement.php', + 'PHPUnit\\PharIo\\Manifest\\PhpExtensionRequirement' => '/phar-io-manifest/values/PhpExtensionRequirement.php', + 'PHPUnit\\PharIo\\Manifest\\PhpVersionRequirement' => '/phar-io-manifest/values/PhpVersionRequirement.php', + 'PHPUnit\\PharIo\\Manifest\\Requirement' => '/phar-io-manifest/values/Requirement.php', + 'PHPUnit\\PharIo\\Manifest\\RequirementCollection' => '/phar-io-manifest/values/RequirementCollection.php', + 'PHPUnit\\PharIo\\Manifest\\RequirementCollectionIterator' => '/phar-io-manifest/values/RequirementCollectionIterator.php', + 'PHPUnit\\PharIo\\Manifest\\RequiresElement' => '/phar-io-manifest/xml/RequiresElement.php', + 'PHPUnit\\PharIo\\Manifest\\Type' => '/phar-io-manifest/values/Type.php', + 'PHPUnit\\PharIo\\Manifest\\Url' => '/phar-io-manifest/values/Url.php', + 'PHPUnit\\PharIo\\Version\\AbstractVersionConstraint' => '/phar-io-version/constraints/AbstractVersionConstraint.php', + 'PHPUnit\\PharIo\\Version\\AndVersionConstraintGroup' => '/phar-io-version/constraints/AndVersionConstraintGroup.php', + 'PHPUnit\\PharIo\\Version\\AnyVersionConstraint' => '/phar-io-version/constraints/AnyVersionConstraint.php', + 'PHPUnit\\PharIo\\Version\\ExactVersionConstraint' => '/phar-io-version/constraints/ExactVersionConstraint.php', + 'PHPUnit\\PharIo\\Version\\Exception' => '/phar-io-version/exceptions/Exception.php', + 'PHPUnit\\PharIo\\Version\\GreaterThanOrEqualToVersionConstraint' => '/phar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.php', + 'PHPUnit\\PharIo\\Version\\InvalidPreReleaseSuffixException' => '/phar-io-version/exceptions/InvalidPreReleaseSuffixException.php', + 'PHPUnit\\PharIo\\Version\\InvalidVersionException' => '/phar-io-version/exceptions/InvalidVersionException.php', + 'PHPUnit\\PharIo\\Version\\NoPreReleaseSuffixException' => '/phar-io-version/exceptions/NoPreReleaseSuffixException.php', + 'PHPUnit\\PharIo\\Version\\OrVersionConstraintGroup' => '/phar-io-version/constraints/OrVersionConstraintGroup.php', + 'PHPUnit\\PharIo\\Version\\PreReleaseSuffix' => '/phar-io-version/PreReleaseSuffix.php', + 'PHPUnit\\PharIo\\Version\\SpecificMajorAndMinorVersionConstraint' => '/phar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.php', + 'PHPUnit\\PharIo\\Version\\SpecificMajorVersionConstraint' => '/phar-io-version/constraints/SpecificMajorVersionConstraint.php', + 'PHPUnit\\PharIo\\Version\\UnsupportedVersionConstraintException' => '/phar-io-version/exceptions/UnsupportedVersionConstraintException.php', + 'PHPUnit\\PharIo\\Version\\Version' => '/phar-io-version/Version.php', + 'PHPUnit\\PharIo\\Version\\VersionConstraint' => '/phar-io-version/constraints/VersionConstraint.php', + 'PHPUnit\\PharIo\\Version\\VersionConstraintParser' => '/phar-io-version/VersionConstraintParser.php', + 'PHPUnit\\PharIo\\Version\\VersionConstraintValue' => '/phar-io-version/VersionConstraintValue.php', + 'PHPUnit\\PharIo\\Version\\VersionNumber' => '/phar-io-version/VersionNumber.php', + 'PHPUnit\\PhpParser\\Builder' => '/nikic-php-parser/PhpParser/Builder.php', + 'PHPUnit\\PhpParser\\BuilderFactory' => '/nikic-php-parser/PhpParser/BuilderFactory.php', + 'PHPUnit\\PhpParser\\BuilderHelpers' => '/nikic-php-parser/PhpParser/BuilderHelpers.php', + 'PHPUnit\\PhpParser\\Builder\\ClassConst' => '/nikic-php-parser/PhpParser/Builder/ClassConst.php', + 'PHPUnit\\PhpParser\\Builder\\Class_' => '/nikic-php-parser/PhpParser/Builder/Class_.php', + 'PHPUnit\\PhpParser\\Builder\\Declaration' => '/nikic-php-parser/PhpParser/Builder/Declaration.php', + 'PHPUnit\\PhpParser\\Builder\\FunctionLike' => '/nikic-php-parser/PhpParser/Builder/FunctionLike.php', + 'PHPUnit\\PhpParser\\Builder\\Function_' => '/nikic-php-parser/PhpParser/Builder/Function_.php', + 'PHPUnit\\PhpParser\\Builder\\Interface_' => '/nikic-php-parser/PhpParser/Builder/Interface_.php', + 'PHPUnit\\PhpParser\\Builder\\Method' => '/nikic-php-parser/PhpParser/Builder/Method.php', + 'PHPUnit\\PhpParser\\Builder\\Namespace_' => '/nikic-php-parser/PhpParser/Builder/Namespace_.php', + 'PHPUnit\\PhpParser\\Builder\\Param' => '/nikic-php-parser/PhpParser/Builder/Param.php', + 'PHPUnit\\PhpParser\\Builder\\Property' => '/nikic-php-parser/PhpParser/Builder/Property.php', + 'PHPUnit\\PhpParser\\Builder\\TraitUse' => '/nikic-php-parser/PhpParser/Builder/TraitUse.php', + 'PHPUnit\\PhpParser\\Builder\\TraitUseAdaptation' => '/nikic-php-parser/PhpParser/Builder/TraitUseAdaptation.php', + 'PHPUnit\\PhpParser\\Builder\\Trait_' => '/nikic-php-parser/PhpParser/Builder/Trait_.php', + 'PHPUnit\\PhpParser\\Builder\\Use_' => '/nikic-php-parser/PhpParser/Builder/Use_.php', + 'PHPUnit\\PhpParser\\Comment' => '/nikic-php-parser/PhpParser/Comment.php', + 'PHPUnit\\PhpParser\\Comment\\Doc' => '/nikic-php-parser/PhpParser/Comment/Doc.php', + 'PHPUnit\\PhpParser\\ConstExprEvaluationException' => '/nikic-php-parser/PhpParser/ConstExprEvaluationException.php', + 'PHPUnit\\PhpParser\\ConstExprEvaluator' => '/nikic-php-parser/PhpParser/ConstExprEvaluator.php', + 'PHPUnit\\PhpParser\\Error' => '/nikic-php-parser/PhpParser/Error.php', + 'PHPUnit\\PhpParser\\ErrorHandler' => '/nikic-php-parser/PhpParser/ErrorHandler.php', + 'PHPUnit\\PhpParser\\ErrorHandler\\Collecting' => '/nikic-php-parser/PhpParser/ErrorHandler/Collecting.php', + 'PHPUnit\\PhpParser\\ErrorHandler\\Throwing' => '/nikic-php-parser/PhpParser/ErrorHandler/Throwing.php', + 'PHPUnit\\PhpParser\\Internal\\DiffElem' => '/nikic-php-parser/PhpParser/Internal/DiffElem.php', + 'PHPUnit\\PhpParser\\Internal\\Differ' => '/nikic-php-parser/PhpParser/Internal/Differ.php', + 'PHPUnit\\PhpParser\\Internal\\PrintableNewAnonClassNode' => '/nikic-php-parser/PhpParser/Internal/PrintableNewAnonClassNode.php', + 'PHPUnit\\PhpParser\\Internal\\TokenStream' => '/nikic-php-parser/PhpParser/Internal/TokenStream.php', + 'PHPUnit\\PhpParser\\JsonDecoder' => '/nikic-php-parser/PhpParser/JsonDecoder.php', + 'PHPUnit\\PhpParser\\Lexer' => '/nikic-php-parser/PhpParser/Lexer.php', + 'PHPUnit\\PhpParser\\Lexer\\Emulative' => '/nikic-php-parser/PhpParser/Lexer/Emulative.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\AttributeEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\CoaleseEqualTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\EnumTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\ExplicitOctalEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\FlexibleDocStringEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\FnTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\KeywordEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\MatchTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\NullsafeTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\NumericLiteralSeparatorEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\ReadonlyTokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\ReverseEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php', + 'PHPUnit\\PhpParser\\Lexer\\TokenEmulator\\TokenEmulator' => '/nikic-php-parser/PhpParser/Lexer/TokenEmulator/TokenEmulator.php', + 'PHPUnit\\PhpParser\\NameContext' => '/nikic-php-parser/PhpParser/NameContext.php', + 'PHPUnit\\PhpParser\\Node' => '/nikic-php-parser/PhpParser/Node.php', + 'PHPUnit\\PhpParser\\NodeAbstract' => '/nikic-php-parser/PhpParser/NodeAbstract.php', + 'PHPUnit\\PhpParser\\NodeDumper' => '/nikic-php-parser/PhpParser/NodeDumper.php', + 'PHPUnit\\PhpParser\\NodeFinder' => '/nikic-php-parser/PhpParser/NodeFinder.php', + 'PHPUnit\\PhpParser\\NodeTraverser' => '/nikic-php-parser/PhpParser/NodeTraverser.php', + 'PHPUnit\\PhpParser\\NodeTraverserInterface' => '/nikic-php-parser/PhpParser/NodeTraverserInterface.php', + 'PHPUnit\\PhpParser\\NodeVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor.php', + 'PHPUnit\\PhpParser\\NodeVisitorAbstract' => '/nikic-php-parser/PhpParser/NodeVisitorAbstract.php', + 'PHPUnit\\PhpParser\\NodeVisitor\\CloningVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/CloningVisitor.php', + 'PHPUnit\\PhpParser\\NodeVisitor\\FindingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/FindingVisitor.php', + 'PHPUnit\\PhpParser\\NodeVisitor\\FirstFindingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/FirstFindingVisitor.php', + 'PHPUnit\\PhpParser\\NodeVisitor\\NameResolver' => '/nikic-php-parser/PhpParser/NodeVisitor/NameResolver.php', + 'PHPUnit\\PhpParser\\NodeVisitor\\NodeConnectingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/NodeConnectingVisitor.php', + 'PHPUnit\\PhpParser\\NodeVisitor\\ParentConnectingVisitor' => '/nikic-php-parser/PhpParser/NodeVisitor/ParentConnectingVisitor.php', + 'PHPUnit\\PhpParser\\Node\\Arg' => '/nikic-php-parser/PhpParser/Node/Arg.php', + 'PHPUnit\\PhpParser\\Node\\Attribute' => '/nikic-php-parser/PhpParser/Node/Attribute.php', + 'PHPUnit\\PhpParser\\Node\\AttributeGroup' => '/nikic-php-parser/PhpParser/Node/AttributeGroup.php', + 'PHPUnit\\PhpParser\\Node\\ComplexType' => '/nikic-php-parser/PhpParser/Node/ComplexType.php', + 'PHPUnit\\PhpParser\\Node\\Const_' => '/nikic-php-parser/PhpParser/Node/Const_.php', + 'PHPUnit\\PhpParser\\Node\\Expr' => '/nikic-php-parser/PhpParser/Node/Expr.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\ArrayDimFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ArrayDimFetch.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\ArrayItem' => '/nikic-php-parser/PhpParser/Node/Expr/ArrayItem.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Array_' => '/nikic-php-parser/PhpParser/Node/Expr/Array_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\ArrowFunction' => '/nikic-php-parser/PhpParser/Node/Expr/ArrowFunction.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Assign' => '/nikic-php-parser/PhpParser/Node/Expr/Assign.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseAnd' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseOr' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseOr.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\BitwiseXor' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseXor.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Coalesce' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Coalesce.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Concat' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Concat.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Div' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Div.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Minus' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Minus.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Mod' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mod.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Mul' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mul.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Plus' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Plus.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\Pow' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/Pow.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\ShiftLeft' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftLeft.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignOp\\ShiftRight' => '/nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftRight.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\AssignRef' => '/nikic-php-parser/PhpParser/Node/Expr/AssignRef.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseOr.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BitwiseXor' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseXor.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BooleanAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanAnd.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\BooleanOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanOr.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Coalesce' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Coalesce.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Concat' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Concat.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Div' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Div.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Equal' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Equal.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Greater' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Greater.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\GreaterOrEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Identical' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Identical.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalAnd' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalAnd.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalOr' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalOr.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\LogicalXor' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalXor.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Minus' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Minus.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Mod' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mod.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Mul' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mul.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\NotEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotEqual.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\NotIdentical' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotIdentical.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Plus' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Plus.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Pow' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Pow.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\ShiftLeft' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftLeft.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\ShiftRight' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftRight.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Smaller' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Smaller.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\SmallerOrEqual' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BinaryOp\\Spaceship' => '/nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Spaceship.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BitwiseNot' => '/nikic-php-parser/PhpParser/Node/Expr/BitwiseNot.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\BooleanNot' => '/nikic-php-parser/PhpParser/Node/Expr/BooleanNot.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\CallLike' => '/nikic-php-parser/PhpParser/Node/Expr/CallLike.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Cast' => '/nikic-php-parser/PhpParser/Node/Expr/Cast.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Array_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Array_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Bool_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Bool_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Double' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Double.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Int_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Int_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Object_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Object_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\String_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/String_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Cast\\Unset_' => '/nikic-php-parser/PhpParser/Node/Expr/Cast/Unset_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\ClassConstFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ClassConstFetch.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Clone_' => '/nikic-php-parser/PhpParser/Node/Expr/Clone_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Closure' => '/nikic-php-parser/PhpParser/Node/Expr/Closure.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\ClosureUse' => '/nikic-php-parser/PhpParser/Node/Expr/ClosureUse.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\ConstFetch' => '/nikic-php-parser/PhpParser/Node/Expr/ConstFetch.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Empty_' => '/nikic-php-parser/PhpParser/Node/Expr/Empty_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Error' => '/nikic-php-parser/PhpParser/Node/Expr/Error.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\ErrorSuppress' => '/nikic-php-parser/PhpParser/Node/Expr/ErrorSuppress.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Eval_' => '/nikic-php-parser/PhpParser/Node/Expr/Eval_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Exit_' => '/nikic-php-parser/PhpParser/Node/Expr/Exit_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\FuncCall' => '/nikic-php-parser/PhpParser/Node/Expr/FuncCall.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Include_' => '/nikic-php-parser/PhpParser/Node/Expr/Include_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Instanceof_' => '/nikic-php-parser/PhpParser/Node/Expr/Instanceof_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Isset_' => '/nikic-php-parser/PhpParser/Node/Expr/Isset_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\List_' => '/nikic-php-parser/PhpParser/Node/Expr/List_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Match_' => '/nikic-php-parser/PhpParser/Node/Expr/Match_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\MethodCall' => '/nikic-php-parser/PhpParser/Node/Expr/MethodCall.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\New_' => '/nikic-php-parser/PhpParser/Node/Expr/New_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\NullsafeMethodCall' => '/nikic-php-parser/PhpParser/Node/Expr/NullsafeMethodCall.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\NullsafePropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/NullsafePropertyFetch.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\PostDec' => '/nikic-php-parser/PhpParser/Node/Expr/PostDec.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\PostInc' => '/nikic-php-parser/PhpParser/Node/Expr/PostInc.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\PreDec' => '/nikic-php-parser/PhpParser/Node/Expr/PreDec.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\PreInc' => '/nikic-php-parser/PhpParser/Node/Expr/PreInc.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Print_' => '/nikic-php-parser/PhpParser/Node/Expr/Print_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\PropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/PropertyFetch.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\ShellExec' => '/nikic-php-parser/PhpParser/Node/Expr/ShellExec.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\StaticCall' => '/nikic-php-parser/PhpParser/Node/Expr/StaticCall.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\StaticPropertyFetch' => '/nikic-php-parser/PhpParser/Node/Expr/StaticPropertyFetch.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Ternary' => '/nikic-php-parser/PhpParser/Node/Expr/Ternary.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Throw_' => '/nikic-php-parser/PhpParser/Node/Expr/Throw_.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\UnaryMinus' => '/nikic-php-parser/PhpParser/Node/Expr/UnaryMinus.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\UnaryPlus' => '/nikic-php-parser/PhpParser/Node/Expr/UnaryPlus.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Variable' => '/nikic-php-parser/PhpParser/Node/Expr/Variable.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\YieldFrom' => '/nikic-php-parser/PhpParser/Node/Expr/YieldFrom.php', + 'PHPUnit\\PhpParser\\Node\\Expr\\Yield_' => '/nikic-php-parser/PhpParser/Node/Expr/Yield_.php', + 'PHPUnit\\PhpParser\\Node\\FunctionLike' => '/nikic-php-parser/PhpParser/Node/FunctionLike.php', + 'PHPUnit\\PhpParser\\Node\\Identifier' => '/nikic-php-parser/PhpParser/Node/Identifier.php', + 'PHPUnit\\PhpParser\\Node\\IntersectionType' => '/nikic-php-parser/PhpParser/Node/IntersectionType.php', + 'PHPUnit\\PhpParser\\Node\\MatchArm' => '/nikic-php-parser/PhpParser/Node/MatchArm.php', + 'PHPUnit\\PhpParser\\Node\\Name' => '/nikic-php-parser/PhpParser/Node/Name.php', + 'PHPUnit\\PhpParser\\Node\\Name\\FullyQualified' => '/nikic-php-parser/PhpParser/Node/Name/FullyQualified.php', + 'PHPUnit\\PhpParser\\Node\\Name\\Relative' => '/nikic-php-parser/PhpParser/Node/Name/Relative.php', + 'PHPUnit\\PhpParser\\Node\\NullableType' => '/nikic-php-parser/PhpParser/Node/NullableType.php', + 'PHPUnit\\PhpParser\\Node\\Param' => '/nikic-php-parser/PhpParser/Node/Param.php', + 'PHPUnit\\PhpParser\\Node\\Scalar' => '/nikic-php-parser/PhpParser/Node/Scalar.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\DNumber' => '/nikic-php-parser/PhpParser/Node/Scalar/DNumber.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\Encapsed' => '/nikic-php-parser/PhpParser/Node/Scalar/Encapsed.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\EncapsedStringPart' => '/nikic-php-parser/PhpParser/Node/Scalar/EncapsedStringPart.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\LNumber' => '/nikic-php-parser/PhpParser/Node/Scalar/LNumber.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Class_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Class_.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Dir' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Dir.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\File' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/File.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Function_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Function_.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Line' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Line.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Method' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Method.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Namespace_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Namespace_.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\MagicConst\\Trait_' => '/nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Trait_.php', + 'PHPUnit\\PhpParser\\Node\\Scalar\\String_' => '/nikic-php-parser/PhpParser/Node/Scalar/String_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt' => '/nikic-php-parser/PhpParser/Node/Stmt.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Break_' => '/nikic-php-parser/PhpParser/Node/Stmt/Break_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Case_' => '/nikic-php-parser/PhpParser/Node/Stmt/Case_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Catch_' => '/nikic-php-parser/PhpParser/Node/Stmt/Catch_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\ClassConst' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassConst.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\ClassLike' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassLike.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\ClassMethod' => '/nikic-php-parser/PhpParser/Node/Stmt/ClassMethod.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Class_' => '/nikic-php-parser/PhpParser/Node/Stmt/Class_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Const_' => '/nikic-php-parser/PhpParser/Node/Stmt/Const_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Continue_' => '/nikic-php-parser/PhpParser/Node/Stmt/Continue_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\DeclareDeclare' => '/nikic-php-parser/PhpParser/Node/Stmt/DeclareDeclare.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Declare_' => '/nikic-php-parser/PhpParser/Node/Stmt/Declare_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Do_' => '/nikic-php-parser/PhpParser/Node/Stmt/Do_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Echo_' => '/nikic-php-parser/PhpParser/Node/Stmt/Echo_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\ElseIf_' => '/nikic-php-parser/PhpParser/Node/Stmt/ElseIf_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Else_' => '/nikic-php-parser/PhpParser/Node/Stmt/Else_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\EnumCase' => '/nikic-php-parser/PhpParser/Node/Stmt/EnumCase.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Enum_' => '/nikic-php-parser/PhpParser/Node/Stmt/Enum_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Expression' => '/nikic-php-parser/PhpParser/Node/Stmt/Expression.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Finally_' => '/nikic-php-parser/PhpParser/Node/Stmt/Finally_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\For_' => '/nikic-php-parser/PhpParser/Node/Stmt/For_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Foreach_' => '/nikic-php-parser/PhpParser/Node/Stmt/Foreach_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Function_' => '/nikic-php-parser/PhpParser/Node/Stmt/Function_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Global_' => '/nikic-php-parser/PhpParser/Node/Stmt/Global_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Goto_' => '/nikic-php-parser/PhpParser/Node/Stmt/Goto_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\GroupUse' => '/nikic-php-parser/PhpParser/Node/Stmt/GroupUse.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\HaltCompiler' => '/nikic-php-parser/PhpParser/Node/Stmt/HaltCompiler.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\If_' => '/nikic-php-parser/PhpParser/Node/Stmt/If_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\InlineHTML' => '/nikic-php-parser/PhpParser/Node/Stmt/InlineHTML.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Interface_' => '/nikic-php-parser/PhpParser/Node/Stmt/Interface_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Label' => '/nikic-php-parser/PhpParser/Node/Stmt/Label.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Namespace_' => '/nikic-php-parser/PhpParser/Node/Stmt/Namespace_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Nop' => '/nikic-php-parser/PhpParser/Node/Stmt/Nop.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Property' => '/nikic-php-parser/PhpParser/Node/Stmt/Property.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\PropertyProperty' => '/nikic-php-parser/PhpParser/Node/Stmt/PropertyProperty.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Return_' => '/nikic-php-parser/PhpParser/Node/Stmt/Return_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\StaticVar' => '/nikic-php-parser/PhpParser/Node/Stmt/StaticVar.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Static_' => '/nikic-php-parser/PhpParser/Node/Stmt/Static_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Switch_' => '/nikic-php-parser/PhpParser/Node/Stmt/Switch_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Throw_' => '/nikic-php-parser/PhpParser/Node/Stmt/Throw_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\TraitUse' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUse.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\TraitUseAdaptation' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Alias' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Precedence' => '/nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Trait_' => '/nikic-php-parser/PhpParser/Node/Stmt/Trait_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\TryCatch' => '/nikic-php-parser/PhpParser/Node/Stmt/TryCatch.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Unset_' => '/nikic-php-parser/PhpParser/Node/Stmt/Unset_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\UseUse' => '/nikic-php-parser/PhpParser/Node/Stmt/UseUse.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\Use_' => '/nikic-php-parser/PhpParser/Node/Stmt/Use_.php', + 'PHPUnit\\PhpParser\\Node\\Stmt\\While_' => '/nikic-php-parser/PhpParser/Node/Stmt/While_.php', + 'PHPUnit\\PhpParser\\Node\\UnionType' => '/nikic-php-parser/PhpParser/Node/UnionType.php', + 'PHPUnit\\PhpParser\\Node\\VarLikeIdentifier' => '/nikic-php-parser/PhpParser/Node/VarLikeIdentifier.php', + 'PHPUnit\\PhpParser\\Node\\VariadicPlaceholder' => '/nikic-php-parser/PhpParser/Node/VariadicPlaceholder.php', + 'PHPUnit\\PhpParser\\Parser' => '/nikic-php-parser/PhpParser/Parser.php', + 'PHPUnit\\PhpParser\\ParserAbstract' => '/nikic-php-parser/PhpParser/ParserAbstract.php', + 'PHPUnit\\PhpParser\\ParserFactory' => '/nikic-php-parser/PhpParser/ParserFactory.php', + 'PHPUnit\\PhpParser\\Parser\\Multiple' => '/nikic-php-parser/PhpParser/Parser/Multiple.php', + 'PHPUnit\\PhpParser\\Parser\\Php5' => '/nikic-php-parser/PhpParser/Parser/Php5.php', + 'PHPUnit\\PhpParser\\Parser\\Php7' => '/nikic-php-parser/PhpParser/Parser/Php7.php', + 'PHPUnit\\PhpParser\\Parser\\Tokens' => '/nikic-php-parser/PhpParser/Parser/Tokens.php', + 'PHPUnit\\PhpParser\\PrettyPrinterAbstract' => '/nikic-php-parser/PhpParser/PrettyPrinterAbstract.php', + 'PHPUnit\\PhpParser\\PrettyPrinter\\Standard' => '/nikic-php-parser/PhpParser/PrettyPrinter/Standard.php', + 'PHPUnit\\Runner\\AfterIncompleteTestHook' => '/phpunit/Runner/Hook/AfterIncompleteTestHook.php', + 'PHPUnit\\Runner\\AfterLastTestHook' => '/phpunit/Runner/Hook/AfterLastTestHook.php', + 'PHPUnit\\Runner\\AfterRiskyTestHook' => '/phpunit/Runner/Hook/AfterRiskyTestHook.php', + 'PHPUnit\\Runner\\AfterSkippedTestHook' => '/phpunit/Runner/Hook/AfterSkippedTestHook.php', + 'PHPUnit\\Runner\\AfterSuccessfulTestHook' => '/phpunit/Runner/Hook/AfterSuccessfulTestHook.php', + 'PHPUnit\\Runner\\AfterTestErrorHook' => '/phpunit/Runner/Hook/AfterTestErrorHook.php', + 'PHPUnit\\Runner\\AfterTestFailureHook' => '/phpunit/Runner/Hook/AfterTestFailureHook.php', + 'PHPUnit\\Runner\\AfterTestHook' => '/phpunit/Runner/Hook/AfterTestHook.php', + 'PHPUnit\\Runner\\AfterTestWarningHook' => '/phpunit/Runner/Hook/AfterTestWarningHook.php', + 'PHPUnit\\Runner\\BaseTestRunner' => '/phpunit/Runner/BaseTestRunner.php', + 'PHPUnit\\Runner\\BeforeFirstTestHook' => '/phpunit/Runner/Hook/BeforeFirstTestHook.php', + 'PHPUnit\\Runner\\BeforeTestHook' => '/phpunit/Runner/Hook/BeforeTestHook.php', + 'PHPUnit\\Runner\\DefaultTestResultCache' => '/phpunit/Runner/DefaultTestResultCache.php', + 'PHPUnit\\Runner\\Exception' => '/phpunit/Runner/Exception.php', + 'PHPUnit\\Runner\\Extension\\ExtensionHandler' => '/phpunit/Runner/Extension/ExtensionHandler.php', + 'PHPUnit\\Runner\\Extension\\PharLoader' => '/phpunit/Runner/Extension/PharLoader.php', + 'PHPUnit\\Runner\\Filter\\ExcludeGroupFilterIterator' => '/phpunit/Runner/Filter/ExcludeGroupFilterIterator.php', + 'PHPUnit\\Runner\\Filter\\Factory' => '/phpunit/Runner/Filter/Factory.php', + 'PHPUnit\\Runner\\Filter\\GroupFilterIterator' => '/phpunit/Runner/Filter/GroupFilterIterator.php', + 'PHPUnit\\Runner\\Filter\\IncludeGroupFilterIterator' => '/phpunit/Runner/Filter/IncludeGroupFilterIterator.php', + 'PHPUnit\\Runner\\Filter\\NameFilterIterator' => '/phpunit/Runner/Filter/NameFilterIterator.php', + 'PHPUnit\\Runner\\Hook' => '/phpunit/Runner/Hook/Hook.php', + 'PHPUnit\\Runner\\NullTestResultCache' => '/phpunit/Runner/NullTestResultCache.php', + 'PHPUnit\\Runner\\PhptTestCase' => '/phpunit/Runner/PhptTestCase.php', + 'PHPUnit\\Runner\\ResultCacheExtension' => '/phpunit/Runner/ResultCacheExtension.php', + 'PHPUnit\\Runner\\StandardTestSuiteLoader' => '/phpunit/Runner/StandardTestSuiteLoader.php', + 'PHPUnit\\Runner\\TestHook' => '/phpunit/Runner/Hook/TestHook.php', + 'PHPUnit\\Runner\\TestListenerAdapter' => '/phpunit/Runner/Hook/TestListenerAdapter.php', + 'PHPUnit\\Runner\\TestResultCache' => '/phpunit/Runner/TestResultCache.php', + 'PHPUnit\\Runner\\TestSuiteLoader' => '/phpunit/Runner/TestSuiteLoader.php', + 'PHPUnit\\Runner\\TestSuiteSorter' => '/phpunit/Runner/TestSuiteSorter.php', + 'PHPUnit\\Runner\\Version' => '/phpunit/Runner/Version.php', + 'PHPUnit\\SebastianBergmann\\CliParser\\AmbiguousOptionException' => '/sebastian-cli-parser/exceptions/AmbiguousOptionException.php', + 'PHPUnit\\SebastianBergmann\\CliParser\\Exception' => '/sebastian-cli-parser/exceptions/Exception.php', + 'PHPUnit\\SebastianBergmann\\CliParser\\OptionDoesNotAllowArgumentException' => '/sebastian-cli-parser/exceptions/OptionDoesNotAllowArgumentException.php', + 'PHPUnit\\SebastianBergmann\\CliParser\\Parser' => '/sebastian-cli-parser/Parser.php', + 'PHPUnit\\SebastianBergmann\\CliParser\\RequiredOptionArgumentMissingException' => '/sebastian-cli-parser/exceptions/RequiredOptionArgumentMissingException.php', + 'PHPUnit\\SebastianBergmann\\CliParser\\UnknownOptionException' => '/sebastian-cli-parser/exceptions/UnknownOptionException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\BranchAndPathCoverageNotSupportedException' => '/php-code-coverage/Exception/BranchAndPathCoverageNotSupportedException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\CodeCoverage' => '/php-code-coverage/CodeCoverage.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\CrapIndex' => '/php-code-coverage/CrapIndex.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\DeadCodeDetectionNotSupportedException' => '/php-code-coverage/Exception/DeadCodeDetectionNotSupportedException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Directory' => '/php-code-coverage/Directory.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\DirectoryCouldNotBeCreatedException' => '/php-code-coverage/Exception/DirectoryCouldNotBeCreatedException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Driver' => '/php-code-coverage/Driver/Driver.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PathExistsButIsNotDirectoryException' => '/php-code-coverage/Exception/PathExistsButIsNotDirectoryException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PcovDriver' => '/php-code-coverage/Driver/PcovDriver.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PcovNotAvailableException' => '/php-code-coverage/Exception/PcovNotAvailableException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgDriver' => '/php-code-coverage/Driver/PhpdbgDriver.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgNotAvailableException' => '/php-code-coverage/Exception/PhpdbgNotAvailableException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Selector' => '/php-code-coverage/Driver/Selector.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\WriteOperationFailedException' => '/php-code-coverage/Exception/WriteOperationFailedException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\WrongXdebugVersionException' => '/php-code-coverage/Exception/WrongXdebugVersionException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2Driver' => '/php-code-coverage/Driver/Xdebug2Driver.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2NotEnabledException' => '/php-code-coverage/Exception/Xdebug2NotEnabledException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3Driver' => '/php-code-coverage/Driver/Xdebug3Driver.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3NotEnabledException' => '/php-code-coverage/Exception/Xdebug3NotEnabledException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Driver\\XdebugNotAvailableException' => '/php-code-coverage/Exception/XdebugNotAvailableException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Exception' => '/php-code-coverage/Exception/Exception.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Filter' => '/php-code-coverage/Filter.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\InvalidArgumentException' => '/php-code-coverage/Exception/InvalidArgumentException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverAvailableException' => '/php-code-coverage/Exception/NoCodeCoverageDriverAvailableException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverWithPathCoverageSupportAvailableException' => '/php-code-coverage/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\AbstractNode' => '/php-code-coverage/Node/AbstractNode.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\Builder' => '/php-code-coverage/Node/Builder.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\Directory' => '/php-code-coverage/Node/Directory.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\File' => '/php-code-coverage/Node/File.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Node\\Iterator' => '/php-code-coverage/Node/Iterator.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\ParserException' => '/php-code-coverage/Exception/ParserException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Percentage' => '/php-code-coverage/Percentage.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\ProcessedCodeCoverageData' => '/php-code-coverage/ProcessedCodeCoverageData.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\RawCodeCoverageData' => '/php-code-coverage/RawCodeCoverageData.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\ReflectionException' => '/php-code-coverage/Exception/ReflectionException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\ReportAlreadyFinalizedException' => '/php-code-coverage/Exception/ReportAlreadyFinalizedException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Clover' => '/php-code-coverage/Report/Clover.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Cobertura' => '/php-code-coverage/Report/Cobertura.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Crap4j' => '/php-code-coverage/Report/Crap4j.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Dashboard' => '/php-code-coverage/Report/Html/Renderer/Dashboard.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Directory' => '/php-code-coverage/Report/Html/Renderer/Directory.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Facade' => '/php-code-coverage/Report/Html/Facade.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\File' => '/php-code-coverage/Report/Html/Renderer/File.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Html\\Renderer' => '/php-code-coverage/Report/Html/Renderer.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\PHP' => '/php-code-coverage/Report/PHP.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Text' => '/php-code-coverage/Report/Text.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\BuildInformation' => '/php-code-coverage/Report/Xml/BuildInformation.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Coverage' => '/php-code-coverage/Report/Xml/Coverage.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Directory' => '/php-code-coverage/Report/Xml/Directory.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Facade' => '/php-code-coverage/Report/Xml/Facade.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\File' => '/php-code-coverage/Report/Xml/File.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Method' => '/php-code-coverage/Report/Xml/Method.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Node' => '/php-code-coverage/Report/Xml/Node.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Project' => '/php-code-coverage/Report/Xml/Project.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Report' => '/php-code-coverage/Report/Xml/Report.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Source' => '/php-code-coverage/Report/Xml/Source.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Tests' => '/php-code-coverage/Report/Xml/Tests.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Totals' => '/php-code-coverage/Report/Xml/Totals.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Report\\Xml\\Unit' => '/php-code-coverage/Report/Xml/Unit.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysisCacheNotConfiguredException' => '/php-code-coverage/Exception/StaticAnalysisCacheNotConfiguredException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\Cache' => '/php-code-coverage/StaticAnalysis/Cache.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CacheWarmer' => '/php-code-coverage/StaticAnalysis/CacheWarmer.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CachingCoveredFileAnalyser' => '/php-code-coverage/StaticAnalysis/CachingCoveredFileAnalyser.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CachingUncoveredFileAnalyser' => '/php-code-coverage/StaticAnalysis/CachingUncoveredFileAnalyser.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CodeUnitFindingVisitor' => '/php-code-coverage/StaticAnalysis/CodeUnitFindingVisitor.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CoveredFileAnalyser' => '/php-code-coverage/StaticAnalysis/CoveredFileAnalyser.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ExecutableLinesFindingVisitor' => '/php-code-coverage/StaticAnalysis/ExecutableLinesFindingVisitor.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\IgnoredLinesFindingVisitor' => '/php-code-coverage/StaticAnalysis/IgnoredLinesFindingVisitor.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingCoveredFileAnalyser' => '/php-code-coverage/StaticAnalysis/ParsingCoveredFileAnalyser.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingUncoveredFileAnalyser' => '/php-code-coverage/StaticAnalysis/ParsingUncoveredFileAnalyser.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\StaticAnalysis\\UncoveredFileAnalyser' => '/php-code-coverage/StaticAnalysis/UncoveredFileAnalyser.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\TestIdMissingException' => '/php-code-coverage/Exception/TestIdMissingException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => '/php-code-coverage/Exception/UnintentionallyCoveredCodeException.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\Version' => '/php-code-coverage/Version.php', + 'PHPUnit\\SebastianBergmann\\CodeCoverage\\XmlException' => '/php-code-coverage/Exception/XmlException.php', + 'PHPUnit\\SebastianBergmann\\CodeUnitReverseLookup\\Wizard' => '/sebastian-code-unit-reverse-lookup/Wizard.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\ClassMethodUnit' => '/sebastian-code-unit/ClassMethodUnit.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\ClassUnit' => '/sebastian-code-unit/ClassUnit.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\CodeUnit' => '/sebastian-code-unit/CodeUnit.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\CodeUnitCollection' => '/sebastian-code-unit/CodeUnitCollection.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\CodeUnitCollectionIterator' => '/sebastian-code-unit/CodeUnitCollectionIterator.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\Exception' => '/sebastian-code-unit/exceptions/Exception.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\FunctionUnit' => '/sebastian-code-unit/FunctionUnit.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\InterfaceMethodUnit' => '/sebastian-code-unit/InterfaceMethodUnit.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\InterfaceUnit' => '/sebastian-code-unit/InterfaceUnit.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\InvalidCodeUnitException' => '/sebastian-code-unit/exceptions/InvalidCodeUnitException.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\Mapper' => '/sebastian-code-unit/Mapper.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\NoTraitException' => '/sebastian-code-unit/exceptions/NoTraitException.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\ReflectionException' => '/sebastian-code-unit/exceptions/ReflectionException.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\TraitMethodUnit' => '/sebastian-code-unit/TraitMethodUnit.php', + 'PHPUnit\\SebastianBergmann\\CodeUnit\\TraitUnit' => '/sebastian-code-unit/TraitUnit.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\ArrayComparator' => '/sebastian-comparator/ArrayComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\Comparator' => '/sebastian-comparator/Comparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\ComparisonFailure' => '/sebastian-comparator/ComparisonFailure.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\DOMNodeComparator' => '/sebastian-comparator/DOMNodeComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\DateTimeComparator' => '/sebastian-comparator/DateTimeComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\DoubleComparator' => '/sebastian-comparator/DoubleComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\Exception' => '/sebastian-comparator/exceptions/Exception.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\ExceptionComparator' => '/sebastian-comparator/ExceptionComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\Factory' => '/sebastian-comparator/Factory.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\MockObjectComparator' => '/sebastian-comparator/MockObjectComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\NumericComparator' => '/sebastian-comparator/NumericComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\ObjectComparator' => '/sebastian-comparator/ObjectComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\ResourceComparator' => '/sebastian-comparator/ResourceComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\RuntimeException' => '/sebastian-comparator/exceptions/RuntimeException.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\ScalarComparator' => '/sebastian-comparator/ScalarComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\SplObjectStorageComparator' => '/sebastian-comparator/SplObjectStorageComparator.php', + 'PHPUnit\\SebastianBergmann\\Comparator\\TypeComparator' => '/sebastian-comparator/TypeComparator.php', + 'PHPUnit\\SebastianBergmann\\Complexity\\Calculator' => '/sebastian-complexity/Calculator.php', + 'PHPUnit\\SebastianBergmann\\Complexity\\Complexity' => '/sebastian-complexity/Complexity/Complexity.php', + 'PHPUnit\\SebastianBergmann\\Complexity\\ComplexityCalculatingVisitor' => '/sebastian-complexity/Visitor/ComplexityCalculatingVisitor.php', + 'PHPUnit\\SebastianBergmann\\Complexity\\ComplexityCollection' => '/sebastian-complexity/Complexity/ComplexityCollection.php', + 'PHPUnit\\SebastianBergmann\\Complexity\\ComplexityCollectionIterator' => '/sebastian-complexity/Complexity/ComplexityCollectionIterator.php', + 'PHPUnit\\SebastianBergmann\\Complexity\\CyclomaticComplexityCalculatingVisitor' => '/sebastian-complexity/Visitor/CyclomaticComplexityCalculatingVisitor.php', + 'PHPUnit\\SebastianBergmann\\Complexity\\Exception' => '/sebastian-complexity/Exception/Exception.php', + 'PHPUnit\\SebastianBergmann\\Complexity\\RuntimeException' => '/sebastian-complexity/Exception/RuntimeException.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Chunk' => '/sebastian-diff/Chunk.php', + 'PHPUnit\\SebastianBergmann\\Diff\\ConfigurationException' => '/sebastian-diff/Exception/ConfigurationException.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Diff' => '/sebastian-diff/Diff.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Differ' => '/sebastian-diff/Differ.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Exception' => '/sebastian-diff/Exception/Exception.php', + 'PHPUnit\\SebastianBergmann\\Diff\\InvalidArgumentException' => '/sebastian-diff/Exception/InvalidArgumentException.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Line' => '/sebastian-diff/Line.php', + 'PHPUnit\\SebastianBergmann\\Diff\\LongestCommonSubsequenceCalculator' => '/sebastian-diff/LongestCommonSubsequenceCalculator.php', + 'PHPUnit\\SebastianBergmann\\Diff\\MemoryEfficientLongestCommonSubsequenceCalculator' => '/sebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Output\\AbstractChunkOutputBuilder' => '/sebastian-diff/Output/AbstractChunkOutputBuilder.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Output\\DiffOnlyOutputBuilder' => '/sebastian-diff/Output/DiffOnlyOutputBuilder.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Output\\DiffOutputBuilderInterface' => '/sebastian-diff/Output/DiffOutputBuilderInterface.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Output\\StrictUnifiedDiffOutputBuilder' => '/sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Output\\UnifiedDiffOutputBuilder' => '/sebastian-diff/Output/UnifiedDiffOutputBuilder.php', + 'PHPUnit\\SebastianBergmann\\Diff\\Parser' => '/sebastian-diff/Parser.php', + 'PHPUnit\\SebastianBergmann\\Diff\\TimeEfficientLongestCommonSubsequenceCalculator' => '/sebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php', + 'PHPUnit\\SebastianBergmann\\Environment\\Console' => '/sebastian-environment/Console.php', + 'PHPUnit\\SebastianBergmann\\Environment\\OperatingSystem' => '/sebastian-environment/OperatingSystem.php', + 'PHPUnit\\SebastianBergmann\\Environment\\Runtime' => '/sebastian-environment/Runtime.php', + 'PHPUnit\\SebastianBergmann\\Exporter\\Exporter' => '/sebastian-exporter/Exporter.php', + 'PHPUnit\\SebastianBergmann\\FileIterator\\Facade' => '/php-file-iterator/Facade.php', + 'PHPUnit\\SebastianBergmann\\FileIterator\\Factory' => '/php-file-iterator/Factory.php', + 'PHPUnit\\SebastianBergmann\\FileIterator\\Iterator' => '/php-file-iterator/Iterator.php', + 'PHPUnit\\SebastianBergmann\\GlobalState\\CodeExporter' => '/sebastian-global-state/CodeExporter.php', + 'PHPUnit\\SebastianBergmann\\GlobalState\\Exception' => '/sebastian-global-state/exceptions/Exception.php', + 'PHPUnit\\SebastianBergmann\\GlobalState\\ExcludeList' => '/sebastian-global-state/ExcludeList.php', + 'PHPUnit\\SebastianBergmann\\GlobalState\\Restorer' => '/sebastian-global-state/Restorer.php', + 'PHPUnit\\SebastianBergmann\\GlobalState\\RuntimeException' => '/sebastian-global-state/exceptions/RuntimeException.php', + 'PHPUnit\\SebastianBergmann\\GlobalState\\Snapshot' => '/sebastian-global-state/Snapshot.php', + 'PHPUnit\\SebastianBergmann\\Invoker\\Exception' => '/php-invoker/exceptions/Exception.php', + 'PHPUnit\\SebastianBergmann\\Invoker\\Invoker' => '/php-invoker/Invoker.php', + 'PHPUnit\\SebastianBergmann\\Invoker\\ProcessControlExtensionNotLoadedException' => '/php-invoker/exceptions/ProcessControlExtensionNotLoadedException.php', + 'PHPUnit\\SebastianBergmann\\Invoker\\TimeoutException' => '/php-invoker/exceptions/TimeoutException.php', + 'PHPUnit\\SebastianBergmann\\LinesOfCode\\Counter' => '/sebastian-lines-of-code/Counter.php', + 'PHPUnit\\SebastianBergmann\\LinesOfCode\\Exception' => '/sebastian-lines-of-code/Exception/Exception.php', + 'PHPUnit\\SebastianBergmann\\LinesOfCode\\IllogicalValuesException' => '/sebastian-lines-of-code/Exception/IllogicalValuesException.php', + 'PHPUnit\\SebastianBergmann\\LinesOfCode\\LineCountingVisitor' => '/sebastian-lines-of-code/LineCountingVisitor.php', + 'PHPUnit\\SebastianBergmann\\LinesOfCode\\LinesOfCode' => '/sebastian-lines-of-code/LinesOfCode.php', + 'PHPUnit\\SebastianBergmann\\LinesOfCode\\NegativeValueException' => '/sebastian-lines-of-code/Exception/NegativeValueException.php', + 'PHPUnit\\SebastianBergmann\\LinesOfCode\\RuntimeException' => '/sebastian-lines-of-code/Exception/RuntimeException.php', + 'PHPUnit\\SebastianBergmann\\ObjectEnumerator\\Enumerator' => '/sebastian-object-enumerator/Enumerator.php', + 'PHPUnit\\SebastianBergmann\\ObjectEnumerator\\Exception' => '/sebastian-object-enumerator/Exception.php', + 'PHPUnit\\SebastianBergmann\\ObjectEnumerator\\InvalidArgumentException' => '/sebastian-object-enumerator/InvalidArgumentException.php', + 'PHPUnit\\SebastianBergmann\\ObjectReflector\\Exception' => '/sebastian-object-reflector/Exception.php', + 'PHPUnit\\SebastianBergmann\\ObjectReflector\\InvalidArgumentException' => '/sebastian-object-reflector/InvalidArgumentException.php', + 'PHPUnit\\SebastianBergmann\\ObjectReflector\\ObjectReflector' => '/sebastian-object-reflector/ObjectReflector.php', + 'PHPUnit\\SebastianBergmann\\RecursionContext\\Context' => '/sebastian-recursion-context/Context.php', + 'PHPUnit\\SebastianBergmann\\RecursionContext\\Exception' => '/sebastian-recursion-context/Exception.php', + 'PHPUnit\\SebastianBergmann\\RecursionContext\\InvalidArgumentException' => '/sebastian-recursion-context/InvalidArgumentException.php', + 'PHPUnit\\SebastianBergmann\\ResourceOperations\\ResourceOperations' => '/sebastian-resource-operations/ResourceOperations.php', + 'PHPUnit\\SebastianBergmann\\Template\\Exception' => '/php-text-template/exceptions/Exception.php', + 'PHPUnit\\SebastianBergmann\\Template\\InvalidArgumentException' => '/php-text-template/exceptions/InvalidArgumentException.php', + 'PHPUnit\\SebastianBergmann\\Template\\RuntimeException' => '/php-text-template/exceptions/RuntimeException.php', + 'PHPUnit\\SebastianBergmann\\Template\\Template' => '/php-text-template/Template.php', + 'PHPUnit\\SebastianBergmann\\Timer\\Duration' => '/php-timer/Duration.php', + 'PHPUnit\\SebastianBergmann\\Timer\\Exception' => '/php-timer/exceptions/Exception.php', + 'PHPUnit\\SebastianBergmann\\Timer\\NoActiveTimerException' => '/php-timer/exceptions/NoActiveTimerException.php', + 'PHPUnit\\SebastianBergmann\\Timer\\ResourceUsageFormatter' => '/php-timer/ResourceUsageFormatter.php', + 'PHPUnit\\SebastianBergmann\\Timer\\TimeSinceStartOfRequestNotAvailableException' => '/php-timer/exceptions/TimeSinceStartOfRequestNotAvailableException.php', + 'PHPUnit\\SebastianBergmann\\Timer\\Timer' => '/php-timer/Timer.php', + 'PHPUnit\\SebastianBergmann\\Type\\CallableType' => '/sebastian-type/CallableType.php', + 'PHPUnit\\SebastianBergmann\\Type\\Exception' => '/sebastian-type/exception/Exception.php', + 'PHPUnit\\SebastianBergmann\\Type\\FalseType' => '/sebastian-type/FalseType.php', + 'PHPUnit\\SebastianBergmann\\Type\\GenericObjectType' => '/sebastian-type/GenericObjectType.php', + 'PHPUnit\\SebastianBergmann\\Type\\IterableType' => '/sebastian-type/IterableType.php', + 'PHPUnit\\SebastianBergmann\\Type\\LogicException' => '/sebastian-type/exception/LogicException.php', + 'PHPUnit\\SebastianBergmann\\Type\\MixedType' => '/sebastian-type/MixedType.php', + 'PHPUnit\\SebastianBergmann\\Type\\NullType' => '/sebastian-type/NullType.php', + 'PHPUnit\\SebastianBergmann\\Type\\ObjectType' => '/sebastian-type/ObjectType.php', + 'PHPUnit\\SebastianBergmann\\Type\\ReflectionMapper' => '/sebastian-type/ReflectionMapper.php', + 'PHPUnit\\SebastianBergmann\\Type\\RuntimeException' => '/sebastian-type/exception/RuntimeException.php', + 'PHPUnit\\SebastianBergmann\\Type\\SimpleType' => '/sebastian-type/SimpleType.php', + 'PHPUnit\\SebastianBergmann\\Type\\StaticType' => '/sebastian-type/StaticType.php', + 'PHPUnit\\SebastianBergmann\\Type\\Type' => '/sebastian-type/Type.php', + 'PHPUnit\\SebastianBergmann\\Type\\TypeName' => '/sebastian-type/TypeName.php', + 'PHPUnit\\SebastianBergmann\\Type\\UnionType' => '/sebastian-type/UnionType.php', + 'PHPUnit\\SebastianBergmann\\Type\\UnknownType' => '/sebastian-type/UnknownType.php', + 'PHPUnit\\SebastianBergmann\\Type\\VoidType' => '/sebastian-type/VoidType.php', + 'PHPUnit\\SebastianBergmann\\Version' => '/sebastian-version/Version.php', + 'PHPUnit\\Symfony\\Polyfill\\Ctype\\Ctype' => '/symfony-polyfill-ctype/Ctype.php', + 'PHPUnit\\TextUI\\CliArguments\\Builder' => '/phpunit/TextUI/CliArguments/Builder.php', + 'PHPUnit\\TextUI\\CliArguments\\Configuration' => '/phpunit/TextUI/CliArguments/Configuration.php', + 'PHPUnit\\TextUI\\CliArguments\\Exception' => '/phpunit/TextUI/CliArguments/Exception.php', + 'PHPUnit\\TextUI\\CliArguments\\Mapper' => '/phpunit/TextUI/CliArguments/Mapper.php', + 'PHPUnit\\TextUI\\Command' => '/phpunit/TextUI/Command.php', + 'PHPUnit\\TextUI\\DefaultResultPrinter' => '/phpunit/TextUI/DefaultResultPrinter.php', + 'PHPUnit\\TextUI\\Exception' => '/phpunit/TextUI/Exception/Exception.php', + 'PHPUnit\\TextUI\\Help' => '/phpunit/TextUI/Help.php', + 'PHPUnit\\TextUI\\ReflectionException' => '/phpunit/TextUI/Exception/ReflectionException.php', + 'PHPUnit\\TextUI\\ResultPrinter' => '/phpunit/TextUI/ResultPrinter.php', + 'PHPUnit\\TextUI\\RuntimeException' => '/phpunit/TextUI/Exception/RuntimeException.php', + 'PHPUnit\\TextUI\\TestDirectoryNotFoundException' => '/phpunit/TextUI/Exception/TestDirectoryNotFoundException.php', + 'PHPUnit\\TextUI\\TestFileNotFoundException' => '/phpunit/TextUI/Exception/TestFileNotFoundException.php', + 'PHPUnit\\TextUI\\TestRunner' => '/phpunit/TextUI/TestRunner.php', + 'PHPUnit\\TextUI\\TestSuiteMapper' => '/phpunit/TextUI/TestSuiteMapper.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\CodeCoverage' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\FilterMapper' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/FilterMapper.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\Directory' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/Directory.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\DirectoryCollection' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\DirectoryCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Clover' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Clover.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Cobertura' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Crap4j' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Html' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Html.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Php' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Php.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Text' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Text.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Xml' => '/phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Xml.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Configuration' => '/phpunit/TextUI/XmlConfiguration/Configuration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Constant' => '/phpunit/TextUI/XmlConfiguration/PHP/Constant.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ConstantCollection' => '/phpunit/TextUI/XmlConfiguration/PHP/ConstantCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ConstantCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHP/ConstantCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ConvertLogTypes' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/ConvertLogTypes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCloverToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCloverToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCrap4jToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCrap4jToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageHtmlToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageHtmlToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoveragePhpToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoveragePhpToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageTextToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageTextToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageXmlToReport' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageXmlToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Directory' => '/phpunit/TextUI/XmlConfiguration/Filesystem/Directory.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\DirectoryCollection' => '/phpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\DirectoryCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Exception' => '/phpunit/TextUI/XmlConfiguration/Exception.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Extension' => '/phpunit/TextUI/XmlConfiguration/PHPUnit/Extension.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ExtensionCollection' => '/phpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ExtensionCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\File' => '/phpunit/TextUI/XmlConfiguration/Filesystem/File.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\FileCollection' => '/phpunit/TextUI/XmlConfiguration/Filesystem/FileCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\FileCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/Filesystem/FileCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Generator' => '/phpunit/TextUI/XmlConfiguration/Generator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Group' => '/phpunit/TextUI/XmlConfiguration/Group/Group.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\GroupCollection' => '/phpunit/TextUI/XmlConfiguration/Group/GroupCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\GroupCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/Group/GroupCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Groups' => '/phpunit/TextUI/XmlConfiguration/Group/Groups.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IniSetting' => '/phpunit/TextUI/XmlConfiguration/PHP/IniSetting.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IniSettingCollection' => '/phpunit/TextUI/XmlConfiguration/PHP/IniSettingCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IniSettingCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHP/IniSettingCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IntroduceCoverageElement' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/IntroduceCoverageElement.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Loader' => '/phpunit/TextUI/XmlConfiguration/Loader.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\LogToReportMigration' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/LogToReportMigration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Junit' => '/phpunit/TextUI/XmlConfiguration/Logging/Junit.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Logging' => '/phpunit/TextUI/XmlConfiguration/Logging/Logging.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TeamCity' => '/phpunit/TextUI/XmlConfiguration/Logging/TeamCity.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Html' => '/phpunit/TextUI/XmlConfiguration/Logging/TestDox/Html.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Text' => '/phpunit/TextUI/XmlConfiguration/Logging/TestDox/Text.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Xml' => '/phpunit/TextUI/XmlConfiguration/Logging/TestDox/Xml.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Text' => '/phpunit/TextUI/XmlConfiguration/Logging/Text.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Migration' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/Migration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilder' => '/phpunit/TextUI/XmlConfiguration/Migration/MigrationBuilder.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilderException' => '/phpunit/TextUI/XmlConfiguration/Migration/MigrationBuilderException.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationException' => '/phpunit/TextUI/XmlConfiguration/Migration/MigrationException.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Migrator' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromFilterWhitelistToCoverage' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromRootToCoverage' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistDirectoriesToCoverage' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistDirectoriesToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistExcludesToCoverage' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistExcludesToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\PHPUnit' => '/phpunit/TextUI/XmlConfiguration/PHPUnit/PHPUnit.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Php' => '/phpunit/TextUI/XmlConfiguration/PHP/Php.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\PhpHandler' => '/phpunit/TextUI/XmlConfiguration/PHP/PhpHandler.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveCacheTokensAttribute' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveCacheTokensAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveEmptyFilter' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveEmptyFilter.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveLogTypes' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveLogTypes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectory' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectory.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectoryCollection' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectoryCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestFile' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestFile.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestFileCollection' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestFileCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuite' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestSuite.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteCollection' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\UpdateSchemaLocationTo93' => '/phpunit/TextUI/XmlConfiguration/Migration/Migrations/UpdateSchemaLocationTo93.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Variable' => '/phpunit/TextUI/XmlConfiguration/PHP/Variable.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollection' => '/phpunit/TextUI/XmlConfiguration/PHP/VariableCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollectionIterator' => '/phpunit/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.php', + 'PHPUnit\\TheSeer\\Tokenizer\\Exception' => '/theseer-tokenizer/Exception.php', + 'PHPUnit\\TheSeer\\Tokenizer\\NamespaceUri' => '/theseer-tokenizer/NamespaceUri.php', + 'PHPUnit\\TheSeer\\Tokenizer\\NamespaceUriException' => '/theseer-tokenizer/NamespaceUriException.php', + 'PHPUnit\\TheSeer\\Tokenizer\\Token' => '/theseer-tokenizer/Token.php', + 'PHPUnit\\TheSeer\\Tokenizer\\TokenCollection' => '/theseer-tokenizer/TokenCollection.php', + 'PHPUnit\\TheSeer\\Tokenizer\\TokenCollectionException' => '/theseer-tokenizer/TokenCollectionException.php', + 'PHPUnit\\TheSeer\\Tokenizer\\Tokenizer' => '/theseer-tokenizer/Tokenizer.php', + 'PHPUnit\\TheSeer\\Tokenizer\\XMLSerializer' => '/theseer-tokenizer/XMLSerializer.php', + 'PHPUnit\\Util\\Annotation\\DocBlock' => '/phpunit/Util/Annotation/DocBlock.php', + 'PHPUnit\\Util\\Annotation\\Registry' => '/phpunit/Util/Annotation/Registry.php', + 'PHPUnit\\Util\\Blacklist' => '/phpunit/Util/Blacklist.php', + 'PHPUnit\\Util\\Color' => '/phpunit/Util/Color.php', + 'PHPUnit\\Util\\ErrorHandler' => '/phpunit/Util/ErrorHandler.php', + 'PHPUnit\\Util\\Exception' => '/phpunit/Util/Exception.php', + 'PHPUnit\\Util\\ExcludeList' => '/phpunit/Util/ExcludeList.php', + 'PHPUnit\\Util\\FileLoader' => '/phpunit/Util/FileLoader.php', + 'PHPUnit\\Util\\Filesystem' => '/phpunit/Util/Filesystem.php', + 'PHPUnit\\Util\\Filter' => '/phpunit/Util/Filter.php', + 'PHPUnit\\Util\\GlobalState' => '/phpunit/Util/GlobalState.php', + 'PHPUnit\\Util\\InvalidDataSetException' => '/phpunit/Util/InvalidDataSetException.php', + 'PHPUnit\\Util\\Json' => '/phpunit/Util/Json.php', + 'PHPUnit\\Util\\Log\\JUnit' => '/phpunit/Util/Log/JUnit.php', + 'PHPUnit\\Util\\Log\\TeamCity' => '/phpunit/Util/Log/TeamCity.php', + 'PHPUnit\\Util\\PHP\\AbstractPhpProcess' => '/phpunit/Util/PHP/AbstractPhpProcess.php', + 'PHPUnit\\Util\\PHP\\DefaultPhpProcess' => '/phpunit/Util/PHP/DefaultPhpProcess.php', + 'PHPUnit\\Util\\PHP\\WindowsPhpProcess' => '/phpunit/Util/PHP/WindowsPhpProcess.php', + 'PHPUnit\\Util\\Printer' => '/phpunit/Util/Printer.php', + 'PHPUnit\\Util\\RegularExpression' => '/phpunit/Util/RegularExpression.php', + 'PHPUnit\\Util\\Test' => '/phpunit/Util/Test.php', + 'PHPUnit\\Util\\TestDox\\CliTestDoxPrinter' => '/phpunit/Util/TestDox/CliTestDoxPrinter.php', + 'PHPUnit\\Util\\TestDox\\HtmlResultPrinter' => '/phpunit/Util/TestDox/HtmlResultPrinter.php', + 'PHPUnit\\Util\\TestDox\\NamePrettifier' => '/phpunit/Util/TestDox/NamePrettifier.php', + 'PHPUnit\\Util\\TestDox\\ResultPrinter' => '/phpunit/Util/TestDox/ResultPrinter.php', + 'PHPUnit\\Util\\TestDox\\TestDoxPrinter' => '/phpunit/Util/TestDox/TestDoxPrinter.php', + 'PHPUnit\\Util\\TestDox\\TextResultPrinter' => '/phpunit/Util/TestDox/TextResultPrinter.php', + 'PHPUnit\\Util\\TestDox\\XmlResultPrinter' => '/phpunit/Util/TestDox/XmlResultPrinter.php', + 'PHPUnit\\Util\\TextTestListRenderer' => '/phpunit/Util/TextTestListRenderer.php', + 'PHPUnit\\Util\\Type' => '/phpunit/Util/Type.php', + 'PHPUnit\\Util\\VersionComparisonOperator' => '/phpunit/Util/VersionComparisonOperator.php', + 'PHPUnit\\Util\\XdebugFilterScriptGenerator' => '/phpunit/Util/XdebugFilterScriptGenerator.php', + 'PHPUnit\\Util\\Xml' => '/phpunit/Util/Xml.php', + 'PHPUnit\\Util\\XmlTestListRenderer' => '/phpunit/Util/XmlTestListRenderer.php', + 'PHPUnit\\Util\\Xml\\Exception' => '/phpunit/Util/Xml/Exception.php', + 'PHPUnit\\Util\\Xml\\FailedSchemaDetectionResult' => '/phpunit/Util/Xml/FailedSchemaDetectionResult.php', + 'PHPUnit\\Util\\Xml\\Loader' => '/phpunit/Util/Xml/Loader.php', + 'PHPUnit\\Util\\Xml\\SchemaDetectionResult' => '/phpunit/Util/Xml/SchemaDetectionResult.php', + 'PHPUnit\\Util\\Xml\\SchemaDetector' => '/phpunit/Util/Xml/SchemaDetector.php', + 'PHPUnit\\Util\\Xml\\SchemaFinder' => '/phpunit/Util/Xml/SchemaFinder.php', + 'PHPUnit\\Util\\Xml\\SnapshotNodeList' => '/phpunit/Util/Xml/SnapshotNodeList.php', + 'PHPUnit\\Util\\Xml\\SuccessfulSchemaDetectionResult' => '/phpunit/Util/Xml/SuccessfulSchemaDetectionResult.php', + 'PHPUnit\\Util\\Xml\\ValidationResult' => '/phpunit/Util/Xml/ValidationResult.php', + 'PHPUnit\\Util\\Xml\\Validator' => '/phpunit/Util/Xml/Validator.php', + 'PHPUnit\\Webmozart\\Assert\\Assert' => '/webmozart-assert/Assert.php', + 'PHPUnit\\Webmozart\\Assert\\InvalidArgumentException' => '/webmozart-assert/InvalidArgumentException.php', + 'PHPUnit\\Webmozart\\Assert\\Mixin' => '/webmozart-assert/Mixin.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock' => '/phpdocumentor-reflection-docblock/DocBlock.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlockFactory' => '/phpdocumentor-reflection-docblock/DocBlockFactory.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlockFactoryInterface' => '/phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Description' => '/phpdocumentor-reflection-docblock/DocBlock/Description.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\DescriptionFactory' => '/phpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\ExampleFinder' => '/phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Serializer' => '/phpdocumentor-reflection-docblock/DocBlock/Serializer.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\StandardTagFactory' => '/phpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tag' => '/phpdocumentor-reflection-docblock/DocBlock/Tag.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\TagFactory' => '/phpdocumentor-reflection-docblock/DocBlock/TagFactory.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Author' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\BaseTag' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Covers' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Deprecated' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Example' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Example.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Factory\\StaticMethod' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\AlignFormatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\PassthroughFormatter' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Generic' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\InvalidTag' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/InvalidTag.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Link' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Link.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Method' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Param' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Property' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyRead' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyWrite' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Fqsen' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Reference' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Url' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Return_' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\See' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/See.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Since' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Since.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Source' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Source.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\TagWithType' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/TagWithType.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Throws' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Uses' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Var_' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\DocBlock\\Tags\\Version' => '/phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Element' => '/phpdocumentor-reflection-common/Element.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Exception\\PcreException' => '/phpdocumentor-reflection-docblock/Exception/PcreException.php', + 'PHPUnit\\phpDocumentor\\Reflection\\File' => '/phpdocumentor-reflection-common/File.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Fqsen' => '/phpdocumentor-reflection-common/Fqsen.php', + 'PHPUnit\\phpDocumentor\\Reflection\\FqsenResolver' => '/phpdocumentor-type-resolver/FqsenResolver.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Location' => '/phpdocumentor-reflection-common/Location.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Project' => '/phpdocumentor-reflection-common/Project.php', + 'PHPUnit\\phpDocumentor\\Reflection\\ProjectFactory' => '/phpdocumentor-reflection-common/ProjectFactory.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoType' => '/phpdocumentor-type-resolver/PseudoType.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\CallableString' => '/phpdocumentor-type-resolver/PseudoTypes/CallableString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\False_' => '/phpdocumentor-type-resolver/PseudoTypes/False_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\HtmlEscapedString' => '/phpdocumentor-type-resolver/PseudoTypes/HtmlEscapedString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\LowercaseString' => '/phpdocumentor-type-resolver/PseudoTypes/LowercaseString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyLowercaseString' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyLowercaseString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyString' => '/phpdocumentor-type-resolver/PseudoTypes/NonEmptyString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\NumericString' => '/phpdocumentor-type-resolver/PseudoTypes/NumericString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\PositiveInteger' => '/phpdocumentor-type-resolver/PseudoTypes/PositiveInteger.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\TraitString' => '/phpdocumentor-type-resolver/PseudoTypes/TraitString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\True_' => '/phpdocumentor-type-resolver/PseudoTypes/True_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Type' => '/phpdocumentor-type-resolver/Type.php', + 'PHPUnit\\phpDocumentor\\Reflection\\TypeResolver' => '/phpdocumentor-type-resolver/TypeResolver.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\AbstractList' => '/phpdocumentor-type-resolver/Types/AbstractList.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\AggregatedType' => '/phpdocumentor-type-resolver/Types/AggregatedType.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\ArrayKey' => '/phpdocumentor-type-resolver/Types/ArrayKey.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Array_' => '/phpdocumentor-type-resolver/Types/Array_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Boolean' => '/phpdocumentor-type-resolver/Types/Boolean.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Callable_' => '/phpdocumentor-type-resolver/Types/Callable_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\ClassString' => '/phpdocumentor-type-resolver/Types/ClassString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Collection' => '/phpdocumentor-type-resolver/Types/Collection.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Compound' => '/phpdocumentor-type-resolver/Types/Compound.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Context' => '/phpdocumentor-type-resolver/Types/Context.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\ContextFactory' => '/phpdocumentor-type-resolver/Types/ContextFactory.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Expression' => '/phpdocumentor-type-resolver/Types/Expression.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Float_' => '/phpdocumentor-type-resolver/Types/Float_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Integer' => '/phpdocumentor-type-resolver/Types/Integer.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\InterfaceString' => '/phpdocumentor-type-resolver/Types/InterfaceString.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Intersection' => '/phpdocumentor-type-resolver/Types/Intersection.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Iterable_' => '/phpdocumentor-type-resolver/Types/Iterable_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Mixed_' => '/phpdocumentor-type-resolver/Types/Mixed_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Never_' => '/phpdocumentor-type-resolver/Types/Never_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Null_' => '/phpdocumentor-type-resolver/Types/Null_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Nullable' => '/phpdocumentor-type-resolver/Types/Nullable.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Object_' => '/phpdocumentor-type-resolver/Types/Object_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Parent_' => '/phpdocumentor-type-resolver/Types/Parent_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Resource_' => '/phpdocumentor-type-resolver/Types/Resource_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Scalar' => '/phpdocumentor-type-resolver/Types/Scalar.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Self_' => '/phpdocumentor-type-resolver/Types/Self_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Static_' => '/phpdocumentor-type-resolver/Types/Static_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\String_' => '/phpdocumentor-type-resolver/Types/String_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\This' => '/phpdocumentor-type-resolver/Types/This.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Types\\Void_' => '/phpdocumentor-type-resolver/Types/Void_.php', + 'PHPUnit\\phpDocumentor\\Reflection\\Utils' => '/phpdocumentor-reflection-docblock/Utils.php', + 'Prophecy\\Argument' => '/phpspec-prophecy/Prophecy/Argument.php', + 'Prophecy\\Argument\\ArgumentsWildcard' => '/phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php', + 'Prophecy\\Argument\\Token\\AnyValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.php', + 'Prophecy\\Argument\\Token\\AnyValuesToken' => '/phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.php', + 'Prophecy\\Argument\\Token\\ApproximateValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.php', + 'Prophecy\\Argument\\Token\\ArrayCountToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.php', + 'Prophecy\\Argument\\Token\\ArrayEntryToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.php', + 'Prophecy\\Argument\\Token\\ArrayEveryEntryToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.php', + 'Prophecy\\Argument\\Token\\CallbackToken' => '/phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.php', + 'Prophecy\\Argument\\Token\\ExactValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php', + 'Prophecy\\Argument\\Token\\IdenticalValueToken' => '/phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.php', + 'Prophecy\\Argument\\Token\\InArrayToken' => '/phpspec-prophecy/Prophecy/Argument/Token/InArrayToken.php', + 'Prophecy\\Argument\\Token\\LogicalAndToken' => '/phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.php', + 'Prophecy\\Argument\\Token\\LogicalNotToken' => '/phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.php', + 'Prophecy\\Argument\\Token\\NotInArrayToken' => '/phpspec-prophecy/Prophecy/Argument/Token/NotInArrayToken.php', + 'Prophecy\\Argument\\Token\\ObjectStateToken' => '/phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php', + 'Prophecy\\Argument\\Token\\StringContainsToken' => '/phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.php', + 'Prophecy\\Argument\\Token\\TokenInterface' => '/phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.php', + 'Prophecy\\Argument\\Token\\TypeToken' => '/phpspec-prophecy/Prophecy/Argument/Token/TypeToken.php', + 'Prophecy\\Call\\Call' => '/phpspec-prophecy/Prophecy/Call/Call.php', + 'Prophecy\\Call\\CallCenter' => '/phpspec-prophecy/Prophecy/Call/CallCenter.php', + 'Prophecy\\Comparator\\ClosureComparator' => '/phpspec-prophecy/Prophecy/Comparator/ClosureComparator.php', + 'Prophecy\\Comparator\\Factory' => '/phpspec-prophecy/Prophecy/Comparator/Factory.php', + 'Prophecy\\Comparator\\ProphecyComparator' => '/phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.php', + 'Prophecy\\Doubler\\CachedDoubler' => '/phpspec-prophecy/Prophecy/Doubler/CachedDoubler.php', + 'Prophecy\\Doubler\\ClassPatch\\ClassPatchInterface' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.php', + 'Prophecy\\Doubler\\ClassPatch\\DisableConstructorPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php', + 'Prophecy\\Doubler\\ClassPatch\\HhvmExceptionPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php', + 'Prophecy\\Doubler\\ClassPatch\\KeywordPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php', + 'Prophecy\\Doubler\\ClassPatch\\MagicCallPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.php', + 'Prophecy\\Doubler\\ClassPatch\\ProphecySubjectPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php', + 'Prophecy\\Doubler\\ClassPatch\\ReflectionClassNewInstancePatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php', + 'Prophecy\\Doubler\\ClassPatch\\SplFileInfoPatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php', + 'Prophecy\\Doubler\\ClassPatch\\ThrowablePatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ThrowablePatch.php', + 'Prophecy\\Doubler\\ClassPatch\\TraversablePatch' => '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php', + 'Prophecy\\Doubler\\DoubleInterface' => '/phpspec-prophecy/Prophecy/Doubler/DoubleInterface.php', + 'Prophecy\\Doubler\\Doubler' => '/phpspec-prophecy/Prophecy/Doubler/Doubler.php', + 'Prophecy\\Doubler\\Generator\\ClassCodeGenerator' => '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php', + 'Prophecy\\Doubler\\Generator\\ClassCreator' => '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCreator.php', + 'Prophecy\\Doubler\\Generator\\ClassMirror' => '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.php', + 'Prophecy\\Doubler\\Generator\\Node\\ArgumentNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.php', + 'Prophecy\\Doubler\\Generator\\Node\\ArgumentTypeNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentTypeNode.php', + 'Prophecy\\Doubler\\Generator\\Node\\ClassNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.php', + 'Prophecy\\Doubler\\Generator\\Node\\MethodNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.php', + 'Prophecy\\Doubler\\Generator\\Node\\ReturnTypeNode' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ReturnTypeNode.php', + 'Prophecy\\Doubler\\Generator\\Node\\TypeNodeAbstract' => '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/TypeNodeAbstract.php', + 'Prophecy\\Doubler\\Generator\\ReflectionInterface' => '/phpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.php', + 'Prophecy\\Doubler\\Generator\\TypeHintReference' => '/phpspec-prophecy/Prophecy/Doubler/Generator/TypeHintReference.php', + 'Prophecy\\Doubler\\LazyDouble' => '/phpspec-prophecy/Prophecy/Doubler/LazyDouble.php', + 'Prophecy\\Doubler\\NameGenerator' => '/phpspec-prophecy/Prophecy/Doubler/NameGenerator.php', + 'Prophecy\\Exception\\Call\\UnexpectedCallException' => '/phpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.php', + 'Prophecy\\Exception\\Doubler\\ClassCreatorException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.php', + 'Prophecy\\Exception\\Doubler\\ClassMirrorException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.php', + 'Prophecy\\Exception\\Doubler\\ClassNotFoundException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.php', + 'Prophecy\\Exception\\Doubler\\DoubleException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.php', + 'Prophecy\\Exception\\Doubler\\DoublerException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.php', + 'Prophecy\\Exception\\Doubler\\InterfaceNotFoundException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.php', + 'Prophecy\\Exception\\Doubler\\MethodNotExtendableException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.php', + 'Prophecy\\Exception\\Doubler\\MethodNotFoundException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.php', + 'Prophecy\\Exception\\Doubler\\ReturnByReferenceException' => '/phpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.php', + 'Prophecy\\Exception\\Exception' => '/phpspec-prophecy/Prophecy/Exception/Exception.php', + 'Prophecy\\Exception\\InvalidArgumentException' => '/phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.php', + 'Prophecy\\Exception\\Prediction\\AggregateException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.php', + 'Prophecy\\Exception\\Prediction\\FailedPredictionException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.php', + 'Prophecy\\Exception\\Prediction\\NoCallsException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.php', + 'Prophecy\\Exception\\Prediction\\PredictionException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.php', + 'Prophecy\\Exception\\Prediction\\UnexpectedCallsCountException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php', + 'Prophecy\\Exception\\Prediction\\UnexpectedCallsException' => '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.php', + 'Prophecy\\Exception\\Prophecy\\MethodProphecyException' => '/phpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.php', + 'Prophecy\\Exception\\Prophecy\\ObjectProphecyException' => '/phpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.php', + 'Prophecy\\Exception\\Prophecy\\ProphecyException' => '/phpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.php', + 'Prophecy\\PhpDocumentor\\ClassAndInterfaceTagRetriever' => '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php', + 'Prophecy\\PhpDocumentor\\ClassTagRetriever' => '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassTagRetriever.php', + 'Prophecy\\PhpDocumentor\\LegacyClassTagRetriever' => '/phpspec-prophecy/Prophecy/PhpDocumentor/LegacyClassTagRetriever.php', + 'Prophecy\\PhpDocumentor\\MethodTagRetrieverInterface' => '/phpspec-prophecy/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php', + 'Prophecy\\Prediction\\CallPrediction' => '/phpspec-prophecy/Prophecy/Prediction/CallPrediction.php', + 'Prophecy\\Prediction\\CallTimesPrediction' => '/phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.php', + 'Prophecy\\Prediction\\CallbackPrediction' => '/phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.php', + 'Prophecy\\Prediction\\NoCallsPrediction' => '/phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.php', + 'Prophecy\\Prediction\\PredictionInterface' => '/phpspec-prophecy/Prophecy/Prediction/PredictionInterface.php', + 'Prophecy\\Promise\\CallbackPromise' => '/phpspec-prophecy/Prophecy/Promise/CallbackPromise.php', + 'Prophecy\\Promise\\PromiseInterface' => '/phpspec-prophecy/Prophecy/Promise/PromiseInterface.php', + 'Prophecy\\Promise\\ReturnArgumentPromise' => '/phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.php', + 'Prophecy\\Promise\\ReturnPromise' => '/phpspec-prophecy/Prophecy/Promise/ReturnPromise.php', + 'Prophecy\\Promise\\ThrowPromise' => '/phpspec-prophecy/Prophecy/Promise/ThrowPromise.php', + 'Prophecy\\Prophecy\\MethodProphecy' => '/phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.php', + 'Prophecy\\Prophecy\\ObjectProphecy' => '/phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.php', + 'Prophecy\\Prophecy\\ProphecyInterface' => '/phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.php', + 'Prophecy\\Prophecy\\ProphecySubjectInterface' => '/phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.php', + 'Prophecy\\Prophecy\\Revealer' => '/phpspec-prophecy/Prophecy/Prophecy/Revealer.php', + 'Prophecy\\Prophecy\\RevealerInterface' => '/phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.php', + 'Prophecy\\Prophet' => '/phpspec-prophecy/Prophecy/Prophet.php', + 'Prophecy\\Util\\ExportUtil' => '/phpspec-prophecy/Prophecy/Util/ExportUtil.php', + 'Prophecy\\Util\\StringUtil' => '/phpspec-prophecy/Prophecy/Util/StringUtil.php'] as $file) { + require_once 'phar://phpunit-9.5.10.phar' . $file; +} + require __PHPUNIT_PHAR_ROOT__ . '/phpunit/Framework/Assert/Functions.php'; if ($execute) { @@ -1092,34279 +2282,35354 @@ if ($execute) { } __HALT_COMPILER(); ?> -Vcphpunit-9.4.4.pharschema/9.2.xsdB_BI*)schema/8.5.xsdB_BWo*sebastian-exporter/Exporter.php)#_)#,sebastian-exporter/LICENSE_螷ͤ0phpspec-prophecy/Prophecy/Comparator/Factory.php9_9b[;phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.php_QHg:phpspec-prophecy/Prophecy/Comparator/ClosureComparator.php_Yu-phpspec-prophecy/Prophecy/Call/CallCenter.php_ڤ'phpspec-prophecy/Prophecy/Call/Call.php _ 33դ:phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.php_{ܤ<phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.phpX_X5)9phpspec-prophecy/Prophecy/Argument/Token/InArrayToken.php_?xnAphpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.php_#<phpspec-prophecy/Prophecy/Argument/Token/NotInArrayToken.php_;<phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.phpD_D(bLBphpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.php_ ;phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.php_'`;phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.php_(nGw@phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.php-_-3xD@phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.php_+ݣ6phpspec-prophecy/Prophecy/Argument/Token/TypeToken.php_47|:phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.php*_*Yd=phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.phpR -_R -qe7<phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php. _. َ<phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.php_/*2<phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.php_$Eiʤ8phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.phpY _Y 0?5phpspec-prophecy/Prophecy/Promise/CallbackPromise.php_-;phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.phpD_D72phpspec-prophecy/Prophecy/Promise/ThrowPromise.php _ K~/6phpspec-prophecy/Prophecy/Promise/PromiseInterface.phpo_ov3phpspec-prophecy/Prophecy/Promise/ReturnPromise.phpK_K׳5phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.php9_9AΈ5phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.php_ 8phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.phpG_GWnZ8phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.php+_+X?phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.php_</phpspec-prophecy/Prophecy/Prophecy/Revealer.php_ m-phpspec-prophecy/Prophecy/Doubler/Doubler.php3_3R'$5phpspec-prophecy/Prophecy/Doubler/DoubleInterface.php_Bۤ3phpspec-prophecy/Prophecy/Doubler/NameGenerator.php_8Sֈ0phpspec-prophecy/Prophecy/Doubler/LazyDouble.phpB _B =3phpspec-prophecy/Prophecy/Doubler/CachedDoubler.php_L4Bphpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php - _ - 2s;phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.php_Cphpspec-prophecy/Prophecy/Doubler/Generator/Node/ReturnTypeNode.php_=?phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.php_rAphpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.phpz _z CiEphpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentTypeNode.php_ˤEphpspec-prophecy/Prophecy/Doubler/Generator/Node/TypeNodeAbstract.php_\ؤ>phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.php_Lphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.php_[Gphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.php_e:Dphpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.php_ЎTJphpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.php_0+5,Jphpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.php_&q1phpspec-prophecy/Prophecy/Exception/Exception.php*_*(d@phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.php_󱙤Fphpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.php_$¤Cphpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.php_sPphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php6_6@Lphpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.phph_hVEphpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.phpK_KACKphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.phpv_v %phpspec-prophecy/Prophecy/Prophet.php_F}-phpspec-prophecy/Prophecy/Util/ExportUtil.php_-phpspec-prophecy/Prophecy/Util/StringUtil.php -_ -7ݤ&phpspec-prophecy/Prophecy/Argument.php_iW:phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.php_覤<phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.phpV _V SsΤ<phpspec-prophecy/Prophecy/Prediction/PredictionInterface.php_P7phpspec-prophecy/Prophecy/Prediction/CallPrediction.php _ SHߤ;phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.php_phpspec-prophecy/LICENSE}_}6*sebastian-comparator/ComparisonFailure.php _ 1i(sebastian-comparator/ArrayComparator.php_1 sebastian-comparator/Factory.php<_<î'sebastian-comparator/TypeComparator.php6_6YBˤ+sebastian-comparator/ResourceComparator.phpj_j*sebastian-comparator/NumericComparator.php _ -ݤ-sebastian-comparator/exceptions/Exception.phpw_w4sebastian-comparator/exceptions/RuntimeException.php_8t3sebastian-comparator/SplObjectStorageComparator.phpq_q:kh#sebastian-comparator/Comparator.php_i0+sebastian-comparator/DateTimeComparator.php _ u)sebastian-comparator/DoubleComparator.php_@G^,sebastian-comparator/ExceptionComparator.php_Ѐe)sebastian-comparator/ScalarComparator.phpn _n uᮞ)sebastian-comparator/ObjectComparator.php _  -sebastian-comparator/MockObjectComparator.php0_0\I*sebastian-comparator/DOMNodeComparator.phpv _v *{sebastian-comparator/LICENSE _ =(7myclabs-deep-copy/DeepCopy/Exception/CloneException.php_H:myclabs-deep-copy/DeepCopy/Exception/PropertyException.php_=:myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.phpT_TBmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php_n)KLmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php;_;K1dGmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php@_@1 ,3myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.php_ 0myclabs-deep-copy/DeepCopy/Filter/KeepFilter.php(_(9 [3myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.php'_'L,myclabs-deep-copy/DeepCopy/Filter/Filter.phpd_dM6myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php_QBŤ'myclabs-deep-copy/DeepCopy/DeepCopy.php`_`I*C;myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.php_a7Amyclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.php_N?myclabs-deep-copy/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php_7Amyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php_ nGmyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php`_`#QK4myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php_VD7myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.php0_0/Dmyclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php_B姍:myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.php_(Z.myclabs-deep-copy/DeepCopy/Matcher/Matcher.php_:myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.phpj_j036myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.php_(myclabs-deep-copy/DeepCopy/deep_copy.php_bS٤myclabs-deep-copy/LICENSE5_5ʭ˄*php-text-template/exceptions/Exception.phpz_zZq9php-text-template/exceptions/InvalidArgumentException.php_1ɤ1php-text-template/exceptions/RuntimeException.php_%,Gmphp-text-template/Template.phpy _y n]php-text-template/LICENSE_u#sebastian-global-state/Restorer.php_tH}/sebastian-global-state/exceptions/Exception.phpz_z@l66sebastian-global-state/exceptions/RuntimeException.php_Wy&sebastian-global-state/ExcludeList.php -_ -s!2'sebastian-global-state/CodeExporter.php _ 7#sebastian-global-state/Snapshot.php*_*ܤsebastian-global-state/LICENSE_a<doctrine-instantiator/Doctrine/Instantiator/Instantiator.php_`Rdoctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.php_O>Rdoctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.phpX_X7nLdoctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php_Vm̤Edoctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php_vhdoctrine-instantiator/LICENSE$_$ -͂.phpdocumentor-reflection-docblock/DocBlock.php_Y#/:phpdocumentor-reflection-docblock/DocBlock/Description.phpA_AGphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php_ Aphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.php!_!x&Cphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php_mQ>@phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php_T^꘤;phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.phpw_w}f>phpdocumentor-reflection-docblock/DocBlock/Tags/InvalidTag.php_]먤;phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php _ Rphpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php_, GLphpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.php_b0<phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php_QOp:phpdocumentor-reflection-docblock/DocBlock/Tags/Source.php_ݷ:phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php* -_* -8phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php_e ;phpdocumentor-reflection-docblock/DocBlock/Tags/Example.php&_&-Aphpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php_">phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php _ i"b9phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php_F0:phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php!_!{혤:phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.phpb_ba9phpdocumentor-reflection-docblock/DocBlock/Tags/Since.phpf _f 2;phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.php -_ - %ӝ8phpdocumentor-reflection-docblock/DocBlock/Tags/Link.php_Za#v?phpdocumentor-reflection-docblock/DocBlock/Tags/TagWithType.php_.̤8phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.phpz _z X Hphpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php_.ͤ=phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.phpI_Iͤ;phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php9_9@ؤ7phpdocumentor-reflection-docblock/DocBlock/Tags/See.php_::phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.php _ \ڤAphpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.php_Ķ <phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.phpn_n/م2phpdocumentor-reflection-docblock/DocBlock/Tag.php_-5Aphpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.php7_7?9phpdocumentor-reflection-docblock/DocBlock/TagFactory.php_ϖ[9phpdocumentor-reflection-docblock/DocBlock/Serializer.php_^e+phpdocumentor-reflection-docblock/Utils.phpj _j (c=phpdocumentor-reflection-docblock/Exception/PcreException.php_Q>phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php _ ߤ)phpdocumentor-reflection-docblock/LICENSE8_8ʤ5phpdocumentor-reflection-docblock/DocBlockFactory.php0'_0'旚Hphp-timer/Timer.php_ܭ"php-timer/exceptions/Exception.phpo_oy/php-timer/exceptions/NoActiveTimerException.php_NCEphp-timer/exceptions/TimeSinceStartOfRequestNotAvailableException.php_&פphp-timer/Duration.php" -_" -aM$php-timer/ResourceUsageFormatter.php1_1iphp-timer/LICENSE_x4sebastian-resource-operations/ResourceOperations.php߲_߲%sebastian-resource-operations/LICENSE_]<+phar-io-version/VersionConstraintParser.php_^d8phar-io-version/Version.phpm_mzw(phar-io-version/exceptions/Exception.php_?ֺDphar-io-version/exceptions/UnsupportedVersionConstraintException.php_xؤ?phar-io-version/exceptions/InvalidPreReleaseSuffixException.php_F56phar-io-version/exceptions/InvalidVersionException.php_vw$phar-io-version/PreReleaseSuffix.php__:*phar-io-version/VersionConstraintValue.php~ _~ Z<1phar-io-version/constraints/VersionConstraint.php_S9phar-io-version/constraints/AbstractVersionConstraint.php_ -&c>phar-io-version/constraints/SpecificMajorVersionConstraint.php_cY٤6phar-io-version/constraints/ExactVersionConstraint.phpn_ng XEphar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.php_d捤4phar-io-version/constraints/AnyVersionConstraint.php_s8phar-io-version/constraints/OrVersionConstraintGroup.php>_>)09phar-io-version/constraints/AndVersionConstraintGroup.php_4`Fphar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.php^_^ʑ l!phar-io-version/VersionNumber.php_O1phar-io-version/LICENSE1_1>:Dphp-invoker/exceptions/ProcessControlExtensionNotLoadedException.php_Pd+php-invoker/exceptions/TimeoutException.php_ j$php-invoker/exceptions/Exception.phps_sȁphp-invoker/Invoker.phpX_X!L)sebastian-object-enumerator/Exception.php_-3}8sebastian-object-enumerator/InvalidArgumentException.php_XD*sebastian-object-enumerator/Enumerator.php _  | sebastian-code-unit/CodeUnit.php)_)IB3+sebastian-code-unit/InterfaceMethodUnit.phpC_C X*sebastian-code-unit/CodeUnitCollection.php_Y6<!sebastian-code-unit/ClassUnit.php)_)FҦ,sebastian-code-unit/exceptions/Exception.phpt_t|o;sebastian-code-unit/exceptions/InvalidCodeUnitException.php_(x3sebastian-code-unit/exceptions/NoTraitException.php_g@\6sebastian-code-unit/exceptions/ReflectionException.php_+2$sebastian-code-unit/FunctionUnit.php2_2Ғ2sebastian-code-unit/CodeUnitCollectionIterator.php_)JJKsebastian-code-unit/Mapper.php3_3Ƥ'sebastian-code-unit/ClassMethodUnit.php;_;=q!sebastian-code-unit/TraitUnit.php)_)*%sebastian-code-unit/InterfaceUnit.php5_5'sebastian-code-unit/TraitMethodUnit.php;_;LEؤsebastian-code-unit/LICENSE _ p!sebastian-environment/Console.php_kg¤)sebastian-environment/OperatingSystem.php_sebastian-environment/LICENSE_lm|!sebastian-environment/Runtime.php_UϤ manifest.txt_z-sebastian-cli-parser/exceptions/Exception.phpv_vUGsebastian-cli-parser/exceptions/OptionDoesNotAllowArgumentException.php_O:sebastian-cli-parser/exceptions/UnknownOptionException.phpf_fkr\Jsebastian-cli-parser/exceptions/RequiredOptionArgumentMissingException.php_l#<sebastian-cli-parser/exceptions/AmbiguousOptionException.phpm_m7dsebastian-cli-parser/Parser.php_3sebastian-cli-parser/LICENSE_uobject-reflector/LICENSE_9v,phpdocumentor-type-resolver/TypeResolver.phpL_L6 -*phpdocumentor-type-resolver/PseudoType.php_U2phpdocumentor-type-resolver/PseudoTypes/False_.php<_<p1phpdocumentor-type-resolver/PseudoTypes/True_.php8_88$phpdocumentor-type-resolver/Type.php_b&-phpdocumentor-type-resolver/FqsenResolver.php" -_" -F#phpdocumentor-type-resolver/LICENSE8_8ʤ*phpdocumentor-type-resolver/Types/This.php{_{6+phpdocumentor-type-resolver/Types/Void_.php)_)ly@E0phpdocumentor-type-resolver/Types/Expression.php_=̤-phpdocumentor-type-resolver/Types/String_.php_#K ,phpdocumentor-type-resolver/Types/Scalar.php_t/phpdocumentor-type-resolver/Types/Callable_.php_g-phpdocumentor-type-resolver/Types/Integer.php_Wɋ-phpdocumentor-type-resolver/Types/Boolean.php_ K,phpdocumentor-type-resolver/Types/Array_.php_5+phpdocumentor-type-resolver/Types/Null_.php_ Ԥ2phpdocumentor-type-resolver/Types/Intersection.php>_>ƍQ-phpdocumentor-type-resolver/Types/Object_.phpQ_QF,phpdocumentor-type-resolver/Types/Float_.php_dW/phpdocumentor-type-resolver/Types/Resource_.php_KWMg-phpdocumentor-type-resolver/Types/Static_.php'_'Ĥ2phpdocumentor-type-resolver/Types/AbstractList.php _ N8,3.phpdocumentor-type-resolver/Types/Nullable.php_+phpdocumentor-type-resolver/Types/Self_.php_/phpdocumentor-type-resolver/Types/Iterable_.php_ѕe֤4phpdocumentor-type-resolver/Types/AggregatedType.php` _` WM-phpdocumentor-type-resolver/Types/Parent_.php_Qͤ4phpdocumentor-type-resolver/Types/ContextFactory.php%:_%:޹,phpdocumentor-type-resolver/Types/Mixed_.php_S-phpdocumentor-type-resolver/Types/Context.php _ p}1phpdocumentor-type-resolver/Types/ClassString.php _ gEW0phpdocumentor-type-resolver/Types/Collection.phpP_P8v.phpdocumentor-type-resolver/Types/Compound.php/_/2@:פsebastian-version/Version.php_ ƪsebastian-version/LICENSE_Zwebmozart-assert/Assert.php_hwebmozart-assert/Mixin.php_]webmozart-assert/LICENSE<_<t}php-file-iterator/Factory.php _ Vphp-file-iterator/Iterator.phpd _d >php-file-iterator/Facade.phpZ -_Z - qphp-file-iterator/LICENSE_.%theseer-tokenizer/TokenCollection.php_K栤+theseer-tokenizer/NamespaceUriException.php_>JDtheseer-tokenizer/Exception.phpn_n'Ǥ"theseer-tokenizer/NamespaceUri.phpc_cjpw#theseer-tokenizer/XMLSerializer.phpq _q !.theseer-tokenizer/TokenCollectionException.php_'pޤtheseer-tokenizer/Tokenizer.php _  `Ǥtheseer-tokenizer/Token.php_4theseer-tokenizer/LICENSE_R (+phpdocumentor-reflection-common/Project.php_J,phpdocumentor-reflection-common/Location.php_=(+phpdocumentor-reflection-common/Element.php+_+_)phpdocumentor-reflection-common/Fqsen.php_R'phpdocumentor-reflection-common/LICENSE9_9*2Ȑ2phpdocumentor-reflection-common/ProjectFactory.php_Aah(phpdocumentor-reflection-common/File.php_I)sebastian-diff/Differ.php%_%D'F&sebastian-diff/Exception/Exception.phpk_kq{8^5sebastian-diff/Exception/InvalidArgumentException.php_"b3sebastian-diff/Exception/ConfigurationException.phpb_b@sebastian-diff/Diff.phpj_jbXAsebastian-diff/Parser.php _ z4sebastian-diff/Output/AbstractChunkOutputBuilder.php_d4sebastian-diff/Output/DiffOutputBuilderInterface.php_V2sebastian-diff/Output/UnifiedDiffOutputBuilder.php _ ޴/sebastian-diff/Output/DiffOnlyOutputBuilder.php _ {N8sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php*_*2tJBsebastian-diff/Line.phpL_L -qBsebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php_%`sebastian-diff/Chunk.php_#IDsebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.php_yc5sebastian-diff/LongestCommonSubsequenceCalculator.php_}e7zsebastian-diff/LICENSE _ a1"php-code-coverage/CodeCoverage.phpI_I(*php-code-coverage/StaticAnalysis/Cache.phpf_f oAphp-code-coverage/StaticAnalysis/CachingUncoveredFileAnalyser.phpF_F -D:php-code-coverage/StaticAnalysis/UncoveredFileAnalyser.php+_+B=?php-code-coverage/StaticAnalysis/IgnoredLinesFindingVisitor.php= _= 5\8php-code-coverage/StaticAnalysis/CoveredFileAnalyser.phpu_uNPu?php-code-coverage/StaticAnalysis/CachingCoveredFileAnalyser.php _ ) ?php-code-coverage/StaticAnalysis/ParsingCoveredFileAnalyser.php_:;php-code-coverage/StaticAnalysis/CodeUnitFindingVisitor.phpF"_F"zl3Aphp-code-coverage/StaticAnalysis/ParsingUncoveredFileAnalyser.php<_<4Τ0php-code-coverage/StaticAnalysis/CacheWarmer.phpF_FBphp-code-coverage/StaticAnalysis/ExecutableLinesFindingVisitor.php _ Gjphp-code-coverage/Version.php_g2X'php-code-coverage/Node/AbstractNode.php_"J #php-code-coverage/Node/Iterator.php)_)~."php-code-coverage/Node/Builder.php_*uu$php-code-coverage/Node/Directory.php*%_*%0ephp-code-coverage/Node/File.phpWL_WL2(php-code-coverage/Report/Xml/Project.phpg _g K7Ϥ'php-code-coverage/Report/Xml/Source.php_~2B'php-code-coverage/Report/Xml/Report.php -_ -̌i%php-code-coverage/Report/Xml/Unit.php_\%php-code-coverage/Report/Xml/Node.phpi _i 9 d*php-code-coverage/Report/Xml/Directory.php_W;)php-code-coverage/Report/Xml/Coverage.phpU_UTar'php-code-coverage/Report/Xml/Method.phpX_XQU&php-code-coverage/Report/Xml/Tests.php_{\Ǥ'php-code-coverage/Report/Xml/Facade.php%_%.'php-code-coverage/Report/Xml/Totals.php_861php-code-coverage/Report/Xml/BuildInformation.php -_ -Nua%php-code-coverage/Report/Xml/File.php_S*php-code-coverage/Report/Html/Renderer.php:%_:%4php-code-coverage/Report/Html/Renderer/Directory.php*_*z4php-code-coverage/Report/Html/Renderer/Dashboard.php !_ !׈<Jphp-code-coverage/Report/Html/Renderer/Template/file_item_branch.html.distn_n>php-code-coverage/Report/Html/Renderer/Template/file.html.dist_FHphp-code-coverage/Report/Html/Renderer/Template/directory_item.html.distA_AdsJphp-code-coverage/Report/Html/Renderer/Template/directory_branch.html.distj_jH?php-code-coverage/Report/Html/Renderer/Template/lines.html.diste_edf Bphp-code-coverage/Report/Html/Renderer/Template/branches.html.dist_h2+Ephp-code-coverage/Report/Html/Renderer/Template/file_branch.html.dist _ ?php-code-coverage/Report/Html/Renderer/Template/paths.html.dist_*'ݤEphp-code-coverage/Report/Html/Renderer/Template/method_item.html.dist_s:Ophp-code-coverage/Report/Html/Renderer/Template/directory_item_branch.html.dist;_;mۤJphp-code-coverage/Report/Html/Renderer/Template/dashboard_branch.html.distG_Gl>php-code-coverage/Report/Html/Renderer/Template/line.html.dist_{Mphp-code-coverage/Report/Html/Renderer/Template/coverage_bar_branch.html.dist'_'O}Lphp-code-coverage/Report/Html/Renderer/Template/method_item_branch.html.dist|_|A\Cphp-code-coverage/Report/Html/Renderer/Template/icons/file-code.svg0_0QUUHphp-code-coverage/Report/Html/Renderer/Template/icons/file-directory.svg_ZCphp-code-coverage/Report/Html/Renderer/Template/dashboard.html.distG_GlFphp-code-coverage/Report/Html/Renderer/Template/coverage_bar.html.dist'_'O}Cphp-code-coverage/Report/Html/Renderer/Template/file_item.html.distt_t@php-code-coverage/Report/Html/Renderer/Template/css/octicons.cssX_X'#>php-code-coverage/Report/Html/Renderer/Template/css/custom.css_Aphp-code-coverage/Report/Html/Renderer/Template/css/nv.d3.min.cssX%_X%0,=php-code-coverage/Report/Html/Renderer/Template/css/style.css_:Ephp-code-coverage/Report/Html/Renderer/Template/css/bootstrap.min.cssr_r?lCphp-code-coverage/Report/Html/Renderer/Template/directory.html.dist_GMCphp-code-coverage/Report/Html/Renderer/Template/js/bootstrap.min.js_s@php-code-coverage/Report/Html/Renderer/Template/js/jquery.min.js]_]=?php-code-coverage/Report/Html/Renderer/Template/js/nv.d3.min.jsR_R/*php-code-coverage/Driver/Xdebug2Driver.php_l"%php-code-coverage/Driver/Selector.php _ Q)php-code-coverage/Driver/PhpdbgDriver.php _ qMF)#php-code-coverage/Driver/Driver.php_~'php-code-coverage/Driver/PcovDriver.php|_|Djphp-code-coverage/CrapIndex.php_ %php-code-coverage/LICENSE_@php-code-coverage/Filter.php^ _^ 3:aT phpunit.xsd -F_ -FDOFI)phar-io-manifest/xml/ExtensionElement.php_[Ȥ-phar-io-manifest/xml/ExtElementCollection.phpn_neYV#phar-io-manifest/xml/PhpElement.phpK_K̺J)phar-io-manifest/xml/ManifestDocument.phpm _m _k(phar-io-manifest/xml/RequiresElement.phpo_o!^*phar-io-manifest/xml/ElementCollection.php_#`(phar-io-manifest/xml/ManifestElement.php_0phar-io-manifest/xml/AuthorElementCollection.phpw_w3 -&phar-io-manifest/xml/AuthorElement.php_x 3phar-io-manifest/xml/ComponentElementCollection.php_&'phar-io-manifest/xml/BundlesElement.php_a)phar-io-manifest/xml/ComponentElement.php_#phar-io-manifest/xml/ExtElement.php"_";'phar-io-manifest/xml/LicenseElement.php_6r)phar-io-manifest/xml/CopyrightElement.phpP_P57麤(phar-io-manifest/xml/ContainsElement.php_ ,phar-io-manifest/values/BundledComponent.phpp_p~z0phar-io-manifest/values/CopyrightInformation.php_w&36phar-io-manifest/values/BundledComponentCollection.phpk_k}>phar-io-manifest/values/BundledComponentCollectionIterator.php_b9phar-io-manifest/values/RequirementCollectionIterator.php_@v8$phar-io-manifest/values/Manifest.phpd _d ij&"phar-io-manifest/values/Author.php_ % 'phar-io-manifest/values/Requirement.php_d1phar-io-manifest/values/RequirementCollection.php*_*4*Ҥ%phar-io-manifest/values/Extension.phpe_e6phar-io-manifest/values/Url.php_+1phar-io-manifest/values/PhpVersionRequirement.phpe_ea{j3phar-io-manifest/values/PhpExtensionRequirement.php_a\ phar-io-manifest/values/Type.php{_{JlĤ#phar-io-manifest/values/License.php!_!"v'phar-io-manifest/values/Application.php_8פ4phar-io-manifest/values/AuthorCollectionIterator.phpe_eÁ+phar-io-manifest/values/ApplicationName.php_]3Ta#phar-io-manifest/values/Library.php_Bt\!phar-io-manifest/values/Email.phpg_gA ,phar-io-manifest/values/AuthorCollection.php_n񁍤7phar-io-manifest/exceptions/ManifestLoaderException.php_証դ?phar-io-manifest/exceptions/InvalidApplicationNameException.php_-֤:phar-io-manifest/exceptions/ElementCollectionException.php_İ5phar-io-manifest/exceptions/InvalidEmailException.php_c?phar-io-manifest/exceptions/ManifestDocumentMapperException.php_澂ä@phar-io-manifest/exceptions/ManifestDocumentLoadingException.phpa_a"/)phar-io-manifest/exceptions/Exception.php_8phar-io-manifest/exceptions/ManifestElementException.php_KY3phar-io-manifest/exceptions/InvalidUrlException.php_D9phar-io-manifest/exceptions/ManifestDocumentException.php_{#phar-io-manifest/ManifestLoader.php_\+phar-io-manifest/ManifestDocumentMapper.php_Zbphar-io-manifest/LICENSE`_`p'phar-io-manifest/ManifestSerializer.php_׵.sebastian-object-reflector/ObjectReflector.php_C(sebastian-object-reflector/Exception.php_[87sebastian-object-reflector/InvalidArgumentException.php_j{,sebastian-complexity/Exception/Exception.phpw_wȀ3sebastian-complexity/Exception/RuntimeException.php_y#sebastian-complexity/Calculator.php _ E=sebastian-complexity/Visitor/ComplexityCalculatingVisitor.php{ _{ ^Gsebastian-complexity/Visitor/CyclomaticComplexityCalculatingVisitor.php_1a.sebastian-complexity/Complexity/Complexity.phpQ_Ql@sebastian-complexity/Complexity/ComplexityCollectionIterator.phpy_yu8sebastian-complexity/Complexity/ComplexityCollection.phpM_Msebastian-complexity/LICENSE_=ݤ.phpstorm.meta.php_G-phpunit/TextUI/XmlConfiguration/Generator.php_8phpunit/TextUI/XmlConfiguration/Logging/TestDox/Html.php_--.7phpunit/TextUI/XmlConfiguration/Logging/TestDox/Xml.php_O] 8phpunit/TextUI/XmlConfiguration/Logging/TestDox/Text.php_%d4phpunit/TextUI/XmlConfiguration/Logging/TeamCity.php _ ~X3phpunit/TextUI/XmlConfiguration/Logging/Logging.php_ 0phpunit/TextUI/XmlConfiguration/Logging/Text.php_<\}1phpunit/TextUI/XmlConfiguration/Logging/Junit.php _ Xd=phpunit/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.php$_$1B|<phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Html.php_>phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Clover.php_#AL;phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Xml.php'_'}";phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Php.php_߁>phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.php_Y]]Aphpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.php_.<phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Text.php_"ؤAphpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/Directory.php_ ϕKphpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollection.php_퇤Sphpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.php_I=phpunit/TextUI/XmlConfiguration/CodeCoverage/FilterMapper.php_q鉤-phpunit/TextUI/XmlConfiguration/Exception.php_K[2phpunit/TextUI/XmlConfiguration/PHP/IniSetting.phpG_GT+0phpunit/TextUI/XmlConfiguration/PHP/Constant.php4_4,-qBphpunit/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.phpc_c^=*0phpunit/TextUI/XmlConfiguration/PHP/Variable.php_\M,Dphpunit/TextUI/XmlConfiguration/PHP/IniSettingCollectionIterator.phpw_wi+phpunit/TextUI/XmlConfiguration/PHP/Php.php_Bphpunit/TextUI/XmlConfiguration/PHP/ConstantCollectionIterator.phpc_c:2<phpunit/TextUI/XmlConfiguration/PHP/IniSettingCollection.phpP_P(;:phpunit/TextUI/XmlConfiguration/PHP/ConstantCollection.php0_01Z:phpunit/TextUI/XmlConfiguration/PHP/VariableCollection.php0_0!#2phpunit/TextUI/XmlConfiguration/PHP/PhpHandler.php_N<1phpunit/TextUI/XmlConfiguration/Configuration.php_5 h7phpunit/TextUI/XmlConfiguration/TestSuite/TestSuite.php_Hphpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollectionIterator.phpK_KW=6phpunit/TextUI/XmlConfiguration/TestSuite/TestFile.php_F9@phpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollection.phpb_bķIphpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollectionIterator.phpm_m;ϤEphpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollection.php_xܤ=phpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteMapper.phpX _X lMphpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollectionIterator.php_aphpunit/TextUI/XmlConfiguration/Migration/MigrationBuilder.php& _& ˤ5phpunit/TextUI/XmlConfiguration/PHPUnit/Extension.php_ -J.?phpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollection.php_[%Gphpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollectionIterator.phpm_mk3phpunit/TextUI/XmlConfiguration/PHPUnit/PHPUnit.phpiC_iC 6<phpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionHandler.phpc -_c -[/phpunit/TextUI/XmlConfiguration/Group/Group.php_ELAphpunit/TextUI/XmlConfiguration/Group/GroupCollectionIterator.phpE_ECq9phpunit/TextUI/XmlConfiguration/Group/GroupCollection.php_d6ä0phpunit/TextUI/XmlConfiguration/Group/Groups.php_=phpunit/TextUI/XmlConfiguration/Filesystem/FileCollection.phpF_FVQ?Ephpunit/TextUI/XmlConfiguration/Filesystem/FileCollectionIterator.php;_;)H8phpunit/TextUI/XmlConfiguration/Filesystem/Directory.php_sOBphpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollection.php_AˤJphpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollectionIterator.phps_s-z3phpunit/TextUI/XmlConfiguration/Filesystem/File.php_*phpunit/TextUI/XmlConfiguration/Loader.phpҟ_ҟ9ݤphpunit/TextUI/TestRunner.php;_;j'phpunit/TextUI/DefaultResultPrinter.php9_9z|phpunit/TextUI/Help.phpl/_l/f -phpunit/TextUI/Command.php~l_~liŤphpunit/TextUI/Exception.php_o)'phpunit/TextUI/CliArguments/Builder.phpU_UP,)phpunit/TextUI/CliArguments/Exception.php_-phpunit/TextUI/CliArguments/Configuration.php_0&phpunit/TextUI/CliArguments/Mapper.php+,_+,'a phpunit/TextUI/ResultPrinter.php_y4kphpunit/Exception.php_jM%phpunit/Util/Xml/SnapshotNodeList.php_i+h%phpunit/Util/Xml/ValidationResult.php_ۤphpunit/Util/Xml/Validator.php_`:Ϥphpunit/Util/Xml/Exception.php_lJ#phpunit/Util/Xml/SchemaDetector.php-_-0phpunit/Util/Xml/FailedSchemaDetectionResult.php_#S*phpunit/Util/Xml/SchemaDetectionResult.php_4χz4phpunit/Util/Xml/SuccessfulSchemaDetectionResult.php'_'g!phpunit/Util/Xml/SchemaFinder.php_gphpunit/Util/Xml/Loader.php _ <"phpunit/Util/RegularExpression.php_ͤphpunit/Util/Test.php `_ `pphpunit/Util/GlobalState.php8_8Ɠ{*phpunit/Util/TestDox/TextResultPrinter.php_k'phpunit/Util/TestDox/TestDoxPrinter.php+_+[*phpunit/Util/TestDox/HtmlResultPrinter.php -_ -@֤*phpunit/Util/TestDox/CliTestDoxPrinter.phpX,_X,ٿ'phpunit/Util/TestDox/NamePrettifier.php$_$z#)phpunit/Util/TestDox/XmlResultPrinter.php_#&phpunit/Util/TestDox/ResultPrinter.php_-&ܤphpunit/Util/Filesystem.php_5ˤphpunit/Util/Printer.php -_ -ephpunit/Util/FileLoader.php, _, %`7$phpunit/Util/Annotation/DocBlock.phpB_B - $phpunit/Util/Annotation/Registry.phpm -_m -d٤phpunit/Util/Color.php_phpunit/Util/Xml.php_BK*,phpunit/Util/XdebugFilterScriptGenerator.php_-aphpunit/Util/Log/JUnit.php,_,phpunit/Util/Log/TeamCity.php(_(U*phpunit/Util/VersionComparisonOperator.php_&Vphpunit/Util/Json.phpe _e Нphpunit/Util/Exception.php_6i'phpunit/Util/PHP/AbstractPhpProcess.php'_'\&phpunit/Util/PHP/WindowsPhpProcess.php_C.m,phpunit/Util/PHP/Template/TestCaseMethod.tpl _ mD+phpunit/Util/PHP/Template/TestCaseClass.tplp _p 3 H݀*phpunit/Util/PHP/Template/PhptTestCase.tpl_1&phpunit/Util/PHP/DefaultPhpProcess.php_qphpunit/Util/ExcludeList.php_zphpunit/Util/Type.php_XVѤphpunit/Util/ErrorHandler.php_e$phpunit/Util/XmlTestListRenderer.php -_ -%phpunit/Util/TextTestListRenderer.phps_swphpunit/Util/Blacklist.php_s(phpunit/Util/InvalidDataSetException.php_kX8phpunit/Util/Filter.php _ Dphpunit/Runner/Version.php_Eq!phpunit/Runner/BaseTestRunner.php-_-ʤ)phpunit/Runner/DefaultTestResultCache.php_k!phpunit/Runner/Filter/Factory.php_k4phpunit/Runner/Filter/ExcludeGroupFilterIterator.phpt_t$K,phpunit/Runner/Filter/NameFilterIterator.phpo _o eR-phpunit/Runner/Filter/GroupFilterIterator.php_Z@4phpunit/Runner/Filter/IncludeGroupFilterIterator.phps_s?phpunit/Runner/Exception.php_$z'phpunit/Runner/ResultCacheExtension.phpD_D "phpunit/Runner/TestResultCache.php_K"phpunit/Runner/TestSuiteSorter.php._.~,phpunit/Runner/Hook/AfterTestWarningHook.php/_/)pԤ,phpunit/Runner/Hook/AfterSkippedTestHook.php/_/7Ԥphpunit/Runner/Hook/Hook.php_b phpunit/Runner/Hook/TestHook.php_aB&phpunit/Runner/Hook/BeforeTestHook.php_R -*phpunit/Runner/Hook/AfterTestErrorHook.php+_+ fz+phpunit/Runner/Hook/TestListenerAdapter.php_]9+phpunit/Runner/Hook/BeforeFirstTestHook.php_?]{դ*phpunit/Runner/Hook/AfterRiskyTestHook.php+_+5ÿ)phpunit/Runner/Hook/AfterLastTestHook.php_ -"ܤ%phpunit/Runner/Hook/AfterTestHook.php_3uݤ,phpunit/Runner/Hook/AfterTestFailureHook.php/_/u./phpunit/Runner/Hook/AfterSuccessfulTestHook.php$_$]O/phpunit/Runner/Hook/AfterIncompleteTestHook.php5_5 ɤ&phpunit/Runner/NullTestResultCache.php_W<"phpunit/Runner/TestSuiteLoader.php_h**phpunit/Runner/StandardTestSuiteLoader.phpq _q >Mphpunit/Runner/PhptTestCase.phpX_X͢wphpunit/Framework/TestSuite.phpb_bR;phpunit/Framework/Test.php_p3 phpunit/Framework/TestResult.phpk|_k|"6%phpunit/Framework/SkippedTestCase.php_l]!phpunit/Framework/TestBuilder.php_Sͤphpunit/Framework/Assert.phpr_rd(phpunit/Framework/IncompleteTestCase.php_I +phpunit/Framework/DataProviderTestSuite.php_9"phpunit/Framework/TestListener.php _ I-95phpunit/Framework/Exception/SkippedTestSuiteError.php_x4phpunit/Framework/Exception/AssertionFailedError.php_.phpunit/Framework/Exception/RiskyTestError.php_*y9phpunit/Framework/Exception/NoChildTestSuiteException.php_P$.phpunit/Framework/Exception/SyntheticError.php5_5KYUphpunit/Framework/Exception/ComparisonMethodDoesNotDeclareBoolReturnTypeException.phpT_Tp5phpunit/Framework/Exception/CodeCoverageException.php_[+phpunit/Framework/Exception/OutputError.php_Tphpunit/Framework/Exception/ComparisonMethodDoesNotDeclareParameterTypeException.php\_\jyA)phpunit/Framework/Exception/Exception.php _ (Sphpunit/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.phpm_mQ%0phpunit/Framework/Exception/SkippedTestError.php_ O~Zphpunit/Framework/Exception/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php^_^!!Aphpunit/Framework/Exception/ActualValueIsNotAnObjectException.php_iOp8phpunit/Framework/Exception/PHPTAssertionFailedError.php4_4#M8phpunit/Framework/Exception/InvalidArgumentException.php$_$rUi?phpunit/Framework/Exception/UnintentionallyCoveredCodeError.php_3phpunit/Framework/Exception/IncompleteTestError.php_םܤ@phpunit/Framework/Exception/MissingCoversAnnotationException.php_|5phpunit/Framework/Exception/SyntheticSkippedError.php_ԗ<phpunit/Framework/Exception/InvalidCoversTargetException.php_o苤?phpunit/Framework/Exception/CoveredCodeNotExecutedException.php_8YФ:phpunit/Framework/Exception/ExpectationFailedException.php_X{'phpunit/Framework/Exception/Warning.php_8;<phpunit/Framework/Exception/InvalidDataProviderException.php_.ڜɤEphpunit/Framework/Exception/ComparisonMethodDoesNotExistException.php1_1!phpunit/Framework/Reorderable.php_z0*phpunit/Framework/MockObject/Generator.php@r_@rN )phpunit/Framework/MockObject/MockType.php_FFt,phpunit/Framework/MockObject/MockBuilder.php)_)GJ+phpunit/Framework/MockObject/MockObject.php_T+phpunit/Framework/MockObject/Api/Method.php_8phpunit/Framework/MockObject/Api/UnmockedCloneMethod.phpG_Gө(phpunit/Framework/MockObject/Api/Api.php _ Uo6phpunit/Framework/MockObject/Api/MockedCloneMethod.php*_*?z2phpunit/Framework/MockObject/InvocationHandler.phpC_C-Kphpunit/Framework/MockObject/Exception/IncompatibleReturnValueException.php_YAphpunit/Framework/MockObject/Exception/BadMethodCallException.php_ΫXYphpunit/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php _ ɅW4phpunit/Framework/MockObject/Exception/Exception.php_[.;phpunit/Framework/MockObject/Exception/RuntimeException.php__|+phpunit/Framework/MockObject/Verifiable.php_̐ s(phpunit/Framework/MockObject/Matcher.php_5phpunit/Framework/MockObject/MethodNameConstraint.php,_,+_4phpunit/Framework/MockObject/Stub/ReturnValueMap.php_f 6phpunit/Framework/MockObject/Stub/ConsecutiveCalls.phpQ_Q/phpunit/Framework/MockObject/Stub/Exception.phpl_ln *phpunit/Framework/MockObject/Stub/Stub.phpd_dqb5phpunit/Framework/MockObject/Stub/ReturnReference.phpN_N%}4phpunit/Framework/MockObject/Stub/ReturnCallback.php_aq4phpunit/Framework/MockObject/Stub/ReturnArgument.php;_;0phpunit/Framework/MockObject/Stub/ReturnSelf.phpR_R-0phpunit/Framework/MockObject/Stub/ReturnStub.php+_+vä%phpunit/Framework/MockObject/Stub.php_Ŏ*phpunit/Framework/MockObject/MockClass.php_+phpunit/Framework/MockObject/Invocation.php_ZKҤ3phpunit/Framework/MockObject/ConfigurableMethod.php_ -5phpunit/Framework/MockObject/Rule/AnyInvokedCount.php_1Xo.8phpunit/Framework/MockObject/Rule/InvokedAtLeastOnce.phpt_t=(Z0phpunit/Framework/MockObject/Rule/Parameters.php_YrI9phpunit/Framework/MockObject/Rule/InvokedAtLeastCount.php_4phpunit/Framework/MockObject/Rule/InvokedAtIndex.phpq_q?l4phpunit/Framework/MockObject/Rule/ParametersRule.php_0W13phpunit/Framework/MockObject/Rule/AnyParameters.php_{G<2phpunit/Framework/MockObject/Rule/InvokedCount.php -_ -X"ä0phpunit/Framework/MockObject/Rule/MethodName.phpX_XBM' 5phpunit/Framework/MockObject/Rule/InvocationOrder.phpI_I)P*8phpunit/Framework/MockObject/Rule/InvokedAtMostCount.php_Ia;phpunit/Framework/MockObject/Rule/ConsecutiveParameters.php -_ -ȭ6phpunit/Framework/MockObject/Generator/deprecation.tpl;_;O5s>phpunit/Framework/MockObject/Generator/proxied_method_void.tplv_vT8phpunit/Framework/MockObject/Generator/mocked_method.tplF_FK?phpunit/Framework/MockObject/Generator/mocked_static_method.tpl_ 4R7phpunit/Framework/MockObject/Generator/mocked_class.tpl_wZ5phpunit/Framework/MockObject/Generator/wsdl_class.tpl_9phpunit/Framework/MockObject/Generator/proxied_method.tpl}_}@ė=phpunit/Framework/MockObject/Generator/mocked_method_void.tpl_p6phpunit/Framework/MockObject/Generator/trait_class.tplQ_Q<Ȥ6phpunit/Framework/MockObject/Generator/wsdl_method.tpl<_<i1phpunit/Framework/MockObject/Builder/Identity.php_-phpunit/Framework/MockObject/Builder/Stub.php0_0:l8phpunit/Framework/MockObject/Builder/ParametersMatch.php_ڃ9phpunit/Framework/MockObject/Builder/InvocationMocker.php:!_:! :phpunit/Framework/MockObject/Builder/InvocationStubber.php_r78phpunit/Framework/MockObject/Builder/MethodNameMatch.phpk_k0-x*phpunit/Framework/MockObject/MockTrait.php_:Y.phpunit/Framework/MockObject/MockMethodSet.php<_<+phpunit/Framework/MockObject/MockMethod.php@)_@)?隤$phpunit/Framework/SelfDescribing.php -_ -s$phpunit/Framework/IncompleteTest.php_C -phpunit/Framework/TestCase.php-_-P)!phpunit/Framework/SkippedTest.php_tʤ&phpunit/Framework/ExceptionWrapper.php _ O7phpunit/Framework/TestListenerDefaultImplementation.php(_(aۏ@phpunit/Framework/Constraint/JsonMatchesErrorMessageProvider.php<_<:4/phpunit/Framework/Constraint/Boolean/IsTrue.php_}0phpunit/Framework/Constraint/Boolean/IsFalse.php_+phpunit/Framework/Constraint/Constraint.php"_"(8phpunit/Framework/Constraint/Cardinality/GreaterThan.php_h,d}5phpunit/Framework/Constraint/Cardinality/LessThan.php_a T4phpunit/Framework/Constraint/Cardinality/IsEmpty.php_T.2phpunit/Framework/Constraint/Cardinality/Count.php _ Rx5phpunit/Framework/Constraint/Cardinality/SameSize.php___uŤ2phpunit/Framework/Constraint/Type/IsInstanceOf.php>_>F,phpunit/Framework/Constraint/Type/IsNull.php_?),phpunit/Framework/Constraint/Type/IsType.php_k)phpunit/Framework/Constraint/Callback.php_#;1phpunit/Framework/Constraint/Equality/IsEqual.php) _) x;?phpunit/Framework/Constraint/Equality/IsEqualCanonicalizing.php _ q60:phpunit/Framework/Constraint/Equality/IsEqualWithDelta.phpf -_f -N =phpunit/Framework/Constraint/Equality/IsEqualIgnoringCase.php _ W<,phpunit/Framework/Constraint/JsonMatches.php _ ,8phpunit/Framework/Constraint/Exception/ExceptionCode.php_%4phpunit/Framework/Constraint/Exception/Exception.php_,쾤;phpunit/Framework/Constraint/Exception/ExceptionMessage.php_P4Lphpunit/Framework/Constraint/Exception/ExceptionMessageRegularExpression.php_jUB,phpunit/Framework/Constraint/IsIdentical.php_T64phpunit/Framework/Constraint/Operator/LogicalXor.php&_&3phpunit/Framework/Constraint/Operator/LogicalOr.php_Z4phpunit/Framework/Constraint/Operator/LogicalNot.php _ #Ȥ4phpunit/Framework/Constraint/Operator/LogicalAnd.php_bJ8phpunit/Framework/Constraint/Operator/BinaryOperator.php-_-#ir2phpunit/Framework/Constraint/Operator/Operator.php&_& Dܤ7phpunit/Framework/Constraint/Operator/UnaryOperator.php _ /v0phpunit/Framework/Constraint/Math/IsInfinite.php_WoE.phpunit/Framework/Constraint/Math/IsFinite.php_>+phpunit/Framework/Constraint/Math/IsNan.php_Dx9phpunit/Framework/Constraint/String/RegularExpression.php_ `̤6phpunit/Framework/Constraint/String/StringContains.php_u.phpunit/Framework/Constraint/String/IsJson.php_6phpunit/Framework/Constraint/String/StringEndsWith.php_dFphpunit/Framework/Constraint/String/StringMatchesFormatDescription.php _ 6)8phpunit/Framework/Constraint/String/StringStartsWith.phpW_W碰8phpunit/Framework/Constraint/Traversable/ArrayHasKey.php_R Iphpunit/Framework/Constraint/Traversable/TraversableContainsIdentical.php(_(dE"Dphpunit/Framework/Constraint/Traversable/TraversableContainsOnly.php _ hΤEphpunit/Framework/Constraint/Traversable/TraversableContainsEqual.phpb_b]eo@phpunit/Framework/Constraint/Traversable/TraversableContains.php_4n4phpunit/Framework/Constraint/Object/ObjectEquals.php_Kn?phpunit/Framework/Constraint/Object/ClassHasStaticAttribute.php_z:phpunit/Framework/Constraint/Object/ObjectHasAttribute.php\_\&9phpunit/Framework/Constraint/Object/ClassHasAttribute.php_rˤ6phpunit/Framework/Constraint/Filesystem/FileExists.phpg_g4+6phpunit/Framework/Constraint/Filesystem/IsWritable.phpg_gV;phpunit/Framework/Constraint/Filesystem/DirectoryExists.phpl_lY6phpunit/Framework/Constraint/Filesystem/IsReadable.phpg_guU+phpunit/Framework/Constraint/IsAnything.php_E!phpunit/Framework/TestFailure.php_n@ .phpunit/Framework/ExecutionOrderDependency.php_)e'phpunit/Framework/TestSuiteIterator.php_̤&phpunit/Framework/Assert/Functions.php_Q13!phpunit/Framework/Error/Error.php_B&phpunit/Framework/Error/Deprecated.phpz_zV"phpunit/Framework/Error/Notice.phpv_vˤ#phpunit/Framework/Error/Warning.phpw_wG%phpunit/Framework/WarningTestCase.php$_$Hޤ4phpunit/Framework/InvalidParameterGroupException.php_ symfony-polyfill-ctype/Ctype.php~_~JҤ$symfony-polyfill-ctype/bootstrap.php#_#]symfony-polyfill-ctype/LICENSE)_)`e0object-enumerator/LICENSE_y{sebastian-type/ObjectType.phpy_yN$sebastian-type/GenericObjectType.php(_(*zsebastian-type/UnknownType.phpm_mlV&sebastian-type/exception/Exception.phpk_k-sebastian-type/exception/RuntimeException.php_rsebastian-type/MixedType.php_/Ϥsebastian-type/Type.php _ Q sebastian-type/NullType.php_1Ksebastian-type/UnionType.php -_ -UΤ#sebastian-type/ReflectionMapper.php _ (Ȥsebastian-type/TypeName.php _ `c8Ԥsebastian-type/IterableType.phpY_YP㜤sebastian-type/StaticType.php_k_Csebastian-type/SimpleType.php_p2sebastian-type/CallableType.php_]fsebastian-type/VoidType.php_lsebastian-type/LICENSE _  +Τ-nikic-php-parser/PhpParser/BuilderFactory.php6+_6+&5nikic-php-parser/PhpParser/PrettyPrinter/Standard.php_$u$nikic-php-parser/PhpParser/Lexer.phpW_W_5$nikic-php-parser/PhpParser/Error.php_QZ;nikic-php-parser/PhpParser/ConstExprEvaluationException.php___I )nikic-php-parser/PhpParser/NodeDumper.php@_@}¤1nikic-php-parser/PhpParser/ConstExprEvaluator.php''_''R>`0nikic-php-parser/PhpParser/Node/NullableType.php'_'J/3nikic-php-parser/PhpParser/Node/Scalar/Encapsed.php_1T2nikic-php-parser/PhpParser/Node/Scalar/String_.php}_}'19nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Dir.phpl_l;K:nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Line.phpo_oo@nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Namespace_.php_o<nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Class_.phps_s i<nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Method.phpu_u!$?nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Function_.php|_|22*<nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Trait_.phps_s'9pY:nikic-php-parser/PhpParser/Node/Scalar/MagicConst/File.phpo_oℤ=nikic-php-parser/PhpParser/Node/Scalar/EncapsedStringPart.php_02nikic-php-parser/PhpParser/Node/Scalar/DNumber.phpi_iؤ5nikic-php-parser/PhpParser/Node/Scalar/MagicConst.php{_{l:2nikic-php-parser/PhpParser/Node/Scalar/LNumber.php: -_: -_:5nikic-php-parser/PhpParser/Node/VarLikeIdentifier.php_D(P*nikic-php-parser/PhpParser/Node/Scalar.php_671nikic-php-parser/PhpParser/Node/Expr/BinaryOp.php_uu63nikic-php-parser/PhpParser/Node/Expr/StaticCall.php_|Z1nikic-php-parser/PhpParser/Node/Expr/Include_.php_:Z?/6nikic-php-parser/PhpParser/Node/Expr/ArrayDimFetch.php_0.nikic-php-parser/PhpParser/Node/Expr/Eval_.php_Ϥ1nikic-php-parser/PhpParser/Node/Expr/FuncCall.php_/nikic-php-parser/PhpParser/Node/Expr/Isset_.php_:u.nikic-php-parser/PhpParser/Node/Expr/Error.php_\^<nikic-php-parser/PhpParser/Node/Expr/StaticPropertyFetch.phpV_V</nikic-php-parser/PhpParser/Node/Expr/Throw_.php_ո2nikic-php-parser/PhpParser/Node/Expr/ArrayItem.php_3nikic-php-parser/PhpParser/Node/Expr/BooleanNot.php_x'>nikic-php-parser/PhpParser/Node/Expr/NullsafePropertyFetch.php9_9a2nikic-php-parser/PhpParser/Node/Expr/ShellExec.php_z-6nikic-php-parser/PhpParser/Node/Expr/PropertyFetch.php_C'2nikic-php-parser/PhpParser/Node/Expr/YieldFrom.php_">6nikic-php-parser/PhpParser/Node/Expr/ArrowFunction.php _ # 3nikic-php-parser/PhpParser/Node/Expr/ConstFetch.php_dT1nikic-php-parser/PhpParser/Node/Expr/Variable.php_0nikic-php-parser/PhpParser/Node/Expr/Ternary.php6_6P/nikic-php-parser/PhpParser/Node/Expr/Empty_.php_/e3nikic-php-parser/PhpParser/Node/Expr/BitwiseNot.php_ 6nikic-php-parser/PhpParser/Node/Expr/ErrorSuppress.php_cӤ0nikic-php-parser/PhpParser/Node/Expr/Closure.php -_ -/nikic-php-parser/PhpParser/Node/Expr/Array_.phpP_Pb2nikic-php-parser/PhpParser/Node/Expr/UnaryPlus.php_¤<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalXor.phpo_onؤ:nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotEqual.phpj_jd*7nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Equal.phpd_d֤<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanAnd.phpn_n|;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanOr.phpl_l( (5nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mul.php___02(@nikic-php-parser/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.phpv_vnuw6nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Plus.phpa_a!g;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalOr.phpl_ll5nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Pow.php`_`->nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotIdentical.phps_sߪ;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Spaceship.phpm_mC:nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Coalesce.phpj_jפ;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Identical.phpm_mm+y5nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mod.php___=S<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseXor.phpm_mx㮤7nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Minus.phpc_c_R5nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Div.php___<9nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Greater.phpg_gEq@nikic-php-parser/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.phpv_v8:;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftLeft.phpl_l6(;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseOr.phpk_k܀X<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalAnd.phpo_o=^<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.phpm_m\9nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Smaller.phpg_g8nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Concat.phpe_e!<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftRight.phpn_n5nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mul.php_$6nikic-php-parser/PhpParser/Node/Expr/AssignOp/Plus.php_'nݤ5nikic-php-parser/PhpParser/Node/Expr/AssignOp/Pow.php_f:nikic-php-parser/PhpParser/Node/Expr/AssignOp/Coalesce.php_U75nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mod.php_ksLH<nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseXor.php_ݤ7nikic-php-parser/PhpParser/Node/Expr/AssignOp/Minus.php_O6:5nikic-php-parser/PhpParser/Node/Expr/AssignOp/Div.php_A -;nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftLeft.php_6Ǎ;nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseOr.php_*<nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php_8nikic-php-parser/PhpParser/Node/Expr/AssignOp/Concat.php_<nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftRight.php_FX/nikic-php-parser/PhpParser/Node/Expr/Assign.php___!/nikic-php-parser/PhpParser/Node/Expr/Match_.php_K3nikic-php-parser/PhpParser/Node/Expr/MethodCall.php_՜3nikic-php-parser/PhpParser/Node/Expr/ClosureUse.php_,դ/nikic-php-parser/PhpParser/Node/Expr/PreInc.php_tw{.nikic-php-parser/PhpParser/Node/Expr/Exit_.php5_50};nikic-php-parser/PhpParser/Node/Expr/NullsafeMethodCall.php_t/nikic-php-parser/PhpParser/Node/Expr/Yield_.php_t8nikic-php-parser/PhpParser/Node/Expr/ClassConstFetch.php$_$V63nikic-php-parser/PhpParser/Node/Expr/Cast/Bool_.php_m2nikic-php-parser/PhpParser/Node/Expr/Cast/Int_.php_V!4nikic-php-parser/PhpParser/Node/Expr/Cast/Double.php_5nikic-php-parser/PhpParser/Node/Expr/Cast/String_.php_D4nikic-php-parser/PhpParser/Node/Expr/Cast/Array_.php_'a5nikic-php-parser/PhpParser/Node/Expr/Cast/Object_.php_B7lS4nikic-php-parser/PhpParser/Node/Expr/Cast/Unset_.php_vd/nikic-php-parser/PhpParser/Node/Expr/Clone_.php_1{I/nikic-php-parser/PhpParser/Node/Expr/PreDec.php_*i/nikic-php-parser/PhpParser/Node/Expr/Print_.php_K>.nikic-php-parser/PhpParser/Node/Expr/List_.php_m-nikic-php-parser/PhpParser/Node/Expr/Cast.phpq_q2:4nikic-php-parser/PhpParser/Node/Expr/Instanceof_.php_c(o0nikic-php-parser/PhpParser/Node/Expr/PostDec.php_]2nikic-php-parser/PhpParser/Node/Expr/AssignRef.php_'~1nikic-php-parser/PhpParser/Node/Expr/AssignOp.php+_+uK0nikic-php-parser/PhpParser/Node/Expr/PostInc.php_Ӹ/3nikic-php-parser/PhpParser/Node/Expr/UnaryMinus.php_-nikic-php-parser/PhpParser/Node/Expr/New_.php_Ĥ0nikic-php-parser/PhpParser/Node/FunctionLike.php_i*nikic-php-parser/PhpParser/Node/Const_.php1_1t[1)nikic-php-parser/PhpParser/Node/Param.php _ ݤ,nikic-php-parser/PhpParser/Node/MatchArm.php_.(nikic-php-parser/PhpParser/Node/Stmt.php_Oޤ-nikic-php-parser/PhpParser/Node/UnionType.php_+ͤ-nikic-php-parser/PhpParser/Node/Attribute.phps_s: (nikic-php-parser/PhpParser/Node/Expr.php_/J.nikic-php-parser/PhpParser/Node/Identifier.php_.(nikic-php-parser/PhpParser/Node/Name.php%_%k& 'nikic-php-parser/PhpParser/Node/Arg.phpC_C)1nikic-php-parser/PhpParser/Node/Name/Relative.php_ǛEf7nikic-php-parser/PhpParser/Node/Name/FullyQualified.php_ 2nikic-php-parser/PhpParser/Node/AttributeGroup.php_1s.nikic-php-parser/PhpParser/Node/Stmt/Goto_.php@_@kp5 3nikic-php-parser/PhpParser/Node/Stmt/ClassConst.php_F0nikic-php-parser/PhpParser/Node/Stmt/Return_.php__/,nikic-php-parser/PhpParser/Node/Stmt/Do_.phph_h̤1nikic-php-parser/PhpParser/Node/Stmt/GroupUse.phpW_WzȤ/nikic-php-parser/PhpParser/Node/Stmt/Throw_.php_@1nikic-php-parser/PhpParser/Node/Stmt/Property.php -_ -f1nikic-php-parser/PhpParser/Node/Stmt/Finally_.php_".nikic-php-parser/PhpParser/Node/Stmt/Echo_.php_3nikic-php-parser/PhpParser/Node/Stmt/Expression.php_ı,nikic-php-parser/PhpParser/Node/Stmt/Nop.phpS_S^1nikic-php-parser/PhpParser/Node/Stmt/Declare_.php_K=3nikic-php-parser/PhpParser/Node/Stmt/InlineHTML.php_01nikic-php-parser/PhpParser/Node/Stmt/Foreach_.php_"0nikic-php-parser/PhpParser/Node/Stmt/ElseIf_.phpo_o<23nikic-php-parser/PhpParser/Node/Stmt/Namespace_.php_z.C/nikic-php-parser/PhpParser/Node/Stmt/Const_.php_ȍΤFnikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php_\Z3Anikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.phpz_z>2nikic-php-parser/PhpParser/Node/Stmt/StaticVar.php_KE2nikic-php-parser/PhpParser/Node/Stmt/ClassLike.phpn _n BȤ/nikic-php-parser/PhpParser/Node/Stmt/Class_.php _ n/nikic-php-parser/PhpParser/Node/Stmt/UseUse.php_9.nikic-php-parser/PhpParser/Node/Stmt/Else_.php_ -0nikic-php-parser/PhpParser/Node/Stmt/Global_.php_"/nikic-php-parser/PhpParser/Node/Stmt/Unset_.php_|A 9nikic-php-parser/PhpParser/Node/Stmt/PropertyProperty.php_/nikic-php-parser/PhpParser/Node/Stmt/Break_.php__ᤰ,nikic-php-parser/PhpParser/Node/Stmt/If_.php`_`P.nikic-php-parser/PhpParser/Node/Stmt/Label.php_d2nikic-php-parser/PhpParser/Node/Stmt/Function_.php -_ -&Ǥ-nikic-php-parser/PhpParser/Node/Stmt/Use_.php_@;nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation.php-_-bY8!2nikic-php-parser/PhpParser/Node/Stmt/Continue_.php_?Ų0nikic-php-parser/PhpParser/Node/Stmt/Switch_.php[_[B|/nikic-php-parser/PhpParser/Node/Stmt/Trait_.php3_3 %QT5nikic-php-parser/PhpParser/Node/Stmt/HaltCompiler.php_30/-nikic-php-parser/PhpParser/Node/Stmt/For_.phpQ_Q[|&nikic-php-parser/PhpParser/Builder.php_+`4nikic-php-parser/PhpParser/PrettyPrinterAbstract.phpK_K k*nikic-php-parser/PhpParser/Comment/Doc.phpx_xp&nikic-php-parser/PhpParser/Comment.php_*nikic-php-parser/PhpParser/JsonDecoder.phpl _l +;)Z2nikic-php-parser/PhpParser/NodeVisitorAbstract.php_%8@%nikic-php-parser/PhpParser/Parser.php_#nikic-php-parser/PhpParser/Node.php_yŤ-nikic-php-parser/PhpParser/BuilderHelpers.php&_&ke;Dnikic-php-parser/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php'_'t}Lnikic-php-parser/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.phpK_Kc_Rnikic-php-parser/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php_Bnikic-php-parser/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php6_6لkEnikic-php-parser/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php_lJ3Bnikic-php-parser/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php_Nj@nikic-php-parser/PhpParser/Lexer/TokenEmulator/TokenEmulator.phpu_uD4hLnikic-php-parser/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.php _ dMBnikic-php-parser/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php_ʤHnikic-php-parser/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php_p .nikic-php-parser/PhpParser/Lexer/Emulative.phpC#_C#NZ\ .nikic-php-parser/PhpParser/Internal/Differ.phpa_ah\Anikic-php-parser/PhpParser/Internal/PrintableNewAnonClassNode.phpz_z94`0nikic-php-parser/PhpParser/Internal/DiffElem.php7_7$3nikic-php-parser/PhpParser/Internal/TokenStream.php"_"j -)nikic-php-parser/PhpParser/NodeFinder.php -_ -cU-nikic-php-parser/PhpParser/ParserAbstract.phpv_vIX,nikic-php-parser/PhpParser/ParserFactory.php_tI*nikic-php-parser/PhpParser/NodeVisitor.php_HQä+nikic-php-parser/PhpParser/ErrorHandler.phpB_BQ+nikic-php-parser/PhpParser/NodeAbstract.php_wꈤ,nikic-php-parser/PhpParser/NodeTraverser.phpz(_z(DZǤ6nikic-php-parser/PhpParser/ErrorHandler/Collecting.php_L4nikic-php-parser/PhpParser/ErrorHandler/Throwing.php_$7nikic-php-parser/PhpParser/NodeVisitor/NameResolver.phpK(_K(@nikic-php-parser/PhpParser/NodeVisitor/NodeConnectingVisitor.php_>nikic-php-parser/PhpParser/NodeVisitor/FirstFindingVisitor.php5_5Y9nikic-php-parser/PhpParser/NodeVisitor/CloningVisitor.php8_8ײWܤBnikic-php-parser/PhpParser/NodeVisitor/ParentConnectingVisitor.php_+ -ڤ9nikic-php-parser/PhpParser/NodeVisitor/FindingVisitor.php_[/nikic-php-parser/PhpParser/Builder/Property.php _ _b3nikic-php-parser/PhpParser/Builder/FunctionLike.php_"gG1nikic-php-parser/PhpParser/Builder/Namespace_.php_:[O<,nikic-php-parser/PhpParser/Builder/Param.php2 -_2 -ܤ-nikic-php-parser/PhpParser/Builder/Class_.php1 _1 oʤ2nikic-php-parser/PhpParser/Builder/Declaration.php_@%w/nikic-php-parser/PhpParser/Builder/TraitUse.php_N9r-nikic-php-parser/PhpParser/Builder/Method.php_wJ1nikic-php-parser/PhpParser/Builder/Interface_.phpo_o (0nikic-php-parser/PhpParser/Builder/Function_.php_2+nikic-php-parser/PhpParser/Builder/Use_.phpC_Cs9nikic-php-parser/PhpParser/Builder/TraitUseAdaptation.phpa_a7OU-nikic-php-parser/PhpParser/Builder/Trait_.php_*nikic-php-parser/PhpParser/NameContext.php)_).nikic-php-parser/PhpParser/Parser/Multiple.php_ *nikic-php-parser/PhpParser/Parser/Php7.phpFN_FN6/,nikic-php-parser/PhpParser/Parser/Tokens.php_( *nikic-php-parser/PhpParser/Parser/Php5.phpWB_WB좌5nikic-php-parser/PhpParser/NodeTraverserInterface.php_{nikic-php-parser/LICENSE_*)sebastian-recursion-context/Exception.php_9S8sebastian-recursion-context/InvalidArgumentException.php_I'sebastian-recursion-context/Context.phpC_C)E#sebastian-recursion-context/LICENSE_`#sebastian-lines-of-code/Counter.phpO -_O ->_'sebastian-lines-of-code/LinesOfCode.php -_ -0yl<sebastian-lines-of-code/Exception/NegativeValueException.php_^Й/sebastian-lines-of-code/Exception/Exception.php{_{b_>sebastian-lines-of-code/Exception/IllogicalValuesException.php_6sebastian-lines-of-code/Exception/RuntimeException.php_VK/sebastian-lines-of-code/LineCountingVisitor.php_sebastian-lines-of-code/LICENSE_bS~-sebastian-code-unit-reverse-lookup/Wizard.php _ K*sebastian-code-unit-reverse-lookup/LICENSE_3G ( - - - - This Schema file defines the rules by which the XML configuration file of PHPUnit 9.2 may be structured. - - - - - - Root Element - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The main type specifying the document structure - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This Schema file defines the rules by which the XML configuration file of PHPUnit 8.5 may be structured. - - - - - - Root Element - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The main type specifying the document structure - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Na>tΤ4phar-io-version/constraints/AnyVersionConstraint.phpR NaR #>phar-io-version/constraints/SpecificMajorVersionConstraint.php Na`9q:Fphar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.php NaɍEphar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.php NaVU9phar-io-version/constraints/AbstractVersionConstraint.php NaxB9phar-io-version/constraints/AndVersionConstraintGroup.php NaY!phar-io-version/VersionNumber.php NaO1+phar-io-version/VersionConstraintParser.php< Na< ͮ*Ҥphar-io-version/Version.php Na<&?phar-io-version/exceptions/InvalidPreReleaseSuffixException.php NaҵDphar-io-version/exceptions/UnsupportedVersionConstraintException.php Na9(phar-io-version/exceptions/Exception.php Na$eb:phar-io-version/exceptions/NoPreReleaseSuffixException.php NaT46phar-io-version/exceptions/InvalidVersionException.php Na4/S$phar-io-version/PreReleaseSuffix.phpf Naf#Ѥphar-io-version/LICENSE1 Na1>:*phar-io-version/VersionConstraintValue.phpH + NaH +F{~4*sebastian-object-enumerator/Enumerator.php Nax})sebastian-object-enumerator/Exception.php Na}Ȥ8sebastian-object-enumerator/InvalidArgumentException.php Naâ phpunit.xsd F Na Fuynikic-php-parser/LICENSE Na*)nikic-php-parser/PhpParser/NodeDumper.phpd NadY l3nikic-php-parser/PhpParser/Internal/TokenStream.php%# Na%# D0nikic-php-parser/PhpParser/Internal/DiffElem.php7 Na7$.nikic-php-parser/PhpParser/Internal/Differ.php- Na-^Anikic-php-parser/PhpParser/Internal/PrintableNewAnonClassNode.php$ Na$'c4nikic-php-parser/PhpParser/PrettyPrinterAbstract.php Naޤ*nikic-php-parser/PhpParser/Comment/Doc.phpx Naxp0nikic-php-parser/PhpParser/Builder/Function_.phpF NaFux1nikic-php-parser/PhpParser/Builder/Interface_.php Na -nikic-php-parser/PhpParser/Builder/Method.php Na}-nikic-php-parser/PhpParser/Builder/Class_.php Na -nikic-php-parser/PhpParser/Builder/Trait_.php Nakj3nikic-php-parser/PhpParser/Builder/FunctionLike.php NaZqe/nikic-php-parser/PhpParser/Builder/TraitUse.phpW NaWL@2nikic-php-parser/PhpParser/Builder/Declaration.php NaE7,nikic-php-parser/PhpParser/Builder/Param.php Na ֤+nikic-php-parser/PhpParser/Builder/Use_.php Nas1nikic-php-parser/PhpParser/Builder/Namespace_.php: Na:ˆp1nikic-php-parser/PhpParser/Builder/ClassConst.phpm Nam z/nikic-php-parser/PhpParser/Builder/Property.php| Na|O 9nikic-php-parser/PhpParser/Builder/TraitUseAdaptation.php NaUVx$nikic-php-parser/PhpParser/Error.php NaQZ7nikic-php-parser/PhpParser/NodeVisitor/NameResolver.phpm& Nam&f[&Bnikic-php-parser/PhpParser/NodeVisitor/ParentConnectingVisitor.phpu NauME9nikic-php-parser/PhpParser/NodeVisitor/CloningVisitor.php Na"WJ9nikic-php-parser/PhpParser/NodeVisitor/FindingVisitor.php NaB>nikic-php-parser/PhpParser/NodeVisitor/FirstFindingVisitor.php Nam4Ť@nikic-php-parser/PhpParser/NodeVisitor/NodeConnectingVisitor.php Nau +ä,nikic-php-parser/PhpParser/ParserFactory.php Na +~&1nikic-php-parser/PhpParser/ConstExprEvaluator.php$ Na$.( Hnikic-php-parser/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php Na*#Dnikic-php-parser/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php NaLFLnikic-php-parser/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php  Na *§o@nikic-php-parser/PhpParser/Lexer/TokenEmulator/TokenEmulator.phpu NauD4hDnikic-php-parser/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php NarBnikic-php-parser/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php NaI}Bnikic-php-parser/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php NajHnikic-php-parser/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php Na0kEnikic-php-parser/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php Nac/Lnikic-php-parser/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.phpn Nan 1Rnikic-php-parser/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.phpV NaVBnikic-php-parser/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php Na Hnikic-php-parser/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php Na:&E.nikic-php-parser/PhpParser/Lexer/Emulative.php" Na"A*nikic-php-parser/PhpParser/NameContext.php% Na%G-&nikic-php-parser/PhpParser/Comment.php NaA-nikic-php-parser/PhpParser/ParserAbstract.php Na;nikic-php-parser/PhpParser/ConstExprEvaluationException.php_ Na_I 2nikic-php-parser/PhpParser/NodeVisitorAbstract.php Na-nikic-php-parser/PhpParser/BuilderFactory.php( Na(5nikic-php-parser/PhpParser/NodeTraverserInterface.php| Na|Ś &nikic-php-parser/PhpParser/Builder.php Na64nikic-php-parser/PhpParser/ErrorHandler/Throwing.php NaS}<6nikic-php-parser/PhpParser/ErrorHandler/Collecting.php Na&Ȥ)nikic-php-parser/PhpParser/NodeFinder.php Na #nikic-php-parser/PhpParser/Node.php Nayݗ-nikic-php-parser/PhpParser/BuilderHelpers.php_# Na_#,15nikic-php-parser/PhpParser/PrettyPrinter/Standard.phpx NaxLu+nikic-php-parser/PhpParser/ErrorHandler.php/ Na/#\-nikic-php-parser/PhpParser/Node/Attribute.phpH NaHhqK5nikic-php-parser/PhpParser/Node/VarLikeIdentifier.php Na&'nikic-php-parser/PhpParser/Node/Arg.php0 Na0q H-nikic-php-parser/PhpParser/Node/UnionType.php NajViΤ/nikic-php-parser/PhpParser/Node/ComplexType.phpS NaS(0nikic-php-parser/PhpParser/Node/FunctionLike.php Na4ͤ5nikic-php-parser/PhpParser/Node/Stmt/HaltCompiler.php Na];/nikic-php-parser/PhpParser/Node/Stmt/Break_.php Na֤,nikic-php-parser/PhpParser/Node/Stmt/If_.php: Na:u٤/nikic-php-parser/PhpParser/Node/Stmt/While_.phpE NaEա2nikic-php-parser/PhpParser/Node/Stmt/Function_.php + Na +̤0nikic-php-parser/PhpParser/Node/Stmt/ElseIf_.phpI NaIEä3nikic-php-parser/PhpParser/Node/Stmt/Interface_.php NaL/Ǥ/nikic-php-parser/PhpParser/Node/Stmt/Class_.phpA NaAO1nikic-php-parser/PhpParser/Node/Stmt/GroupUse.php + Na +ߎ0|/nikic-php-parser/PhpParser/Node/Stmt/Throw_.php Na2nikic-php-parser/PhpParser/Node/Stmt/Continue_.php Na.nikic-php-parser/PhpParser/Node/Stmt/Else_.php Na|ä/nikic-php-parser/PhpParser/Node/Stmt/Trait_.php Na$v/nikic-php-parser/PhpParser/Node/Stmt/Unset_.php Na=oB.nikic-php-parser/PhpParser/Node/Stmt/Echo_.php Na͘Ƥ.nikic-php-parser/PhpParser/Node/Stmt/Label.php NaӤ,nikic-php-parser/PhpParser/Node/Stmt/Do_.phpB NaB +@7nikic-php-parser/PhpParser/Node/Stmt/DeclareDeclare.php Naƀ1nikic-php-parser/PhpParser/Node/Stmt/Finally_.php Na1AAnikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.phpA NaAdFnikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.phpZ NaZP֤0nikic-php-parser/PhpParser/Node/Stmt/Switch_.php5 Na5FFY1nikic-php-parser/PhpParser/Node/Stmt/TraitUse.php Nag,3nikic-php-parser/PhpParser/Node/Stmt/InlineHTML.php Na]2nikic-php-parser/PhpParser/Node/Stmt/ClassLike.php Na .nikic-php-parser/PhpParser/Node/Stmt/Goto_.php NaVyPn0nikic-php-parser/PhpParser/Node/Stmt/Return_.php NaͿ)e1nikic-php-parser/PhpParser/Node/Stmt/Declare_.php Na.. +2nikic-php-parser/PhpParser/Node/Stmt/StaticVar.php Na.nikic-php-parser/PhpParser/Node/Stmt/Case_.phpl Nalu/nikic-php-parser/PhpParser/Node/Stmt/Const_.php Na,nikic-php-parser/PhpParser/Node/Stmt/Nop.php@ Na@G0nikic-php-parser/PhpParser/Node/Stmt/Static_.php Na1nikic-php-parser/PhpParser/Node/Stmt/TryCatch.php$ Na$W1nikic-php-parser/PhpParser/Node/Stmt/EnumCase.php NajD1nikic-php-parser/PhpParser/Node/Stmt/Foreach_.phpo Nao9/nikic-php-parser/PhpParser/Node/Stmt/UseUse.phpd Nadb.nikic-php-parser/PhpParser/Node/Stmt/Enum_.php= Na=dA3nikic-php-parser/PhpParser/Node/Stmt/Expression.php NaRK4nikic-php-parser/PhpParser/Node/Stmt/ClassMethod.php Na(( +/nikic-php-parser/PhpParser/Node/Stmt/Catch_.php| Na|*V>0nikic-php-parser/PhpParser/Node/Stmt/Global_.php Na9nikic-php-parser/PhpParser/Node/Stmt/PropertyProperty.php Na҉-nikic-php-parser/PhpParser/Node/Stmt/For_.php> Na>NQ-nikic-php-parser/PhpParser/Node/Stmt/Use_.phpl Nal9=|3nikic-php-parser/PhpParser/Node/Stmt/Namespace_.php Na㹀3nikic-php-parser/PhpParser/Node/Stmt/ClassConst.php NaeX?ͤ1nikic-php-parser/PhpParser/Node/Stmt/Property.phpO + NaO +=;nikic-php-parser/PhpParser/Node/Stmt/TraitUseAdaptation.php Naa8>nikic-php-parser/PhpParser/Node/Expr/NullsafePropertyFetch.php Na /N2nikic-php-parser/PhpParser/Node/Expr/ArrayItem.phpx Nax| 2/nikic-php-parser/PhpParser/Node/Expr/PreDec.php Natg-nikic-php-parser/PhpParser/Node/Expr/Cast.phpA NaA:Vs/nikic-php-parser/PhpParser/Node/Expr/Clone_.php NaW2nikic-php-parser/PhpParser/Node/Expr/UnaryPlus.php Nae̤1nikic-php-parser/PhpParser/Node/Expr/AssignOp.php Na,1nikic-php-parser/PhpParser/Node/Expr/CallLike.php& Na&KS0/nikic-php-parser/PhpParser/Node/Expr/Throw_.php Na ?/nikic-php-parser/PhpParser/Node/Expr/Yield_.php\ Na\ /nikic-php-parser/PhpParser/Node/Expr/Match_.php NaW /nikic-php-parser/PhpParser/Node/Expr/Print_.php NanX1nikic-php-parser/PhpParser/Node/Expr/Variable.php NamJr.nikic-php-parser/PhpParser/Node/Expr/Error.php Naa\0nikic-php-parser/PhpParser/Node/Expr/PostDec.php Naw:.nikic-php-parser/PhpParser/Node/Expr/Eval_.php Na356-nikic-php-parser/PhpParser/Node/Expr/New_.php NaiĤ6nikic-php-parser/PhpParser/Node/Expr/ArrowFunction.php Na K5nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mul.phpB NaB|;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanOr.phpO NaOeӸ9nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Smaller.phpJ NaJf<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalXor.phpR NaR4e5nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Pow.phpC NaC;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Identical.phpP NaP";nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseOr.phpN NaN_|7nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Equal.phpG NaGݙʤ;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Spaceship.phpP NaPHƉ.6nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Plus.phpD NaD' ,@nikic-php-parser/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.phpY NaY⍤<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.phpP NaP6L6<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BitwiseXor.phpP NaP~Ƥ;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalOr.phpO NaO@<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/BooleanAnd.phpQ NaQ5v<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/LogicalAnd.phpR NaRi9nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Greater.phpJ NaJ4ͤ5nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Div.phpB NaBi<nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftRight.phpQ NaQǤ:nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Coalesce.phpM NaMY ;nikic-php-parser/PhpParser/Node/Expr/BinaryOp/ShiftLeft.phpO NaOQ#5nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Mod.phpB NaBʤ:nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotEqual.phpM NaM7nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Minus.phpF NaF$Lˤ>nikic-php-parser/PhpParser/Node/Expr/BinaryOp/NotIdentical.phpV NaVh< +@nikic-php-parser/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.phpY NaY^ز8nikic-php-parser/PhpParser/Node/Expr/BinaryOp/Concat.phpH NaH @q0nikic-php-parser/PhpParser/Node/Expr/PostInc.php Naᦦ!0nikic-php-parser/PhpParser/Node/Expr/Ternary.php NaQͤ;nikic-php-parser/PhpParser/Node/Expr/NullsafeMethodCall.php NaY,l6nikic-php-parser/PhpParser/Node/Expr/PropertyFetch.php Naɾ4nikic-php-parser/PhpParser/Node/Expr/Instanceof_.phpa Naa< /nikic-php-parser/PhpParser/Node/Expr/Empty_.php Na'6nikic-php-parser/PhpParser/Node/Expr/ArrayDimFetch.phpM NaMIY8nikic-php-parser/PhpParser/Node/Expr/ClassConstFetch.php Na3nikic-php-parser/PhpParser/Node/Expr/ConstFetch.php Na޶%3nikic-php-parser/PhpParser/Node/Expr/StaticCall.phpe Nae1nikic-php-parser/PhpParser/Node/Expr/FuncCall.php3 Na3%A.nikic-php-parser/PhpParser/Node/Expr/List_.php Na/nikic-php-parser/PhpParser/Node/Expr/Isset_.php NaI5nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mul.php Naπ/5nikic-php-parser/PhpParser/Node/Expr/AssignOp/Pow.php NayV;nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseOr.php Na;6nikic-php-parser/PhpParser/Node/Expr/AssignOp/Plus.php Na&|<nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php Nau<nikic-php-parser/PhpParser/Node/Expr/AssignOp/BitwiseXor.php NalϚ5nikic-php-parser/PhpParser/Node/Expr/AssignOp/Div.php NaYP +<nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftRight.php Nas*:nikic-php-parser/PhpParser/Node/Expr/AssignOp/Coalesce.php Naq,;nikic-php-parser/PhpParser/Node/Expr/AssignOp/ShiftLeft.php Na5nikic-php-parser/PhpParser/Node/Expr/AssignOp/Mod.php Na]10Y7nikic-php-parser/PhpParser/Node/Expr/AssignOp/Minus.php Na隤8nikic-php-parser/PhpParser/Node/Expr/AssignOp/Concat.php Na6nikic-php-parser/PhpParser/Node/Expr/ErrorSuppress.php Nag3nikic-php-parser/PhpParser/Node/Expr/BooleanNot.php NaDC/nikic-php-parser/PhpParser/Node/Expr/Array_.php8 Na8;p3nikic-php-parser/PhpParser/Node/Expr/UnaryMinus.php NalA0nikic-php-parser/PhpParser/Node/Expr/Closure.php + Na +U;4nikic-php-parser/PhpParser/Node/Expr/Cast/Double.php Na>,4nikic-php-parser/PhpParser/Node/Expr/Cast/Unset_.php Na1Ӥ2nikic-php-parser/PhpParser/Node/Expr/Cast/Int_.php Nac5nikic-php-parser/PhpParser/Node/Expr/Cast/String_.php Na3nikic-php-parser/PhpParser/Node/Expr/Cast/Bool_.php Na V]S4nikic-php-parser/PhpParser/Node/Expr/Cast/Array_.php NaI|5nikic-php-parser/PhpParser/Node/Expr/Cast/Object_.php Na/nikic-php-parser/PhpParser/Node/Expr/PreInc.php NaYä/nikic-php-parser/PhpParser/Node/Expr/Assign.php Na1nikic-php-parser/PhpParser/Node/Expr/BinaryOp.phpo Nao Ѥ2nikic-php-parser/PhpParser/Node/Expr/YieldFrom.php Naw8<nikic-php-parser/PhpParser/Node/Expr/StaticPropertyFetch.php& Na&ܐ2nikic-php-parser/PhpParser/Node/Expr/AssignRef.phpH NaHE`ob3nikic-php-parser/PhpParser/Node/Expr/MethodCall.phpO NaODWX3nikic-php-parser/PhpParser/Node/Expr/BitwiseNot.php Na~'3nikic-php-parser/PhpParser/Node/Expr/ClosureUse.php Nah2nikic-php-parser/PhpParser/Node/Expr/ShellExec.php Nahy.nikic-php-parser/PhpParser/Node/Expr/Exit_.php Na1nikic-php-parser/PhpParser/Node/Expr/Include_.php Nai0nikic-php-parser/PhpParser/Node/NullableType.php Na6C5nikic-php-parser/PhpParser/Node/Scalar/MagicConst.phpc Nac,xG=nikic-php-parser/PhpParser/Node/Scalar/EncapsedStringPart.php Na%2nikic-php-parser/PhpParser/Node/Scalar/LNumber.php Na v6훤2nikic-php-parser/PhpParser/Node/Scalar/String_.phpR NaRؽ?nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Function_.php] Na]HnY<nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Method.phpV NaVΤ<nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Class_.phpT NaT㨘X9nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Dir.phpM NaMal<nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Trait_.phpT NaTd:nikic-php-parser/PhpParser/Node/Scalar/MagicConst/File.phpP NaP#:nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Line.phpP NaPM4@nikic-php-parser/PhpParser/Node/Scalar/MagicConst/Namespace_.php` Na`>3nikic-php-parser/PhpParser/Node/Scalar/Encapsed.php NaRU2nikic-php-parser/PhpParser/Node/Scalar/DNumber.phpQ NaQ_ 7nikic-php-parser/PhpParser/Node/VariadicPlaceholder.php NaP(nikic-php-parser/PhpParser/Node/Name.php Na-g2nikic-php-parser/PhpParser/Node/AttributeGroup.php NaB9*nikic-php-parser/PhpParser/Node/Const_.php Nayh1nikic-php-parser/PhpParser/Node/Name/Relative.php NaǛEf7nikic-php-parser/PhpParser/Node/Name/FullyQualified.php Na .nikic-php-parser/PhpParser/Node/Identifier.php NaJa*nikic-php-parser/PhpParser/Node/Scalar.phpk Nak,ߤ(nikic-php-parser/PhpParser/Node/Expr.php Nah傤(nikic-php-parser/PhpParser/Node/Stmt.php Nav2/)nikic-php-parser/PhpParser/Node/Param.phpb NabMߤ,nikic-php-parser/PhpParser/Node/MatchArm.php Na+m64nikic-php-parser/PhpParser/Node/IntersectionType.php Nao,nikic-php-parser/PhpParser/NodeTraverser.php]' Na]'TG:Ƥ%nikic-php-parser/PhpParser/Parser.php} Na}{*nikic-php-parser/PhpParser/NodeVisitor.php Na3*nikic-php-parser/PhpParser/JsonDecoder.php Na xg+nikic-php-parser/PhpParser/NodeAbstract.phpZ NaZ׻@$nikic-php-parser/PhpParser/Lexer.phpuZ NauZ~g*nikic-php-parser/PhpParser/Parser/Php7.phpA NaA,nikic-php-parser/PhpParser/Parser/Tokens.php& Na&<.nikic-php-parser/PhpParser/Parser/Multiple.php NasF)7*nikic-php-parser/PhpParser/Parser/Php5.php* Na* [Qphp-timer/Timer.php NacAɤ$php-timer/ResourceUsageFormatter.php NaPھphp-timer/Duration.php + Na +tXy/php-timer/exceptions/NoActiveTimerException.php Nal٤Ephp-timer/exceptions/TimeSinceStartOfRequestNotAvailableException.php Na$b"php-timer/exceptions/Exception.phpn Naniuۤphp-timer/LICENSE Nax#sebastian-global-state/Snapshot.php) Na)'sebastian-global-state/CodeExporter.php Na j!դ#sebastian-global-state/Restorer.php NaGJ 6sebastian-global-state/exceptions/RuntimeException.php Na;/sebastian-global-state/exceptions/Exception.phpy NayJsebastian-global-state/LICENSE Na&sebastian-global-state/ExcludeList.php + Na +R{:myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.php NaR6myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.php Na=Bv.myclabs-deep-copy/DeepCopy/Matcher/Matcher.php NaDmyclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php NaP:myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php2 Na2ZQͤ:myclabs-deep-copy/DeepCopy/Exception/PropertyException.php Na3Gz7myclabs-deep-copy/DeepCopy/Exception/CloneException.php Na {ˤ(myclabs-deep-copy/DeepCopy/deep_copy.php Narx3myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.php Na3myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.php Na䊉0myclabs-deep-copy/DeepCopy/Filter/KeepFilter.php NaYn,myclabs-deep-copy/DeepCopy/Filter/Filter.phpd NadMGmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php + Na +DgLmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php Na)$Bmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php Na)6myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php NaQBŤ7myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.php Naz4myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php NaVD?myclabs-deep-copy/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php Na^Amyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php Nav|Gmyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php NaT+;myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.php NaؤAmyclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.php NaƤ:myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php5 Na5ى'myclabs-deep-copy/DeepCopy/DeepCopy.php Nawv˾myclabs-deep-copy/LICENSE5 Na5ʭ˄sebastian-type/UnknownType.php- Na-}Y$sebastian-type/GenericObjectType.php Na+Msebastian-type/UnionType.phpO NaO Esebastian-type/MixedType.php Na +HKsebastian-type/CallableType.php4 Na4/psebastian-type/VoidType.phpQ NaQsebastian-type/NullType.php< Na<4sebastian-type/ObjectType.php Nałsebastian-type/SimpleType.php  Na Ťsebastian-type/TypeName.php~ Na~ h=xsebastian-type/StaticType.php@ Na@`sebastian-type/FalseType.php NamZ#sebastian-type/ReflectionMapper.php Na )R sebastian-type/IterableType.php Na+sebastian-type/Type.php Na [sebastian-type/LICENSE  Na  +Τ-sebastian-type/exception/RuntimeException.php Na%+sebastian-type/exception/LogicException.php} Na}/[H&sebastian-type/exception/Exception.phpj Najbᮧsebastian-version/Version.php Na ƪsebastian-version/LICENSE NaZschema/8.5.xsdB NaB贅schema/9.2.xsdB NaB|lAphpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.php0 Na0<9phpdocumentor-reflection-docblock/DocBlock/Serializer.php NaiB9phpdocumentor-reflection-docblock/DocBlock/TagFactory.php NaJMx7phpdocumentor-reflection-docblock/DocBlock/Tags/See.php Na  +;phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.phpx Nax Bn>phpdocumentor-reflection-docblock/DocBlock/Tags/InvalidTag.php& Na&,l:phpdocumentor-reflection-docblock/DocBlock/Tags/Source.phpH NaH Q%΋:phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php NaBD7=phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php Na}BܤAphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.php Nac[]Cphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php, Na,%8Gphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php Na ;phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php NaZr:phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php Na Lt9phpdocumentor-reflection-docblock/DocBlock/Tags/Since.phpW + NaW +1>Rphpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php NaP~Lphpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.phpq Naq@phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php Na Q'H?phpdocumentor-reflection-docblock/DocBlock/Tags/TagWithType.php Na;uHphpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php Na.ͤAphpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php Na };phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php + Na +@S>phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php + Na +}CO8phpdocumentor-reflection-docblock/DocBlock/Tags/Link.php Na8phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php Na oԤ;phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php% Na%5ä:phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.phpg + Nag +w8;phpdocumentor-reflection-docblock/DocBlock/Tags/Example.php NaUVb9phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php NaW8phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.php> + Na> + +:phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php Na"G<phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php Na e_2phpdocumentor-reflection-docblock/DocBlock/Tag.php Na78^<phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php0 Na0ێAphpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.php Na"L:phpdocumentor-reflection-docblock/DocBlock/Description.php Na 545phpdocumentor-reflection-docblock/DocBlockFactory.php$ Na$br+phpdocumentor-reflection-docblock/Utils.php@ Na@ $昤=phpdocumentor-reflection-docblock/Exception/PcreException.php NaϊWƤ.phpdocumentor-reflection-docblock/DocBlock.php; Na;%p)phpdocumentor-reflection-docblock/LICENSE8 Na8ʤ>phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php Na)%ߤ/phpdocumentor-type-resolver/Types/Resource_.php NaŞX.phpdocumentor-type-resolver/Types/Compound.php Na>7-phpdocumentor-type-resolver/Types/Integer.phpj Najv,phpdocumentor-type-resolver/Types/Never_.php Naj+phpdocumentor-type-resolver/Types/Null_.phpx Naxs-phpdocumentor-type-resolver/Types/Parent_.php NaO!.+phpdocumentor-type-resolver/Types/Void_.php Nak-phpdocumentor-type-resolver/Types/Boolean.phpn NanrĤ.phpdocumentor-type-resolver/Types/ArrayKey.php Naʫ 4phpdocumentor-type-resolver/Types/ContextFactory.php7 Na7q⬤2phpdocumentor-type-resolver/Types/AbstractList.phpt Natt-phpdocumentor-type-resolver/Types/String_.phps NasH1phpdocumentor-type-resolver/Types/ClassString.php Na=t+phpdocumentor-type-resolver/Types/Self_.php NaoȤ,phpdocumentor-type-resolver/Types/Float_.phpm Nam)J-phpdocumentor-type-resolver/Types/Static_.php Na8-phpdocumentor-type-resolver/Types/Context.php Na ]Z,phpdocumentor-type-resolver/Types/Array_.php Na섍D-phpdocumentor-type-resolver/Types/Object_.php NawEhN/phpdocumentor-type-resolver/Types/Callable_.php{ Na{E0phpdocumentor-type-resolver/Types/Collection.php Na?,phpdocumentor-type-resolver/Types/Scalar.php Na0phpdocumentor-type-resolver/Types/Expression.php8 Na8g,phpdocumentor-type-resolver/Types/Mixed_.php Na3i4phpdocumentor-type-resolver/Types/AggregatedType.php + Na +Hɵ/phpdocumentor-type-resolver/Types/Iterable_.php? Na?Q82phpdocumentor-type-resolver/Types/Intersection.php NaUz$*phpdocumentor-type-resolver/Types/This.phpY NaY^?ֈ.phpdocumentor-type-resolver/Types/Nullable.phpR NaRCp\5phpdocumentor-type-resolver/Types/InterfaceString.php Na-phpdocumentor-type-resolver/FqsenResolver.php Naj^=phpdocumentor-type-resolver/PseudoTypes/HtmlEscapedString.phpg Nagwe2phpdocumentor-type-resolver/PseudoTypes/False_.php Nao䈤:phpdocumentor-type-resolver/PseudoTypes/NonEmptyString.phpa Naa²,7phpdocumentor-type-resolver/PseudoTypes/TraitString.phpZ NaZgC1phpdocumentor-type-resolver/PseudoTypes/True_.php Nal:phpdocumentor-type-resolver/PseudoTypes/CallableString.php` Na`ZCphpdocumentor-type-resolver/PseudoTypes/NonEmptyLowercaseString.phpt Nat)9phpdocumentor-type-resolver/PseudoTypes/NumericString.php^ Na^8M;phpdocumentor-type-resolver/PseudoTypes/LowercaseString.phpb Nab7 ;phpdocumentor-type-resolver/PseudoTypes/PositiveInteger.php^ Na^$phpdocumentor-type-resolver/Type.php Nab&*phpdocumentor-type-resolver/PseudoType.phpu Nau]\#phpdocumentor-type-resolver/LICENSE8 Na8ʤ,phpdocumentor-type-resolver/TypeResolver.phpJ NaJ!sebastian-environment/Console.php NauҤ)sebastian-environment/OperatingSystem.php Na^!sebastian-environment/Runtime.php Nar*usebastian-environment/LICENSE Nalm|*sebastian-comparator/ComparisonFailure.php Na %+sebastian-comparator/ResourceComparator.php NaJ)sebastian-comparator/DoubleComparator.php Naa*sebastian-comparator/NumericComparator.php Na I sebastian-comparator/Factory.php Na$/*sebastian-comparator/DOMNodeComparator.php Na 1i)sebastian-comparator/ObjectComparator.phpX NaX ׌)sebastian-comparator/ScalarComparator.php Na d+sebastian-comparator/DateTimeComparator.php Na KQ'sebastian-comparator/TypeComparator.php NacX\,sebastian-comparator/ExceptionComparator.php Na14sebastian-comparator/exceptions/RuntimeException.php NaV'-sebastian-comparator/exceptions/Exception.phpv NavEᵤ3sebastian-comparator/SplObjectStorageComparator.php Na?/#sebastian-comparator/Comparator.php Nat(sebastian-comparator/ArrayComparator.phpu NauEmhfsebastian-comparator/LICENSE  Na =(-sebastian-comparator/MockObjectComparator.php NaI+phar-io-manifest/ManifestDocumentMapper.php Na:?phar-io-manifest/exceptions/ManifestDocumentMapperException.php Na:9z3phar-io-manifest/exceptions/InvalidUrlException.php Na ?phar-io-manifest/exceptions/InvalidApplicationNameException.php Na:@>8phar-io-manifest/exceptions/ManifestElementException.php NaA47phar-io-manifest/exceptions/ManifestLoaderException.php NaD>9phar-io-manifest/exceptions/ManifestDocumentException.php Na!P45phar-io-manifest/exceptions/InvalidEmailException.php Na<)phar-io-manifest/exceptions/Exception.php Na@phar-io-manifest/exceptions/ManifestDocumentLoadingException.phpH NaHǃ:phar-io-manifest/exceptions/ElementCollectionException.php Na I'phar-io-manifest/ManifestSerializer.php Narp#phar-io-manifest/xml/ExtElement.php  Na y>)phar-io-manifest/xml/CopyrightElement.php Na7#phar-io-manifest/xml/PhpElement.php NaB:50phar-io-manifest/xml/AuthorElementCollection.php, Na,-*phar-io-manifest/xml/ElementCollection.php Na@ )phar-io-manifest/xml/ExtensionElement.php} Na}0'phar-io-manifest/xml/LicenseElement.phpo Nao%:'-phar-io-manifest/xml/ExtElementCollection.php# Na#E(phar-io-manifest/xml/RequiresElement.php$ Na$>'phar-io-manifest/xml/BundlesElement.phpS NaSWN>(phar-io-manifest/xml/ContainsElement.phpn Nanf)phar-io-manifest/xml/ManifestDocument.php + Na + 4)phar-io-manifest/xml/ComponentElement.phpy Nayݤ(phar-io-manifest/xml/ManifestElement.php4 Na4&phar-io-manifest/xml/AuthorElement.phpr Nar<3phar-io-manifest/xml/ComponentElementCollection.php5 Na5(\phar-io-manifest/LICENSE` Na`p$phar-io-manifest/values/Manifest.php + Na +=Laphar-io-manifest/values/Url.php Na͚4phar-io-manifest/values/AuthorCollectionIterator.php3 Na3џ"phar-io-manifest/values/Author.php NaF+phar-io-manifest/values/ApplicationName.php; Na;D'phar-io-manifest/values/Application.php NaI$ۤ1phar-io-manifest/values/PhpVersionRequirement.php Nam?#phar-io-manifest/values/License.php Na&!o#phar-io-manifest/values/Library.php NaO,phar-io-manifest/values/AuthorCollection.php Nao'phar-io-manifest/values/Requirement.php Nad%phar-io-manifest/values/Extension.php Naq}>phar-io-manifest/values/BundledComponentCollectionIterator.php NaVh1phar-io-manifest/values/RequirementCollection.php NaP9phar-io-manifest/values/RequirementCollectionIterator.phpj Najܭ:!phar-io-manifest/values/Email.phpN NaNZ& phar-io-manifest/values/Type.php Na=%6phar-io-manifest/values/BundledComponentCollection.php  Na ¾W63phar-io-manifest/values/PhpExtensionRequirement.php Na1,phar-io-manifest/values/BundledComponent.php@ Na@DP`0phar-io-manifest/values/CopyrightInformation.phpP NaP ai#phar-io-manifest/ManifestLoader.php Na.-a<sebastian-cli-parser/exceptions/AmbiguousOptionException.phpF NaFm\:sebastian-cli-parser/exceptions/UnknownOptionException.php? Na?vDGsebastian-cli-parser/exceptions/OptionDoesNotAllowArgumentException.php_ Na_|13-sebastian-cli-parser/exceptions/Exception.phpu NauӫJsebastian-cli-parser/exceptions/RequiredOptionArgumentMissingException.phph NahCsebastian-cli-parser/Parser.php NakMsebastian-cli-parser/LICENSE Nau$symfony-polyfill-ctype/bootstrap.php Na(S&symfony-polyfill-ctype/bootstrap80.phpR NaRL + symfony-polyfill-ctype/Ctype.php~ Na~JҤsymfony-polyfill-ctype/LICENSE) Na)`e03sebastian-complexity/Exception/RuntimeException.php NaCdW,sebastian-complexity/Exception/Exception.phpv Nav7#sebastian-complexity/Calculator.phpe Nae (68sebastian-complexity/Complexity/ComplexityCollection.php Nail.sebastian-complexity/Complexity/Complexity.phpQ NaQl@sebastian-complexity/Complexity/ComplexityCollectionIterator.php, Na,esebastian-complexity/LICENSE Na=ݤ=sebastian-complexity/Visitor/ComplexityCalculatingVisitor.php Na OGsebastian-complexity/Visitor/CyclomaticComplexityCalculatingVisitor.php  Na 7Yobject-reflector/LICENSE Na9v<sebastian-lines-of-code/Exception/NegativeValueException.php Na +ڤ>sebastian-lines-of-code/Exception/IllogicalValuesException.php NaG6sebastian-lines-of-code/Exception/RuntimeException.php NaK/sebastian-lines-of-code/Exception/Exception.phpz Naz aV#sebastian-lines-of-code/Counter.php NaH5'sebastian-lines-of-code/LinesOfCode.php Na fӤ/sebastian-lines-of-code/LineCountingVisitor.php Na~Asebastian-lines-of-code/LICENSE NabS~ manifest.txt Na.Rdoctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php, Na,ALdoctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php NabRdoctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.php Na+<doctrine-instantiator/Doctrine/Instantiator/Instantiator.php Na=Edoctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php Navhdoctrine-instantiator/LICENSE$ Na$ +͂4sebastian-resource-operations/ResourceOperations.php߲ Na߲%sebastian-resource-operations/LICENSE Na]<$sebastian-code-unit/FunctionUnit.php Na`2sebastian-code-unit/CodeUnitCollectionIterator.php; Na;Lʤsebastian-code-unit/Mapper.php- Na-#%sebastian-code-unit/InterfaceUnit.php Nac sebastian-code-unit/CodeUnit.php~% Na~%D){!sebastian-code-unit/TraitUnit.php NaXA+sebastian-code-unit/InterfaceMethodUnit.php NaǦ'sebastian-code-unit/ClassMethodUnit.php Na@[3sebastian-code-unit/exceptions/NoTraitException.php NaQ36sebastian-code-unit/exceptions/ReflectionException.php Na$,sebastian-code-unit/exceptions/Exception.phps Nastg;sebastian-code-unit/exceptions/InvalidCodeUnitException.php Na6-!sebastian-code-unit/ClassUnit.php NaF'sebastian-code-unit/TraitMethodUnit.php Naqzsebastian-code-unit/LICENSE  Na p*sebastian-code-unit/CodeUnitCollection.php NaJ.phpstorm.meta.php NaO;php-code-coverage/Exception/WrongXdebugVersionException.php Na Ȥ;php-code-coverage/Exception/PhpdbgNotAvailableException.php` Na`Fphp-code-coverage/Exception/NoCodeCoverageDriverAvailableException.php/ Na/6R,php-code-coverage/Exception/XmlException.php NaWܤ?php-code-coverage/Exception/ReportAlreadyFinalizedException.php: Na:d%6;php-code-coverage/Exception/XdebugNotAvailableException.phpe NaeNG=php-code-coverage/Exception/WriteOperationFailedException.php Na(eCphp-code-coverage/Exception/DirectoryCouldNotBeCreatedException.php NaC*eFphp-code-coverage/Exception/DeadCodeDetectionNotSupportedException.php Na:php-code-coverage/Exception/Xdebug2NotEnabledException.phpf Naf,'6php-code-coverage/Exception/TestIdMissingException.php Na +3php-code-coverage/Exception/ReflectionException.php Nak):php-code-coverage/Exception/Xdebug3NotEnabledException.phpy Nay<>]php-code-coverage/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.phpa Naa"A)php-code-coverage/Exception/Exception.php} Na}z9php-code-coverage/Exception/PcovNotAvailableException.phpa NaajDphp-code-coverage/Exception/PathExistsButIsNotDirectoryException.php Na.2Iphp-code-coverage/Exception/StaticAnalysisCacheNotConfiguredException.php Na}Cphp-code-coverage/Exception/UnintentionallyCoveredCodeException.php+ Na+Q_ªJphp-code-coverage/Exception/BranchAndPathCoverageNotSupportedException.php Na778php-code-coverage/Exception/InvalidArgumentException.php NaK.n/php-code-coverage/Exception/ParserException.php Na,/ php-code-coverage/Percentage.php Naݤphp-code-coverage/Filter.php Na }'php-code-coverage/Driver/PcovDriver.php Na#php-code-coverage/Driver/Driver.php Na3A*php-code-coverage/Driver/Xdebug3Driver.php Na TJ*php-code-coverage/Driver/Xdebug2Driver.phpO NaO =󛫤)php-code-coverage/Driver/PhpdbgDriver.php` + Na` +WH%php-code-coverage/Driver/Selector.php Na 6]?php-code-coverage/StaticAnalysis/CachingCoveredFileAnalyser.php + Na +Ȥ:php-code-coverage/StaticAnalysis/UncoveredFileAnalyser.php+ Na+B=?php-code-coverage/StaticAnalysis/ParsingCoveredFileAnalyser.phpF NaFiaBphp-code-coverage/StaticAnalysis/ExecutableLinesFindingVisitor.php + Na +zp{*php-code-coverage/StaticAnalysis/Cache.php6 Na6M`;php-code-coverage/StaticAnalysis/CodeUnitFindingVisitor.php Na8Aphp-code-coverage/StaticAnalysis/ParsingUncoveredFileAnalyser.phpo Nao 㭤Aphp-code-coverage/StaticAnalysis/CachingUncoveredFileAnalyser.php Nahf8php-code-coverage/StaticAnalysis/CoveredFileAnalyser.phpN NaN|֤?php-code-coverage/StaticAnalysis/IgnoredLinesFindingVisitor.php Na v٤0php-code-coverage/StaticAnalysis/CacheWarmer.phpB NaBJphp-code-coverage/Version.php NaCK(*"php-code-coverage/CodeCoverage.phpD NaDPL/php-code-coverage/ProcessedCodeCoverageData.php$ Na$'#php-code-coverage/Report/Crap4j.php Na+!php-code-coverage/Report/Text.php' Na' (php-code-coverage/Report/Html/Facade.php4 Na4k*php-code-coverage/Report/Html/Renderer.phpU! NaU!}>php-code-coverage/Report/Html/Renderer/Template/file.html.dist NaFMphp-code-coverage/Report/Html/Renderer/Template/coverage_bar_branch.html.dist' Na'O}Cphp-code-coverage/Report/Html/Renderer/Template/dashboard.html.distG NaGl?php-code-coverage/Report/Html/Renderer/Template/paths.html.dist Na*'ݤCphp-code-coverage/Report/Html/Renderer/Template/directory.html.dist NaGM?php-code-coverage/Report/Html/Renderer/Template/js/nv.d3.min.jsR NaRphp-code-coverage/Report/Html/Renderer/Template/line.html.dist Na{Hphp-code-coverage/Report/Html/Renderer/Template/directory_item.html.distA NaAdsFphp-code-coverage/Report/Html/Renderer/Template/coverage_bar.html.dist' Na'O}Ephp-code-coverage/Report/Html/Renderer/Template/method_item.html.dist Nas:>php-code-coverage/Report/Html/Renderer/Template/css/custom.css NaEphp-code-coverage/Report/Html/Renderer/Template/css/bootstrap.min.cssr Nar?l=php-code-coverage/Report/Html/Renderer/Template/css/style.css Na:@php-code-coverage/Report/Html/Renderer/Template/css/octicons.cssX NaX'#Aphp-code-coverage/Report/Html/Renderer/Template/css/nv.d3.min.cssX% NaX%0,Jphp-code-coverage/Report/Html/Renderer/Template/directory_branch.html.distj NajHBphp-code-coverage/Report/Html/Renderer/Template/branches.html.dist Nah2+Cphp-code-coverage/Report/Html/Renderer/Template/file_item.html.distt NatOphp-code-coverage/Report/Html/Renderer/Template/directory_item_branch.html.dist; Na;mۤJphp-code-coverage/Report/Html/Renderer/Template/dashboard_branch.html.distG NaGlCphp-code-coverage/Report/Html/Renderer/Template/icons/file-code.svg0 Na0QUUHphp-code-coverage/Report/Html/Renderer/Template/icons/file-directory.svg NaZ/php-code-coverage/Report/Html/Renderer/File.phpS NaSY4php-code-coverage/Report/Html/Renderer/Dashboard.phpC NaC L+4php-code-coverage/Report/Html/Renderer/Directory.php  Na ( php-code-coverage/Report/PHP.php Na>*\1php-code-coverage/Report/Xml/BuildInformation.php Na T3e'php-code-coverage/Report/Xml/Source.phpz Naz'1'php-code-coverage/Report/Xml/Facade.php! Na! P'php-code-coverage/Report/Xml/Method.phpW NaW ʤ'php-code-coverage/Report/Xml/Report.php Na HC%php-code-coverage/Report/Xml/File.php+ Na+g׃'php-code-coverage/Report/Xml/Totals.php Nak &php-code-coverage/Report/Xml/Tests.php Na%php-code-coverage/Report/Xml/Node.php3 Na3(php-code-coverage/Report/Xml/Project.phpf NafPe)php-code-coverage/Report/Xml/Coverage.php+ Na+9E*php-code-coverage/Report/Xml/Directory.php NaAf%php-code-coverage/Report/Xml/Unit.php NaY#php-code-coverage/Report/Clover.php' Na'-&php-code-coverage/Report/Cobertura.php0 Na0q)php-code-coverage/RawCodeCoverageData.php Na<php-code-coverage/CrapIndex.php NakS;php-code-coverage/Node/File.phpJ NaJK"php-code-coverage/Node/Builder.php Naq#php-code-coverage/Node/Iterator.php Na&$php-code-coverage/Node/Directory.phpK$ NaK$m''php-code-coverage/Node/AbstractNode.php Na5sphp-code-coverage/LICENSE Na\Tphp-code-coverage/Directory.php Na Hphpunit/Util/Xml.php Nax@phpunit/Util/GlobalState.php Na=Ut(phpunit/Util/InvalidDataSetException.php Na1 phpunit/Util/Log/JUnit.phpn* Nan*)LBphpunit/Util/Log/TeamCity.php& Na&cphpunit/Util/Blacklist.php Nasphpunit/Util/Filter.php Na l* phpunit/Util/Test.php] Na]phpunit/Util/FileLoader.php Na Mꖤ$phpunit/Util/XmlTestListRenderer.php Na Z,phpunit/Util/XdebugFilterScriptGenerator.phpx Nax=phpunit/Util/Filesystem.php Nafޤ*phpunit/Util/VersionComparisonOperator.php Na`,phpunit/Util/Color.php Naפ%phpunit/Util/TextTestListRenderer.php8 Na8>6"phpunit/Util/RegularExpression.php Na0uR)phpunit/Util/ErrorHandler.php Na`uΤphpunit/Util/Exception.php Na다,phpunit/Util/PHP/Template/TestCaseMethod.tpl Na mD+phpunit/Util/PHP/Template/TestCaseClass.tplp Nap 3 H݀*phpunit/Util/PHP/Template/PhptTestCase.tpl Na1&phpunit/Util/PHP/WindowsPhpProcess.php Na$&phpunit/Util/PHP/DefaultPhpProcess.phpz NazCp'phpunit/Util/PHP/AbstractPhpProcess.php& Na&{99phpunit/Util/Printer.php Na aVphpunit/Util/Type.php Na܌*phpunit/Util/Json.phpH NaH A$$phpunit/Util/Annotation/DocBlock.phpA NaA.$phpunit/Util/Annotation/Registry.phpL + NaL +I phpunit/Util/ExcludeList.php= Na=(1m!phpunit/Util/Xml/SchemaFinder.php NaSphpunit/Util/Xml/Validator.php NaVphpunit/Util/Xml/Loader.php Na ,?%phpunit/Util/Xml/ValidationResult.php Naxv:phpunit/Util/Xml/Exception.php NaӤ0phpunit/Util/Xml/FailedSchemaDetectionResult.php Na#S*phpunit/Util/Xml/SchemaDetectionResult.php Na4χz#phpunit/Util/Xml/SchemaDetector.php- Na-4phpunit/Util/Xml/SuccessfulSchemaDetectionResult.php' Na'g%phpunit/Util/Xml/SnapshotNodeList.php Na`6*phpunit/Util/TestDox/TextResultPrinter.php Naȹ!.'phpunit/Util/TestDox/NamePrettifier.php# Na#[֤*phpunit/Util/TestDox/CliTestDoxPrinter.php1* Na1*Dl&phpunit/Util/TestDox/ResultPrinter.php" Na"1q$)phpunit/Util/TestDox/XmlResultPrinter.php Nay'phpunit/Util/TestDox/TestDoxPrinter.php) Na)ڤ*phpunit/Util/TestDox/HtmlResultPrinter.php + Na +t&*phpunit/Runner/StandardTestSuiteLoader.php> Na> "u"phpunit/Runner/TestSuiteSorter.php, Na,kڤ"phpunit/Runner/TestSuiteLoader.php Naޤ"phpunit/Runner/TestResultCache.php NaK'phpunit/Runner/Extension/PharLoader.php Na R -phpunit/Runner/Extension/ExtensionHandler.phpz Naz 3Τ4phpunit/Runner/Filter/ExcludeGroupFilterIterator.phps Nas} +Z4phpunit/Runner/Filter/IncludeGroupFilterIterator.phpr NarP;AD!phpunit/Runner/Filter/Factory.php NadcΤ-phpunit/Runner/Filter/GroupFilterIterator.php Na=;,phpunit/Runner/Filter/NameFilterIterator.phpv Nav Zphpunit/Runner/Version.php Nakr3)phpunit/Runner/DefaultTestResultCache.php NaWphpunit/Runner/Exception.php NazZ*phpunit/Runner/Hook/AfterTestErrorHook.php+ Na+ fz,phpunit/Runner/Hook/AfterTestWarningHook.php/ Na/)pԤ,phpunit/Runner/Hook/AfterTestFailureHook.php/ Na/u./phpunit/Runner/Hook/AfterIncompleteTestHook.php5 Na5 ɤ,phpunit/Runner/Hook/AfterSkippedTestHook.php/ Na/7Ԥ+phpunit/Runner/Hook/TestListenerAdapter.php Na\6E*phpunit/Runner/Hook/AfterRiskyTestHook.php+ Na+5ÿ+phpunit/Runner/Hook/BeforeFirstTestHook.php Na?]{դ/phpunit/Runner/Hook/AfterSuccessfulTestHook.php$ Na$]O phpunit/Runner/Hook/TestHook.php NaaB&phpunit/Runner/Hook/BeforeTestHook.php NaR +%phpunit/Runner/Hook/AfterTestHook.php Na3uݤphpunit/Runner/Hook/Hook.php Nab)phpunit/Runner/Hook/AfterLastTestHook.php Na +"ܤ&phpunit/Runner/NullTestResultCache.php NaW<phpunit/Runner/PhptTestCase.phpWV NaWVT_y!phpunit/Runner/BaseTestRunner.php Na C +'phpunit/Runner/ResultCacheExtension.php< Na<6 _Aphpunit/TextUI/XmlConfiguration/Group/GroupCollectionIterator.phpA NaA8%9phpunit/TextUI/XmlConfiguration/Group/GroupCollection.php Nak0phpunit/TextUI/XmlConfiguration/Group/Groups.php Na/phpunit/TextUI/XmlConfiguration/Group/Group.php NaEL-phpunit/TextUI/XmlConfiguration/Generator.php NaF 0phpunit/TextUI/XmlConfiguration/Logging/Text.php Na-301phpunit/TextUI/XmlConfiguration/Logging/Junit.php Naz珥3phpunit/TextUI/XmlConfiguration/Logging/Logging.php Na d44phpunit/TextUI/XmlConfiguration/Logging/TeamCity.php Nam7phpunit/TextUI/XmlConfiguration/Logging/TestDox/Xml.php Nao8phpunit/TextUI/XmlConfiguration/Logging/TestDox/Html.php Na}78phpunit/TextUI/XmlConfiguration/Logging/TestDox/Text.php Na*JjJphpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollectionIterator.phpo Nao\VBphpunit/TextUI/XmlConfiguration/Filesystem/DirectoryCollection.php NaV93phpunit/TextUI/XmlConfiguration/Filesystem/File.php NaEphpunit/TextUI/XmlConfiguration/Filesystem/FileCollectionIterator.php7 Na7te7=phpunit/TextUI/XmlConfiguration/Filesystem/FileCollection.phpC NaC8phpunit/TextUI/XmlConfiguration/Filesystem/Directory.php NasO*phpunit/TextUI/XmlConfiguration/Loader.php̗ Na̗m_|`1phpunit/TextUI/XmlConfiguration/Configuration.php2 Na2N'Ƥ3phpunit/TextUI/XmlConfiguration/PHPUnit/PHPUnit.phpiC NaiC 65phpunit/TextUI/XmlConfiguration/PHPUnit/Extension.php Na +J.?phpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollection.php NaܔGphpunit/TextUI/XmlConfiguration/PHPUnit/ExtensionCollectionIterator.phpi Naio(7phpunit/TextUI/XmlConfiguration/TestSuite/TestSuite.php Na;phpunit/TextUI/XmlConfiguration/TestSuite/TestDirectory.php@ Na@)eIphpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollectionIterator.phpi Nai #6phpunit/TextUI/XmlConfiguration/TestSuite/TestFile.php Na Ephpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollection.php Na4XAphpunit/TextUI/XmlConfiguration/TestSuite/TestSuiteCollection.php NaCSHphpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollectionIterator.phpG NaGwǤ@phpunit/TextUI/XmlConfiguration/TestSuite/TestFileCollection.php_ Na_.+ޤMphpunit/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollectionIterator.php NaypSphpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.php NajeKphpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollection.php NaQ4XAphpunit/TextUI/XmlConfiguration/CodeCoverage/Filter/Directory.php Na ϕ=phpunit/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.php Nac%=phpunit/TextUI/XmlConfiguration/CodeCoverage/FilterMapper.php Na}ݚ;phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Xml.php Na8;phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Php.php NaN_A<phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Html.php NaڴQ>phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.php NacaѤ<phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Text.php Na>phpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Clover.php Na: Aphpunit/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.php Na"e-phpunit/TextUI/XmlConfiguration/Exception.php NaN5++phpunit/TextUI/XmlConfiguration/PHP/Php.php Na:phpunit/TextUI/XmlConfiguration/PHP/VariableCollection.php- Na-c0phpunit/TextUI/XmlConfiguration/PHP/Variable.php Na\M,Bphpunit/TextUI/XmlConfiguration/PHP/ConstantCollectionIterator.php_ Na_4VDphpunit/TextUI/XmlConfiguration/PHP/IniSettingCollectionIterator.phps NasiBt <phpunit/TextUI/XmlConfiguration/PHP/IniSettingCollection.phpM NaMB12phpunit/TextUI/XmlConfiguration/PHP/PhpHandler.phpy Nay]b0phpunit/TextUI/XmlConfiguration/PHP/Constant.php4 Na4,-qBphpunit/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.php_ Na_K:phpunit/TextUI/XmlConfiguration/PHP/ConstantCollection.php- Na-U%2phpunit/TextUI/XmlConfiguration/PHP/IniSetting.phpG NaGT+6phpunit/TextUI/XmlConfiguration/Migration/Migrator.php Nao$VGphpunit/TextUI/XmlConfiguration/Migration/MigrationBuilderException.php NaUWĝdphpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php NaU%5Yphpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.phpC NaCcFSphpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveCacheTokensAttribute.php NawHphpunit/TextUI/XmlConfiguration/Migration/Migrations/ConvertLogTypes.php NahoeOphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCloverToReport.phpX NaXijLphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoveragePhpToReport.phpF NaF^ӤBphpunit/TextUI/XmlConfiguration/Migration/Migrations/Migration.php Na'Qphpunit/TextUI/XmlConfiguration/Migration/Migrations/UpdateSchemaLocationTo93.php NabJOphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageCrap4jToReport.php Na$i'Qphpunit/TextUI/XmlConfiguration/Migration/Migrations/IntroduceCoverageElement.php NaUJphpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveEmptyFilter.php{ Na{K[phpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistDirectoriesToCoverage.php Na†踤Gphpunit/TextUI/XmlConfiguration/Migration/Migrations/RemoveLogTypes.php& Na&q3{Mphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageHtmlToReport.php NaՄjMphpunit/TextUI/XmlConfiguration/Migration/Migrations/LogToReportMigration.php NaUMphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageTextToReport.php NaV_Xphpunit/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistExcludesToCoverage.php} Na} +Lphpunit/TextUI/XmlConfiguration/Migration/Migrations/CoverageXmlToReport.phpK NaK_ >phpunit/TextUI/XmlConfiguration/Migration/MigrationBuilder.php# Na# g@phpunit/TextUI/XmlConfiguration/Migration/MigrationException.php Na\Zphpunit/TextUI/Help.php. Na.%phpunit/TextUI/TestRunner.php Na6phpunit/TextUI/Exception/TestFileNotFoundException.php NapC0phpunit/TextUI/Exception/ReflectionException.php Na Y-phpunit/TextUI/Exception/RuntimeException.php NaF;phpunit/TextUI/Exception/TestDirectoryNotFoundException.php Na&phpunit/TextUI/Exception/Exception.php NaD{i phpunit/TextUI/ResultPrinter.phpp Napܤ'phpunit/TextUI/DefaultResultPrinter.php67 Na67l&phpunit/TextUI/CliArguments/Mapper.php+, Na+,'a-phpunit/TextUI/CliArguments/Configuration.php Na0'phpunit/TextUI/CliArguments/Builder.phpT NaT66)phpunit/TextUI/CliArguments/Exception.php Na%zEphpunit/TextUI/Command.php&f Na&f"phpunit/TextUI/TestSuiteMapper.php Na A+8#phpunit/Framework/ErrorTestCase.php Na̤!phpunit/Framework/SkippedTest.php NaS.phpunit/Framework/TestSuite.phpb Nab!S+phpunit/Framework/DataProviderTestSuite.php Na\8$phpunit/Framework/IncompleteTest.php Na,+!phpunit/Framework/TestFailure.php Na'q%phpunit/Framework/SkippedTestCase.php Nal]8phpunit/Framework/Exception/PHPTAssertionFailedError.php4 Na4#MSphpunit/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.phpl Nal.~25phpunit/Framework/Exception/SyntheticSkippedError.php NaԗEphpunit/Framework/Exception/ComparisonMethodDoesNotExistException.php0 Na0}c+phpunit/Framework/Exception/OutputError.php Na3phpunit/Framework/Exception/IncompleteTestError.php Naםܤ5phpunit/Framework/Exception/CodeCoverageException.php Na[.phpunit/Framework/Exception/RiskyTestError.php Na*y%phpunit/Framework/Exception/Error.php Naj10phpunit/Framework/Exception/SkippedTestError.php Na O~?phpunit/Framework/Exception/CoveredCodeNotExecutedException.php Na8YФ9phpunit/Framework/Exception/NoChildTestSuiteException.php NaP$'phpunit/Framework/Exception/Warning.php Na8;Zphpunit/Framework/Exception/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php] Na]Ns~:phpunit/Framework/Exception/ExpectationFailedException.php Nag٤5phpunit/Framework/Exception/SkippedTestSuiteError.php Nax@phpunit/Framework/Exception/MissingCoversAnnotationException.php Na|Aphpunit/Framework/Exception/ActualValueIsNotAnObjectException.php NaiOp.phpunit/Framework/Exception/SyntheticError.php5 Na5KY?phpunit/Framework/Exception/UnintentionallyCoveredCodeError.php Na<phpunit/Framework/Exception/InvalidDataProviderException.php Na.ڜɤ)phpunit/Framework/Exception/Exception.php Na w4phpunit/Framework/Exception/AssertionFailedError.php NaUphpunit/Framework/Exception/ComparisonMethodDoesNotDeclareBoolReturnTypeException.phpS NaSzGt}<phpunit/Framework/Exception/InvalidCoversTargetException.php Nao苤Tphpunit/Framework/Exception/ComparisonMethodDoesNotDeclareParameterTypeException.php[ Na[]8A8phpunit/Framework/Exception/InvalidArgumentException.php  Na &phpunit/Framework/Assert/Functions.phpa Naaphpunit/Framework/Test.php~ Na~wtphpunit/Framework/Assert.phppR NapR4b'phpunit/Framework/TestSuiteIterator.php Na򨔬%phpunit/Framework/MockObject/Stub.php NaŎ*phpunit/Framework/MockObject/MockClass.php Na'C)phpunit/Framework/MockObject/MockType.php NaFFt8phpunit/Framework/MockObject/Builder/MethodNameMatch.phpw NawTy-phpunit/Framework/MockObject/Builder/Stub.php Na(:phpunit/Framework/MockObject/Builder/InvocationStubber.php Na1phpunit/Framework/MockObject/Builder/Identity.php Na8phpunit/Framework/MockObject/Builder/ParametersMatch.php Naڃ9phpunit/Framework/MockObject/Builder/InvocationMocker.php Na bKLphpunit/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.php Naz'Yphpunit/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php  Na ɅWFphpunit/Framework/MockObject/Exception/ClassAlreadyExistsException.php NaLphpunit/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php! Na!$ہKphpunit/Framework/MockObject/Exception/IncompatibleReturnValueException.php Na8=Mphpunit/Framework/MockObject/Exception/SoapExtensionNotAvailableException.php NazGphpunit/Framework/MockObject/Exception/CannotUseAddMethodsException.php5 Na5{Uphpunit/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.php Na rHphpunit/Framework/MockObject/Exception/MatchBuilderNotFoundException.php NaYphpunit/Framework/MockObject/Exception/OriginalConstructorInvocationRequiredException.php Naک?phpunit/Framework/MockObject/Exception/UnknownTypeException.php Na~Hphpunit/Framework/MockObject/Exception/CannotUseOnlyMethodsException.phpE NaE>phpunit/Framework/MockObject/Exception/ReflectionException.php Na.ؔ@phpunit/Framework/MockObject/Exception/UnknownClassException.php Na5uW;phpunit/Framework/MockObject/Exception/RuntimeException.php Na_|Aphpunit/Framework/MockObject/Exception/BadMethodCallException.php NaΫXOphpunit/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.php NaӁƤ4phpunit/Framework/MockObject/Exception/Exception.php NaB'@phpunit/Framework/MockObject/Exception/UnknownTraitException.php Naq¥Lphpunit/Framework/MockObject/Exception/MethodCannotBeConfiguredException.php Na}QKphpunit/Framework/MockObject/Exception/MethodNameNotConfiguredException.php~ Na~x1)Cphpunit/Framework/MockObject/Exception/DuplicateMethodException.php Na_Ephpunit/Framework/MockObject/Exception/InvalidMethodNameException.php Na ܤ@phpunit/Framework/MockObject/Exception/ClassIsFinalException.php Na()5phpunit/Framework/MockObject/Stub/ReturnReference.php  Na f*phpunit/Framework/MockObject/Stub/Stub.php3 Na3>+4phpunit/Framework/MockObject/Stub/ReturnValueMap.php Naۤ4phpunit/Framework/MockObject/Stub/ReturnCallback.php NaD0Ӥ0phpunit/Framework/MockObject/Stub/ReturnSelf.php4 Na4DD/phpunit/Framework/MockObject/Stub/Exception.php( Na(J4phpunit/Framework/MockObject/Stub/ReturnArgument.php Na?}60phpunit/Framework/MockObject/Stub/ReturnStub.php Na6phpunit/Framework/MockObject/Stub/ConsecutiveCalls.php  Na .*phpunit/Framework/MockObject/Generator.phpw Naw4Iɇ=phpunit/Framework/MockObject/Generator/mocked_method_void.tpl Nap7phpunit/Framework/MockObject/Generator/mocked_class.tpl NawZ8phpunit/Framework/MockObject/Generator/mocked_method.tplF NaFK6phpunit/Framework/MockObject/Generator/wsdl_method.tpl< Na<i5phpunit/Framework/MockObject/Generator/wsdl_class.tpl Na9phpunit/Framework/MockObject/Generator/proxied_method.tpl} Na}@ė>phpunit/Framework/MockObject/Generator/proxied_method_void.tplv NavT?phpunit/Framework/MockObject/Generator/mocked_static_method.tpl Na 4R6phpunit/Framework/MockObject/Generator/trait_class.tplQ NaQ<Ȥ6phpunit/Framework/MockObject/Generator/deprecation.tpl; Na;O5s,phpunit/Framework/MockObject/MockBuilder.php=+ Na=+B5.phpunit/Framework/MockObject/MockMethodSet.php8 Na8G\+phpunit/Framework/MockObject/Invocation.php Na +phpunit/Framework/MockObject/MockMethod.php) Na)ޙ5phpunit/Framework/MockObject/MethodNameConstraint.php + Na +A1|;phpunit/Framework/MockObject/Rule/ConsecutiveParameters.phpl Nal z'%0phpunit/Framework/MockObject/Rule/MethodName.php Na +WG4phpunit/Framework/MockObject/Rule/InvokedAtIndex.php) Na)A3phpunit/Framework/MockObject/Rule/AnyParameters.php Na~'5phpunit/Framework/MockObject/Rule/InvocationOrder.php NaLDӤ8phpunit/Framework/MockObject/Rule/InvokedAtLeastOnce.php- Na- (8phpunit/Framework/MockObject/Rule/InvokedAtMostCount.php NagY9phpunit/Framework/MockObject/Rule/InvokedAtLeastCount.php NaB5phpunit/Framework/MockObject/Rule/AnyInvokedCount.phpj Naj`Ť4phpunit/Framework/MockObject/Rule/ParametersRule.phpc Nac?(2phpunit/Framework/MockObject/Rule/InvokedCount.php Na ^ 0phpunit/Framework/MockObject/Rule/Parameters.phpQ NaQ`g|*phpunit/Framework/MockObject/MockTrait.php Na&nä3phpunit/Framework/MockObject/ConfigurableMethod.php Na+phpunit/Framework/MockObject/Verifiable.php Na̐ s+phpunit/Framework/MockObject/Api/Method.php Na(phpunit/Framework/MockObject/Api/Api.php Na s6phpunit/Framework/MockObject/Api/MockedCloneMethod.php* Na*?z8phpunit/Framework/MockObject/Api/UnmockedCloneMethod.phpG NaGө(phpunit/Framework/MockObject/Matcher.phpB NaB*2phpunit/Framework/MockObject/InvocationHandler.php: Na:ˤ+phpunit/Framework/MockObject/MockObject.php Nabt$phpunit/Framework/SelfDescribing.php + Na +sphpunit/Framework/TestCase.php" Na"`q%phpunit/Framework/WarningTestCase.php$ Na$Hޤ4phpunit/Framework/InvalidParameterGroupException.php Na;phpunit/Framework/Constraint/Exception/ExceptionMessage.php Naw;8phpunit/Framework/Constraint/Exception/ExceptionCode.php NaiأLphpunit/Framework/Constraint/Exception/ExceptionMessageRegularExpression.php NaLj[i4phpunit/Framework/Constraint/Exception/Exception.php NaRu{8phpunit/Framework/Constraint/Operator/BinaryOperator.php) Na)Ow4phpunit/Framework/Constraint/Operator/LogicalXor.php$ Na$O4phpunit/Framework/Constraint/Operator/LogicalNot.php Na 7phpunit/Framework/Constraint/Operator/UnaryOperator.php + Na + a2phpunit/Framework/Constraint/Operator/Operator.php& Na& Dܤ3phpunit/Framework/Constraint/Operator/LogicalOr.php NaZ4phpunit/Framework/Constraint/Operator/LogicalAnd.php NabJ6phpunit/Framework/Constraint/Filesystem/FileExists.phpe NaeK6phpunit/Framework/Constraint/Filesystem/IsWritable.phpe Nae;phpunit/Framework/Constraint/Filesystem/DirectoryExists.phpj Naji+6phpunit/Framework/Constraint/Filesystem/IsReadable.phpe Nae1/phpunit/Framework/Constraint/Boolean/IsTrue.php Na}0phpunit/Framework/Constraint/Boolean/IsFalse.php Na?phpunit/Framework/Constraint/Equality/IsEqualCanonicalizing.php + Na +~=phpunit/Framework/Constraint/Equality/IsEqualIgnoringCase.php + Na +C\:phpunit/Framework/Constraint/Equality/IsEqualWithDelta.php + Na +61phpunit/Framework/Constraint/Equality/IsEqual.php Na Ӥ)phpunit/Framework/Constraint/Callback.php? Na? +b+phpunit/Framework/Constraint/IsAnything.php NaE+phpunit/Framework/Constraint/Math/IsNan.php Na4g0.phpunit/Framework/Constraint/Math/IsFinite.php NaZҗ0phpunit/Framework/Constraint/Math/IsInfinite.php Na'*~2phpunit/Framework/Constraint/Cardinality/Count.phpj Naj xR@ؤ8phpunit/Framework/Constraint/Cardinality/GreaterThan.php Nah,d}4phpunit/Framework/Constraint/Cardinality/IsEmpty.php Nahf5phpunit/Framework/Constraint/Cardinality/LessThan.php Naa T5phpunit/Framework/Constraint/Cardinality/SameSize.php_ Na_uŤ,phpunit/Framework/Constraint/IsIdentical.php NaO4phpunit/Framework/Constraint/Object/ObjectEquals.php + Na +0W:phpunit/Framework/Constraint/Object/ObjectHasAttribute.php[ Na[Fm9phpunit/Framework/Constraint/Object/ClassHasAttribute.phpn Nan9<?phpunit/Framework/Constraint/Object/ClassHasStaticAttribute.php NadR8phpunit/Framework/Constraint/Traversable/ArrayHasKey.php Na6@!Iphpunit/Framework/Constraint/Traversable/TraversableContainsIdentical.php' Na'sӤ@phpunit/Framework/Constraint/Traversable/TraversableContains.php NaEphpunit/Framework/Constraint/Traversable/TraversableContainsEqual.phpa NaawADphpunit/Framework/Constraint/Traversable/TraversableContainsOnly.php Na RuФ+phpunit/Framework/Constraint/Constraint.phpc" Nac"e@@phpunit/Framework/Constraint/JsonMatchesErrorMessageProvider.php; Na;त,phpunit/Framework/Constraint/JsonMatches.phpz Naz 'RFphpunit/Framework/Constraint/String/StringMatchesFormatDescription.php + Na +1.phpunit/Framework/Constraint/String/IsJson.php Na\@8phpunit/Framework/Constraint/String/StringStartsWith.phpB NaBߤ6phpunit/Framework/Constraint/String/StringContains.php Naij"9phpunit/Framework/Constraint/String/RegularExpression.php Na+J6phpunit/Framework/Constraint/String/StringEndsWith.php Na{,phpunit/Framework/Constraint/Type/IsNull.php Na?),phpunit/Framework/Constraint/Type/IsType.php NaGȤ2phpunit/Framework/Constraint/Type/IsInstanceOf.php: Na:@.phpunit/Framework/ExecutionOrderDependency.php NaR- "phpunit/Framework/TestListener.php Na b!phpunit/Framework/TestBuilder.php" Na"14j phpunit/Framework/TestResult.phpC| NaC|d$!phpunit/Framework/Reorderable.php Naz07phpunit/Framework/TestListenerDefaultImplementation.php$ Na$C!phpunit/Framework/Error/Error.phpl Nal]#phpunit/Framework/Error/Warning.phpw NawG&phpunit/Framework/Error/Deprecated.phpz NazV"phpunit/Framework/Error/Notice.phpv Navˤ&phpunit/Framework/ExceptionWrapper.php Na m(phpunit/Framework/IncompleteTestCase.php NaI phpunit/Exception.php Naa#sebastian-exporter/Exporter.phpy" Nay"Osebastian-exporter/LICENSE Na螷ͤphp-file-iterator/Facade.php* + Na* + Kphp-file-iterator/Factory.php Na{0Ԥphp-file-iterator/Iterator.phpZ NaZ C܎php-file-iterator/LICENSE Na./phpspec-prophecy/Prophecy/Prophecy/Revealer.php Na m5phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.php29 Na29S8phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.phpG NaGWnZ?phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.php Na<8phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.php+ Na+X5phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.php Na#=&phpspec-prophecy/Prophecy/Argument.php Nan-phpspec-prophecy/Prophecy/Util/StringUtil.php + Na +S-phpspec-prophecy/Prophecy/Util/ExportUtil.phpb NabBphpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.php NaD7jHphpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.phpA NaAcHphpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.php2 Na2ePphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php# Na#ߤKphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.phpF NaF|bLphpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.phpg Nag3'}}Ephpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.php8 Na8 .ڤCphpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.php NaZFphpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.php NaR2ͤDphpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.php Na}1phpspec-prophecy/Prophecy/Exception/Exception.php NaxGphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.php Nae:Fphpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.php Na>Dphpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.php Nab@phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.php NahJphpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.php Na&qJphpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.php Na0+5,Lphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.php Na[?phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.php NaV"^Ephpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.php Nar@phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.php Na󱙤6phpspec-prophecy/Prophecy/Promise/PromiseInterface.phpI NaIyv2phpspec-prophecy/Prophecy/Promise/ThrowPromise.php% Na% Q33phpspec-prophecy/Prophecy/Promise/ReturnPromise.php% Na%&;phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.php + Na +,s5phpspec-prophecy/Prophecy/Promise/CallbackPromise.php NaԌӤ;phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.php Na~*7phpspec-prophecy/Prophecy/Prediction/CallPrediction.phpZ NaZ%U<phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.php + Na +#c<phpspec-prophecy/Prophecy/Prediction/PredictionInterface.php Nav:phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.php NaܼCphpspec-prophecy/Prophecy/PhpDocumentor/LegacyClassTagRetriever.php Na|6Gphpspec-prophecy/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php Na1=phpspec-prophecy/Prophecy/PhpDocumentor/ClassTagRetriever.php Na@%Iphpspec-prophecy/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php NaƤ-phpspec-prophecy/Prophecy/Call/CallCenter.php Naɝ.'phpspec-prophecy/Prophecy/Call/Call.phpc Nac ڟJ;phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.php| Na|b0phpspec-prophecy/Prophecy/Comparator/Factory.php Na8! +Ԥ:phpspec-prophecy/Prophecy/Comparator/ClosureComparator.php NaWp%phpspec-prophecy/Prophecy/Prophet.phpE NaE:.b3phpspec-prophecy/Prophecy/Doubler/NameGenerator.php Nad0phpspec-prophecy/Prophecy/Doubler/LazyDouble.php Na 3phpspec-prophecy/Prophecy/Doubler/CachedDoubler.php NaOd\5phpspec-prophecy/Prophecy/Doubler/DoubleInterface.php NaBۤAphpspec-prophecy/Prophecy/Doubler/Generator/TypeHintReference.php NaiBphpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php Na phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.php Na"B(?phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.phpo Nao}-Aphpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.phpz Naz Ci;phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.php NatCphpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.php Na Y-phpspec-prophecy/Prophecy/Doubler/Doubler.php5 Na55?phpspec-prophecy/Prophecy/Doubler/ClassPatch/ThrowablePatch.php Na 83Hphpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php Na?phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.php Na Q)7Dphpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.phph Nahq!ʤEphpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php Na /Aphpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.phpi Nai [ꢤ=phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php  Na Cphpspec-prophecy/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php Na雷Aphpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php Na wpPphpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php Naۤ<phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.phpX NaX5)Aphpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.php Na#<phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.phpD NaD(bL<phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.php Na<phpspec-prophecy/Prophecy/Argument/Token/NotInArrayToken.php Na;@phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.php Nau`S6phpspec-prophecy/Prophecy/Argument/Token/TypeToken.php Na$=phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php Na T9phpspec-prophecy/Prophecy/Argument/Token/InArrayToken.php Na?xn:phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.php Na{ܤBphpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.php Na ;phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.php Na'`<phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php Na j\<phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.php Na/*2@phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.php- Na-3xD;phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.php Na(nGw:phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.php Nav8phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.phpY NaY 0?phpspec-prophecy/LICENSE} Na} ߦ/sebastian-diff/Output/DiffOnlyOutputBuilder.phpz Nazc4sebastian-diff/Output/DiffOutputBuilderInterface.php NaV2sebastian-diff/Output/UnifiedDiffOutputBuilder.php> Na>'q)8sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php( Na(kv4sebastian-diff/Output/AbstractChunkOutputBuilder.php Na\t3sebastian-diff/Exception/ConfigurationException.php= Na=1/Ff&sebastian-diff/Exception/Exception.phpj Naj05sebastian-diff/Exception/InvalidArgumentException.php NaqBsebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php Nat٤Dsebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.php Na9 sebastian-diff/Chunk.php_ Na_vsebastian-diff/Diff.phpj NajbXA5sebastian-diff/LongestCommonSubsequenceCalculator.php Na}e7zsebastian-diff/Differ.php$ Na$]Esebastian-diff/Parser.php Na X{sebastian-diff/Line.phpL NaL +qsebastian-diff/LICENSE  Na a1theseer-tokenizer/Token.php Na4theseer-tokenizer/Tokenizer.php + Na +zl+theseer-tokenizer/NamespaceUriException.phpy Nay'He.theseer-tokenizer/TokenCollectionException.php| Na|`g-theseer-tokenizer/Exception.phpn Nan'Ǥ#theseer-tokenizer/XMLSerializer.php Nag; "theseer-tokenizer/NamespaceUri.phpH NaH=Ctheseer-tokenizer/LICENSE NaR (%theseer-tokenizer/TokenCollection.php + Na +aobject-enumerator/LICENSE Nay{-sebastian-code-unit-reverse-lookup/Wizard.php Na }Z[*sebastian-code-unit-reverse-lookup/LICENSE Na3G ('sebastian-recursion-context/Context.php Na`Mm)sebastian-recursion-context/Exception.php NaPFA#sebastian-recursion-context/LICENSE Na`8sebastian-recursion-context/InvalidArgumentException.php Nab21 + * (c) Bernhard Schussek * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Exporter; +namespace PHPUnit\Webmozart\Assert; -use function bin2hex; -use function count; -use function function_exists; -use function get_class; -use function get_resource_type; -use function implode; -use function is_array; -use function is_float; -use function is_object; -use function is_resource; -use function is_string; -use function mb_strlen; -use function mb_substr; -use function preg_match; -use function spl_object_hash; -use function sprintf; -use function str_repeat; -use function str_replace; -use function strlen; -use function substr; -use function var_export; -use PHPUnit\SebastianBergmann\RecursionContext\Context; -use SplObjectStorage; +use ArrayAccess; +use BadMethodCallException; +use Closure; +use Countable; +use DateTime; +use DateTimeImmutable; +use Exception; +use ResourceBundle; +use SimpleXMLElement; +use Throwable; +use Traversable; /** - * A nifty utility for visualizing PHP variables. + * Efficient assertions to validate the input/output of your methods. * - * - * export(new Exception); - * + * @author Bernhard Schussek */ -class Exporter +class Assert { + use Mixin; /** - * Exports a value as a string. + * @psalm-pure + * @psalm-assert string $value * - * The output of this method is similar to the output of print_r(), but - * improved in various aspects: + * @param mixed $value + * @param string $message * - * - NULL is rendered as "null" (instead of "") - * - TRUE is rendered as "true" (instead of "1") - * - FALSE is rendered as "false" (instead of "") - * - Strings are always quoted with single quotes - * - Carriage returns and newlines are normalized to \n - * - Recursion and repeated rendering is treated properly + * @throws InvalidArgumentException + */ + public static function string($value, $message = '') + { + if (!\is_string($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a string. Got: %s', static::typeToString($value))); + } + } + /** + * @psalm-pure + * @psalm-assert non-empty-string $value * - * @param int $indentation The indentation level of the 2nd+ line + * @param mixed $value + * @param string $message * - * @return string + * @throws InvalidArgumentException */ - public function export($value, $indentation = 0) + public static function stringNotEmpty($value, $message = '') { - return $this->recursiveExport($value, $indentation); + static::string($value, $message); + static::notEq($value, '', $message); } /** - * @param array $data - * @param Context $context + * @psalm-pure + * @psalm-assert int $value * - * @return string + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException */ - public function shortenedRecursiveExport(&$data, \PHPUnit\SebastianBergmann\RecursionContext\Context $context = null) + public static function integer($value, $message = '') { - $result = []; - $exporter = new self(); - if (!$context) { - $context = new \PHPUnit\SebastianBergmann\RecursionContext\Context(); - } - $array = $data; - $context->add($data); - foreach ($array as $key => $value) { - if (\is_array($value)) { - if ($context->contains($data[$key]) !== \false) { - $result[] = '*RECURSION*'; - } else { - $result[] = \sprintf('array(%s)', $this->shortenedRecursiveExport($data[$key], $context)); - } - } else { - $result[] = $exporter->shortenedExport($value); - } + if (!\is_int($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected an integer. Got: %s', static::typeToString($value))); } - return \implode(', ', $result); } /** - * Exports a value into a single-line string. + * @psalm-pure + * @psalm-assert numeric $value * - * The output of this method is similar to the output of - * SebastianBergmann\Exporter\Exporter::export(). + * @param mixed $value + * @param string $message * - * Newlines are replaced by the visible string '\n'. - * Contents of arrays and objects (if any) are replaced by '...'. + * @throws InvalidArgumentException + */ + public static function integerish($value, $message = '') + { + if (!\is_numeric($value) || $value != (int) $value) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected an integerish value. Got: %s', static::typeToString($value))); + } + } + /** + * @psalm-pure + * @psalm-assert positive-int $value * - * @return string + * @param mixed $value + * @param string $message * - * @see SebastianBergmann\Exporter\Exporter::export + * @throws InvalidArgumentException */ - public function shortenedExport($value) + public static function positiveInteger($value, $message = '') { - if (\is_string($value)) { - $string = \str_replace("\n", '', $this->export($value)); - if (\function_exists('mb_strlen')) { - if (\mb_strlen($string) > 40) { - $string = \mb_substr($string, 0, 30) . '...' . \mb_substr($string, -7); - } - } else { - if (\strlen($string) > 40) { - $string = \substr($string, 0, 30) . '...' . \substr($string, -7); - } - } - return $string; + if (!(\is_int($value) && $value > 0)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a positive integer. Got: %s', static::valueToString($value))); } - if (\is_object($value)) { - return \sprintf('%s Object (%s)', \get_class($value), \count($this->toArray($value)) > 0 ? '...' : ''); - } - if (\is_array($value)) { - return \sprintf('Array (%s)', \count($value) > 0 ? '...' : ''); - } - return $this->export($value); } /** - * Converts an object to an array containing all of its private, protected - * and public properties. + * @psalm-pure + * @psalm-assert float $value * - * @return array + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException */ - public function toArray($value) + public static function float($value, $message = '') { - if (!\is_object($value)) { - return (array) $value; - } - $array = []; - foreach ((array) $value as $key => $val) { - // Exception traces commonly reference hundreds to thousands of - // objects currently loaded in memory. Including them in the result - // has a severe negative performance impact. - if ("\0Error\0trace" === $key || "\0Exception\0trace" === $key) { - continue; - } - // properties are transformed to keys in the following way: - // private $property => "\0Classname\0property" - // protected $property => "\0*\0property" - // public $property => "property" - if (\preg_match('/^\\0.+\\0(.+)$/', (string) $key, $matches)) { - $key = $matches[1]; - } - // See https://github.com/php/php-src/commit/5721132 - if ($key === "\0gcdata") { - continue; - } - $array[$key] = $val; - } - // Some internal classes like SplObjectStorage don't work with the - // above (fast) mechanism nor with reflection in Zend. - // Format the output similarly to print_r() in this case - if ($value instanceof \SplObjectStorage) { - foreach ($value as $key => $val) { - $array[\spl_object_hash($val)] = ['obj' => $val, 'inf' => $value->getInfo()]; - } + if (!\is_float($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a float. Got: %s', static::typeToString($value))); } - return $array; } /** - * Recursive implementation of export. - * - * @param mixed $value The value to export - * @param int $indentation The indentation level of the 2nd+ line - * @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects + * @psalm-pure + * @psalm-assert numeric $value * - * @return string + * @param mixed $value + * @param string $message * - * @see SebastianBergmann\Exporter\Exporter::export + * @throws InvalidArgumentException */ - protected function recursiveExport(&$value, $indentation, $processed = null) + public static function numeric($value, $message = '') { - if ($value === null) { - return 'null'; - } - if ($value === \true) { - return 'true'; - } - if ($value === \false) { - return 'false'; - } - if (\is_float($value) && (float) (int) $value === $value) { - return "{$value}.0"; - } - if (\is_resource($value)) { - return \sprintf('resource(%d) of type (%s)', $value, \get_resource_type($value)); - } - if (\is_string($value)) { - // Match for most non printable chars somewhat taking multibyte chars into account - if (\preg_match('/[^\\x09-\\x0d\\x1b\\x20-\\xff]/', $value)) { - return 'Binary String: 0x' . \bin2hex($value); - } - return "'" . \str_replace('', "\n", \str_replace(["\r\n", "\n\r", "\r", "\n"], ['\\r\\n', '\\n\\r', '\\r', '\\n'], $value)) . "'"; - } - $whitespace = \str_repeat(' ', (int) (4 * $indentation)); - if (!$processed) { - $processed = new \PHPUnit\SebastianBergmann\RecursionContext\Context(); + if (!\is_numeric($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a numeric. Got: %s', static::typeToString($value))); } - if (\is_array($value)) { - if (($key = $processed->contains($value)) !== \false) { - return 'Array &' . $key; - } - $array = $value; - $key = $processed->add($value); - $values = ''; - if (\count($array) > 0) { - foreach ($array as $k => $v) { - $values .= \sprintf('%s %s => %s' . "\n", $whitespace, $this->recursiveExport($k, $indentation), $this->recursiveExport($value[$k], $indentation + 1, $processed)); - } - $values = "\n" . $values . $whitespace; - } - return \sprintf('Array &%s (%s)', $key, $values); + } + /** + * @psalm-pure + * @psalm-assert positive-int|0 $value + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + */ + public static function natural($value, $message = '') + { + if (!\is_int($value) || $value < 0) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a non-negative integer. Got: %s', static::valueToString($value))); } - if (\is_object($value)) { - $class = \get_class($value); - if ($hash = $processed->contains($value)) { - return \sprintf('%s Object &%s', $class, $hash); - } - $hash = $processed->add($value); - $values = ''; - $array = $this->toArray($value); - if (\count($array) > 0) { - foreach ($array as $k => $v) { - $values .= \sprintf('%s %s => %s' . "\n", $whitespace, $this->recursiveExport($k, $indentation), $this->recursiveExport($v, $indentation + 1, $processed)); - } - $values = "\n" . $values . $whitespace; - } - return \sprintf('%s Object &%s (%s)', $class, $hash, $values); + } + /** + * @psalm-pure + * @psalm-assert bool $value + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + */ + public static function boolean($value, $message = '') + { + if (!\is_bool($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a boolean. Got: %s', static::typeToString($value))); } - return \var_export($value, \true); } -} -Exporter - -Copyright (c) 2002-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Comparator; - -use PHPUnit\SebastianBergmann\Comparator\Factory as BaseFactory; -/** - * Prophecy comparator factory. - * - * @author Konstantin Kudryashov - */ -final class Factory extends \PHPUnit\SebastianBergmann\Comparator\Factory -{ /** - * @var Factory + * @psalm-pure + * @psalm-assert scalar $value + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException */ - private static $instance; - public function __construct() + public static function scalar($value, $message = '') { - parent::__construct(); - $this->register(new \Prophecy\Comparator\ClosureComparator()); - $this->register(new \Prophecy\Comparator\ProphecyComparator()); + if (!\is_scalar($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a scalar. Got: %s', static::typeToString($value))); + } } /** - * @return Factory + * @psalm-pure + * @psalm-assert object $value + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException */ - public static function getInstance() + public static function object($value, $message = '') { - if (self::$instance === null) { - self::$instance = new \Prophecy\Comparator\Factory(); + if (!\is_object($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected an object. Got: %s', static::typeToString($value))); } - return self::$instance; } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Comparator; - -use Prophecy\Prophecy\ProphecyInterface; -use PHPUnit\SebastianBergmann\Comparator\ObjectComparator; -class ProphecyComparator extends \PHPUnit\SebastianBergmann\Comparator\ObjectComparator -{ - public function accepts($expected, $actual) - { - return \is_object($expected) && \is_object($actual) && $actual instanceof \Prophecy\Prophecy\ProphecyInterface; - } - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false, array &$processed = array()) - { - parent::assertEquals($expected, $actual->reveal(), $delta, $canonicalize, $ignoreCase, $processed); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Comparator; - -use PHPUnit\SebastianBergmann\Comparator\Comparator; -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -/** - * Closure comparator. - * - * @author Konstantin Kudryashov - */ -final class ClosureComparator extends \PHPUnit\SebastianBergmann\Comparator\Comparator -{ - public function accepts($expected, $actual) - { - return \is_object($expected) && $expected instanceof \Closure && \is_object($actual) && $actual instanceof \Closure; - } - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false, array &$processed = array()) - { - if ($expected !== $actual) { - throw new \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure( - $expected, - $actual, - // we don't need a diff - '', - '', - \false, - 'all closures are different if not identical' - ); - } - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Call; - -use Prophecy\Exception\Prophecy\MethodProphecyException; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Argument\ArgumentsWildcard; -use Prophecy\Util\StringUtil; -use Prophecy\Exception\Call\UnexpectedCallException; -use SplObjectStorage; -/** - * Calls receiver & manager. - * - * @author Konstantin Kudryashov - */ -class CallCenter -{ - private $util; - /** - * @var Call[] - */ - private $recordedCalls = array(); - /** - * @var SplObjectStorage - */ - private $unexpectedCalls; /** - * Initializes call center. + * @psalm-pure + * @psalm-assert resource $value * - * @param StringUtil $util + * @param mixed $value + * @param string|null $type type of resource this should be. @see https://www.php.net/manual/en/function.get-resource-type.php + * @param string $message + * + * @throws InvalidArgumentException */ - public function __construct(\Prophecy\Util\StringUtil $util = null) + public static function resource($value, $type = null, $message = '') { - $this->util = $util ?: new \Prophecy\Util\StringUtil(); - $this->unexpectedCalls = new \SplObjectStorage(); + if (!\is_resource($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a resource. Got: %s', static::typeToString($value))); + } + if ($type && $type !== \get_resource_type($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a resource of type %2$s. Got: %s', static::typeToString($value), $type)); + } } /** - * Makes and records specific method call for object prophecy. - * - * @param ObjectProphecy $prophecy - * @param string $methodName - * @param array $arguments + * @psalm-pure + * @psalm-assert callable $value * - * @return mixed Returns null if no promise for prophecy found or promise return value. + * @param mixed $value + * @param string $message * - * @throws \Prophecy\Exception\Call\UnexpectedCallException If no appropriate method prophecy found + * @throws InvalidArgumentException */ - public function makeCall(\Prophecy\Prophecy\ObjectProphecy $prophecy, $methodName, array $arguments) + public static function isCallable($value, $message = '') { - // For efficiency exclude 'args' from the generated backtrace - // Limit backtrace to last 3 calls as we don't use the rest - $backtrace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 3); - $file = $line = null; - if (isset($backtrace[2]) && isset($backtrace[2]['file'])) { - $file = $backtrace[2]['file']; - $line = $backtrace[2]['line']; - } - // If no method prophecies defined, then it's a dummy, so we'll just return null - if ('__destruct' === \strtolower($methodName) || 0 == \count($prophecy->getMethodProphecies())) { - $this->recordedCalls[] = new \Prophecy\Call\Call($methodName, $arguments, null, null, $file, $line); - return null; - } - // There are method prophecies, so it's a fake/stub. Searching prophecy for this call - $matches = $this->findMethodProphecies($prophecy, $methodName, $arguments); - // If fake/stub doesn't have method prophecy for this call - throw exception - if (!\count($matches)) { - $this->unexpectedCalls->attach(new \Prophecy\Call\Call($methodName, $arguments, null, null, $file, $line), $prophecy); - $this->recordedCalls[] = new \Prophecy\Call\Call($methodName, $arguments, null, null, $file, $line); - return null; - } - // Sort matches by their score value - @\usort($matches, function ($match1, $match2) { - return $match2[0] - $match1[0]; - }); - $score = $matches[0][0]; - // If Highest rated method prophecy has a promise - execute it or return null instead - $methodProphecy = $matches[0][1]; - $returnValue = null; - $exception = null; - if ($promise = $methodProphecy->getPromise()) { - try { - $returnValue = $promise->execute($arguments, $prophecy, $methodProphecy); - } catch (\Exception $e) { - $exception = $e; - } - } - if ($methodProphecy->hasReturnVoid() && $returnValue !== null) { - throw new \Prophecy\Exception\Prophecy\MethodProphecyException("The method \"{$methodName}\" has a void return type, but the promise returned a value", $methodProphecy); - } - $this->recordedCalls[] = $call = new \Prophecy\Call\Call($methodName, $arguments, $returnValue, $exception, $file, $line); - $call->addScore($methodProphecy->getArgumentsWildcard(), $score); - if (null !== $exception) { - throw $exception; + if (!\is_callable($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a callable. Got: %s', static::typeToString($value))); } - return $returnValue; } /** - * Searches for calls by method name & arguments wildcard. + * @psalm-pure + * @psalm-assert array $value * - * @param string $methodName - * @param ArgumentsWildcard $wildcard + * @param mixed $value + * @param string $message * - * @return Call[] + * @throws InvalidArgumentException */ - public function findCalls($methodName, \Prophecy\Argument\ArgumentsWildcard $wildcard) + public static function isArray($value, $message = '') { - $methodName = \strtolower($methodName); - return \array_values(\array_filter($this->recordedCalls, function (\Prophecy\Call\Call $call) use($methodName, $wildcard) { - return $methodName === \strtolower($call->getMethodName()) && 0 < $call->getScore($wildcard); - })); + if (!\is_array($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected an array. Got: %s', static::typeToString($value))); + } } /** - * @throws UnexpectedCallException + * @psalm-pure + * @psalm-assert iterable $value + * + * @deprecated use "isIterable" or "isInstanceOf" instead + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException */ - public function checkUnexpectedCalls() + public static function isTraversable($value, $message = '') { - /** @var Call $call */ - foreach ($this->unexpectedCalls as $call) { - $prophecy = $this->unexpectedCalls[$call]; - // If fake/stub doesn't have method prophecy for this call - throw exception - if (!\count($this->findMethodProphecies($prophecy, $call->getMethodName(), $call->getArguments()))) { - throw $this->createUnexpectedCallException($prophecy, $call->getMethodName(), $call->getArguments()); - } + @\trigger_error(\sprintf('The "%s" assertion is deprecated. You should stop using it, as it will soon be removed in 2.0 version. Use "isIterable" or "isInstanceOf" instead.', __METHOD__), \E_USER_DEPRECATED); + if (!\is_array($value) && !$value instanceof Traversable) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a traversable. Got: %s', static::typeToString($value))); } } - private function createUnexpectedCallException(\Prophecy\Prophecy\ObjectProphecy $prophecy, $methodName, array $arguments) + /** + * @psalm-pure + * @psalm-assert array|ArrayAccess $value + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + */ + public static function isArrayAccessible($value, $message = '') { - $classname = \get_class($prophecy->reveal()); - $indentationLength = 8; - // looks good - $argstring = \implode(",\n", $this->indentArguments(\array_map(array($this->util, 'stringify'), $arguments), $indentationLength)); - $expected = array(); - foreach (\array_merge(...\array_values($prophecy->getMethodProphecies())) as $methodProphecy) { - $expected[] = \sprintf(" - %s(\n" . "%s\n" . " )", $methodProphecy->getMethodName(), \implode(",\n", $this->indentArguments(\array_map('strval', $methodProphecy->getArgumentsWildcard()->getTokens()), $indentationLength))); + if (!\is_array($value) && !$value instanceof ArrayAccess) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected an array accessible. Got: %s', static::typeToString($value))); } - return new \Prophecy\Exception\Call\UnexpectedCallException(\sprintf("Unexpected method call on %s:\n" . " - %s(\n" . "%s\n" . " )\n" . "expected calls were:\n" . "%s", $classname, $methodName, $argstring, \implode("\n", $expected)), $prophecy, $methodName, $arguments); - } - private function indentArguments(array $arguments, $indentationLength) - { - return \preg_replace_callback('/^/m', function () use($indentationLength) { - return \str_repeat(' ', $indentationLength); - }, $arguments); } /** - * @param ObjectProphecy $prophecy - * @param string $methodName - * @param array $arguments + * @psalm-pure + * @psalm-assert countable $value * - * @return array + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException */ - private function findMethodProphecies(\Prophecy\Prophecy\ObjectProphecy $prophecy, $methodName, array $arguments) + public static function isCountable($value, $message = '') { - $matches = array(); - foreach ($prophecy->getMethodProphecies($methodName) as $methodProphecy) { - if (0 < ($score = $methodProphecy->getArgumentsWildcard()->scoreArguments($arguments))) { - $matches[] = array($score, $methodProphecy); - } + if (!\is_array($value) && !$value instanceof Countable && !$value instanceof ResourceBundle && !$value instanceof SimpleXMLElement) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a countable. Got: %s', static::typeToString($value))); } - return $matches; } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Call; - -use Exception; -use Prophecy\Argument\ArgumentsWildcard; -/** - * Call object. - * - * @author Konstantin Kudryashov - */ -class Call -{ - private $methodName; - private $arguments; - private $returnValue; - private $exception; - private $file; - private $line; - private $scores; /** - * Initializes call. + * @psalm-pure + * @psalm-assert iterable $value * - * @param string $methodName - * @param array $arguments - * @param mixed $returnValue - * @param Exception $exception - * @param null|string $file - * @param null|int $line + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException */ - public function __construct($methodName, array $arguments, $returnValue, \Exception $exception = null, $file, $line) + public static function isIterable($value, $message = '') { - $this->methodName = $methodName; - $this->arguments = $arguments; - $this->returnValue = $returnValue; - $this->exception = $exception; - $this->scores = new \SplObjectStorage(); - if ($file) { - $this->file = $file; - $this->line = \intval($line); + if (!\is_array($value) && !$value instanceof Traversable) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected an iterable. Got: %s', static::typeToString($value))); } } /** - * Returns called method name. + * @psalm-pure + * @psalm-template ExpectedType of object + * @psalm-param class-string $class + * @psalm-assert ExpectedType $value * - * @return string + * @param mixed $value + * @param string|object $class + * @param string $message + * + * @throws InvalidArgumentException */ - public function getMethodName() + public static function isInstanceOf($value, $class, $message = '') { - return $this->methodName; + if (!$value instanceof $class) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected an instance of %2$s. Got: %s', static::typeToString($value), $class)); + } } /** - * Returns called method arguments. + * @psalm-pure + * @psalm-template ExpectedType of object + * @psalm-param class-string $class + * @psalm-assert !ExpectedType $value * - * @return array + * @param mixed $value + * @param string|object $class + * @param string $message + * + * @throws InvalidArgumentException */ - public function getArguments() + public static function notInstanceOf($value, $class, $message = '') { - return $this->arguments; + if ($value instanceof $class) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected an instance other than %2$s. Got: %s', static::typeToString($value), $class)); + } } /** - * Returns called method return value. + * @psalm-pure + * @psalm-param array $classes * - * @return null|mixed + * @param mixed $value + * @param array $classes + * @param string $message + * + * @throws InvalidArgumentException */ - public function getReturnValue() + public static function isInstanceOfAny($value, array $classes, $message = '') { - return $this->returnValue; + foreach ($classes as $class) { + if ($value instanceof $class) { + return; + } + } + static::reportInvalidArgument(\sprintf($message ?: 'Expected an instance of any of %2$s. Got: %s', static::typeToString($value), \implode(', ', \array_map(array('static', 'valueToString'), $classes)))); } /** - * Returns exception that call thrown. + * @psalm-pure + * @psalm-template ExpectedType of object + * @psalm-param class-string $class + * @psalm-assert ExpectedType|class-string $value * - * @return null|Exception + * @param object|string $value + * @param string $class + * @param string $message + * + * @throws InvalidArgumentException */ - public function getException() + public static function isAOf($value, $class, $message = '') { - return $this->exception; + static::string($class, 'Expected class as a string. Got: %s'); + if (!\is_a($value, $class, \is_string($value))) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected an instance of this class or to this class among his parents %2$s. Got: %s', static::typeToString($value), $class)); + } } /** - * Returns callee filename. + * @psalm-pure + * @psalm-template UnexpectedType of object + * @psalm-param class-string $class + * @psalm-assert !UnexpectedType $value + * @psalm-assert !class-string $value * - * @return string + * @param object|string $value + * @param string $class + * @param string $message + * + * @throws InvalidArgumentException */ - public function getFile() + public static function isNotA($value, $class, $message = '') { - return $this->file; + static::string($class, 'Expected class as a string. Got: %s'); + if (\is_a($value, $class, \is_string($value))) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected an instance of this class or to this class among his parents other than %2$s. Got: %s', static::typeToString($value), $class)); + } } /** - * Returns callee line number. + * @psalm-pure + * @psalm-param array $classes * - * @return int + * @param object|string $value + * @param string[] $classes + * @param string $message + * + * @throws InvalidArgumentException */ - public function getLine() + public static function isAnyOf($value, array $classes, $message = '') { - return $this->line; + foreach ($classes as $class) { + static::string($class, 'Expected class as a string. Got: %s'); + if (\is_a($value, $class, \is_string($value))) { + return; + } + } + static::reportInvalidArgument(\sprintf($message ?: 'Expected an any of instance of this class or to this class among his parents other than %2$s. Got: %s', static::typeToString($value), \implode(', ', \array_map(array('static', 'valueToString'), $classes)))); } /** - * Returns short notation for callee place. + * @psalm-pure + * @psalm-assert empty $value * - * @return string + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException */ - public function getCallPlace() + public static function isEmpty($value, $message = '') { - if (null === $this->file) { - return 'unknown'; + if (!empty($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected an empty value. Got: %s', static::valueToString($value))); } - return \sprintf('%s:%d', $this->file, $this->line); } /** - * Adds the wildcard match score for the provided wildcard. + * @psalm-pure + * @psalm-assert !empty $value * - * @param ArgumentsWildcard $wildcard - * @param false|int $score + * @param mixed $value + * @param string $message * - * @return $this + * @throws InvalidArgumentException */ - public function addScore(\Prophecy\Argument\ArgumentsWildcard $wildcard, $score) + public static function notEmpty($value, $message = '') { - $this->scores[$wildcard] = $score; - return $this; + if (empty($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a non-empty value. Got: %s', static::valueToString($value))); + } } /** - * Returns wildcard match score for the provided wildcard. The score is - * calculated if not already done. + * @psalm-pure + * @psalm-assert null $value * - * @param ArgumentsWildcard $wildcard + * @param mixed $value + * @param string $message * - * @return false|int False OR integer score (higher - better) + * @throws InvalidArgumentException */ - public function getScore(\Prophecy\Argument\ArgumentsWildcard $wildcard) + public static function null($value, $message = '') { - if (isset($this->scores[$wildcard])) { - return $this->scores[$wildcard]; + if (null !== $value) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected null. Got: %s', static::valueToString($value))); } - return $this->scores[$wildcard] = $wildcard->scoreArguments($this->getArguments()); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Argument\Token; - -/** - * Any single value token. - * - * @author Konstantin Kudryashov - */ -class AnyValueToken implements \Prophecy\Argument\Token\TokenInterface -{ /** - * Always scores 3 for any argument. + * @psalm-pure + * @psalm-assert !null $value * - * @param $argument + * @param mixed $value + * @param string $message * - * @return int + * @throws InvalidArgumentException */ - public function scoreArgument($argument) + public static function notNull($value, $message = '') { - return 3; + if (null === $value) { + static::reportInvalidArgument($message ?: 'Expected a value other than null.'); + } } /** - * Returns false. + * @psalm-pure + * @psalm-assert true $value * - * @return bool + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException */ - public function isLast() + public static function true($value, $message = '') { - return \false; + if (\true !== $value) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to be true. Got: %s', static::valueToString($value))); + } } /** - * Returns string representation for token. + * @psalm-pure + * @psalm-assert false $value * - * @return string + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException */ - public function __toString() + public static function false($value, $message = '') { - return '*'; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Argument\Token; - -/** - * Logical NOT token. - * - * @author Boris Mikhaylov - */ -class LogicalNotToken implements \Prophecy\Argument\Token\TokenInterface -{ - /** @var \Prophecy\Argument\Token\TokenInterface */ - private $token; - /** - * @param mixed $value exact value or token - */ - public function __construct($value) - { - $this->token = $value instanceof \Prophecy\Argument\Token\TokenInterface ? $value : new \Prophecy\Argument\Token\ExactValueToken($value); + if (\false !== $value) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to be false. Got: %s', static::valueToString($value))); + } } /** - * Scores 4 when preset token does not match the argument. + * @psalm-pure + * @psalm-assert !false $value * - * @param $argument + * @param mixed $value + * @param string $message * - * @return bool|int + * @throws InvalidArgumentException */ - public function scoreArgument($argument) + public static function notFalse($value, $message = '') { - return \false === $this->token->scoreArgument($argument) ? 4 : \false; + if (\false === $value) { + static::reportInvalidArgument($message ?: 'Expected a value other than false.'); + } } /** - * Returns true if preset token is last. + * @param mixed $value + * @param string $message * - * @return bool|int + * @throws InvalidArgumentException */ - public function isLast() + public static function ip($value, $message = '') { - return $this->token->isLast(); + if (\false === \filter_var($value, \FILTER_VALIDATE_IP)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to be an IP. Got: %s', static::valueToString($value))); + } } /** - * Returns originating token. + * @param mixed $value + * @param string $message * - * @return TokenInterface + * @throws InvalidArgumentException */ - public function getOriginatingToken() + public static function ipv4($value, $message = '') { - return $this->token; + if (\false === \filter_var($value, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to be an IPv4. Got: %s', static::valueToString($value))); + } } /** - * Returns string representation for token. + * @param mixed $value + * @param string $message * - * @return string + * @throws InvalidArgumentException */ - public function __toString() + public static function ipv6($value, $message = '') { - return \sprintf('not(%s)', $this->token); + if (\false === \filter_var($value, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to be an IPv6. Got: %s', static::valueToString($value))); + } } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Argument\Token; - -/** - * Check if values is in array - * - * @author Vinícius Alonso - */ -class InArrayToken implements \Prophecy\Argument\Token\TokenInterface -{ - private $token = array(); - private $strict; /** - * @param array $arguments tokens - * @param bool $strict + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException */ - public function __construct(array $arguments, $strict = \true) + public static function email($value, $message = '') { - $this->token = $arguments; - $this->strict = $strict; + if (\false === \filter_var($value, \FILTER_VALIDATE_EMAIL)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to be a valid e-mail address. Got: %s', static::valueToString($value))); + } } /** - * Return scores 8 score if argument is in array. + * Does non strict comparisons on the items, so ['3', 3] will not pass the assertion. * - * @param $argument + * @param array $values + * @param string $message * - * @return bool|int + * @throws InvalidArgumentException */ - public function scoreArgument($argument) + public static function uniqueValues(array $values, $message = '') { - if (\count($this->token) === 0) { - return \false; - } - if (\in_array($argument, $this->token, $this->strict)) { - return 8; + $allValues = \count($values); + $uniqueValues = \count(\array_unique($values)); + if ($allValues !== $uniqueValues) { + $difference = $allValues - $uniqueValues; + static::reportInvalidArgument(\sprintf($message ?: 'Expected an array of unique values, but %s of them %s duplicated', $difference, 1 === $difference ? 'is' : 'are')); } - return \false; } /** - * Returns false. + * @param mixed $value + * @param mixed $expect + * @param string $message * - * @return boolean + * @throws InvalidArgumentException */ - public function isLast() + public static function eq($value, $expect, $message = '') { - return \false; + if ($expect != $value) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value equal to %2$s. Got: %s', static::valueToString($value), static::valueToString($expect))); + } } /** - * Returns string representation for token. + * @param mixed $value + * @param mixed $expect + * @param string $message * - * @return string + * @throws InvalidArgumentException */ - public function __toString() + public static function notEq($value, $expect, $message = '') { - $arrayAsString = \implode(', ', $this->token); - return "[{$arrayAsString}]"; + if ($expect == $value) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a different value than %s.', static::valueToString($expect))); + } } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Argument\Token; - -/** - * Array every entry token. - * - * @author Adrien Brault - */ -class ArrayEveryEntryToken implements \Prophecy\Argument\Token\TokenInterface -{ - /** - * @var TokenInterface - */ - private $value; /** - * @param mixed $value exact value or token + * @psalm-pure + * + * @param mixed $value + * @param mixed $expect + * @param string $message + * + * @throws InvalidArgumentException */ - public function __construct($value) + public static function same($value, $expect, $message = '') { - if (!$value instanceof \Prophecy\Argument\Token\TokenInterface) { - $value = new \Prophecy\Argument\Token\ExactValueToken($value); + if ($expect !== $value) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value identical to %2$s. Got: %s', static::valueToString($value), static::valueToString($expect))); } - $this->value = $value; } /** - * {@inheritdoc} + * @psalm-pure + * + * @param mixed $value + * @param mixed $expect + * @param string $message + * + * @throws InvalidArgumentException */ - public function scoreArgument($argument) + public static function notSame($value, $expect, $message = '') { - if (!$argument instanceof \Traversable && !\is_array($argument)) { - return \false; - } - $scores = array(); - foreach ($argument as $key => $argumentEntry) { - $scores[] = $this->value->scoreArgument($argumentEntry); - } - if (empty($scores) || \in_array(\false, $scores, \true)) { - return \false; + if ($expect === $value) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value not identical to %s.', static::valueToString($expect))); } - return \array_sum($scores) / \count($scores); } /** - * {@inheritdoc} + * @psalm-pure + * + * @param mixed $value + * @param mixed $limit + * @param string $message + * + * @throws InvalidArgumentException */ - public function isLast() + public static function greaterThan($value, $limit, $message = '') { - return \false; + if ($value <= $limit) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value greater than %2$s. Got: %s', static::valueToString($value), static::valueToString($limit))); + } } /** - * {@inheritdoc} + * @psalm-pure + * + * @param mixed $value + * @param mixed $limit + * @param string $message + * + * @throws InvalidArgumentException */ - public function __toString() + public static function greaterThanEq($value, $limit, $message = '') { - return \sprintf('[%s, ..., %s]', $this->value, $this->value); + if ($value < $limit) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value greater than or equal to %2$s. Got: %s', static::valueToString($value), static::valueToString($limit))); + } } /** - * @return TokenInterface + * @psalm-pure + * + * @param mixed $value + * @param mixed $limit + * @param string $message + * + * @throws InvalidArgumentException */ - public function getValue() + public static function lessThan($value, $limit, $message = '') { - return $this->value; + if ($value >= $limit) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value less than %2$s. Got: %s', static::valueToString($value), static::valueToString($limit))); + } } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Argument\Token; - -/** - * Check if values is not in array - * - * @author Vinícius Alonso - */ -class NotInArrayToken implements \Prophecy\Argument\Token\TokenInterface -{ - private $token = array(); - private $strict; /** - * @param array $arguments tokens - * @param bool $strict + * @psalm-pure + * + * @param mixed $value + * @param mixed $limit + * @param string $message + * + * @throws InvalidArgumentException */ - public function __construct(array $arguments, $strict = \true) + public static function lessThanEq($value, $limit, $message = '') { - $this->token = $arguments; - $this->strict = $strict; + if ($value > $limit) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value less than or equal to %2$s. Got: %s', static::valueToString($value), static::valueToString($limit))); + } } /** - * Return scores 8 score if argument is in array. + * Inclusive range, so Assert::(3, 3, 5) passes. * - * @param $argument + * @psalm-pure * - * @return bool|int + * @param mixed $value + * @param mixed $min + * @param mixed $max + * @param string $message + * + * @throws InvalidArgumentException */ - public function scoreArgument($argument) + public static function range($value, $min, $max, $message = '') { - if (\count($this->token) === 0) { - return \false; - } - if (!\in_array($argument, $this->token, $this->strict)) { - return 8; + if ($value < $min || $value > $max) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value between %2$s and %3$s. Got: %s', static::valueToString($value), static::valueToString($min), static::valueToString($max))); } - return \false; } /** - * Returns false. + * A more human-readable alias of Assert::inArray(). * - * @return boolean + * @psalm-pure + * + * @param mixed $value + * @param array $values + * @param string $message + * + * @throws InvalidArgumentException */ - public function isLast() + public static function oneOf($value, array $values, $message = '') { - return \false; + static::inArray($value, $values, $message); } /** - * Returns string representation for token. + * Does strict comparison, so Assert::inArray(3, ['3']) does not pass the assertion. * - * @return string + * @psalm-pure + * + * @param mixed $value + * @param array $values + * @param string $message + * + * @throws InvalidArgumentException */ - public function __toString() + public static function inArray($value, array $values, $message = '') { - $arrayAsString = \implode(', ', $this->token); - return "[{$arrayAsString}]"; + if (!\in_array($value, $values, \true)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected one of: %2$s. Got: %s', static::valueToString($value), \implode(', ', \array_map(array('static', 'valueToString'), $values)))); + } } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Argument\Token; - -/** - * Logical AND token. - * - * @author Boris Mikhaylov - */ -class LogicalAndToken implements \Prophecy\Argument\Token\TokenInterface -{ - private $tokens = array(); /** - * @param array $arguments exact values or tokens + * @psalm-pure + * + * @param string $value + * @param string $subString + * @param string $message + * + * @throws InvalidArgumentException */ - public function __construct(array $arguments) + public static function contains($value, $subString, $message = '') { - foreach ($arguments as $argument) { - if (!$argument instanceof \Prophecy\Argument\Token\TokenInterface) { - $argument = new \Prophecy\Argument\Token\ExactValueToken($argument); - } - $this->tokens[] = $argument; + if (\false === \strpos($value, $subString)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain %2$s. Got: %s', static::valueToString($value), static::valueToString($subString))); } } /** - * Scores maximum score from scores returned by tokens for this argument if all of them score. + * @psalm-pure * - * @param $argument + * @param string $value + * @param string $subString + * @param string $message * - * @return bool|int + * @throws InvalidArgumentException */ - public function scoreArgument($argument) + public static function notContains($value, $subString, $message = '') { - if (0 === \count($this->tokens)) { - return \false; - } - $maxScore = 0; - foreach ($this->tokens as $token) { - $score = $token->scoreArgument($argument); - if (\false === $score) { - return \false; - } - $maxScore = \max($score, $maxScore); + if (\false !== \strpos($value, $subString)) { + static::reportInvalidArgument(\sprintf($message ?: '%2$s was not expected to be contained in a value. Got: %s', static::valueToString($value), static::valueToString($subString))); } - return $maxScore; } /** - * Returns false. + * @psalm-pure * - * @return boolean + * @param string $value + * @param string $message + * + * @throws InvalidArgumentException */ - public function isLast() + public static function notWhitespaceOnly($value, $message = '') { - return \false; + if (\preg_match('/^\\s*$/', $value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a non-whitespace string. Got: %s', static::valueToString($value))); + } } /** - * Returns string representation for token. + * @psalm-pure * - * @return string + * @param string $value + * @param string $prefix + * @param string $message + * + * @throws InvalidArgumentException */ - public function __toString() - { - return \sprintf('bool(%s)', \implode(' AND ', $this->tokens)); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Argument\Token; - -/** - * Approximate value token - * - * @author Daniel Leech - */ -class ApproximateValueToken implements \Prophecy\Argument\Token\TokenInterface -{ - private $value; - private $precision; - public function __construct($value, $precision = 0) + public static function startsWith($value, $prefix, $message = '') { - $this->value = $value; - $this->precision = $precision; + if (0 !== \strpos($value, $prefix)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to start with %2$s. Got: %s', static::valueToString($value), static::valueToString($prefix))); + } } /** - * {@inheritdoc} + * @psalm-pure + * + * @param string $value + * @param string $prefix + * @param string $message + * + * @throws InvalidArgumentException */ - public function scoreArgument($argument) + public static function notStartsWith($value, $prefix, $message = '') { - return \round((float) $argument, $this->precision) === \round($this->value, $this->precision) ? 10 : \false; + if (0 === \strpos($value, $prefix)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value not to start with %2$s. Got: %s', static::valueToString($value), static::valueToString($prefix))); + } } /** - * {@inheritdoc} + * @psalm-pure + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException */ - public function isLast() + public static function startsWithLetter($value, $message = '') { - return \false; + static::string($value); + $valid = isset($value[0]); + if ($valid) { + $locale = \setlocale(\LC_CTYPE, 0); + \setlocale(\LC_CTYPE, 'C'); + $valid = \ctype_alpha($value[0]); + \setlocale(\LC_CTYPE, $locale); + } + if (!$valid) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to start with a letter. Got: %s', static::valueToString($value))); + } } /** - * Returns string representation for token. + * @psalm-pure * - * @return string + * @param string $value + * @param string $suffix + * @param string $message + * + * @throws InvalidArgumentException */ - public function __toString() + public static function endsWith($value, $suffix, $message = '') { - return \sprintf('≅%s', \round($this->value, $this->precision)); + if ($suffix !== \substr($value, -\strlen($suffix))) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to end with %2$s. Got: %s', static::valueToString($value), static::valueToString($suffix))); + } } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Argument\Token; - -/** - * Any values token. - * - * @author Konstantin Kudryashov - */ -class AnyValuesToken implements \Prophecy\Argument\Token\TokenInterface -{ /** - * Always scores 2 for any argument. + * @psalm-pure * - * @param $argument + * @param string $value + * @param string $suffix + * @param string $message * - * @return int + * @throws InvalidArgumentException */ - public function scoreArgument($argument) + public static function notEndsWith($value, $suffix, $message = '') { - return 2; + if ($suffix === \substr($value, -\strlen($suffix))) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value not to end with %2$s. Got: %s', static::valueToString($value), static::valueToString($suffix))); + } } /** - * Returns true to stop wildcard from processing other tokens. + * @psalm-pure * - * @return bool + * @param string $value + * @param string $pattern + * @param string $message + * + * @throws InvalidArgumentException */ - public function isLast() + public static function regex($value, $pattern, $message = '') { - return \true; + if (!\preg_match($pattern, $value)) { + static::reportInvalidArgument(\sprintf($message ?: 'The value %s does not match the expected pattern.', static::valueToString($value))); + } } /** - * Returns string representation for token. + * @psalm-pure * - * @return string + * @param string $value + * @param string $pattern + * @param string $message + * + * @throws InvalidArgumentException */ - public function __toString() + public static function notRegex($value, $pattern, $message = '') { - return '* [, ...]'; + if (\preg_match($pattern, $value, $matches, \PREG_OFFSET_CAPTURE)) { + static::reportInvalidArgument(\sprintf($message ?: 'The value %s matches the pattern %s (at offset %d).', static::valueToString($value), static::valueToString($pattern), $matches[0][1])); + } } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Argument\Token; - -/** - * Argument token interface. - * - * @author Konstantin Kudryashov - */ -interface TokenInterface -{ /** - * Calculates token match score for provided argument. + * @psalm-pure * - * @param $argument + * @param mixed $value + * @param string $message * - * @return bool|int + * @throws InvalidArgumentException */ - public function scoreArgument($argument); + public static function unicodeLetters($value, $message = '') + { + static::string($value); + if (!\preg_match('/^\\p{L}+$/u', $value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain only Unicode letters. Got: %s', static::valueToString($value))); + } + } /** - * Returns true if this token prevents check of other tokens (is last one). + * @psalm-pure * - * @return bool|int - */ - public function isLast(); - /** - * Returns string representation for token. + * @param mixed $value + * @param string $message * - * @return string + * @throws InvalidArgumentException */ - public function __toString(); -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Argument\Token; - -/** - * String contains token. - * - * @author Peter Mitchell - */ -class StringContainsToken implements \Prophecy\Argument\Token\TokenInterface -{ - private $value; + public static function alpha($value, $message = '') + { + static::string($value); + $locale = \setlocale(\LC_CTYPE, 0); + \setlocale(\LC_CTYPE, 'C'); + $valid = !\ctype_alpha($value); + \setlocale(\LC_CTYPE, $locale); + if ($valid) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain only letters. Got: %s', static::valueToString($value))); + } + } /** - * Initializes token. + * @psalm-pure * * @param string $value + * @param string $message + * + * @throws InvalidArgumentException */ - public function __construct($value) - { - $this->value = $value; - } - public function scoreArgument($argument) + public static function digits($value, $message = '') { - return \is_string($argument) && \strpos($argument, $this->value) !== \false ? 6 : \false; + $locale = \setlocale(\LC_CTYPE, 0); + \setlocale(\LC_CTYPE, 'C'); + $valid = !\ctype_digit($value); + \setlocale(\LC_CTYPE, $locale); + if ($valid) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain digits only. Got: %s', static::valueToString($value))); + } } /** - * Returns preset value against which token checks arguments. + * @psalm-pure * - * @return mixed + * @param string $value + * @param string $message + * + * @throws InvalidArgumentException */ - public function getValue() + public static function alnum($value, $message = '') { - return $this->value; + $locale = \setlocale(\LC_CTYPE, 0); + \setlocale(\LC_CTYPE, 'C'); + $valid = !\ctype_alnum($value); + \setlocale(\LC_CTYPE, $locale); + if ($valid) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain letters and digits only. Got: %s', static::valueToString($value))); + } } /** - * Returns false. + * @psalm-pure + * @psalm-assert lowercase-string $value * - * @return bool + * @param string $value + * @param string $message + * + * @throws InvalidArgumentException */ - public function isLast() + public static function lower($value, $message = '') { - return \false; + $locale = \setlocale(\LC_CTYPE, 0); + \setlocale(\LC_CTYPE, 'C'); + $valid = !\ctype_lower($value); + \setlocale(\LC_CTYPE, $locale); + if ($valid) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain lowercase characters only. Got: %s', static::valueToString($value))); + } } /** - * Returns string representation for token. + * @psalm-pure + * @psalm-assert !lowercase-string $value * - * @return string + * @param string $value + * @param string $message + * + * @throws InvalidArgumentException */ - public function __toString() + public static function upper($value, $message = '') { - return \sprintf('contains("%s")', $this->value); + $locale = \setlocale(\LC_CTYPE, 0); + \setlocale(\LC_CTYPE, 'C'); + $valid = !\ctype_upper($value); + \setlocale(\LC_CTYPE, $locale); + if ($valid) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain uppercase characters only. Got: %s', static::valueToString($value))); + } } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Argument\Token; - -use Prophecy\Util\StringUtil; -/** - * Identical value token. - * - * @author Florian Voutzinos - */ -class IdenticalValueToken implements \Prophecy\Argument\Token\TokenInterface -{ - private $value; - private $string; - private $util; /** - * Initializes token. + * @psalm-pure * - * @param mixed $value - * @param StringUtil $util + * @param string $value + * @param int $length + * @param string $message + * + * @throws InvalidArgumentException */ - public function __construct($value, \Prophecy\Util\StringUtil $util = null) + public static function length($value, $length, $message = '') { - $this->value = $value; - $this->util = $util ?: new \Prophecy\Util\StringUtil(); + if ($length !== static::strlen($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain %2$s characters. Got: %s', static::valueToString($value), $length)); + } } /** - * Scores 11 if argument matches preset value. + * Inclusive min. * - * @param $argument + * @psalm-pure * - * @return bool|int + * @param string $value + * @param int|float $min + * @param string $message + * + * @throws InvalidArgumentException */ - public function scoreArgument($argument) + public static function minLength($value, $min, $message = '') { - return $argument === $this->value ? 11 : \false; + if (static::strlen($value) < $min) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain at least %2$s characters. Got: %s', static::valueToString($value), $min)); + } } /** - * Returns false. + * Inclusive max. * - * @return bool + * @psalm-pure + * + * @param string $value + * @param int|float $max + * @param string $message + * + * @throws InvalidArgumentException */ - public function isLast() + public static function maxLength($value, $max, $message = '') { - return \false; + if (static::strlen($value) > $max) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain at most %2$s characters. Got: %s', static::valueToString($value), $max)); + } } /** - * Returns string representation for token. + * Inclusive , so Assert::lengthBetween('asd', 3, 5); passes the assertion. * - * @return string + * @psalm-pure + * + * @param string $value + * @param int|float $min + * @param int|float $max + * @param string $message + * + * @throws InvalidArgumentException */ - public function __toString() + public static function lengthBetween($value, $min, $max, $message = '') { - if (null === $this->string) { - $this->string = \sprintf('identical(%s)', $this->util->stringify($this->value)); + $length = static::strlen($value); + if ($length < $min || $length > $max) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s', static::valueToString($value), $min, $max)); } - return $this->string; } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Argument\Token; - -use Prophecy\Exception\InvalidArgumentException; -/** - * Value type token. - * - * @author Konstantin Kudryashov - */ -class TypeToken implements \Prophecy\Argument\Token\TokenInterface -{ - private $type; /** - * @param string $type + * Will also pass if $value is a directory, use Assert::file() instead if you need to be sure it is a file. + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException */ - public function __construct($type) + public static function fileExists($value, $message = '') { - $checker = "is_{$type}"; - if (!\function_exists($checker) && !\interface_exists($type) && !\class_exists($type)) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf('Type or class name expected as an argument to TypeToken, but got %s.', $type)); + static::string($value); + if (!\file_exists($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'The file %s does not exist.', static::valueToString($value))); } - $this->type = $type; } /** - * Scores 5 if argument has the same type this token was constructed with. - * - * @param $argument + * @param mixed $value + * @param string $message * - * @return bool|int + * @throws InvalidArgumentException */ - public function scoreArgument($argument) + public static function file($value, $message = '') { - $checker = "is_{$this->type}"; - if (\function_exists($checker)) { - return \call_user_func($checker, $argument) ? 5 : \false; + static::fileExists($value, $message); + if (!\is_file($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'The path %s is not a file.', static::valueToString($value))); } - return $argument instanceof $this->type ? 5 : \false; } /** - * Returns false. + * @param mixed $value + * @param string $message * - * @return bool + * @throws InvalidArgumentException */ - public function isLast() + public static function directory($value, $message = '') { - return \false; + static::fileExists($value, $message); + if (!\is_dir($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'The path %s is no directory.', static::valueToString($value))); + } } /** - * Returns string representation for token. + * @param string $value + * @param string $message * - * @return string + * @throws InvalidArgumentException */ - public function __toString() + public static function readable($value, $message = '') { - return \sprintf('type(%s)', $this->type); + if (!\is_readable($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'The path %s is not readable.', static::valueToString($value))); + } } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Argument\Token; - -use Prophecy\Exception\InvalidArgumentException; -/** - * Callback-verified token. - * - * @author Konstantin Kudryashov - */ -class CallbackToken implements \Prophecy\Argument\Token\TokenInterface -{ - private $callback; /** - * Initializes token. - * - * @param callable $callback + * @param string $value + * @param string $message * - * @throws \Prophecy\Exception\InvalidArgumentException + * @throws InvalidArgumentException */ - public function __construct($callback) + public static function writable($value, $message = '') { - if (!\is_callable($callback)) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf('Callable expected as an argument to CallbackToken, but got %s.', \gettype($callback))); + if (!\is_writable($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'The path %s is not writable.', static::valueToString($value))); } - $this->callback = $callback; } /** - * Scores 7 if callback returns true, false otherwise. + * @psalm-assert class-string $value * - * @param $argument + * @param mixed $value + * @param string $message * - * @return bool|int + * @throws InvalidArgumentException */ - public function scoreArgument($argument) + public static function classExists($value, $message = '') { - return \call_user_func($this->callback, $argument) ? 7 : \false; + if (!\class_exists($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected an existing class name. Got: %s', static::valueToString($value))); + } } /** - * Returns false. + * @psalm-pure + * @psalm-template ExpectedType of object + * @psalm-param class-string $class + * @psalm-assert class-string|ExpectedType $value * - * @return bool + * @param mixed $value + * @param string|object $class + * @param string $message + * + * @throws InvalidArgumentException */ - public function isLast() + public static function subclassOf($value, $class, $message = '') { - return \false; + if (!\is_subclass_of($value, $class)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected a sub-class of %2$s. Got: %s', static::valueToString($value), static::valueToString($class))); + } } /** - * Returns string representation for token. + * @psalm-assert class-string $value * - * @return string + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException */ - public function __toString() + public static function interfaceExists($value, $message = '') { - return 'callback()'; + if (!\interface_exists($value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected an existing interface name. got %s', static::valueToString($value))); + } } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Argument\Token; - -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -use Prophecy\Comparator\Factory as ComparatorFactory; -use Prophecy\Util\StringUtil; -/** - * Object state-checker token. - * - * @author Konstantin Kudryashov - */ -class ObjectStateToken implements \Prophecy\Argument\Token\TokenInterface -{ - private $name; - private $value; - private $util; - private $comparatorFactory; /** - * Initializes token. + * @psalm-pure + * @psalm-template ExpectedType of object + * @psalm-param class-string $interface + * @psalm-assert class-string $value * - * @param string $methodName - * @param mixed $value Expected return value - * @param null|StringUtil $util - * @param ComparatorFactory $comparatorFactory + * @param mixed $value + * @param mixed $interface + * @param string $message + * + * @throws InvalidArgumentException */ - public function __construct($methodName, $value, \Prophecy\Util\StringUtil $util = null, \Prophecy\Comparator\Factory $comparatorFactory = null) + public static function implementsInterface($value, $interface, $message = '') { - $this->name = $methodName; - $this->value = $value; - $this->util = $util ?: new \Prophecy\Util\StringUtil(); - $this->comparatorFactory = $comparatorFactory ?: \Prophecy\Comparator\Factory::getInstance(); + if (!\in_array($interface, \class_implements($value))) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected an implementation of %2$s. Got: %s', static::valueToString($value), static::valueToString($interface))); + } } /** - * Scores 8 if argument is an object, which method returns expected value. + * @psalm-pure + * @psalm-param class-string|object $classOrObject * - * @param mixed $argument + * @param string|object $classOrObject + * @param mixed $property + * @param string $message * - * @return bool|int + * @throws InvalidArgumentException */ - public function scoreArgument($argument) + public static function propertyExists($classOrObject, $property, $message = '') { - if (\is_object($argument) && \method_exists($argument, $this->name)) { - $actual = \call_user_func(array($argument, $this->name)); - $comparator = $this->comparatorFactory->getComparatorFor($this->value, $actual); - try { - $comparator->assertEquals($this->value, $actual); - return 8; - } catch (\PHPUnit\SebastianBergmann\Comparator\ComparisonFailure $failure) { - return \false; - } - } - if (\is_object($argument) && \property_exists($argument, $this->name)) { - return $argument->{$this->name} === $this->value ? 8 : \false; + if (!\property_exists($classOrObject, $property)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected the property %s to exist.', static::valueToString($property))); } - return \false; } /** - * Returns false. + * @psalm-pure + * @psalm-param class-string|object $classOrObject * - * @return bool + * @param string|object $classOrObject + * @param mixed $property + * @param string $message + * + * @throws InvalidArgumentException */ - public function isLast() + public static function propertyNotExists($classOrObject, $property, $message = '') { - return \false; + if (\property_exists($classOrObject, $property)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected the property %s to not exist.', static::valueToString($property))); + } } /** - * Returns string representation for token. + * @psalm-pure + * @psalm-param class-string|object $classOrObject * - * @return string + * @param string|object $classOrObject + * @param mixed $method + * @param string $message + * + * @throws InvalidArgumentException */ - public function __toString() + public static function methodExists($classOrObject, $method, $message = '') { - return \sprintf('state(%s(), %s)', $this->name, $this->util->stringify($this->value)); + if (!(\is_string($classOrObject) || \is_object($classOrObject)) || !\method_exists($classOrObject, $method)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected the method %s to exist.', static::valueToString($method))); + } } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Argument\Token; - -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -use Prophecy\Comparator\Factory as ComparatorFactory; -use Prophecy\Util\StringUtil; -/** - * Exact value token. - * - * @author Konstantin Kudryashov - */ -class ExactValueToken implements \Prophecy\Argument\Token\TokenInterface -{ - private $value; - private $string; - private $util; - private $comparatorFactory; /** - * Initializes token. + * @psalm-pure + * @psalm-param class-string|object $classOrObject * - * @param mixed $value - * @param StringUtil $util - * @param ComparatorFactory $comparatorFactory + * @param string|object $classOrObject + * @param mixed $method + * @param string $message + * + * @throws InvalidArgumentException */ - public function __construct($value, \Prophecy\Util\StringUtil $util = null, \Prophecy\Comparator\Factory $comparatorFactory = null) + public static function methodNotExists($classOrObject, $method, $message = '') { - $this->value = $value; - $this->util = $util ?: new \Prophecy\Util\StringUtil(); - $this->comparatorFactory = $comparatorFactory ?: \Prophecy\Comparator\Factory::getInstance(); + if ((\is_string($classOrObject) || \is_object($classOrObject)) && \method_exists($classOrObject, $method)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected the method %s to not exist.', static::valueToString($method))); + } } /** - * Scores 10 if argument matches preset value. + * @psalm-pure * - * @param $argument + * @param array $array + * @param string|int $key + * @param string $message * - * @return bool|int + * @throws InvalidArgumentException */ - public function scoreArgument($argument) + public static function keyExists($array, $key, $message = '') { - if (\is_object($argument) && \is_object($this->value)) { - $comparator = $this->comparatorFactory->getComparatorFor($argument, $this->value); - try { - $comparator->assertEquals($argument, $this->value); - return 10; - } catch (\PHPUnit\SebastianBergmann\Comparator\ComparisonFailure $failure) { - return \false; - } - } - // If either one is an object it should be castable to a string - if (\is_object($argument) xor \is_object($this->value)) { - if (\is_object($argument) && !\method_exists($argument, '__toString')) { - return \false; - } - if (\is_object($this->value) && !\method_exists($this->value, '__toString')) { - return \false; - } - } elseif (\is_numeric($argument) && \is_numeric($this->value)) { - // noop - } elseif (\gettype($argument) !== \gettype($this->value)) { - return \false; + if (!(isset($array[$key]) || \array_key_exists($key, $array))) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected the key %s to exist.', static::valueToString($key))); } - return $argument == $this->value ? 10 : \false; } /** - * Returns preset value against which token checks arguments. + * @psalm-pure * - * @return mixed + * @param array $array + * @param string|int $key + * @param string $message + * + * @throws InvalidArgumentException */ - public function getValue() + public static function keyNotExists($array, $key, $message = '') { - return $this->value; + if (isset($array[$key]) || \array_key_exists($key, $array)) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected the key %s to not exist.', static::valueToString($key))); + } } /** - * Returns false. + * Checks if a value is a valid array key (int or string). * - * @return bool + * @psalm-pure + * @psalm-assert array-key $value + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException */ - public function isLast() + public static function validArrayKey($value, $message = '') { - return \false; + if (!(\is_int($value) || \is_string($value))) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected string or integer. Got: %s', static::typeToString($value))); + } } /** - * Returns string representation for token. + * Does not check if $array is countable, this can generate a warning on php versions after 7.2. * - * @return string + * @param Countable|array $array + * @param int $number + * @param string $message + * + * @throws InvalidArgumentException */ - public function __toString() + public static function count($array, $number, $message = '') { - if (null === $this->string) { - $this->string = \sprintf('exact(%s)', $this->util->stringify($this->value)); - } - return $this->string; + static::eq(\count($array), $number, \sprintf($message ?: 'Expected an array to contain %d elements. Got: %d.', $number, \count($array))); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Argument\Token; - -/** - * Array elements count token. - * - * @author Boris Mikhaylov - */ -class ArrayCountToken implements \Prophecy\Argument\Token\TokenInterface -{ - private $count; /** - * @param integer $value + * Does not check if $array is countable, this can generate a warning on php versions after 7.2. + * + * @param Countable|array $array + * @param int|float $min + * @param string $message + * + * @throws InvalidArgumentException */ - public function __construct($value) + public static function minCount($array, $min, $message = '') { - $this->count = $value; + if (\count($array) < $min) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected an array to contain at least %2$d elements. Got: %d', \count($array), $min)); + } } /** - * Scores 6 when argument has preset number of elements. + * Does not check if $array is countable, this can generate a warning on php versions after 7.2. * - * @param $argument + * @param Countable|array $array + * @param int|float $max + * @param string $message * - * @return bool|int + * @throws InvalidArgumentException */ - public function scoreArgument($argument) + public static function maxCount($array, $max, $message = '') { - return $this->isCountable($argument) && $this->hasProperCount($argument) ? 6 : \false; + if (\count($array) > $max) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected an array to contain at most %2$d elements. Got: %d', \count($array), $max)); + } } /** - * Returns false. + * Does not check if $array is countable, this can generate a warning on php versions after 7.2. * - * @return boolean + * @param Countable|array $array + * @param int|float $min + * @param int|float $max + * @param string $message + * + * @throws InvalidArgumentException */ - public function isLast() + public static function countBetween($array, $min, $max, $message = '') { - return \false; + $count = \count($array); + if ($count < $min || $count > $max) { + static::reportInvalidArgument(\sprintf($message ?: 'Expected an array to contain between %2$d and %3$d elements. Got: %d', $count, $min, $max)); + } } /** - * Returns string representation for token. + * @psalm-pure + * @psalm-assert list $array * - * @return string + * @param mixed $array + * @param string $message + * + * @throws InvalidArgumentException */ - public function __toString() + public static function isList($array, $message = '') { - return \sprintf('count(%s)', $this->count); + if (!\is_array($array) || $array !== \array_values($array)) { + static::reportInvalidArgument($message ?: 'Expected list - non-associative array.'); + } } /** - * Returns true if object is either array or instance of \Countable + * @psalm-pure + * @psalm-assert non-empty-list $array * - * @param $argument - * @return bool + * @param mixed $array + * @param string $message + * + * @throws InvalidArgumentException */ - private function isCountable($argument) + public static function isNonEmptyList($array, $message = '') { - return \is_array($argument) || $argument instanceof \Countable; + static::isList($array, $message); + static::notEmpty($array, $message); } /** - * Returns true if $argument has expected number of elements + * @psalm-pure + * @psalm-template T + * @psalm-param mixed|array $array + * @psalm-assert array $array * - * @param array|\Countable $argument + * @param mixed $array + * @param string $message * - * @return bool + * @throws InvalidArgumentException */ - private function hasProperCount($argument) + public static function isMap($array, $message = '') { - return $this->count === \count($argument); + if (!\is_array($array) || \array_keys($array) !== \array_filter(\array_keys($array), '\\is_string')) { + static::reportInvalidArgument($message ?: 'Expected map - associative array with string keys.'); + } } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Argument\Token; - -use Prophecy\Exception\InvalidArgumentException; -/** - * Array entry token. - * - * @author Boris Mikhaylov - */ -class ArrayEntryToken implements \Prophecy\Argument\Token\TokenInterface -{ - /** @var \Prophecy\Argument\Token\TokenInterface */ - private $key; - /** @var \Prophecy\Argument\Token\TokenInterface */ - private $value; /** - * @param mixed $key exact value or token - * @param mixed $value exact value or token + * @psalm-pure + * @psalm-template T + * @psalm-param mixed|array $array + * @psalm-assert array $array + * @psalm-assert !empty $array + * + * @param mixed $array + * @param string $message + * + * @throws InvalidArgumentException */ - public function __construct($key, $value) + public static function isNonEmptyMap($array, $message = '') { - $this->key = $this->wrapIntoExactValueToken($key); - $this->value = $this->wrapIntoExactValueToken($value); + static::isMap($array, $message); + static::notEmpty($array, $message); } /** - * Scores half of combined scores from key and value tokens for same entry. Capped at 8. - * If argument implements \ArrayAccess without \Traversable, then key token is restricted to ExactValueToken. + * @psalm-pure * - * @param array|\ArrayAccess|\Traversable $argument + * @param string $value + * @param string $message * - * @throws \Prophecy\Exception\InvalidArgumentException - * @return bool|int + * @throws InvalidArgumentException */ - public function scoreArgument($argument) + public static function uuid($value, $message = '') { - if ($argument instanceof \Traversable) { - $argument = \iterator_to_array($argument); - } - if ($argument instanceof \ArrayAccess) { - $argument = $this->convertArrayAccessToEntry($argument); + $value = \str_replace(array('urn:', 'uuid:', '{', '}'), '', $value); + // The nil UUID is special form of UUID that is specified to have all + // 128 bits set to zero. + if ('00000000-0000-0000-0000-000000000000' === $value) { + return; } - if (!\is_array($argument) || empty($argument)) { - return \false; + if (!\preg_match('/^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/', $value)) { + static::reportInvalidArgument(\sprintf($message ?: 'Value %s is not a valid UUID.', static::valueToString($value))); } - $keyScores = \array_map(array($this->key, 'scoreArgument'), \array_keys($argument)); - $valueScores = \array_map(array($this->value, 'scoreArgument'), $argument); - $scoreEntry = function ($value, $key) { - return $value && $key ? \min(8, ($key + $value) / 2) : \false; - }; - return \max(\array_map($scoreEntry, $valueScores, $keyScores)); } /** - * Returns false. + * @psalm-param class-string $class * - * @return boolean + * @param Closure $expression + * @param string $class + * @param string $message + * + * @throws InvalidArgumentException */ - public function isLast() + public static function throws(Closure $expression, $class = 'Exception', $message = '') { - return \false; + static::string($class); + $actual = 'none'; + try { + $expression(); + } catch (Exception $e) { + $actual = \get_class($e); + if ($e instanceof $class) { + return; + } + } catch (Throwable $e) { + $actual = \get_class($e); + if ($e instanceof $class) { + return; + } + } + static::reportInvalidArgument($message ?: \sprintf('Expected to throw "%s", got "%s"', $class, $actual)); } /** - * Returns string representation for token. - * - * @return string + * @throws BadMethodCallException */ - public function __toString() + public static function __callStatic($name, $arguments) { - return \sprintf('[..., %s => %s, ...]', $this->key, $this->value); + if ('nullOr' === \substr($name, 0, 6)) { + if (null !== $arguments[0]) { + $method = \lcfirst(\substr($name, 6)); + \call_user_func_array(array('static', $method), $arguments); + } + return; + } + if ('all' === \substr($name, 0, 3)) { + static::isIterable($arguments[0]); + $method = \lcfirst(\substr($name, 3)); + $args = $arguments; + foreach ($arguments[0] as $entry) { + $args[0] = $entry; + \call_user_func_array(array('static', $method), $args); + } + return; + } + throw new BadMethodCallException('No such method: ' . $name); } /** - * Returns key + * @param mixed $value * - * @return TokenInterface + * @return string */ - public function getKey() + protected static function valueToString($value) { - return $this->key; + if (null === $value) { + return 'null'; + } + if (\true === $value) { + return 'true'; + } + if (\false === $value) { + return 'false'; + } + if (\is_array($value)) { + return 'array'; + } + if (\is_object($value)) { + if (\method_exists($value, '__toString')) { + return \get_class($value) . ': ' . self::valueToString($value->__toString()); + } + if ($value instanceof DateTime || $value instanceof DateTimeImmutable) { + return \get_class($value) . ': ' . self::valueToString($value->format('c')); + } + return \get_class($value); + } + if (\is_resource($value)) { + return 'resource'; + } + if (\is_string($value)) { + return '"' . $value . '"'; + } + return (string) $value; } /** - * Returns value + * @param mixed $value * - * @return TokenInterface + * @return string */ - public function getValue() + protected static function typeToString($value) { - return $this->value; + return \is_object($value) ? \get_class($value) : \gettype($value); } - /** - * Wraps non token $value into ExactValueToken - * - * @param $value - * @return TokenInterface - */ - private function wrapIntoExactValueToken($value) + protected static function strlen($value) { - return $value instanceof \Prophecy\Argument\Token\TokenInterface ? $value : new \Prophecy\Argument\Token\ExactValueToken($value); + if (!\function_exists('mb_detect_encoding')) { + return \strlen($value); + } + if (\false === ($encoding = \mb_detect_encoding($value))) { + return \strlen($value); + } + return \mb_strlen($value, $encoding); } /** - * Converts instance of \ArrayAccess to key => value array entry + * @param string $message * - * @param \ArrayAccess $object + * @throws InvalidArgumentException * - * @return array|null - * @throws \Prophecy\Exception\InvalidArgumentException + * @psalm-pure this method is not supposed to perform side-effects */ - private function convertArrayAccessToEntry(\ArrayAccess $object) + protected static function reportInvalidArgument($message) + { + throw new InvalidArgumentException($message); + } + private function __construct() { - if (!$this->key instanceof \Prophecy\Argument\Token\ExactValueToken) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf('You can only use exact value tokens to match key of ArrayAccess object' . \PHP_EOL . 'But you used `%s`.', $this->key)); - } - $key = $this->key->getValue(); - return $object->offsetExists($key) ? array($key => $object[$key]) : array(); } } - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. +/** + * provides type inference and auto-completion for magic static methods of Assert. */ -namespace Prophecy\Argument; +namespace PHPUnit\Webmozart\Assert; +use ArrayAccess; +use Closure; +use Countable; +use Throwable; /** - * Arguments wildcarding. - * - * @author Konstantin Kudryashov + * This trait aids static analysis tooling in introspecting assertion magic methods. + * Do not use this trait directly: it will change, and is not designed for reuse. */ -class ArgumentsWildcard +trait Mixin { /** - * @var Token\TokenInterface[] - */ - private $tokens = array(); - private $string; - /** - * Initializes wildcard. + * @psalm-pure + * @psalm-assert string|null $value * - * @param array $arguments Array of argument tokens or values + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function __construct(array $arguments) + public static function nullOrString($value, $message = '') { - foreach ($arguments as $argument) { - if (!$argument instanceof \Prophecy\Argument\Token\TokenInterface) { - $argument = new \Prophecy\Argument\Token\ExactValueToken($argument); - } - $this->tokens[] = $argument; - } + static::__callStatic('nullOrString', array($value, $message)); } /** - * Calculates wildcard match score for provided arguments. + * @psalm-pure + * @psalm-assert iterable $value * - * @param array $arguments + * @param mixed $value + * @param string $message * - * @return false|int False OR integer score (higher - better) + * @throws InvalidArgumentException + * + * @return void */ - public function scoreArguments(array $arguments) + public static function allString($value, $message = '') { - if (0 == \count($arguments) && 0 == \count($this->tokens)) { - return 1; - } - $arguments = \array_values($arguments); - $totalScore = 0; - foreach ($this->tokens as $i => $token) { - $argument = isset($arguments[$i]) ? $arguments[$i] : null; - if (1 >= ($score = $token->scoreArgument($argument))) { - return \false; - } - $totalScore += $score; - if (\true === $token->isLast()) { - return $totalScore; - } - } - if (\count($arguments) > \count($this->tokens)) { - return \false; - } - return $totalScore; + static::__callStatic('allString', array($value, $message)); } /** - * Returns string representation for wildcard. + * @psalm-pure + * @psalm-assert non-empty-string|null $value * - * @return string + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function __toString() + public static function nullOrStringNotEmpty($value, $message = '') { - if (null === $this->string) { - $this->string = \implode(', ', \array_map(function ($token) { - return (string) $token; - }, $this->tokens)); - } - return $this->string; + static::__callStatic('nullOrStringNotEmpty', array($value, $message)); } /** - * @return array + * @psalm-pure + * @psalm-assert iterable $value + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getTokens() + public static function allStringNotEmpty($value, $message = '') { - return $this->tokens; + static::__callStatic('allStringNotEmpty', array($value, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Promise; - -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Exception\InvalidArgumentException; -use Closure; -/** - * Callback promise. - * - * @author Konstantin Kudryashov - */ -class CallbackPromise implements \Prophecy\Promise\PromiseInterface -{ - private $callback; /** - * Initializes callback promise. + * @psalm-pure + * @psalm-assert int|null $value * - * @param callable $callback Custom callback + * @param mixed $value + * @param string $message * - * @throws \Prophecy\Exception\InvalidArgumentException + * @throws InvalidArgumentException + * + * @return void */ - public function __construct($callback) + public static function nullOrInteger($value, $message = '') { - if (!\is_callable($callback)) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf('Callable expected as an argument to CallbackPromise, but got %s.', \gettype($callback))); - } - $this->callback = $callback; + static::__callStatic('nullOrInteger', array($value, $message)); } /** - * Evaluates promise callback. + * @psalm-pure + * @psalm-assert iterable $value * - * @param array $args - * @param ObjectProphecy $object - * @param MethodProphecy $method + * @param mixed $value + * @param string $message * - * @return mixed + * @throws InvalidArgumentException + * + * @return void */ - public function execute(array $args, \Prophecy\Prophecy\ObjectProphecy $object, \Prophecy\Prophecy\MethodProphecy $method) + public static function allInteger($value, $message = '') { - $callback = $this->callback; - if ($callback instanceof \Closure && \method_exists('Closure', 'bind')) { - $callback = \Closure::bind($callback, $object); - } - return \call_user_func($callback, $args, $object, $method); + static::__callStatic('allInteger', array($value, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Promise; - -use Prophecy\Exception\InvalidArgumentException; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -/** - * Return argument promise. - * - * @author Konstantin Kudryashov - */ -class ReturnArgumentPromise implements \Prophecy\Promise\PromiseInterface -{ - /** - * @var int - */ - private $index; /** - * Initializes callback promise. + * @psalm-pure + * @psalm-assert numeric|null $value * - * @param int $index The zero-indexed number of the argument to return + * @param mixed $value + * @param string $message * - * @throws \Prophecy\Exception\InvalidArgumentException + * @throws InvalidArgumentException + * + * @return void */ - public function __construct($index = 0) + public static function nullOrIntegerish($value, $message = '') { - if (!\is_int($index) || $index < 0) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf('Zero-based index expected as argument to ReturnArgumentPromise, but got %s.', $index)); - } - $this->index = $index; + static::__callStatic('nullOrIntegerish', array($value, $message)); } /** - * Returns nth argument if has one, null otherwise. + * @psalm-pure + * @psalm-assert iterable $value * - * @param array $args - * @param ObjectProphecy $object - * @param MethodProphecy $method + * @param mixed $value + * @param string $message * - * @return null|mixed + * @throws InvalidArgumentException + * + * @return void */ - public function execute(array $args, \Prophecy\Prophecy\ObjectProphecy $object, \Prophecy\Prophecy\MethodProphecy $method) + public static function allIntegerish($value, $message = '') { - return \count($args) > $this->index ? $args[$this->index] : null; + static::__callStatic('allIntegerish', array($value, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Promise; - -use PHPUnit\Doctrine\Instantiator\Instantiator; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Exception\InvalidArgumentException; -use ReflectionClass; -/** - * Throw promise. - * - * @author Konstantin Kudryashov - */ -class ThrowPromise implements \Prophecy\Promise\PromiseInterface -{ - private $exception; - /** - * @var \Doctrine\Instantiator\Instantiator - */ - private $instantiator; /** - * Initializes promise. + * @psalm-pure + * @psalm-assert positive-int|null $value * - * @param string|\Exception|\Throwable $exception Exception class name or instance + * @param mixed $value + * @param string $message * - * @throws \Prophecy\Exception\InvalidArgumentException + * @throws InvalidArgumentException + * + * @return void */ - public function __construct($exception) + public static function nullOrPositiveInteger($value, $message = '') { - if (\is_string($exception)) { - if (!\class_exists($exception) && !\interface_exists($exception) || !$this->isAValidThrowable($exception)) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf('Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.', $exception)); - } - } elseif (!$exception instanceof \Exception && !$exception instanceof \Throwable) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf('Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.', \is_object($exception) ? \get_class($exception) : \gettype($exception))); - } - $this->exception = $exception; + static::__callStatic('nullOrPositiveInteger', array($value, $message)); } /** - * Throws predefined exception. + * @psalm-pure + * @psalm-assert iterable $value * - * @param array $args - * @param ObjectProphecy $object - * @param MethodProphecy $method + * @param mixed $value + * @param string $message * - * @throws object + * @throws InvalidArgumentException + * + * @return void */ - public function execute(array $args, \Prophecy\Prophecy\ObjectProphecy $object, \Prophecy\Prophecy\MethodProphecy $method) + public static function allPositiveInteger($value, $message = '') { - if (\is_string($this->exception)) { - $classname = $this->exception; - $reflection = new \ReflectionClass($classname); - $constructor = $reflection->getConstructor(); - if ($constructor->isPublic() && 0 == $constructor->getNumberOfRequiredParameters()) { - throw $reflection->newInstance(); - } - if (!$this->instantiator) { - $this->instantiator = new \PHPUnit\Doctrine\Instantiator\Instantiator(); - } - throw $this->instantiator->instantiate($classname); - } - throw $this->exception; + static::__callStatic('allPositiveInteger', array($value, $message)); } /** - * @param string $exception + * @psalm-pure + * @psalm-assert float|null $value * - * @return bool + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - private function isAValidThrowable($exception) + public static function nullOrFloat($value, $message = '') { - return \is_a($exception, 'Exception', \true) || \is_a($exception, 'Throwable', \true); + static::__callStatic('nullOrFloat', array($value, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Promise; - -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -/** - * Promise interface. - * Promises are logical blocks, tied to `will...` keyword. - * - * @author Konstantin Kudryashov - */ -interface PromiseInterface -{ /** - * Evaluates promise. + * @psalm-pure + * @psalm-assert iterable $value * - * @param array $args - * @param ObjectProphecy $object - * @param MethodProphecy $method + * @param mixed $value + * @param string $message * - * @return mixed - */ - public function execute(array $args, \Prophecy\Prophecy\ObjectProphecy $object, \Prophecy\Prophecy\MethodProphecy $method); -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Promise; - -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -/** - * Return promise. - * - * @author Konstantin Kudryashov - */ -class ReturnPromise implements \Prophecy\Promise\PromiseInterface -{ - private $returnValues = array(); - /** - * Initializes promise. + * @throws InvalidArgumentException * - * @param array $returnValues Array of values + * @return void */ - public function __construct(array $returnValues) + public static function allFloat($value, $message = '') { - $this->returnValues = $returnValues; + static::__callStatic('allFloat', array($value, $message)); } /** - * Returns saved values one by one until last one, then continuously returns last value. + * @psalm-pure + * @psalm-assert numeric|null $value * - * @param array $args - * @param ObjectProphecy $object - * @param MethodProphecy $method + * @param mixed $value + * @param string $message * - * @return mixed + * @throws InvalidArgumentException + * + * @return void */ - public function execute(array $args, \Prophecy\Prophecy\ObjectProphecy $object, \Prophecy\Prophecy\MethodProphecy $method) + public static function nullOrNumeric($value, $message = '') { - $value = \array_shift($this->returnValues); - if (!\count($this->returnValues)) { - $this->returnValues[] = $value; - } - return $value; + static::__callStatic('nullOrNumeric', array($value, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Prophecy; - -use Prophecy\Argument; -use Prophecy\Prophet; -use Prophecy\Promise; -use Prophecy\Prediction; -use Prophecy\Exception\Doubler\MethodNotFoundException; -use Prophecy\Exception\InvalidArgumentException; -use Prophecy\Exception\Prophecy\MethodProphecyException; -use ReflectionNamedType; -use ReflectionType; -use ReflectionUnionType; -/** - * Method prophecy. - * - * @author Konstantin Kudryashov - */ -class MethodProphecy -{ - private $objectProphecy; - private $methodName; - private $argumentsWildcard; - private $promise; - private $prediction; - private $checkedPredictions = array(); - private $bound = \false; - private $voidReturnType = \false; /** - * Initializes method prophecy. + * @psalm-pure + * @psalm-assert iterable $value * - * @param ObjectProphecy $objectProphecy - * @param string $methodName - * @param null|Argument\ArgumentsWildcard|array $arguments + * @param mixed $value + * @param string $message * - * @throws \Prophecy\Exception\Doubler\MethodNotFoundException If method not found + * @throws InvalidArgumentException + * + * @return void */ - public function __construct(\Prophecy\Prophecy\ObjectProphecy $objectProphecy, $methodName, $arguments = null) + public static function allNumeric($value, $message = '') { - $double = $objectProphecy->reveal(); - if (!\method_exists($double, $methodName)) { - throw new \Prophecy\Exception\Doubler\MethodNotFoundException(\sprintf('Method `%s::%s()` is not defined.', \get_class($double), $methodName), \get_class($double), $methodName, $arguments); - } - $this->objectProphecy = $objectProphecy; - $this->methodName = $methodName; - $reflectedMethod = new \ReflectionMethod($double, $methodName); - if ($reflectedMethod->isFinal()) { - throw new \Prophecy\Exception\Prophecy\MethodProphecyException(\sprintf("Can not add prophecy for a method `%s::%s()`\n" . "as it is a final method.", \get_class($double), $methodName), $this); - } - if (null !== $arguments) { - $this->withArguments($arguments); - } - if (\true === $reflectedMethod->hasReturnType()) { - $reflectionType = $reflectedMethod->getReturnType(); - if ($reflectionType instanceof \ReflectionNamedType) { - $types = [$reflectionType]; - } elseif ($reflectionType instanceof \ReflectionUnionType) { - $types = $reflectionType->getTypes(); - } - $types = \array_map(function (\ReflectionType $type) { - return $type->getName(); - }, $types); - \usort($types, static function (string $type1, string $type2) { - // null is lowest priority - if ($type2 == 'null') { - return -1; - } elseif ($type1 == 'null') { - return 1; - } - // objects are higher priority than scalars - $isObject = static function ($type) { - return \class_exists($type) || \interface_exists($type); - }; - if ($isObject($type1) && !$isObject($type2)) { - return -1; - } elseif (!$isObject($type1) && $isObject($type2)) { - return 1; - } - // don't sort both-scalars or both-objects - return 0; - }); - $defaultType = $types[0]; - if ('void' === $defaultType) { - $this->voidReturnType = \true; - } - $this->will(function () use($defaultType) { - switch ($defaultType) { - case 'void': - return; - case 'string': - return ''; - case 'float': - return 0.0; - case 'int': - return 0; - case 'bool': - return \false; - case 'array': - return array(); - case 'callable': - case 'Closure': - return function () { - }; - case 'Traversable': - case 'Generator': - return (function () { - yield; - })(); - default: - $prophet = new \Prophecy\Prophet(); - return $prophet->prophesize($defaultType)->reveal(); - } - }); - } + static::__callStatic('allNumeric', array($value, $message)); } /** - * Sets argument wildcard. + * @psalm-pure + * @psalm-assert positive-int|0|null $value * - * @param array|Argument\ArgumentsWildcard $arguments + * @param mixed $value + * @param string $message * - * @return $this + * @throws InvalidArgumentException * - * @throws \Prophecy\Exception\InvalidArgumentException + * @return void */ - public function withArguments($arguments) + public static function nullOrNatural($value, $message = '') { - if (\is_array($arguments)) { - $arguments = new \Prophecy\Argument\ArgumentsWildcard($arguments); - } - if (!$arguments instanceof \Prophecy\Argument\ArgumentsWildcard) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf("Either an array or an instance of ArgumentsWildcard expected as\n" . 'a `MethodProphecy::withArguments()` argument, but got %s.', \gettype($arguments))); - } - $this->argumentsWildcard = $arguments; - return $this; + static::__callStatic('nullOrNatural', array($value, $message)); } /** - * Sets custom promise to the prophecy. + * @psalm-pure + * @psalm-assert iterable $value * - * @param callable|Promise\PromiseInterface $promise + * @param mixed $value + * @param string $message * - * @return $this + * @throws InvalidArgumentException * - * @throws \Prophecy\Exception\InvalidArgumentException + * @return void */ - public function will($promise) + public static function allNatural($value, $message = '') { - if (\is_callable($promise)) { - $promise = new \Prophecy\Promise\CallbackPromise($promise); - } - if (!$promise instanceof \Prophecy\Promise\PromiseInterface) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf('Expected callable or instance of PromiseInterface, but got %s.', \gettype($promise))); - } - $this->bindToObjectProphecy(); - $this->promise = $promise; - return $this; + static::__callStatic('allNatural', array($value, $message)); } /** - * Sets return promise to the prophecy. + * @psalm-pure + * @psalm-assert bool|null $value * - * @see \Prophecy\Promise\ReturnPromise + * @param mixed $value + * @param string $message * - * @return $this + * @throws InvalidArgumentException + * + * @return void */ - public function willReturn() + public static function nullOrBoolean($value, $message = '') { - if ($this->voidReturnType) { - throw new \Prophecy\Exception\Prophecy\MethodProphecyException("The method \"{$this->methodName}\" has a void return type, and so cannot return anything", $this); - } - return $this->will(new \Prophecy\Promise\ReturnPromise(\func_get_args())); + static::__callStatic('nullOrBoolean', array($value, $message)); } /** - * @param array $items + * @psalm-pure + * @psalm-assert iterable $value * - * @return $this + * @param mixed $value + * @param string $message * - * @throws \Prophecy\Exception\InvalidArgumentException + * @throws InvalidArgumentException + * + * @return void */ - public function willYield($items) + public static function allBoolean($value, $message = '') { - if ($this->voidReturnType) { - throw new \Prophecy\Exception\Prophecy\MethodProphecyException("The method \"{$this->methodName}\" has a void return type, and so cannot yield anything", $this); - } - if (!\is_array($items)) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf('Expected array, but got %s.', \gettype($items))); - } - $generator = function () use($items) { - foreach ($items as $key => $value) { - (yield $key => $value); - } - }; - return $this->will($generator); + static::__callStatic('allBoolean', array($value, $message)); } /** - * Sets return argument promise to the prophecy. + * @psalm-pure + * @psalm-assert scalar|null $value * - * @param int $index The zero-indexed number of the argument to return + * @param mixed $value + * @param string $message * - * @see \Prophecy\Promise\ReturnArgumentPromise + * @throws InvalidArgumentException * - * @return $this + * @return void */ - public function willReturnArgument($index = 0) + public static function nullOrScalar($value, $message = '') { - if ($this->voidReturnType) { - throw new \Prophecy\Exception\Prophecy\MethodProphecyException("The method \"{$this->methodName}\" has a void return type", $this); - } - return $this->will(new \Prophecy\Promise\ReturnArgumentPromise($index)); + static::__callStatic('nullOrScalar', array($value, $message)); } /** - * Sets throw promise to the prophecy. + * @psalm-pure + * @psalm-assert iterable $value * - * @see \Prophecy\Promise\ThrowPromise + * @param mixed $value + * @param string $message * - * @param string|\Exception $exception Exception class or instance + * @throws InvalidArgumentException * - * @return $this + * @return void */ - public function willThrow($exception) + public static function allScalar($value, $message = '') { - return $this->will(new \Prophecy\Promise\ThrowPromise($exception)); + static::__callStatic('allScalar', array($value, $message)); } /** - * Sets custom prediction to the prophecy. + * @psalm-pure + * @psalm-assert object|null $value * - * @param callable|Prediction\PredictionInterface $prediction + * @param mixed $value + * @param string $message * - * @return $this + * @throws InvalidArgumentException * - * @throws \Prophecy\Exception\InvalidArgumentException + * @return void */ - public function should($prediction) + public static function nullOrObject($value, $message = '') { - if (\is_callable($prediction)) { - $prediction = new \Prophecy\Prediction\CallbackPrediction($prediction); - } - if (!$prediction instanceof \Prophecy\Prediction\PredictionInterface) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf('Expected callable or instance of PredictionInterface, but got %s.', \gettype($prediction))); - } - $this->bindToObjectProphecy(); - $this->prediction = $prediction; - return $this; + static::__callStatic('nullOrObject', array($value, $message)); } /** - * Sets call prediction to the prophecy. + * @psalm-pure + * @psalm-assert iterable $value * - * @see \Prophecy\Prediction\CallPrediction + * @param mixed $value + * @param string $message * - * @return $this + * @throws InvalidArgumentException + * + * @return void */ - public function shouldBeCalled() + public static function allObject($value, $message = '') { - return $this->should(new \Prophecy\Prediction\CallPrediction()); + static::__callStatic('allObject', array($value, $message)); } /** - * Sets no calls prediction to the prophecy. + * @psalm-pure + * @psalm-assert resource|null $value * - * @see \Prophecy\Prediction\NoCallsPrediction + * @param mixed $value + * @param string|null $type type of resource this should be. @see https://www.php.net/manual/en/function.get-resource-type.php + * @param string $message * - * @return $this + * @throws InvalidArgumentException + * + * @return void */ - public function shouldNotBeCalled() + public static function nullOrResource($value, $type = null, $message = '') { - return $this->should(new \Prophecy\Prediction\NoCallsPrediction()); + static::__callStatic('nullOrResource', array($value, $type, $message)); } /** - * Sets call times prediction to the prophecy. + * @psalm-pure + * @psalm-assert iterable $value * - * @see \Prophecy\Prediction\CallTimesPrediction + * @param mixed $value + * @param string|null $type type of resource this should be. @see https://www.php.net/manual/en/function.get-resource-type.php + * @param string $message * - * @param $count + * @throws InvalidArgumentException * - * @return $this + * @return void */ - public function shouldBeCalledTimes($count) + public static function allResource($value, $type = null, $message = '') { - return $this->should(new \Prophecy\Prediction\CallTimesPrediction($count)); + static::__callStatic('allResource', array($value, $type, $message)); } /** - * Sets call times prediction to the prophecy. + * @psalm-pure + * @psalm-assert callable|null $value * - * @see \Prophecy\Prediction\CallTimesPrediction + * @param mixed $value + * @param string $message * - * @return $this + * @throws InvalidArgumentException + * + * @return void */ - public function shouldBeCalledOnce() + public static function nullOrIsCallable($value, $message = '') { - return $this->shouldBeCalledTimes(1); + static::__callStatic('nullOrIsCallable', array($value, $message)); } /** - * Checks provided prediction immediately. + * @psalm-pure + * @psalm-assert iterable $value * - * @param callable|Prediction\PredictionInterface $prediction + * @param mixed $value + * @param string $message * - * @return $this + * @throws InvalidArgumentException * - * @throws \Prophecy\Exception\InvalidArgumentException + * @return void */ - public function shouldHave($prediction) + public static function allIsCallable($value, $message = '') { - if (\is_callable($prediction)) { - $prediction = new \Prophecy\Prediction\CallbackPrediction($prediction); - } - if (!$prediction instanceof \Prophecy\Prediction\PredictionInterface) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf('Expected callable or instance of PredictionInterface, but got %s.', \gettype($prediction))); - } - if (null === $this->promise && !$this->voidReturnType) { - $this->willReturn(); - } - $calls = $this->getObjectProphecy()->findProphecyMethodCalls($this->getMethodName(), $this->getArgumentsWildcard()); - try { - $prediction->check($calls, $this->getObjectProphecy(), $this); - $this->checkedPredictions[] = $prediction; - } catch (\Exception $e) { - $this->checkedPredictions[] = $prediction; - throw $e; - } - return $this; + static::__callStatic('allIsCallable', array($value, $message)); } /** - * Checks call prediction. + * @psalm-pure + * @psalm-assert array|null $value * - * @see \Prophecy\Prediction\CallPrediction + * @param mixed $value + * @param string $message * - * @return $this + * @throws InvalidArgumentException + * + * @return void */ - public function shouldHaveBeenCalled() + public static function nullOrIsArray($value, $message = '') { - return $this->shouldHave(new \Prophecy\Prediction\CallPrediction()); + static::__callStatic('nullOrIsArray', array($value, $message)); } /** - * Checks no calls prediction. + * @psalm-pure + * @psalm-assert iterable $value * - * @see \Prophecy\Prediction\NoCallsPrediction + * @param mixed $value + * @param string $message * - * @return $this + * @throws InvalidArgumentException + * + * @return void */ - public function shouldNotHaveBeenCalled() + public static function allIsArray($value, $message = '') { - return $this->shouldHave(new \Prophecy\Prediction\NoCallsPrediction()); + static::__callStatic('allIsArray', array($value, $message)); } /** - * Checks no calls prediction. + * @psalm-pure + * @psalm-assert iterable|null $value * - * @see \Prophecy\Prediction\NoCallsPrediction - * @deprecated + * @deprecated use "isIterable" or "isInstanceOf" instead * - * @return $this + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function shouldNotBeenCalled() + public static function nullOrIsTraversable($value, $message = '') { - return $this->shouldNotHaveBeenCalled(); + static::__callStatic('nullOrIsTraversable', array($value, $message)); } /** - * Checks call times prediction. + * @psalm-pure + * @psalm-assert iterable $value * - * @see \Prophecy\Prediction\CallTimesPrediction + * @deprecated use "isIterable" or "isInstanceOf" instead * - * @param int $count + * @param mixed $value + * @param string $message * - * @return $this + * @throws InvalidArgumentException + * + * @return void */ - public function shouldHaveBeenCalledTimes($count) + public static function allIsTraversable($value, $message = '') { - return $this->shouldHave(new \Prophecy\Prediction\CallTimesPrediction($count)); + static::__callStatic('allIsTraversable', array($value, $message)); } /** - * Checks call times prediction. + * @psalm-pure + * @psalm-assert array|ArrayAccess|null $value * - * @see \Prophecy\Prediction\CallTimesPrediction + * @param mixed $value + * @param string $message * - * @return $this + * @throws InvalidArgumentException + * + * @return void */ - public function shouldHaveBeenCalledOnce() + public static function nullOrIsArrayAccessible($value, $message = '') { - return $this->shouldHaveBeenCalledTimes(1); + static::__callStatic('nullOrIsArrayAccessible', array($value, $message)); } /** - * Checks currently registered [with should(...)] prediction. + * @psalm-pure + * @psalm-assert iterable $value + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function checkPrediction() + public static function allIsArrayAccessible($value, $message = '') { - if (null === $this->prediction) { - return; - } - $this->shouldHave($this->prediction); + static::__callStatic('allIsArrayAccessible', array($value, $message)); } /** - * Returns currently registered promise. + * @psalm-pure + * @psalm-assert countable|null $value * - * @return null|Promise\PromiseInterface + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getPromise() + public static function nullOrIsCountable($value, $message = '') { - return $this->promise; + static::__callStatic('nullOrIsCountable', array($value, $message)); } /** - * Returns currently registered prediction. + * @psalm-pure + * @psalm-assert iterable $value * - * @return null|Prediction\PredictionInterface + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getPrediction() + public static function allIsCountable($value, $message = '') { - return $this->prediction; + static::__callStatic('allIsCountable', array($value, $message)); } /** - * Returns predictions that were checked on this object. + * @psalm-pure + * @psalm-assert iterable|null $value * - * @return Prediction\PredictionInterface[] + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getCheckedPredictions() + public static function nullOrIsIterable($value, $message = '') { - return $this->checkedPredictions; + static::__callStatic('nullOrIsIterable', array($value, $message)); } /** - * Returns object prophecy this method prophecy is tied to. + * @psalm-pure + * @psalm-assert iterable $value * - * @return ObjectProphecy + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getObjectProphecy() + public static function allIsIterable($value, $message = '') { - return $this->objectProphecy; + static::__callStatic('allIsIterable', array($value, $message)); } /** - * Returns method name. + * @psalm-pure + * @psalm-template ExpectedType of object + * @psalm-param class-string $class + * @psalm-assert ExpectedType|null $value * - * @return string + * @param mixed $value + * @param string|object $class + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getMethodName() + public static function nullOrIsInstanceOf($value, $class, $message = '') { - return $this->methodName; + static::__callStatic('nullOrIsInstanceOf', array($value, $class, $message)); } /** - * Returns arguments wildcard. + * @psalm-pure + * @psalm-template ExpectedType of object + * @psalm-param class-string $class + * @psalm-assert iterable $value * - * @return Argument\ArgumentsWildcard + * @param mixed $value + * @param string|object $class + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getArgumentsWildcard() + public static function allIsInstanceOf($value, $class, $message = '') { - return $this->argumentsWildcard; + static::__callStatic('allIsInstanceOf', array($value, $class, $message)); } /** - * @return bool + * @psalm-pure + * @psalm-template ExpectedType of object + * @psalm-param class-string $class + * + * @param mixed $value + * @param string|object $class + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function hasReturnVoid() - { - return $this->voidReturnType; - } - private function bindToObjectProphecy() + public static function nullOrNotInstanceOf($value, $class, $message = '') { - if ($this->bound) { - return; - } - $this->getObjectProphecy()->addMethodProphecy($this); - $this->bound = \true; + static::__callStatic('nullOrNotInstanceOf', array($value, $class, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Prophecy; - -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -use Prophecy\Comparator\Factory as ComparatorFactory; -use Prophecy\Call\Call; -use Prophecy\Doubler\LazyDouble; -use Prophecy\Argument\ArgumentsWildcard; -use Prophecy\Call\CallCenter; -use Prophecy\Exception\Prophecy\ObjectProphecyException; -use Prophecy\Exception\Prophecy\MethodProphecyException; -use Prophecy\Exception\Prediction\AggregateException; -use Prophecy\Exception\Prediction\PredictionException; -/** - * Object prophecy. - * - * @author Konstantin Kudryashov - */ -class ObjectProphecy implements \Prophecy\Prophecy\ProphecyInterface -{ - private $lazyDouble; - private $callCenter; - private $revealer; - private $comparatorFactory; - /** - * @var MethodProphecy[][] - */ - private $methodProphecies = array(); /** - * Initializes object prophecy. + * @psalm-pure + * @psalm-template ExpectedType of object + * @psalm-param class-string $class * - * @param LazyDouble $lazyDouble - * @param CallCenter $callCenter - * @param RevealerInterface $revealer - * @param ComparatorFactory $comparatorFactory + * @param mixed $value + * @param string|object $class + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function __construct(\Prophecy\Doubler\LazyDouble $lazyDouble, \Prophecy\Call\CallCenter $callCenter = null, \Prophecy\Prophecy\RevealerInterface $revealer = null, \Prophecy\Comparator\Factory $comparatorFactory = null) + public static function allNotInstanceOf($value, $class, $message = '') { - $this->lazyDouble = $lazyDouble; - $this->callCenter = $callCenter ?: new \Prophecy\Call\CallCenter(); - $this->revealer = $revealer ?: new \Prophecy\Prophecy\Revealer(); - $this->comparatorFactory = $comparatorFactory ?: \Prophecy\Comparator\Factory::getInstance(); + static::__callStatic('allNotInstanceOf', array($value, $class, $message)); } /** - * Forces double to extend specific class. + * @psalm-pure + * @psalm-param array $classes * - * @param string $class + * @param mixed $value + * @param array $classes + * @param string $message * - * @return $this + * @throws InvalidArgumentException + * + * @return void */ - public function willExtend($class) + public static function nullOrIsInstanceOfAny($value, $classes, $message = '') { - $this->lazyDouble->setParentClass($class); - return $this; + static::__callStatic('nullOrIsInstanceOfAny', array($value, $classes, $message)); } /** - * Forces double to implement specific interface. + * @psalm-pure + * @psalm-param array $classes * - * @param string $interface + * @param mixed $value + * @param array $classes + * @param string $message * - * @return $this + * @throws InvalidArgumentException + * + * @return void */ - public function willImplement($interface) + public static function allIsInstanceOfAny($value, $classes, $message = '') { - $this->lazyDouble->addInterface($interface); - return $this; + static::__callStatic('allIsInstanceOfAny', array($value, $classes, $message)); } /** - * Sets constructor arguments. + * @psalm-pure + * @psalm-template ExpectedType of object + * @psalm-param class-string $class + * @psalm-assert ExpectedType|class-string|null $value * - * @param array $arguments + * @param object|string|null $value + * @param string $class + * @param string $message * - * @return $this + * @throws InvalidArgumentException + * + * @return void */ - public function willBeConstructedWith(array $arguments = null) + public static function nullOrIsAOf($value, $class, $message = '') { - $this->lazyDouble->setArguments($arguments); - return $this; + static::__callStatic('nullOrIsAOf', array($value, $class, $message)); } /** - * Reveals double. + * @psalm-pure + * @psalm-template ExpectedType of object + * @psalm-param class-string $class + * @psalm-assert iterable> $value * - * @return object + * @param iterable $value + * @param string $class + * @param string $message * - * @throws \Prophecy\Exception\Prophecy\ObjectProphecyException If double doesn't implement needed interface + * @throws InvalidArgumentException + * + * @return void */ - public function reveal() + public static function allIsAOf($value, $class, $message = '') { - $double = $this->lazyDouble->getInstance(); - if (null === $double || !$double instanceof \Prophecy\Prophecy\ProphecySubjectInterface) { - throw new \Prophecy\Exception\Prophecy\ObjectProphecyException("Generated double must implement ProphecySubjectInterface, but it does not.\n" . 'It seems you have wrongly configured doubler without required ClassPatch.', $this); - } - $double->setProphecy($this); - return $double; + static::__callStatic('allIsAOf', array($value, $class, $message)); } /** - * Adds method prophecy to object prophecy. + * @psalm-pure + * @psalm-template UnexpectedType of object + * @psalm-param class-string $class * - * @param MethodProphecy $methodProphecy + * @param object|string|null $value + * @param string $class + * @param string $message * - * @throws \Prophecy\Exception\Prophecy\MethodProphecyException If method prophecy doesn't - * have arguments wildcard + * @throws InvalidArgumentException + * + * @return void */ - public function addMethodProphecy(\Prophecy\Prophecy\MethodProphecy $methodProphecy) + public static function nullOrIsNotA($value, $class, $message = '') { - $argumentsWildcard = $methodProphecy->getArgumentsWildcard(); - if (null === $argumentsWildcard) { - throw new \Prophecy\Exception\Prophecy\MethodProphecyException(\sprintf("Can not add prophecy for a method `%s::%s()`\n" . "as you did not specify arguments wildcard for it.", \get_class($this->reveal()), $methodProphecy->getMethodName()), $methodProphecy); - } - $methodName = \strtolower($methodProphecy->getMethodName()); - if (!isset($this->methodProphecies[$methodName])) { - $this->methodProphecies[$methodName] = array(); - } - $this->methodProphecies[$methodName][] = $methodProphecy; + static::__callStatic('nullOrIsNotA', array($value, $class, $message)); } /** - * Returns either all or related to single method prophecies. + * @psalm-pure + * @psalm-template UnexpectedType of object + * @psalm-param class-string $class * - * @param null|string $methodName + * @param iterable $value + * @param string $class + * @param string $message * - * @return MethodProphecy[] + * @throws InvalidArgumentException + * + * @return void */ - public function getMethodProphecies($methodName = null) + public static function allIsNotA($value, $class, $message = '') { - if (null === $methodName) { - return $this->methodProphecies; - } - $methodName = \strtolower($methodName); - if (!isset($this->methodProphecies[$methodName])) { - return array(); - } - return $this->methodProphecies[$methodName]; + static::__callStatic('allIsNotA', array($value, $class, $message)); } /** - * Makes specific method call. + * @psalm-pure + * @psalm-param array $classes * - * @param string $methodName - * @param array $arguments + * @param object|string|null $value + * @param string[] $classes + * @param string $message * - * @return mixed + * @throws InvalidArgumentException + * + * @return void */ - public function makeProphecyMethodCall($methodName, array $arguments) + public static function nullOrIsAnyOf($value, $classes, $message = '') { - $arguments = $this->revealer->reveal($arguments); - $return = $this->callCenter->makeCall($this, $methodName, $arguments); - return $this->revealer->reveal($return); + static::__callStatic('nullOrIsAnyOf', array($value, $classes, $message)); } /** - * Finds calls by method name & arguments wildcard. + * @psalm-pure + * @psalm-param array $classes * - * @param string $methodName - * @param ArgumentsWildcard $wildcard + * @param iterable $value + * @param string[] $classes + * @param string $message * - * @return Call[] + * @throws InvalidArgumentException + * + * @return void */ - public function findProphecyMethodCalls($methodName, \Prophecy\Argument\ArgumentsWildcard $wildcard) + public static function allIsAnyOf($value, $classes, $message = '') { - return $this->callCenter->findCalls($methodName, $wildcard); + static::__callStatic('allIsAnyOf', array($value, $classes, $message)); } /** - * Checks that registered method predictions do not fail. + * @psalm-pure + * @psalm-assert empty $value * - * @throws \Prophecy\Exception\Prediction\AggregateException If any of registered predictions fail - * @throws \Prophecy\Exception\Call\UnexpectedCallException + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function checkProphecyMethodsPredictions() + public static function nullOrIsEmpty($value, $message = '') { - $exception = new \Prophecy\Exception\Prediction\AggregateException(\sprintf("%s:\n", \get_class($this->reveal()))); - $exception->setObjectProphecy($this); - $this->callCenter->checkUnexpectedCalls(); - foreach ($this->methodProphecies as $prophecies) { - foreach ($prophecies as $prophecy) { - try { - $prophecy->checkPrediction(); - } catch (\Prophecy\Exception\Prediction\PredictionException $e) { - $exception->append($e); - } - } - } - if (\count($exception->getExceptions())) { - throw $exception; - } + static::__callStatic('nullOrIsEmpty', array($value, $message)); } /** - * Creates new method prophecy using specified method name and arguments. + * @psalm-pure + * @psalm-assert iterable $value * - * @param string $methodName - * @param array $arguments + * @param mixed $value + * @param string $message * - * @return MethodProphecy + * @throws InvalidArgumentException + * + * @return void */ - public function __call($methodName, array $arguments) + public static function allIsEmpty($value, $message = '') { - $arguments = new \Prophecy\Argument\ArgumentsWildcard($this->revealer->reveal($arguments)); - foreach ($this->getMethodProphecies($methodName) as $prophecy) { - $argumentsWildcard = $prophecy->getArgumentsWildcard(); - $comparator = $this->comparatorFactory->getComparatorFor($argumentsWildcard, $arguments); - try { - $comparator->assertEquals($argumentsWildcard, $arguments); - return $prophecy; - } catch (\PHPUnit\SebastianBergmann\Comparator\ComparisonFailure $failure) { - } - } - return new \Prophecy\Prophecy\MethodProphecy($this, $methodName, $arguments); + static::__callStatic('allIsEmpty', array($value, $message)); } /** - * Tries to get property value from double. + * @psalm-pure * - * @param string $name + * @param mixed $value + * @param string $message * - * @return mixed + * @throws InvalidArgumentException + * + * @return void */ - public function __get($name) + public static function nullOrNotEmpty($value, $message = '') { - return $this->reveal()->{$name}; + static::__callStatic('nullOrNotEmpty', array($value, $message)); } /** - * Tries to set property value to double. + * @psalm-pure * - * @param string $name * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function __set($name, $value) + public static function allNotEmpty($value, $message = '') { - $this->reveal()->{$name} = $this->revealer->reveal($value); + static::__callStatic('allNotEmpty', array($value, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Prophecy; - -/** - * Prophecies revealer interface. - * - * @author Konstantin Kudryashov - */ -interface RevealerInterface -{ /** - * Unwraps value(s). + * @psalm-pure + * @psalm-assert iterable $value * - * @param mixed $value + * @param mixed $value + * @param string $message * - * @return mixed - */ - public function reveal($value); -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Prophecy; - -/** - * Core Prophecy interface. - * - * @author Konstantin Kudryashov - */ -interface ProphecyInterface -{ - /** - * Reveals prophecy object (double) . + * @throws InvalidArgumentException * - * @return object + * @return void */ - public function reveal(); -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Prophecy; - -/** - * Controllable doubles interface. - * - * @author Konstantin Kudryashov - */ -interface ProphecySubjectInterface -{ + public static function allNull($value, $message = '') + { + static::__callStatic('allNull', array($value, $message)); + } /** - * Sets subject prophecy. + * @psalm-pure * - * @param ProphecyInterface $prophecy + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function setProphecy(\Prophecy\Prophecy\ProphecyInterface $prophecy); + public static function allNotNull($value, $message = '') + { + static::__callStatic('allNotNull', array($value, $message)); + } /** - * Returns subject prophecy. + * @psalm-pure + * @psalm-assert true|null $value * - * @return ProphecyInterface + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getProphecy(); -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Prophecy; - -/** - * Basic prophecies revealer. - * - * @author Konstantin Kudryashov - */ -class Revealer implements \Prophecy\Prophecy\RevealerInterface -{ + public static function nullOrTrue($value, $message = '') + { + static::__callStatic('nullOrTrue', array($value, $message)); + } /** - * Unwraps value(s). + * @psalm-pure + * @psalm-assert iterable $value * - * @param mixed $value + * @param mixed $value + * @param string $message * - * @return mixed + * @throws InvalidArgumentException + * + * @return void */ - public function reveal($value) + public static function allTrue($value, $message = '') { - if (\is_array($value)) { - return \array_map(array($this, __FUNCTION__), $value); - } - if (!\is_object($value)) { - return $value; - } - if ($value instanceof \Prophecy\Prophecy\ProphecyInterface) { - $value = $value->reveal(); - } - return $value; + static::__callStatic('allTrue', array($value, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler; - -use PHPUnit\Doctrine\Instantiator\Instantiator; -use Prophecy\Doubler\ClassPatch\ClassPatchInterface; -use Prophecy\Doubler\Generator\ClassMirror; -use Prophecy\Doubler\Generator\ClassCreator; -use Prophecy\Exception\InvalidArgumentException; -use ReflectionClass; -/** - * Cached class doubler. - * Prevents mirroring/creation of the same structure twice. - * - * @author Konstantin Kudryashov - */ -class Doubler -{ - private $mirror; - private $creator; - private $namer; /** - * @var ClassPatchInterface[] + * @psalm-pure + * @psalm-assert false|null $value + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - private $patches = array(); + public static function nullOrFalse($value, $message = '') + { + static::__callStatic('nullOrFalse', array($value, $message)); + } /** - * @var \Doctrine\Instantiator\Instantiator + * @psalm-pure + * @psalm-assert iterable $value + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - private $instantiator; + public static function allFalse($value, $message = '') + { + static::__callStatic('allFalse', array($value, $message)); + } /** - * Initializes doubler. + * @psalm-pure * - * @param ClassMirror $mirror - * @param ClassCreator $creator - * @param NameGenerator $namer + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function __construct(\Prophecy\Doubler\Generator\ClassMirror $mirror = null, \Prophecy\Doubler\Generator\ClassCreator $creator = null, \Prophecy\Doubler\NameGenerator $namer = null) + public static function nullOrNotFalse($value, $message = '') { - $this->mirror = $mirror ?: new \Prophecy\Doubler\Generator\ClassMirror(); - $this->creator = $creator ?: new \Prophecy\Doubler\Generator\ClassCreator(); - $this->namer = $namer ?: new \Prophecy\Doubler\NameGenerator(); + static::__callStatic('nullOrNotFalse', array($value, $message)); } /** - * Returns list of registered class patches. + * @psalm-pure * - * @return ClassPatchInterface[] + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getClassPatches() + public static function allNotFalse($value, $message = '') { - return $this->patches; + static::__callStatic('allNotFalse', array($value, $message)); } /** - * Registers new class patch. + * @param mixed $value + * @param string $message * - * @param ClassPatchInterface $patch + * @throws InvalidArgumentException + * + * @return void */ - public function registerClassPatch(\Prophecy\Doubler\ClassPatch\ClassPatchInterface $patch) + public static function nullOrIp($value, $message = '') { - $this->patches[] = $patch; - @\usort($this->patches, function (\Prophecy\Doubler\ClassPatch\ClassPatchInterface $patch1, \Prophecy\Doubler\ClassPatch\ClassPatchInterface $patch2) { - return $patch2->getPriority() - $patch1->getPriority(); - }); + static::__callStatic('nullOrIp', array($value, $message)); } /** - * Creates double from specific class or/and list of interfaces. - * - * @param ReflectionClass $class - * @param ReflectionClass[] $interfaces Array of ReflectionClass instances - * @param array $args Constructor arguments + * @param mixed $value + * @param string $message * - * @return DoubleInterface + * @throws InvalidArgumentException * - * @throws \Prophecy\Exception\InvalidArgumentException + * @return void */ - public function double(\ReflectionClass $class = null, array $interfaces, array $args = null) + public static function allIp($value, $message = '') { - foreach ($interfaces as $interface) { - if (!$interface instanceof \ReflectionClass) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf("[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n" . "a second argument to `Doubler::double(...)`, but got %s.", \is_object($interface) ? \get_class($interface) . ' class' : \gettype($interface))); - } - } - $classname = $this->createDoubleClass($class, $interfaces); - $reflection = new \ReflectionClass($classname); - if (null !== $args) { - return $reflection->newInstanceArgs($args); - } - if (null === ($constructor = $reflection->getConstructor()) || $constructor->isPublic() && !$constructor->isFinal()) { - return $reflection->newInstance(); - } - if (!$this->instantiator) { - $this->instantiator = new \PHPUnit\Doctrine\Instantiator\Instantiator(); - } - return $this->instantiator->instantiate($classname); + static::__callStatic('allIp', array($value, $message)); } /** - * Creates double class and returns its FQN. + * @param mixed $value + * @param string $message * - * @param ReflectionClass $class - * @param ReflectionClass[] $interfaces + * @throws InvalidArgumentException * - * @return string + * @return void */ - protected function createDoubleClass(\ReflectionClass $class = null, array $interfaces) + public static function nullOrIpv4($value, $message = '') { - $name = $this->namer->name($class, $interfaces); - $node = $this->mirror->reflect($class, $interfaces); - foreach ($this->patches as $patch) { - if ($patch->supports($node)) { - $patch->apply($node); - } - } - $this->creator->create($name, $node); - return $name; + static::__callStatic('nullOrIpv4', array($value, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler; - -/** - * Core double interface. - * All doubled classes will implement this one. - * - * @author Konstantin Kudryashov - */ -interface DoubleInterface -{ -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler; - -use ReflectionClass; -/** - * Name generator. - * Generates classname for double. - * - * @author Konstantin Kudryashov - */ -class NameGenerator -{ - private static $counter = 1; /** - * Generates name. + * @param mixed $value + * @param string $message * - * @param ReflectionClass $class - * @param ReflectionClass[] $interfaces + * @throws InvalidArgumentException * - * @return string + * @return void */ - public function name(\ReflectionClass $class = null, array $interfaces) + public static function allIpv4($value, $message = '') { - $parts = array(); - if (null !== $class) { - $parts[] = $class->getName(); - } else { - foreach ($interfaces as $interface) { - $parts[] = $interface->getShortName(); - } - } - if (!\count($parts)) { - $parts[] = 'stdClass'; - } - return \sprintf('Double\\%s\\P%d', \implode('\\', $parts), self::$counter++); + static::__callStatic('allIpv4', array($value, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler; - -use Prophecy\Exception\Doubler\DoubleException; -use Prophecy\Exception\Doubler\ClassNotFoundException; -use Prophecy\Exception\Doubler\InterfaceNotFoundException; -use ReflectionClass; -/** - * Lazy double. - * Gives simple interface to describe double before creating it. - * - * @author Konstantin Kudryashov - */ -class LazyDouble -{ - private $doubler; - private $class; - private $interfaces = array(); - private $arguments = null; - private $double; /** - * Initializes lazy double. + * @param mixed $value + * @param string $message * - * @param Doubler $doubler + * @throws InvalidArgumentException + * + * @return void */ - public function __construct(\Prophecy\Doubler\Doubler $doubler) + public static function nullOrIpv6($value, $message = '') { - $this->doubler = $doubler; + static::__callStatic('nullOrIpv6', array($value, $message)); } /** - * Tells doubler to use specific class as parent one for double. + * @param mixed $value + * @param string $message * - * @param string|ReflectionClass $class + * @throws InvalidArgumentException * - * @throws \Prophecy\Exception\Doubler\ClassNotFoundException - * @throws \Prophecy\Exception\Doubler\DoubleException + * @return void */ - public function setParentClass($class) + public static function allIpv6($value, $message = '') { - if (null !== $this->double) { - throw new \Prophecy\Exception\Doubler\DoubleException('Can not extend class with already instantiated double.'); - } - if (!$class instanceof \ReflectionClass) { - if (!\class_exists($class)) { - throw new \Prophecy\Exception\Doubler\ClassNotFoundException(\sprintf('Class %s not found.', $class), $class); - } - $class = new \ReflectionClass($class); - } - $this->class = $class; + static::__callStatic('allIpv6', array($value, $message)); } /** - * Tells doubler to implement specific interface with double. + * @param mixed $value + * @param string $message * - * @param string|ReflectionClass $interface + * @throws InvalidArgumentException * - * @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException - * @throws \Prophecy\Exception\Doubler\DoubleException + * @return void */ - public function addInterface($interface) + public static function nullOrEmail($value, $message = '') { - if (null !== $this->double) { - throw new \Prophecy\Exception\Doubler\DoubleException('Can not implement interface with already instantiated double.'); - } - if (!$interface instanceof \ReflectionClass) { - if (!\interface_exists($interface)) { - throw new \Prophecy\Exception\Doubler\InterfaceNotFoundException(\sprintf('Interface %s not found.', $interface), $interface); - } - $interface = new \ReflectionClass($interface); - } - $this->interfaces[] = $interface; + static::__callStatic('nullOrEmail', array($value, $message)); } /** - * Sets constructor arguments. + * @param mixed $value + * @param string $message * - * @param array $arguments + * @throws InvalidArgumentException + * + * @return void */ - public function setArguments(array $arguments = null) + public static function allEmail($value, $message = '') { - $this->arguments = $arguments; + static::__callStatic('allEmail', array($value, $message)); } /** - * Creates double instance or returns already created one. + * @param array|null $values + * @param string $message * - * @return DoubleInterface + * @throws InvalidArgumentException + * + * @return void */ - public function getInstance() + public static function nullOrUniqueValues($values, $message = '') { - if (null === $this->double) { - if (null !== $this->arguments) { - return $this->double = $this->doubler->double($this->class, $this->interfaces, $this->arguments); - } - $this->double = $this->doubler->double($this->class, $this->interfaces); - } - return $this->double; + static::__callStatic('nullOrUniqueValues', array($values, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler; - -use ReflectionClass; -/** - * Cached class doubler. - * Prevents mirroring/creation of the same structure twice. - * - * @author Konstantin Kudryashov - */ -class CachedDoubler extends \Prophecy\Doubler\Doubler -{ - private static $classes = array(); /** - * {@inheritdoc} + * @param iterable $values + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - protected function createDoubleClass(\ReflectionClass $class = null, array $interfaces) + public static function allUniqueValues($values, $message = '') { - $classId = $this->generateClassId($class, $interfaces); - if (isset(self::$classes[$classId])) { - return self::$classes[$classId]; - } - return self::$classes[$classId] = parent::createDoubleClass($class, $interfaces); + static::__callStatic('allUniqueValues', array($values, $message)); } /** - * @param ReflectionClass $class - * @param ReflectionClass[] $interfaces + * @param mixed $value + * @param mixed $expect + * @param string $message * - * @return string + * @throws InvalidArgumentException + * + * @return void */ - private function generateClassId(\ReflectionClass $class = null, array $interfaces) + public static function nullOrEq($value, $expect, $message = '') { - $parts = array(); - if (null !== $class) { - $parts[] = $class->getName(); - } - foreach ($interfaces as $interface) { - $parts[] = $interface->getName(); - } - foreach ($this->getClassPatches() as $patch) { - $parts[] = \get_class($patch); - } - \sort($parts); - return \md5(\implode('', $parts)); + static::__callStatic('nullOrEq', array($value, $expect, $message)); } - public function resetCache() - { - self::$classes = array(); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler\Generator; - -use Prophecy\Doubler\Generator\Node\ReturnTypeNode; -use Prophecy\Doubler\Generator\Node\TypeNodeAbstract; -/** - * Class code creator. - * Generates PHP code for specific class node tree. - * - * @author Konstantin Kudryashov - */ -class ClassCodeGenerator -{ - public function __construct(\Prophecy\Doubler\Generator\TypeHintReference $typeHintReference = null) + /** + * @param mixed $value + * @param mixed $expect + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allEq($value, $expect, $message = '') { + static::__callStatic('allEq', array($value, $expect, $message)); } /** - * Generates PHP code for class node. + * @param mixed $value + * @param mixed $expect + * @param string $message * - * @param string $classname - * @param Node\ClassNode $class + * @throws InvalidArgumentException * - * @return string + * @return void */ - public function generate($classname, \Prophecy\Doubler\Generator\Node\ClassNode $class) - { - $parts = \explode('\\', $classname); - $classname = \array_pop($parts); - $namespace = \implode('\\', $parts); - $code = \sprintf("class %s extends \\%s implements %s {\n", $classname, $class->getParentClass(), \implode(', ', \array_map(function ($interface) { - return '\\' . $interface; - }, $class->getInterfaces()))); - foreach ($class->getProperties() as $name => $visibility) { - $code .= \sprintf("%s \$%s;\n", $visibility, $name); - } - $code .= "\n"; - foreach ($class->getMethods() as $method) { - $code .= $this->generateMethod($method) . "\n"; - } - $code .= "\n}"; - return \sprintf("namespace %s {\n%s\n}", $namespace, $code); - } - private function generateMethod(\Prophecy\Doubler\Generator\Node\MethodNode $method) - { - $php = \sprintf("%s %s function %s%s(%s)%s {\n", $method->getVisibility(), $method->isStatic() ? 'static' : '', $method->returnsReference() ? '&' : '', $method->getName(), \implode(', ', $this->generateArguments($method->getArguments())), ($ret = $this->generateTypes($method->getReturnTypeNode())) ? ': ' . $ret : ''); - $php .= $method->getCode() . "\n"; - return $php . '}'; - } - private function generateTypes(\Prophecy\Doubler\Generator\Node\TypeNodeAbstract $typeNode) : string + public static function nullOrNotEq($value, $expect, $message = '') { - if (!$typeNode->getTypes()) { - return ''; - } - // When we require PHP 8 we can stop generating ?foo nullables and remove this first block - if ($typeNode->canUseNullShorthand()) { - return \sprintf('?%s', $typeNode->getNonNullTypes()[0]); - } else { - return \join('|', $typeNode->getTypes()); - } + static::__callStatic('nullOrNotEq', array($value, $expect, $message)); } - private function generateArguments(array $arguments) + /** + * @param mixed $value + * @param mixed $expect + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allNotEq($value, $expect, $message = '') { - return \array_map(function (\Prophecy\Doubler\Generator\Node\ArgumentNode $argument) { - $php = $this->generateTypes($argument->getTypeNode()); - $php .= ' ' . ($argument->isPassedByReference() ? '&' : ''); - $php .= $argument->isVariadic() ? '...' : ''; - $php .= '$' . $argument->getName(); - if ($argument->isOptional() && !$argument->isVariadic()) { - $php .= ' = ' . \var_export($argument->getDefault(), \true); - } - return $php; - }, $arguments); + static::__callStatic('allNotEq', array($value, $expect, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler\Generator; - -use Prophecy\Doubler\Generator\Node\ArgumentTypeNode; -use Prophecy\Doubler\Generator\Node\ReturnTypeNode; -use Prophecy\Exception\InvalidArgumentException; -use Prophecy\Exception\Doubler\ClassMirrorException; -use ReflectionClass; -use ReflectionMethod; -use ReflectionNamedType; -use ReflectionParameter; -use ReflectionType; -use ReflectionUnionType; -/** - * Class mirror. - * Core doubler class. Mirrors specific class and/or interfaces into class node tree. - * - * @author Konstantin Kudryashov - */ -class ClassMirror -{ - private static $reflectableMethods = array('__construct', '__destruct', '__sleep', '__wakeup', '__toString', '__call', '__invoke'); /** - * Reflects provided arguments into class node. + * @psalm-pure * - * @param ReflectionClass|null $class - * @param ReflectionClass[] $interfaces + * @param mixed $value + * @param mixed $expect + * @param string $message * - * @return Node\ClassNode + * @throws InvalidArgumentException * + * @return void */ - public function reflect(?\ReflectionClass $class, array $interfaces) - { - $node = new \Prophecy\Doubler\Generator\Node\ClassNode(); - if (null !== $class) { - if (\true === $class->isInterface()) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf("Could not reflect %s as a class, because it\n" . "is interface - use the second argument instead.", $class->getName())); - } - $this->reflectClassToNode($class, $node); - } - foreach ($interfaces as $interface) { - if (!$interface instanceof \ReflectionClass) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf("[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n" . "a second argument to `ClassMirror::reflect(...)`, but got %s.", \is_object($interface) ? \get_class($interface) . ' class' : \gettype($interface))); - } - if (\false === $interface->isInterface()) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf("Could not reflect %s as an interface, because it\n" . "is class - use the first argument instead.", $interface->getName())); - } - $this->reflectInterfaceToNode($interface, $node); - } - $node->addInterface('Prophecy\\Doubler\\Generator\\ReflectionInterface'); - return $node; - } - private function reflectClassToNode(\ReflectionClass $class, \Prophecy\Doubler\Generator\Node\ClassNode $node) - { - if (\true === $class->isFinal()) { - throw new \Prophecy\Exception\Doubler\ClassMirrorException(\sprintf('Could not reflect class %s as it is marked final.', $class->getName()), $class); - } - $node->setParentClass($class->getName()); - foreach ($class->getMethods(\ReflectionMethod::IS_ABSTRACT) as $method) { - if (\false === $method->isProtected()) { - continue; - } - $this->reflectMethodToNode($method, $node); - } - foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { - if (0 === \strpos($method->getName(), '_') && !\in_array($method->getName(), self::$reflectableMethods)) { - continue; - } - if (\true === $method->isFinal()) { - $node->addUnextendableMethod($method->getName()); - continue; - } - $this->reflectMethodToNode($method, $node); - } - } - private function reflectInterfaceToNode(\ReflectionClass $interface, \Prophecy\Doubler\Generator\Node\ClassNode $node) - { - $node->addInterface($interface->getName()); - foreach ($interface->getMethods() as $method) { - $this->reflectMethodToNode($method, $node); - } - } - private function reflectMethodToNode(\ReflectionMethod $method, \Prophecy\Doubler\Generator\Node\ClassNode $classNode) - { - $node = new \Prophecy\Doubler\Generator\Node\MethodNode($method->getName()); - if (\true === $method->isProtected()) { - $node->setVisibility('protected'); - } - if (\true === $method->isStatic()) { - $node->setStatic(); - } - if (\true === $method->returnsReference()) { - $node->setReturnsReference(); - } - if ($method->hasReturnType()) { - $returnTypes = $this->getTypeHints($method->getReturnType(), $method->getDeclaringClass(), $method->getReturnType()->allowsNull()); - $node->setReturnTypeNode(new \Prophecy\Doubler\Generator\Node\ReturnTypeNode(...$returnTypes)); - } - if (\is_array($params = $method->getParameters()) && \count($params)) { - foreach ($params as $param) { - $this->reflectArgumentToNode($param, $node); - } - } - $classNode->addMethod($node); - } - private function reflectArgumentToNode(\ReflectionParameter $parameter, \Prophecy\Doubler\Generator\Node\MethodNode $methodNode) - { - $name = $parameter->getName() == '...' ? '__dot_dot_dot__' : $parameter->getName(); - $node = new \Prophecy\Doubler\Generator\Node\ArgumentNode($name); - $typeHints = $this->getTypeHints($parameter->getType(), $parameter->getDeclaringClass(), $parameter->allowsNull()); - $node->setTypeNode(new \Prophecy\Doubler\Generator\Node\ArgumentTypeNode(...$typeHints)); - if ($parameter->isVariadic()) { - $node->setAsVariadic(); - } - if ($this->hasDefaultValue($parameter)) { - $node->setDefault($this->getDefaultValue($parameter)); - } - if ($parameter->isPassedByReference()) { - $node->setAsPassedByReference(); - } - $methodNode->addArgument($node); - } - private function hasDefaultValue(\ReflectionParameter $parameter) - { - if ($parameter->isVariadic()) { - return \false; - } - if ($parameter->isDefaultValueAvailable()) { - return \true; - } - return $parameter->isOptional() || $parameter->allowsNull() && $parameter->getType(); - } - private function getDefaultValue(\ReflectionParameter $parameter) - { - if (!$parameter->isDefaultValueAvailable()) { - return null; - } - return $parameter->getDefaultValue(); - } - private function getTypeHints(?\ReflectionType $type, ?\ReflectionClass $class, bool $allowsNull) : array - { - $types = []; - if ($type instanceof \ReflectionNamedType) { - $types = [$type->getName()]; - } elseif ($type instanceof \ReflectionUnionType) { - $types = $type->getTypes(); - } - $types = \array_map(function (string $type) use($class) { - if ($type === 'self') { - return $class->getName(); - } - if ($type === 'parent') { - return $class->getParentClass()->getName(); - } - return $type; - }, $types); - if ($types && $types != ['mixed'] && $allowsNull) { - $types[] = 'null'; - } - return $types; - } -} -types['void']) && \count($this->types) !== 1) { - throw new \Prophecy\Exception\Doubler\DoubleException('void cannot be part of a union'); - } - parent::guardIsValidType(); - } - public function isVoid() : bool + public static function nullOrSame($value, $expect, $message = '') { - return $this->types == ['void' => 'void']; + static::__callStatic('nullOrSame', array($value, $expect, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler\Generator\Node; - -use Prophecy\Doubler\Generator\TypeHintReference; -use Prophecy\Exception\InvalidArgumentException; -/** - * Method node. - * - * @author Konstantin Kudryashov - */ -class MethodNode -{ - private $name; - private $code; - private $visibility = 'public'; - private $static = \false; - private $returnsReference = \false; - /** @var ReturnTypeNode */ - private $returnTypeNode; - /** - * @var ArgumentNode[] - */ - private $arguments = array(); /** - * @param string $name - * @param string $code + * @psalm-pure + * + * @param mixed $value + * @param mixed $expect + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function __construct($name, $code = null, \Prophecy\Doubler\Generator\TypeHintReference $typeHintReference = null) - { - $this->name = $name; - $this->code = $code; - $this->returnTypeNode = new \Prophecy\Doubler\Generator\Node\ReturnTypeNode(); - } - public function getVisibility() + public static function allSame($value, $expect, $message = '') { - return $this->visibility; + static::__callStatic('allSame', array($value, $expect, $message)); } /** - * @param string $visibility + * @psalm-pure + * + * @param mixed $value + * @param mixed $expect + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function setVisibility($visibility) - { - $visibility = \strtolower($visibility); - if (!\in_array($visibility, array('public', 'private', 'protected'))) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf('`%s` method visibility is not supported.', $visibility)); - } - $this->visibility = $visibility; - } - public function isStatic() - { - return $this->static; - } - public function setStatic($static = \true) + public static function nullOrNotSame($value, $expect, $message = '') { - $this->static = (bool) $static; + static::__callStatic('nullOrNotSame', array($value, $expect, $message)); } - public function returnsReference() + /** + * @psalm-pure + * + * @param mixed $value + * @param mixed $expect + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allNotSame($value, $expect, $message = '') { - return $this->returnsReference; + static::__callStatic('allNotSame', array($value, $expect, $message)); } - public function setReturnsReference() + /** + * @psalm-pure + * + * @param mixed $value + * @param mixed $limit + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function nullOrGreaterThan($value, $limit, $message = '') { - $this->returnsReference = \true; + static::__callStatic('nullOrGreaterThan', array($value, $limit, $message)); } - public function getName() + /** + * @psalm-pure + * + * @param mixed $value + * @param mixed $limit + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allGreaterThan($value, $limit, $message = '') { - return $this->name; + static::__callStatic('allGreaterThan', array($value, $limit, $message)); } - public function addArgument(\Prophecy\Doubler\Generator\Node\ArgumentNode $argument) + /** + * @psalm-pure + * + * @param mixed $value + * @param mixed $limit + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function nullOrGreaterThanEq($value, $limit, $message = '') { - $this->arguments[] = $argument; + static::__callStatic('nullOrGreaterThanEq', array($value, $limit, $message)); } /** - * @return ArgumentNode[] + * @psalm-pure + * + * @param mixed $value + * @param mixed $limit + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getArguments() + public static function allGreaterThanEq($value, $limit, $message = '') { - return $this->arguments; + static::__callStatic('allGreaterThanEq', array($value, $limit, $message)); } /** - * @deprecated use getReturnTypeNode instead - * @return bool + * @psalm-pure + * + * @param mixed $value + * @param mixed $limit + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function hasReturnType() + public static function nullOrLessThan($value, $limit, $message = '') { - return (bool) $this->returnTypeNode->getNonNullTypes(); + static::__callStatic('nullOrLessThan', array($value, $limit, $message)); } - public function setReturnTypeNode(\Prophecy\Doubler\Generator\Node\ReturnTypeNode $returnTypeNode) : void + /** + * @psalm-pure + * + * @param mixed $value + * @param mixed $limit + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allLessThan($value, $limit, $message = '') { - $this->returnTypeNode = $returnTypeNode; + static::__callStatic('allLessThan', array($value, $limit, $message)); } /** - * @deprecated use setReturnTypeNode instead - * @param string $type + * @psalm-pure + * + * @param mixed $value + * @param mixed $limit + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function setReturnType($type = null) + public static function nullOrLessThanEq($value, $limit, $message = '') { - $this->returnTypeNode = $type === '' || $type === null ? new \Prophecy\Doubler\Generator\Node\ReturnTypeNode() : new \Prophecy\Doubler\Generator\Node\ReturnTypeNode($type); + static::__callStatic('nullOrLessThanEq', array($value, $limit, $message)); } /** - * @deprecated use setReturnTypeNode instead - * @param bool $bool + * @psalm-pure + * + * @param mixed $value + * @param mixed $limit + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function setNullableReturnType($bool = \true) + public static function allLessThanEq($value, $limit, $message = '') { - if ($bool) { - $this->returnTypeNode = new \Prophecy\Doubler\Generator\Node\ReturnTypeNode('null', ...$this->returnTypeNode->getTypes()); - } else { - $this->returnTypeNode = new \Prophecy\Doubler\Generator\Node\ReturnTypeNode(...$this->returnTypeNode->getNonNullTypes()); - } + static::__callStatic('allLessThanEq', array($value, $limit, $message)); } /** - * @deprecated use getReturnTypeNode instead - * @return string|null + * @psalm-pure + * + * @param mixed $value + * @param mixed $min + * @param mixed $max + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getReturnType() + public static function nullOrRange($value, $min, $max, $message = '') { - if ($types = $this->returnTypeNode->getNonNullTypes()) { - return $types[0]; - } - return null; + static::__callStatic('nullOrRange', array($value, $min, $max, $message)); } - public function getReturnTypeNode() : \Prophecy\Doubler\Generator\Node\ReturnTypeNode + /** + * @psalm-pure + * + * @param mixed $value + * @param mixed $min + * @param mixed $max + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allRange($value, $min, $max, $message = '') { - return $this->returnTypeNode; + static::__callStatic('allRange', array($value, $min, $max, $message)); } /** - * @deprecated use getReturnTypeNode instead - * @return bool + * @psalm-pure + * + * @param mixed $value + * @param array $values + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function hasNullableReturnType() + public static function nullOrOneOf($value, $values, $message = '') { - return $this->returnTypeNode->canUseNullShorthand(); + static::__callStatic('nullOrOneOf', array($value, $values, $message)); } /** - * @param string $code + * @psalm-pure + * + * @param mixed $value + * @param array $values + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function setCode($code) + public static function allOneOf($value, $values, $message = '') { - $this->code = $code; + static::__callStatic('allOneOf', array($value, $values, $message)); } - public function getCode() - { - if ($this->returnsReference) { - return "throw new \\Prophecy\\Exception\\Doubler\\ReturnByReferenceException('Returning by reference not supported', get_class(\$this), '{$this->name}');"; - } - return (string) $this->code; - } - public function useParentCode() - { - $this->code = \sprintf('return parent::%s(%s);', $this->getName(), \implode(', ', \array_map(array($this, 'generateArgument'), $this->arguments))); - } - private function generateArgument(\Prophecy\Doubler\Generator\Node\ArgumentNode $arg) - { - $argument = '$' . $arg->getName(); - if ($arg->isVariadic()) { - $argument = '...' . $argument; - } - return $argument; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler\Generator\Node; - -/** - * Argument node. - * - * @author Konstantin Kudryashov - */ -class ArgumentNode -{ - private $name; - private $default; - private $optional = \false; - private $byReference = \false; - private $isVariadic = \false; - /** @var ArgumentTypeNode */ - private $typeNode; /** - * @param string $name + * @psalm-pure + * + * @param mixed $value + * @param array $values + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function __construct($name) - { - $this->name = $name; - $this->typeNode = new \Prophecy\Doubler\Generator\Node\ArgumentTypeNode(); - } - public function getName() - { - return $this->name; - } - public function setTypeNode(\Prophecy\Doubler\Generator\Node\ArgumentTypeNode $typeNode) - { - $this->typeNode = $typeNode; - } - public function getTypeNode() : \Prophecy\Doubler\Generator\Node\ArgumentTypeNode - { - return $this->typeNode; - } - public function hasDefault() - { - return $this->isOptional() && !$this->isVariadic(); - } - public function getDefault() - { - return $this->default; - } - public function setDefault($default = null) - { - $this->optional = \true; - $this->default = $default; - } - public function isOptional() - { - return $this->optional; - } - public function setAsPassedByReference($byReference = \true) - { - $this->byReference = $byReference; - } - public function isPassedByReference() - { - return $this->byReference; - } - public function setAsVariadic($isVariadic = \true) + public static function nullOrInArray($value, $values, $message = '') { - $this->isVariadic = $isVariadic; + static::__callStatic('nullOrInArray', array($value, $values, $message)); } - public function isVariadic() + /** + * @psalm-pure + * + * @param mixed $value + * @param array $values + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allInArray($value, $values, $message = '') { - return $this->isVariadic; + static::__callStatic('allInArray', array($value, $values, $message)); } /** - * @deprecated use getArgumentTypeNode instead - * @return string|null + * @psalm-pure + * + * @param string|null $value + * @param string $subString + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getTypeHint() + public static function nullOrContains($value, $subString, $message = '') { - $type = $this->typeNode->getNonNullTypes() ? $this->typeNode->getNonNullTypes()[0] : null; - return $type ? \ltrim($type, '\\') : null; + static::__callStatic('nullOrContains', array($value, $subString, $message)); } /** - * @deprecated use setArgumentTypeNode instead - * @param string|null $typeHint + * @psalm-pure + * + * @param iterable $value + * @param string $subString + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function setTypeHint($typeHint = null) + public static function allContains($value, $subString, $message = '') { - $this->typeNode = $typeHint === null ? new \Prophecy\Doubler\Generator\Node\ArgumentTypeNode() : new \Prophecy\Doubler\Generator\Node\ArgumentTypeNode($typeHint); + static::__callStatic('allContains', array($value, $subString, $message)); } /** - * @deprecated use getArgumentTypeNode instead - * @return bool + * @psalm-pure + * + * @param string|null $value + * @param string $subString + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function isNullable() + public static function nullOrNotContains($value, $subString, $message = '') { - return $this->typeNode->canUseNullShorthand(); + static::__callStatic('nullOrNotContains', array($value, $subString, $message)); } /** - * @deprecated use getArgumentTypeNode instead - * @param bool $isNullable + * @psalm-pure + * + * @param iterable $value + * @param string $subString + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function setAsNullable($isNullable = \true) + public static function allNotContains($value, $subString, $message = '') { - $nonNullTypes = $this->typeNode->getNonNullTypes(); - $this->typeNode = $isNullable ? new \Prophecy\Doubler\Generator\Node\ArgumentTypeNode('null', ...$nonNullTypes) : new \Prophecy\Doubler\Generator\Node\ArgumentTypeNode(...$nonNullTypes); + static::__callStatic('allNotContains', array($value, $subString, $message)); } -} -getRealType($type); - $this->types[$type] = $type; - } - $this->guardIsValidType(); + static::__callStatic('nullOrNotWhitespaceOnly', array($value, $message)); } - public function canUseNullShorthand() : bool + /** + * @psalm-pure + * + * @param iterable $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allNotWhitespaceOnly($value, $message = '') { - return isset($this->types['null']) && \count($this->types) <= 2; + static::__callStatic('allNotWhitespaceOnly', array($value, $message)); } - public function getTypes() : array + /** + * @psalm-pure + * + * @param string|null $value + * @param string $prefix + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function nullOrStartsWith($value, $prefix, $message = '') { - return \array_values($this->types); + static::__callStatic('nullOrStartsWith', array($value, $prefix, $message)); } - public function getNonNullTypes() : array + /** + * @psalm-pure + * + * @param iterable $value + * @param string $prefix + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allStartsWith($value, $prefix, $message = '') { - $nonNullTypes = $this->types; - unset($nonNullTypes['null']); - return \array_values($nonNullTypes); + static::__callStatic('allStartsWith', array($value, $prefix, $message)); } - protected function prefixWithNsSeparator(string $type) : string + /** + * @psalm-pure + * + * @param string|null $value + * @param string $prefix + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function nullOrNotStartsWith($value, $prefix, $message = '') { - return '\\' . \ltrim($type, '\\'); + static::__callStatic('nullOrNotStartsWith', array($value, $prefix, $message)); } - protected function getRealType(string $type) : string + /** + * @psalm-pure + * + * @param iterable $value + * @param string $prefix + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allNotStartsWith($value, $prefix, $message = '') { - switch ($type) { - // type aliases - case 'double': - case 'real': - return 'float'; - case 'boolean': - return 'bool'; - case 'integer': - return 'int'; - // built in types - case 'self': - case 'array': - case 'callable': - case 'bool': - case 'float': - case 'int': - case 'string': - case 'iterable': - case 'object': - case 'null': - return $type; - case 'mixed': - return \PHP_VERSION_ID < 80000 ? $this->prefixWithNsSeparator($type) : $type; - default: - return $this->prefixWithNsSeparator($type); - } + static::__callStatic('allNotStartsWith', array($value, $prefix, $message)); } - protected function guardIsValidType() + /** + * @psalm-pure + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function nullOrStartsWithLetter($value, $message = '') { - if ($this->types == ['null' => 'null']) { - throw new \Prophecy\Exception\Doubler\DoubleException('Argument type cannot be standalone null'); - } - if (\PHP_VERSION_ID >= 80000 && isset($this->types['mixed']) && \count($this->types) !== 1) { - throw new \Prophecy\Exception\Doubler\DoubleException('mixed cannot be part of a union'); - } + static::__callStatic('nullOrStartsWithLetter', array($value, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler\Generator\Node; - -use Prophecy\Exception\Doubler\MethodNotExtendableException; -use Prophecy\Exception\InvalidArgumentException; -/** - * Class node. - * - * @author Konstantin Kudryashov - */ -class ClassNode -{ - private $parentClass = 'stdClass'; - private $interfaces = array(); - private $properties = array(); - private $unextendableMethods = array(); /** - * @var MethodNode[] + * @psalm-pure + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - private $methods = array(); - public function getParentClass() + public static function allStartsWithLetter($value, $message = '') { - return $this->parentClass; + static::__callStatic('allStartsWithLetter', array($value, $message)); } /** - * @param string $class + * @psalm-pure + * + * @param string|null $value + * @param string $suffix + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function setParentClass($class) + public static function nullOrEndsWith($value, $suffix, $message = '') { - $this->parentClass = $class ?: 'stdClass'; + static::__callStatic('nullOrEndsWith', array($value, $suffix, $message)); } /** - * @return string[] + * @psalm-pure + * + * @param iterable $value + * @param string $suffix + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getInterfaces() + public static function allEndsWith($value, $suffix, $message = '') { - return $this->interfaces; + static::__callStatic('allEndsWith', array($value, $suffix, $message)); } /** - * @param string $interface + * @psalm-pure + * + * @param string|null $value + * @param string $suffix + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function addInterface($interface) + public static function nullOrNotEndsWith($value, $suffix, $message = '') { - if ($this->hasInterface($interface)) { - return; - } - \array_unshift($this->interfaces, $interface); + static::__callStatic('nullOrNotEndsWith', array($value, $suffix, $message)); } /** - * @param string $interface + * @psalm-pure * - * @return bool + * @param iterable $value + * @param string $suffix + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function hasInterface($interface) + public static function allNotEndsWith($value, $suffix, $message = '') { - return \in_array($interface, $this->interfaces); + static::__callStatic('allNotEndsWith', array($value, $suffix, $message)); } - public function getProperties() + /** + * @psalm-pure + * + * @param string|null $value + * @param string $pattern + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function nullOrRegex($value, $pattern, $message = '') { - return $this->properties; + static::__callStatic('nullOrRegex', array($value, $pattern, $message)); } - public function addProperty($name, $visibility = 'public') + /** + * @psalm-pure + * + * @param iterable $value + * @param string $pattern + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allRegex($value, $pattern, $message = '') { - $visibility = \strtolower($visibility); - if (!\in_array($visibility, array('public', 'private', 'protected'))) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf('`%s` property visibility is not supported.', $visibility)); - } - $this->properties[$name] = $visibility; + static::__callStatic('allRegex', array($value, $pattern, $message)); } /** - * @return MethodNode[] + * @psalm-pure + * + * @param string|null $value + * @param string $pattern + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getMethods() + public static function nullOrNotRegex($value, $pattern, $message = '') { - return $this->methods; + static::__callStatic('nullOrNotRegex', array($value, $pattern, $message)); } - public function addMethod(\Prophecy\Doubler\Generator\Node\MethodNode $method, $force = \false) + /** + * @psalm-pure + * + * @param iterable $value + * @param string $pattern + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allNotRegex($value, $pattern, $message = '') { - if (!$this->isExtendable($method->getName())) { - $message = \sprintf('Method `%s` is not extendable, so can not be added.', $method->getName()); - throw new \Prophecy\Exception\Doubler\MethodNotExtendableException($message, $this->getParentClass(), $method->getName()); - } - if ($force || !isset($this->methods[$method->getName()])) { - $this->methods[$method->getName()] = $method; - } + static::__callStatic('allNotRegex', array($value, $pattern, $message)); } - public function removeMethod($name) + /** + * @psalm-pure + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function nullOrUnicodeLetters($value, $message = '') { - unset($this->methods[$name]); + static::__callStatic('nullOrUnicodeLetters', array($value, $message)); } /** - * @param string $name + * @psalm-pure * - * @return MethodNode|null + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getMethod($name) + public static function allUnicodeLetters($value, $message = '') { - return $this->hasMethod($name) ? $this->methods[$name] : null; + static::__callStatic('allUnicodeLetters', array($value, $message)); } /** - * @param string $name + * @psalm-pure * - * @return bool + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function hasMethod($name) + public static function nullOrAlpha($value, $message = '') { - return isset($this->methods[$name]); + static::__callStatic('nullOrAlpha', array($value, $message)); } /** - * @return string[] + * @psalm-pure + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getUnextendableMethods() + public static function allAlpha($value, $message = '') { - return $this->unextendableMethods; + static::__callStatic('allAlpha', array($value, $message)); } /** - * @param string $unextendableMethod + * @psalm-pure + * + * @param string|null $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function addUnextendableMethod($unextendableMethod) + public static function nullOrDigits($value, $message = '') { - if (!$this->isExtendable($unextendableMethod)) { - return; - } - $this->unextendableMethods[] = $unextendableMethod; + static::__callStatic('nullOrDigits', array($value, $message)); } /** - * @param string $method - * @return bool + * @psalm-pure + * + * @param iterable $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function isExtendable($method) + public static function allDigits($value, $message = '') { - return !\in_array($method, $this->unextendableMethods); + static::__callStatic('allDigits', array($value, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler\Generator; - -use Prophecy\Exception\Doubler\ClassCreatorException; -/** - * Class creator. - * Creates specific class in current environment. - * - * @author Konstantin Kudryashov - */ -class ClassCreator -{ - private $generator; /** - * Initializes creator. + * @psalm-pure * - * @param ClassCodeGenerator $generator + * @param string|null $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function __construct(\Prophecy\Doubler\Generator\ClassCodeGenerator $generator = null) + public static function nullOrAlnum($value, $message = '') { - $this->generator = $generator ?: new \Prophecy\Doubler\Generator\ClassCodeGenerator(); + static::__callStatic('nullOrAlnum', array($value, $message)); } /** - * Creates class. + * @psalm-pure * - * @param string $classname - * @param Node\ClassNode $class + * @param iterable $value + * @param string $message * - * @return mixed + * @throws InvalidArgumentException * - * @throws \Prophecy\Exception\Doubler\ClassCreatorException + * @return void */ - public function create($classname, \Prophecy\Doubler\Generator\Node\ClassNode $class) - { - $code = $this->generator->generate($classname, $class); - $return = eval($code); - if (!\class_exists($classname, \false)) { - if (\count($class->getInterfaces())) { - throw new \Prophecy\Exception\Doubler\ClassCreatorException(\sprintf('Could not double `%s` and implement interfaces: [%s].', $class->getParentClass(), \implode(', ', $class->getInterfaces())), $class); - } - throw new \Prophecy\Exception\Doubler\ClassCreatorException(\sprintf('Could not double `%s`.', $class->getParentClass()), $class); - } - return $return; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler\Generator; - -/** - * Reflection interface. - * All reflected classes implement this interface. - * - * @author Konstantin Kudryashov - */ -interface ReflectionInterface -{ -} -= 80000; - default: - return \false; - } - } - public function isBuiltInReturnTypeHint($type) + public static function allAlnum($value, $message = '') { - if ($type === 'void') { - return \true; - } - return $this->isBuiltInParamTypeHint($type); + static::__callStatic('allAlnum', array($value, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ClassNode; -use Prophecy\Doubler\Generator\Node\MethodNode; -/** - * SplFileInfo patch. - * Makes SplFileInfo and derivative classes usable with Prophecy. - * - * @author Konstantin Kudryashov - */ -class SplFileInfoPatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface -{ /** - * Supports everything that extends SplFileInfo. + * @psalm-pure + * @psalm-assert lowercase-string|null $value * - * @param ClassNode $node + * @param string|null $value + * @param string $message * - * @return bool + * @throws InvalidArgumentException + * + * @return void */ - public function supports(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function nullOrLower($value, $message = '') { - if (null === $node->getParentClass()) { - return \false; - } - return 'SplFileInfo' === $node->getParentClass() || \is_subclass_of($node->getParentClass(), 'SplFileInfo'); + static::__callStatic('nullOrLower', array($value, $message)); } /** - * Updated constructor code to call parent one with dummy file argument. + * @psalm-pure + * @psalm-assert iterable $value * - * @param ClassNode $node + * @param iterable $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function apply(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function allLower($value, $message = '') { - if ($node->hasMethod('__construct')) { - $constructor = $node->getMethod('__construct'); - } else { - $constructor = new \Prophecy\Doubler\Generator\Node\MethodNode('__construct'); - $node->addMethod($constructor); - } - if ($this->nodeIsDirectoryIterator($node)) { - $constructor->setCode('return parent::__construct("' . __DIR__ . '");'); - return; - } - if ($this->nodeIsSplFileObject($node)) { - $filePath = \str_replace('\\', '\\\\', __FILE__); - $constructor->setCode('return parent::__construct("' . $filePath . '");'); - return; - } - if ($this->nodeIsSymfonySplFileInfo($node)) { - $filePath = \str_replace('\\', '\\\\', __FILE__); - $constructor->setCode('return parent::__construct("' . $filePath . '", "", "");'); - return; - } - $constructor->useParentCode(); + static::__callStatic('allLower', array($value, $message)); } /** - * Returns patch priority, which determines when patch will be applied. + * @psalm-pure * - * @return int Priority number (higher - earlier) + * @param string|null $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getPriority() + public static function nullOrUpper($value, $message = '') { - return 50; + static::__callStatic('nullOrUpper', array($value, $message)); } /** - * @param ClassNode $node - * @return boolean + * @psalm-pure + * + * @param iterable $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - private function nodeIsDirectoryIterator(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function allUpper($value, $message = '') { - $parent = $node->getParentClass(); - return 'DirectoryIterator' === $parent || \is_subclass_of($parent, 'DirectoryIterator'); + static::__callStatic('allUpper', array($value, $message)); } /** - * @param ClassNode $node - * @return boolean + * @psalm-pure + * + * @param string|null $value + * @param int $length + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - private function nodeIsSplFileObject(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function nullOrLength($value, $length, $message = '') { - $parent = $node->getParentClass(); - return 'SplFileObject' === $parent || \is_subclass_of($parent, 'SplFileObject'); + static::__callStatic('nullOrLength', array($value, $length, $message)); } /** - * @param ClassNode $node - * @return boolean + * @psalm-pure + * + * @param iterable $value + * @param int $length + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - private function nodeIsSymfonySplFileInfo(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function allLength($value, $length, $message = '') { - $parent = $node->getParentClass(); - return 'Symfony\\Component\\Finder\\SplFileInfo' === $parent; + static::__callStatic('allLength', array($value, $length, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ClassNode; -/** - * Class patch interface. - * Class patches extend doubles functionality or help - * Prophecy to avoid some internal PHP bugs. - * - * @author Konstantin Kudryashov - */ -interface ClassPatchInterface -{ /** - * Checks if patch supports specific class node. + * @psalm-pure * - * @param ClassNode $node + * @param string|null $value + * @param int|float $min + * @param string $message * - * @return bool - */ - public function supports(\Prophecy\Doubler\Generator\Node\ClassNode $node); - /** - * Applies patch to the specific class node. + * @throws InvalidArgumentException * - * @param ClassNode $node * @return void */ - public function apply(\Prophecy\Doubler\Generator\Node\ClassNode $node); + public static function nullOrMinLength($value, $min, $message = '') + { + static::__callStatic('nullOrMinLength', array($value, $min, $message)); + } /** - * Returns patch priority, which determines when patch will be applied. + * @psalm-pure * - * @return int Priority number (higher - earlier) + * @param iterable $value + * @param int|float $min + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getPriority(); -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ClassNode; -use Prophecy\Doubler\Generator\Node\MethodNode; -/** - * Traversable interface patch. - * Forces classes that implement interfaces, that extend Traversable to also implement Iterator. - * - * @author Konstantin Kudryashov - */ -class TraversablePatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface -{ + public static function allMinLength($value, $min, $message = '') + { + static::__callStatic('allMinLength', array($value, $min, $message)); + } /** - * Supports nodetree, that implement Traversable, but not Iterator or IteratorAggregate. + * @psalm-pure * - * @param ClassNode $node + * @param string|null $value + * @param int|float $max + * @param string $message * - * @return bool + * @throws InvalidArgumentException + * + * @return void */ - public function supports(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function nullOrMaxLength($value, $max, $message = '') { - if (\in_array('Iterator', $node->getInterfaces())) { - return \false; - } - if (\in_array('IteratorAggregate', $node->getInterfaces())) { - return \false; - } - foreach ($node->getInterfaces() as $interface) { - if ('Traversable' !== $interface && !\is_subclass_of($interface, 'Traversable')) { - continue; - } - if ('Iterator' === $interface || \is_subclass_of($interface, 'Iterator')) { - continue; - } - if ('IteratorAggregate' === $interface || \is_subclass_of($interface, 'IteratorAggregate')) { - continue; - } - return \true; - } - return \false; + static::__callStatic('nullOrMaxLength', array($value, $max, $message)); } /** - * Forces class to implement Iterator interface. + * @psalm-pure * - * @param ClassNode $node + * @param iterable $value + * @param int|float $max + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function apply(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function allMaxLength($value, $max, $message = '') { - $node->addInterface('Iterator'); - $node->addMethod(new \Prophecy\Doubler\Generator\Node\MethodNode('current')); - $node->addMethod(new \Prophecy\Doubler\Generator\Node\MethodNode('key')); - $node->addMethod(new \Prophecy\Doubler\Generator\Node\MethodNode('next')); - $node->addMethod(new \Prophecy\Doubler\Generator\Node\MethodNode('rewind')); - $node->addMethod(new \Prophecy\Doubler\Generator\Node\MethodNode('valid')); + static::__callStatic('allMaxLength', array($value, $max, $message)); } /** - * Returns patch priority, which determines when patch will be applied. + * @psalm-pure * - * @return int Priority number (higher - earlier) + * @param string|null $value + * @param int|float $min + * @param int|float $max + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getPriority() + public static function nullOrLengthBetween($value, $min, $max, $message = '') { - return 100; + static::__callStatic('nullOrLengthBetween', array($value, $min, $max, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ClassNode; -use Prophecy\Doubler\Generator\Node\MethodNode; -/** - * Disable constructor. - * Makes all constructor arguments optional. - * - * @author Konstantin Kudryashov - */ -class DisableConstructorPatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface -{ /** - * Checks if class has `__construct` method. + * @psalm-pure * - * @param ClassNode $node + * @param iterable $value + * @param int|float $min + * @param int|float $max + * @param string $message * - * @return bool + * @throws InvalidArgumentException + * + * @return void */ - public function supports(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function allLengthBetween($value, $min, $max, $message = '') { - return \true; + static::__callStatic('allLengthBetween', array($value, $min, $max, $message)); } /** - * Makes all class constructor arguments optional. + * @param mixed $value + * @param string $message * - * @param ClassNode $node + * @throws InvalidArgumentException + * + * @return void */ - public function apply(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function nullOrFileExists($value, $message = '') { - if (!$node->isExtendable('__construct')) { - return; - } - if (!$node->hasMethod('__construct')) { - $node->addMethod(new \Prophecy\Doubler\Generator\Node\MethodNode('__construct', '')); - return; - } - $constructor = $node->getMethod('__construct'); - foreach ($constructor->getArguments() as $argument) { - $argument->setDefault(null); - } - $constructor->setCode(<< - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ClassNode; -/** - * ReflectionClass::newInstance patch. - * Makes first argument of newInstance optional, since it works but signature is misleading - * - * @author Florian Klein - */ -class ReflectionClassNewInstancePatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface -{ /** - * Supports ReflectionClass + * @param mixed $value + * @param string $message * - * @param ClassNode $node + * @throws InvalidArgumentException * - * @return bool + * @return void */ - public function supports(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function nullOrFile($value, $message = '') { - return 'ReflectionClass' === $node->getParentClass(); + static::__callStatic('nullOrFile', array($value, $message)); } /** - * Updates newInstance's first argument to make it optional + * @param mixed $value + * @param string $message * - * @param ClassNode $node + * @throws InvalidArgumentException + * + * @return void */ - public function apply(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function allFile($value, $message = '') { - foreach ($node->getMethod('newInstance')->getArguments() as $argument) { - $argument->setDefault(null); - } + static::__callStatic('allFile', array($value, $message)); } /** - * Returns patch priority, which determines when patch will be applied. + * @param mixed $value + * @param string $message * - * @return int Priority number (higher = earlier) + * @throws InvalidArgumentException + * + * @return void */ - public function getPriority() + public static function nullOrDirectory($value, $message = '') { - return 50; + static::__callStatic('nullOrDirectory', array($value, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ClassNode; -/** - * Exception patch for HHVM to remove the stubs from special methods - * - * @author Christophe Coevoet - */ -class HhvmExceptionPatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface -{ /** - * Supports exceptions on HHVM. + * @param mixed $value + * @param string $message * - * @param ClassNode $node + * @throws InvalidArgumentException * - * @return bool + * @return void */ - public function supports(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function allDirectory($value, $message = '') { - if (!\defined('HHVM_VERSION')) { - return \false; - } - return 'Exception' === $node->getParentClass() || \is_subclass_of($node->getParentClass(), 'Exception'); + static::__callStatic('allDirectory', array($value, $message)); } /** - * Removes special exception static methods from the doubled methods. + * @param string|null $value + * @param string $message * - * @param ClassNode $node + * @throws InvalidArgumentException * * @return void */ - public function apply(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function nullOrReadable($value, $message = '') { - if ($node->hasMethod('setTraceOptions')) { - $node->getMethod('setTraceOptions')->useParentCode(); - } - if ($node->hasMethod('getTraceOptions')) { - $node->getMethod('getTraceOptions')->useParentCode(); - } + static::__callStatic('nullOrReadable', array($value, $message)); } /** - * {@inheritdoc} + * @param iterable $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getPriority() + public static function allReadable($value, $message = '') { - return -50; + static::__callStatic('allReadable', array($value, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ClassNode; -/** - * Remove method functionality from the double which will clash with php keywords. - * - * @author Milan Magudia - */ -class KeywordPatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface -{ /** - * Support any class + * @param string|null $value + * @param string $message * - * @param ClassNode $node + * @throws InvalidArgumentException * - * @return boolean + * @return void */ - public function supports(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function nullOrWritable($value, $message = '') { - return \true; + static::__callStatic('nullOrWritable', array($value, $message)); } /** - * Remove methods that clash with php keywords + * @param iterable $value + * @param string $message * - * @param ClassNode $node + * @throws InvalidArgumentException + * + * @return void */ - public function apply(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function allWritable($value, $message = '') { - $methodNames = \array_keys($node->getMethods()); - $methodsToRemove = \array_intersect($methodNames, $this->getKeywords()); - foreach ($methodsToRemove as $methodName) { - $node->removeMethod($methodName); - } + static::__callStatic('allWritable', array($value, $message)); } /** - * Returns patch priority, which determines when patch will be applied. + * @psalm-assert class-string|null $value * - * @return int Priority number (higher - earlier) + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getPriority() + public static function nullOrClassExists($value, $message = '') { - return 49; + static::__callStatic('nullOrClassExists', array($value, $message)); } /** - * Returns array of php keywords. + * @psalm-assert iterable $value * - * @return array + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - private function getKeywords() + public static function allClassExists($value, $message = '') { - return ['__halt_compiler']; + static::__callStatic('allClassExists', array($value, $message)); } -} - $class + * @psalm-assert class-string|ExpectedType|null $value * - * @param ClassNode $node - * @return bool + * @param mixed $value + * @param string|object $class + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function supports(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function nullOrSubclassOf($value, $class, $message = '') { - return $this->implementsAThrowableInterface($node) && $this->doesNotExtendAThrowableClass($node); + static::__callStatic('nullOrSubclassOf', array($value, $class, $message)); } /** - * @param ClassNode $node - * @return bool + * @psalm-pure + * @psalm-template ExpectedType of object + * @psalm-param class-string $class + * @psalm-assert iterable|ExpectedType> $value + * + * @param mixed $value + * @param string|object $class + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - private function implementsAThrowableInterface(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function allSubclassOf($value, $class, $message = '') { - foreach ($node->getInterfaces() as $type) { - if (\is_a($type, 'Throwable', \true)) { - return \true; - } - } - return \false; + static::__callStatic('allSubclassOf', array($value, $class, $message)); } /** - * @param ClassNode $node - * @return bool + * @psalm-assert class-string|null $value + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - private function doesNotExtendAThrowableClass(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function nullOrInterfaceExists($value, $message = '') { - return !\is_a($node->getParentClass(), 'Throwable', \true); + static::__callStatic('nullOrInterfaceExists', array($value, $message)); } /** - * Applies patch to the specific class node. + * @psalm-assert iterable $value * - * @param ClassNode $node + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException * * @return void */ - public function apply(\Prophecy\Doubler\Generator\Node\ClassNode $node) - { - $this->checkItCanBeDoubled($node); - $this->setParentClassToException($node); - } - private function checkItCanBeDoubled(\Prophecy\Doubler\Generator\Node\ClassNode $node) - { - $className = $node->getParentClass(); - if ($className !== 'stdClass') { - throw new \Prophecy\Exception\Doubler\ClassCreatorException(\sprintf('Cannot double concrete class %s as well as implement Traversable', $className), $node); - } - } - private function setParentClassToException(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function allInterfaceExists($value, $message = '') { - $node->setParentClass('Exception'); - $node->removeMethod('getMessage'); - $node->removeMethod('getCode'); - $node->removeMethod('getFile'); - $node->removeMethod('getLine'); - $node->removeMethod('getTrace'); - $node->removeMethod('getPrevious'); - $node->removeMethod('getNext'); - $node->removeMethod('getTraceAsString'); + static::__callStatic('allInterfaceExists', array($value, $message)); } /** - * Returns patch priority, which determines when patch will be applied. + * @psalm-pure + * @psalm-template ExpectedType of object + * @psalm-param class-string $interface + * @psalm-assert class-string|null $value * - * @return int Priority number (higher - earlier) + * @param mixed $value + * @param mixed $interface + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getPriority() + public static function nullOrImplementsInterface($value, $interface, $message = '') { - return 100; + static::__callStatic('nullOrImplementsInterface', array($value, $interface, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ClassNode; -use Prophecy\Doubler\Generator\Node\MethodNode; -use Prophecy\PhpDocumentor\ClassAndInterfaceTagRetriever; -use Prophecy\PhpDocumentor\MethodTagRetrieverInterface; -/** - * Discover Magical API using "@method" PHPDoc format. - * - * @author Thomas Tourlourat - * @author Kévin Dunglas - * @author Théo FIDRY - */ -class MagicCallPatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface -{ - private $tagRetriever; - public function __construct(\Prophecy\PhpDocumentor\MethodTagRetrieverInterface $tagRetriever = null) + /** + * @psalm-pure + * @psalm-template ExpectedType of object + * @psalm-param class-string $interface + * @psalm-assert iterable> $value + * + * @param mixed $value + * @param mixed $interface + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allImplementsInterface($value, $interface, $message = '') { - $this->tagRetriever = null === $tagRetriever ? new \Prophecy\PhpDocumentor\ClassAndInterfaceTagRetriever() : $tagRetriever; + static::__callStatic('allImplementsInterface', array($value, $interface, $message)); } /** - * Support any class + * @psalm-pure + * @psalm-param class-string|object|null $classOrObject * - * @param ClassNode $node + * @param string|object|null $classOrObject + * @param mixed $property + * @param string $message * - * @return boolean + * @throws InvalidArgumentException + * + * @return void */ - public function supports(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function nullOrPropertyExists($classOrObject, $property, $message = '') { - return \true; + static::__callStatic('nullOrPropertyExists', array($classOrObject, $property, $message)); } /** - * Discover Magical API + * @psalm-pure + * @psalm-param iterable $classOrObject * - * @param ClassNode $node + * @param iterable $classOrObject + * @param mixed $property + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function apply(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function allPropertyExists($classOrObject, $property, $message = '') { - $types = \array_filter($node->getInterfaces(), function ($interface) { - return 0 !== \strpos($interface, 'Prophecy\\'); - }); - $types[] = $node->getParentClass(); - foreach ($types as $type) { - $reflectionClass = new \ReflectionClass($type); - while ($reflectionClass) { - $tagList = $this->tagRetriever->getTagList($reflectionClass); - foreach ($tagList as $tag) { - $methodName = $tag->getMethodName(); - if (empty($methodName)) { - continue; - } - if (!$reflectionClass->hasMethod($methodName)) { - $methodNode = new \Prophecy\Doubler\Generator\Node\MethodNode($methodName); - $methodNode->setStatic($tag->isStatic()); - $node->addMethod($methodNode); - } - } - $reflectionClass = $reflectionClass->getParentClass(); - } - } + static::__callStatic('allPropertyExists', array($classOrObject, $property, $message)); } /** - * Returns patch priority, which determines when patch will be applied. + * @psalm-pure + * @psalm-param class-string|object|null $classOrObject * - * @return integer Priority number (higher - earlier) + * @param string|object|null $classOrObject + * @param mixed $property + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getPriority() + public static function nullOrPropertyNotExists($classOrObject, $property, $message = '') { - return 50; + static::__callStatic('nullOrPropertyNotExists', array($classOrObject, $property, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ArgumentTypeNode; -use Prophecy\Doubler\Generator\Node\ClassNode; -use Prophecy\Doubler\Generator\Node\MethodNode; -use Prophecy\Doubler\Generator\Node\ArgumentNode; -use Prophecy\Doubler\Generator\Node\ReturnTypeNode; -/** - * Add Prophecy functionality to the double. - * This is a core class patch for Prophecy. - * - * @author Konstantin Kudryashov - */ -class ProphecySubjectPatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface -{ /** - * Always returns true. + * @psalm-pure + * @psalm-param iterable $classOrObject * - * @param ClassNode $node + * @param iterable $classOrObject + * @param mixed $property + * @param string $message * - * @return bool + * @throws InvalidArgumentException + * + * @return void */ - public function supports(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function allPropertyNotExists($classOrObject, $property, $message = '') { - return \true; + static::__callStatic('allPropertyNotExists', array($classOrObject, $property, $message)); } /** - * Apply Prophecy functionality to class node. + * @psalm-pure + * @psalm-param class-string|object|null $classOrObject * - * @param ClassNode $node + * @param string|object|null $classOrObject + * @param mixed $method + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function apply(\Prophecy\Doubler\Generator\Node\ClassNode $node) + public static function nullOrMethodExists($classOrObject, $method, $message = '') { - $node->addInterface('Prophecy\\Prophecy\\ProphecySubjectInterface'); - $node->addProperty('objectProphecyClosure', 'private'); - foreach ($node->getMethods() as $name => $method) { - if ('__construct' === \strtolower($name)) { - continue; - } - if ($method->getReturnTypeNode()->isVoid()) { - $method->setCode('$this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());'); - } else { - $method->setCode('return $this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());'); - } - } - $prophecySetter = new \Prophecy\Doubler\Generator\Node\MethodNode('setProphecy'); - $prophecyArgument = new \Prophecy\Doubler\Generator\Node\ArgumentNode('prophecy'); - $prophecyArgument->setTypeNode(new \Prophecy\Doubler\Generator\Node\ArgumentTypeNode('Prophecy\\Prophecy\\ProphecyInterface')); - $prophecySetter->addArgument($prophecyArgument); - $prophecySetter->setCode(<<objectProphecyClosure) { - \$this->objectProphecyClosure = static function () use (\$prophecy) { - return \$prophecy; - }; -} -PHP -); - $prophecyGetter = new \Prophecy\Doubler\Generator\Node\MethodNode('getProphecy'); - $prophecyGetter->setCode('return \\call_user_func($this->objectProphecyClosure);'); - if ($node->hasMethod('__call')) { - $__call = $node->getMethod('__call'); - } else { - $__call = new \Prophecy\Doubler\Generator\Node\MethodNode('__call'); - $__call->addArgument(new \Prophecy\Doubler\Generator\Node\ArgumentNode('name')); - $__call->addArgument(new \Prophecy\Doubler\Generator\Node\ArgumentNode('arguments')); - $node->addMethod($__call, \true); - } - $__call->setCode(<<getProphecy(), func_get_arg(0) -); -PHP -); - $node->addMethod($prophecySetter, \true); - $node->addMethod($prophecyGetter, \true); + static::__callStatic('nullOrMethodExists', array($classOrObject, $method, $message)); } /** - * Returns patch priority, which determines when patch will be applied. + * @psalm-pure + * @psalm-param iterable $classOrObject * - * @return int Priority number (higher - earlier) + * @param iterable $classOrObject + * @param mixed $method + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getPriority() + public static function allMethodExists($classOrObject, $method, $message = '') { - return 0; + static::__callStatic('allMethodExists', array($classOrObject, $method, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\PhpDocumentor; - -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Method; -/** - * @author Théo FIDRY - * - * @internal - */ -interface MethodTagRetrieverInterface -{ /** - * @param \ReflectionClass $reflectionClass + * @psalm-pure + * @psalm-param class-string|object|null $classOrObject * - * @return LegacyMethodTag[]|Method[] + * @param string|object|null $classOrObject + * @param mixed $method + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getTagList(\ReflectionClass $reflectionClass); -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\PhpDocumentor; - -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Method; -/** - * @author Théo FIDRY - * - * @internal - */ -final class ClassAndInterfaceTagRetriever implements \Prophecy\PhpDocumentor\MethodTagRetrieverInterface -{ - private $classRetriever; - public function __construct(\Prophecy\PhpDocumentor\MethodTagRetrieverInterface $classRetriever = null) + public static function nullOrMethodNotExists($classOrObject, $method, $message = '') { - if (null !== $classRetriever) { - $this->classRetriever = $classRetriever; - return; - } - $this->classRetriever = \class_exists('PHPUnit\\phpDocumentor\\Reflection\\DocBlockFactory') && \class_exists('PHPUnit\\phpDocumentor\\Reflection\\Types\\ContextFactory') ? new \Prophecy\PhpDocumentor\ClassTagRetriever() : new \Prophecy\PhpDocumentor\LegacyClassTagRetriever(); + static::__callStatic('nullOrMethodNotExists', array($classOrObject, $method, $message)); } /** - * @param \ReflectionClass $reflectionClass + * @psalm-pure + * @psalm-param iterable $classOrObject * - * @return LegacyMethodTag[]|Method[] + * @param iterable $classOrObject + * @param mixed $method + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getTagList(\ReflectionClass $reflectionClass) + public static function allMethodNotExists($classOrObject, $method, $message = '') { - return \array_merge($this->classRetriever->getTagList($reflectionClass), $this->getInterfacesTagList($reflectionClass)); + static::__callStatic('allMethodNotExists', array($classOrObject, $method, $message)); } /** - * @param \ReflectionClass $reflectionClass + * @psalm-pure * - * @return LegacyMethodTag[]|Method[] + * @param array|null $array + * @param string|int $key + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - private function getInterfacesTagList(\ReflectionClass $reflectionClass) + public static function nullOrKeyExists($array, $key, $message = '') { - $interfaces = $reflectionClass->getInterfaces(); - $tagList = array(); - foreach ($interfaces as $interface) { - $tagList = \array_merge($tagList, $this->classRetriever->getTagList($interface)); - } - return $tagList; + static::__callStatic('nullOrKeyExists', array($array, $key, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\PhpDocumentor; - -use PHPUnit\phpDocumentor\Reflection\DocBlock; -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; -/** - * @author Théo FIDRY - * - * @internal - */ -final class LegacyClassTagRetriever implements \Prophecy\PhpDocumentor\MethodTagRetrieverInterface -{ /** - * @param \ReflectionClass $reflectionClass + * @psalm-pure * - * @return LegacyMethodTag[] + * @param iterable $array + * @param string|int $key + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getTagList(\ReflectionClass $reflectionClass) + public static function allKeyExists($array, $key, $message = '') { - $phpdoc = new \PHPUnit\phpDocumentor\Reflection\DocBlock($reflectionClass->getDocComment()); - return $phpdoc->getTagsByName('method'); + static::__callStatic('allKeyExists', array($array, $key, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\PhpDocumentor; - -use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Method; -use PHPUnit\phpDocumentor\Reflection\DocBlockFactory; -use PHPUnit\phpDocumentor\Reflection\Types\ContextFactory; -/** - * @author Théo FIDRY - * - * @internal - */ -final class ClassTagRetriever implements \Prophecy\PhpDocumentor\MethodTagRetrieverInterface -{ - private $docBlockFactory; - private $contextFactory; - public function __construct() + /** + * @psalm-pure + * + * @param array|null $array + * @param string|int $key + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function nullOrKeyNotExists($array, $key, $message = '') { - $this->docBlockFactory = \PHPUnit\phpDocumentor\Reflection\DocBlockFactory::createInstance(); - $this->contextFactory = new \PHPUnit\phpDocumentor\Reflection\Types\ContextFactory(); + static::__callStatic('nullOrKeyNotExists', array($array, $key, $message)); } /** - * @param \ReflectionClass $reflectionClass + * @psalm-pure * - * @return Method[] + * @param iterable $array + * @param string|int $key + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getTagList(\ReflectionClass $reflectionClass) + public static function allKeyNotExists($array, $key, $message = '') { - try { - $phpdoc = $this->docBlockFactory->create($reflectionClass, $this->contextFactory->createFromReflector($reflectionClass)); - $methods = array(); - foreach ($phpdoc->getTagsByName('method') as $tag) { - if ($tag instanceof \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Method) { - $methods[] = $tag; - } - } - return $methods; - } catch (\InvalidArgumentException $e) { - return array(); - } + static::__callStatic('allKeyNotExists', array($array, $key, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Exception\Call; - -use Prophecy\Exception\Prophecy\ObjectProphecyException; -use Prophecy\Prophecy\ObjectProphecy; -class UnexpectedCallException extends \Prophecy\Exception\Prophecy\ObjectProphecyException -{ - private $methodName; - private $arguments; - public function __construct($message, \Prophecy\Prophecy\ObjectProphecy $objectProphecy, $methodName, array $arguments) + /** + * @psalm-pure + * @psalm-assert array-key|null $value + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function nullOrValidArrayKey($value, $message = '') { - parent::__construct($message, $objectProphecy); - $this->methodName = $methodName; - $this->arguments = $arguments; + static::__callStatic('nullOrValidArrayKey', array($value, $message)); } - public function getMethodName() + /** + * @psalm-pure + * @psalm-assert iterable $value + * + * @param mixed $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allValidArrayKey($value, $message = '') { - return $this->methodName; + static::__callStatic('allValidArrayKey', array($value, $message)); } - public function getArguments() + /** + * @param Countable|array|null $array + * @param int $number + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function nullOrCount($array, $number, $message = '') { - return $this->arguments; + static::__callStatic('nullOrCount', array($array, $number, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Exception\Prophecy; - -use Prophecy\Prophecy\MethodProphecy; -class MethodProphecyException extends \Prophecy\Exception\Prophecy\ObjectProphecyException -{ - private $methodProphecy; - public function __construct($message, \Prophecy\Prophecy\MethodProphecy $methodProphecy) + /** + * @param iterable $array + * @param int $number + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allCount($array, $number, $message = '') { - parent::__construct($message, $methodProphecy->getObjectProphecy()); - $this->methodProphecy = $methodProphecy; + static::__callStatic('allCount', array($array, $number, $message)); } /** - * @return MethodProphecy + * @param Countable|array|null $array + * @param int|float $min + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getMethodProphecy() + public static function nullOrMinCount($array, $min, $message = '') { - return $this->methodProphecy; + static::__callStatic('nullOrMinCount', array($array, $min, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Exception\Prophecy; - -use Prophecy\Exception\Exception; -interface ProphecyException extends \Prophecy\Exception\Exception -{ -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Exception\Prophecy; - -use Prophecy\Prophecy\ObjectProphecy; -class ObjectProphecyException extends \RuntimeException implements \Prophecy\Exception\Prophecy\ProphecyException -{ - private $objectProphecy; - public function __construct($message, \Prophecy\Prophecy\ObjectProphecy $objectProphecy) + /** + * @param iterable $array + * @param int|float $min + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allMinCount($array, $min, $message = '') { - parent::__construct($message); - $this->objectProphecy = $objectProphecy; + static::__callStatic('allMinCount', array($array, $min, $message)); } /** - * @return ObjectProphecy + * @param Countable|array|null $array + * @param int|float $max + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getObjectProphecy() + public static function nullOrMaxCount($array, $max, $message = '') { - return $this->objectProphecy; + static::__callStatic('nullOrMaxCount', array($array, $max, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Exception\Doubler; - -use Prophecy\Exception\Exception; -interface DoublerException extends \Prophecy\Exception\Exception -{ -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Exception\Doubler; - -use RuntimeException; -class DoubleException extends \RuntimeException implements \Prophecy\Exception\Doubler\DoublerException -{ -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Exception\Doubler; - -use Prophecy\Doubler\Generator\Node\ClassNode; -class ClassCreatorException extends \RuntimeException implements \Prophecy\Exception\Doubler\DoublerException -{ - private $node; - public function __construct($message, \Prophecy\Doubler\Generator\Node\ClassNode $node) + /** + * @param iterable $array + * @param int|float $max + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allMaxCount($array, $max, $message = '') { - parent::__construct($message); - $this->node = $node; + static::__callStatic('allMaxCount', array($array, $max, $message)); } - public function getClassNode() + /** + * @param Countable|array|null $array + * @param int|float $min + * @param int|float $max + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function nullOrCountBetween($array, $min, $max, $message = '') { - return $this->node; + static::__callStatic('nullOrCountBetween', array($array, $min, $max, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Exception\Doubler; - -class ClassNotFoundException extends \Prophecy\Exception\Doubler\DoubleException -{ - private $classname; /** - * @param string $message - * @param string $classname + * @param iterable $array + * @param int|float $min + * @param int|float $max + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function __construct($message, $classname) + public static function allCountBetween($array, $min, $max, $message = '') { - parent::__construct($message); - $this->classname = $classname; + static::__callStatic('allCountBetween', array($array, $min, $max, $message)); } - public function getClassname() + /** + * @psalm-pure + * @psalm-assert list|null $array + * + * @param mixed $array + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function nullOrIsList($array, $message = '') { - return $this->classname; + static::__callStatic('nullOrIsList', array($array, $message)); } -} - $array + * + * @param mixed $array * @param string $message - * @param string $className - * @param string $methodName + * + * @throws InvalidArgumentException + * + * @return void */ - public function __construct($message, $className, $methodName) + public static function allIsList($array, $message = '') { - parent::__construct($message); - $this->methodName = $methodName; - $this->className = $className; + static::__callStatic('allIsList', array($array, $message)); } /** - * @return string + * @psalm-pure + * @psalm-assert non-empty-list|null $array + * + * @param mixed $array + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getMethodName() + public static function nullOrIsNonEmptyList($array, $message = '') { - return $this->methodName; + static::__callStatic('nullOrIsNonEmptyList', array($array, $message)); } /** - * @return string + * @psalm-pure + * @psalm-assert iterable $array + * + * @param mixed $array + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - public function getClassName() + public static function allIsNonEmptyList($array, $message = '') { - return $this->className; + static::__callStatic('allIsNonEmptyList', array($array, $message)); } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Prophecy\Exception\Doubler; - -class MethodNotFoundException extends \Prophecy\Exception\Doubler\DoubleException -{ /** - * @var string|object + * @psalm-pure + * @psalm-template T + * @psalm-param mixed|array|null $array + * @psalm-assert array|null $array + * + * @param mixed $array + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - private $classname; + public static function nullOrIsMap($array, $message = '') + { + static::__callStatic('nullOrIsMap', array($array, $message)); + } /** - * @var string + * @psalm-pure + * @psalm-template T + * @psalm-param iterable> $array + * @psalm-assert iterable> $array + * + * @param mixed $array + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - private $methodName; + public static function allIsMap($array, $message = '') + { + static::__callStatic('allIsMap', array($array, $message)); + } /** - * @var array + * @psalm-pure + * @psalm-template T + * @psalm-param mixed|array|null $array + * + * @param mixed $array + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void */ - private $arguments; + public static function nullOrIsNonEmptyMap($array, $message = '') + { + static::__callStatic('nullOrIsNonEmptyMap', array($array, $message)); + } /** + * @psalm-pure + * @psalm-template T + * @psalm-param iterable> $array + * + * @param mixed $array * @param string $message - * @param string|object $classname - * @param string $methodName - * @param null|Argument\ArgumentsWildcard|array $arguments + * + * @throws InvalidArgumentException + * + * @return void */ - public function __construct($message, $classname, $methodName, $arguments = null) + public static function allIsNonEmptyMap($array, $message = '') { - parent::__construct($message); - $this->classname = $classname; - $this->methodName = $methodName; - $this->arguments = $arguments; + static::__callStatic('allIsNonEmptyMap', array($array, $message)); } - public function getClassname() + /** + * @psalm-pure + * + * @param string|null $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function nullOrUuid($value, $message = '') { - return $this->classname; + static::__callStatic('nullOrUuid', array($value, $message)); } - public function getMethodName() + /** + * @psalm-pure + * + * @param iterable $value + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allUuid($value, $message = '') { - return $this->methodName; + static::__callStatic('allUuid', array($value, $message)); } - public function getArguments() + /** + * @psalm-param class-string $class + * + * @param Closure|null $expression + * @param string $class + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function nullOrThrows($expression, $class = 'Exception', $message = '') { - return $this->arguments; + static::__callStatic('nullOrThrows', array($expression, $class, $message)); + } + /** + * @psalm-param class-string $class + * + * @param iterable $expression + * @param string $class + * @param string $message + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function allThrows($expression, $class = 'Exception', $message = '') + { + static::__callStatic('allThrows', array($expression, $class, $message)); } } +The MIT License (MIT) + +Copyright (c) 2014 Bernhard Schussek + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * Marcello Duarte + * This file is part of the webmozart/assert package. + * + * (c) Bernhard Schussek * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Doubler; +namespace PHPUnit\Webmozart\Assert; -use ReflectionClass; -class ClassMirrorException extends \RuntimeException implements \Prophecy\Exception\Doubler\DoublerException +class InvalidArgumentException extends \InvalidArgumentException { - private $class; - public function __construct($message, \ReflectionClass $class) - { - parent::__construct($message); - $this->class = $class; - } - public function getReflectedClass() - { - return $this->class; - } } - * Marcello Duarte +declare (strict_types=1); +/** + * phpDocumentor * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace Prophecy\Exception\Doubler; +namespace PHPUnit\phpDocumentor\Reflection; -class ReturnByReferenceException extends \Prophecy\Exception\Doubler\DoubleException +use InvalidArgumentException; +use function assert; +use function end; +use function explode; +use function is_string; +use function preg_match; +use function sprintf; +use function trim; +/** + * Value Object for Fqsen. + * + * @link https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc-meta.md + * + * @psalm-immutable + */ +final class Fqsen { - private $classname; - private $methodName; + /** @var string full quallified class name */ + private $fqsen; + /** @var string name of the element without path. */ + private $name; /** - * @param string $message - * @param string $classname - * @param string $methodName + * Initializes the object. + * + * @throws InvalidArgumentException when $fqsen is not matching the format. */ - public function __construct($message, $classname, $methodName) + public function __construct(string $fqsen) { - parent::__construct($message); - $this->classname = $classname; - $this->methodName = $methodName; + $matches = []; + $result = preg_match( + //phpcs:ignore Generic.Files.LineLength.TooLong + '/^\\\\([a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff\\\\]*)?(?:[:]{2}\\$?([a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*))?(?:\\(\\))?$/', + $fqsen, + $matches + ); + if ($result === 0) { + throw new InvalidArgumentException(sprintf('"%s" is not a valid Fqsen.', $fqsen)); + } + $this->fqsen = $fqsen; + if (isset($matches[2])) { + $this->name = $matches[2]; + } else { + $matches = explode('\\', $fqsen); + $name = end($matches); + assert(is_string($name)); + $this->name = trim($name, '()'); + } } - public function getClassname() + /** + * converts this class to string. + */ + public function __toString() : string { - return $this->classname; + return $this->fqsen; } - public function getMethodName() + /** + * Returns the name of the element without path. + */ + public function getName() : string { - return $this->methodName; + return $this->name; } } - * Marcello Duarte +declare (strict_types=1); +/** + * phpDocumentor * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace Prophecy\Exception\Doubler; +namespace PHPUnit\phpDocumentor\Reflection; -class InterfaceNotFoundException extends \Prophecy\Exception\Doubler\ClassNotFoundException +/** + * Interface for project factories. A project factory shall convert a set of files + * into an object implementing the Project interface. + */ +interface ProjectFactory { - public function getInterfaceName() - { - return $this->getClassname(); - } + /** + * Creates a project from the set of files. + * + * @param File[] $files + */ + public function create(string $name, array $files) : Project; } - * Marcello Duarte +declare (strict_types=1); +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace Prophecy\Exception; +namespace PHPUnit\phpDocumentor\Reflection; /** - * Core Prophecy exception interface. - * All Prophecy exceptions implement it. - * - * @author Konstantin Kudryashov + * Interface for files processed by the ProjectFactory */ -interface Exception +interface File { /** - * @return string + * Returns the content of the file as a string. + */ + public function getContents() : string; + /** + * Returns md5 hash of the file. + */ + public function md5() : string; + /** + * Returns an relative path to the file. */ - public function getMessage(); + public function path() : string; } - * Marcello Duarte +declare (strict_types=1); +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace Prophecy\Exception; +namespace PHPUnit\phpDocumentor\Reflection; -class InvalidArgumentException extends \InvalidArgumentException implements \Prophecy\Exception\Exception +/** + * The location where an element occurs within a file. + * + * @psalm-immutable + */ +final class Location { + /** @var int */ + private $lineNumber = 0; + /** @var int */ + private $columnNumber = 0; + /** + * Initializes the location for an element using its line number in the file and optionally the column number. + */ + public function __construct(int $lineNumber, int $columnNumber = 0) + { + $this->lineNumber = $lineNumber; + $this->columnNumber = $columnNumber; + } + /** + * Returns the line number that is covered by this location. + */ + public function getLineNumber() : int + { + return $this->lineNumber; + } + /** + * Returns the column number (character position on a line) for this location object. + */ + public function getColumnNumber() : int + { + return $this->columnNumber; + } } - * Marcello Duarte +declare (strict_types=1); +/** + * phpDocumentor * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace Prophecy\Exception\Prediction; +namespace PHPUnit\phpDocumentor\Reflection; -use Prophecy\Exception\Exception; -interface PredictionException extends \Prophecy\Exception\Exception +/** + * Interface for project. Since the definition of a project can be different per factory this interface will be small. + */ +interface Project { + /** + * Returns the name of the project. + */ + public function getName() : string; } - * Marcello Duarte +declare (strict_types=1); +/** + * phpDocumentor * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace Prophecy\Exception\Prediction; +namespace PHPUnit\phpDocumentor\Reflection; -use Prophecy\Exception\Prophecy\MethodProphecyException; -class NoCallsException extends \Prophecy\Exception\Prophecy\MethodProphecyException implements \Prophecy\Exception\Prediction\PredictionException +/** + * Interface for Api Elements + */ +interface Element { + /** + * Returns the Fqsen of the element. + */ + public function getFqsen() : Fqsen; + /** + * Returns the name of the element. + */ + public function getName() : string; } +The MIT License (MIT) + +Copyright (c) 2015 phpDocumentor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + - * Marcello Duarte + * This file is part of phpunit/php-text-template. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Prediction; +namespace PHPUnit\SebastianBergmann\Template; -use Prophecy\Prophecy\MethodProphecy; -class UnexpectedCallsCountException extends \Prophecy\Exception\Prediction\UnexpectedCallsException +use function array_merge; +use function file_exists; +use function file_get_contents; +use function file_put_contents; +use function sprintf; +use function str_replace; +final class Template { - private $expectedCount; - public function __construct($message, \Prophecy\Prophecy\MethodProphecy $methodProphecy, $count, array $calls) + /** + * @var string + */ + private $template = ''; + /** + * @var string + */ + private $openDelimiter; + /** + * @var string + */ + private $closeDelimiter; + /** + * @var array + */ + private $values = []; + /** + * @throws InvalidArgumentException + */ + public function __construct(string $file = '', string $openDelimiter = '{', string $closeDelimiter = '}') { - parent::__construct($message, $methodProphecy, $calls); - $this->expectedCount = \intval($count); + $this->setFile($file); + $this->openDelimiter = $openDelimiter; + $this->closeDelimiter = $closeDelimiter; } - public function getExpectedCount() + /** + * @throws InvalidArgumentException + */ + public function setFile(string $file) : void { - return $this->expectedCount; + $distFile = $file . '.dist'; + if (file_exists($file)) { + $this->template = file_get_contents($file); + } elseif (file_exists($distFile)) { + $this->template = file_get_contents($distFile); + } else { + throw new InvalidArgumentException(sprintf('Failed to load template "%s"', $file)); + } + } + public function setVar(array $values, bool $merge = \true) : void + { + if (!$merge || empty($this->values)) { + $this->values = $values; + } else { + $this->values = array_merge($this->values, $values); + } + } + public function render() : string + { + $keys = []; + foreach ($this->values as $key => $value) { + $keys[] = $this->openDelimiter . $key . $this->closeDelimiter; + } + return str_replace($keys, $this->values, $this->template); + } + /** + * @codeCoverageIgnore + */ + public function renderTo(string $target) : void + { + if (!file_put_contents($target, $this->render())) { + throw new RuntimeException(sprintf('Writing rendered result to "%s" failed', $target)); + } } } - * Marcello Duarte + * This file is part of phpunit/php-text-template. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Prediction; +namespace PHPUnit\SebastianBergmann\Template; -use RuntimeException; -/** - * Basic failed prediction exception. - * Use it for custom prediction failures. - * - * @author Konstantin Kudryashov - */ -class FailedPredictionException extends \RuntimeException implements \Prophecy\Exception\Prediction\PredictionException +use InvalidArgumentException; +final class RuntimeException extends InvalidArgumentException implements Exception { } - * Marcello Duarte + * This file is part of phpunit/php-text-template. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Prediction; +namespace PHPUnit\SebastianBergmann\Template; -use Prophecy\Prophecy\ObjectProphecy; -class AggregateException extends \RuntimeException implements \Prophecy\Exception\Prediction\PredictionException +use Throwable; +interface Exception extends Throwable { - private $exceptions = array(); - private $objectProphecy; - public function append(\Prophecy\Exception\Prediction\PredictionException $exception) - { - $message = $exception->getMessage(); - $message = \strtr($message, array("\n" => "\n ")) . "\n"; - $message = empty($this->exceptions) ? $message : "\n" . $message; - $this->message = \rtrim($this->message . $message); - $this->exceptions[] = $exception; - } - /** - * @return PredictionException[] - */ - public function getExceptions() - { - return $this->exceptions; - } - public function setObjectProphecy(\Prophecy\Prophecy\ObjectProphecy $objectProphecy) - { - $this->objectProphecy = $objectProphecy; - } - /** - * @return ObjectProphecy - */ - public function getObjectProphecy() - { - return $this->objectProphecy; - } } - * Marcello Duarte + * This file is part of phpunit/php-text-template. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Exception\Prediction; +namespace PHPUnit\SebastianBergmann\Template; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Exception\Prophecy\MethodProphecyException; -class UnexpectedCallsException extends \Prophecy\Exception\Prophecy\MethodProphecyException implements \Prophecy\Exception\Prediction\PredictionException +final class InvalidArgumentException extends \InvalidArgumentException implements Exception { - private $calls = array(); - public function __construct($message, \Prophecy\Prophecy\MethodProphecy $methodProphecy, array $calls) - { - parent::__construct($message, $methodProphecy); - $this->calls = $calls; - } - public function getCalls() - { - return $this->calls; - } } +phpunit/php-text-template + +Copyright (c) 2009-2020, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. - * Marcello Duarte + * This file is part of sebastian/object-reflector. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy; +namespace PHPUnit\SebastianBergmann\ObjectReflector; -use Prophecy\Doubler\CachedDoubler; -use Prophecy\Doubler\Doubler; -use Prophecy\Doubler\LazyDouble; -use Prophecy\Doubler\ClassPatch; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\RevealerInterface; -use Prophecy\Prophecy\Revealer; -use Prophecy\Call\CallCenter; -use Prophecy\Util\StringUtil; -use Prophecy\Exception\Prediction\PredictionException; -use Prophecy\Exception\Prediction\AggregateException; -/** - * Prophet creates prophecies. - * - * @author Konstantin Kudryashov - */ -class Prophet +use function count; +use function explode; +use function get_class; +use function is_object; +class ObjectReflector { - private $doubler; - private $revealer; - private $util; - /** - * @var ObjectProphecy[] - */ - private $prophecies = array(); - /** - * Initializes Prophet. - * - * @param null|Doubler $doubler - * @param null|RevealerInterface $revealer - * @param null|StringUtil $util - */ - public function __construct(\Prophecy\Doubler\Doubler $doubler = null, \Prophecy\Prophecy\RevealerInterface $revealer = null, \Prophecy\Util\StringUtil $util = null) - { - if (null === $doubler) { - $doubler = new \Prophecy\Doubler\CachedDoubler(); - $doubler->registerClassPatch(new \Prophecy\Doubler\ClassPatch\SplFileInfoPatch()); - $doubler->registerClassPatch(new \Prophecy\Doubler\ClassPatch\TraversablePatch()); - $doubler->registerClassPatch(new \Prophecy\Doubler\ClassPatch\ThrowablePatch()); - $doubler->registerClassPatch(new \Prophecy\Doubler\ClassPatch\DisableConstructorPatch()); - $doubler->registerClassPatch(new \Prophecy\Doubler\ClassPatch\ProphecySubjectPatch()); - $doubler->registerClassPatch(new \Prophecy\Doubler\ClassPatch\ReflectionClassNewInstancePatch()); - $doubler->registerClassPatch(new \Prophecy\Doubler\ClassPatch\HhvmExceptionPatch()); - $doubler->registerClassPatch(new \Prophecy\Doubler\ClassPatch\MagicCallPatch()); - $doubler->registerClassPatch(new \Prophecy\Doubler\ClassPatch\KeywordPatch()); - } - $this->doubler = $doubler; - $this->revealer = $revealer ?: new \Prophecy\Prophecy\Revealer(); - $this->util = $util ?: new \Prophecy\Util\StringUtil(); - } /** - * Creates new object prophecy. - * - * @param null|string $classOrInterface Class or interface name + * @param object $object * - * @return ObjectProphecy + * @throws InvalidArgumentException */ - public function prophesize($classOrInterface = null) + public function getAttributes($object) : array { - $this->prophecies[] = $prophecy = new \Prophecy\Prophecy\ObjectProphecy(new \Prophecy\Doubler\LazyDouble($this->doubler), new \Prophecy\Call\CallCenter($this->util), $this->revealer); - if ($classOrInterface && \class_exists($classOrInterface)) { - return $prophecy->willExtend($classOrInterface); - } - if ($classOrInterface && \interface_exists($classOrInterface)) { - return $prophecy->willImplement($classOrInterface); + if (!is_object($object)) { + throw new InvalidArgumentException(); } - return $prophecy; - } - /** - * Returns all created object prophecies. - * - * @return ObjectProphecy[] - */ - public function getProphecies() - { - return $this->prophecies; - } - /** - * Returns Doubler instance assigned to this Prophet. - * - * @return Doubler - */ - public function getDoubler() - { - return $this->doubler; - } - /** - * Checks all predictions defined by prophecies of this Prophet. - * - * @throws Exception\Prediction\AggregateException If any prediction fails - */ - public function checkPredictions() - { - $exception = new \Prophecy\Exception\Prediction\AggregateException("Some predictions failed:\n"); - foreach ($this->prophecies as $prophecy) { - try { - $prophecy->checkProphecyMethodsPredictions(); - } catch (\Prophecy\Exception\Prediction\PredictionException $e) { - $exception->append($e); + $attributes = []; + $className = get_class($object); + foreach ((array) $object as $name => $value) { + $name = explode("\0", (string) $name); + if (count($name) === 1) { + $name = $name[0]; + } else { + if ($name[1] !== $className) { + $name = $name[1] . '::' . $name[2]; + } else { + $name = $name[2]; + } } + $attributes[$name] = $value; } - if (\count($exception->getExceptions())) { - throw $exception; - } + return $attributes; } } - * Marcello Duarte + * This file is part of sebastian/object-reflector. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -/** - * This class is a modification from sebastianbergmann/exporter - * @see https://github.com/sebastianbergmann/exporter - */ -class ExportUtil +namespace PHPUnit\SebastianBergmann\ObjectReflector; + +use Throwable; +interface Exception extends Throwable { - /** - * Exports a value as a string - * - * The output of this method is similar to the output of print_r(), but - * improved in various aspects: - * - * - NULL is rendered as "null" (instead of "") - * - TRUE is rendered as "true" (instead of "1") - * - FALSE is rendered as "false" (instead of "") - * - Strings are always quoted with single quotes - * - Carriage returns and newlines are normalized to \n - * - Recursion and repeated rendering is treated properly - * - * @param mixed $value - * @param int $indentation The indentation level of the 2nd+ line - * @return string - */ - public static function export($value, $indentation = 0) - { - return self::recursiveExport($value, $indentation); - } - /** - * Converts an object to an array containing all of its private, protected - * and public properties. - * - * @param mixed $value - * @return array - */ - public static function toArray($value) - { - if (!\is_object($value)) { - return (array) $value; - } - $array = array(); - foreach ((array) $value as $key => $val) { - // properties are transformed to keys in the following way: - // private $property => "\0Classname\0property" - // protected $property => "\0*\0property" - // public $property => "property" - if (\preg_match('/^\\0.+\\0(.+)$/', $key, $matches)) { - $key = $matches[1]; - } - // See https://github.com/php/php-src/commit/5721132 - if ($key === "\0gcdata") { - continue; - } - $array[$key] = $val; - } - // Some internal classes like SplObjectStorage don't work with the - // above (fast) mechanism nor with reflection in Zend. - // Format the output similarly to print_r() in this case - if ($value instanceof \SplObjectStorage) { - // However, the fast method does work in HHVM, and exposes the - // internal implementation. Hide it again. - if (\property_exists('\\SplObjectStorage', '__storage')) { - unset($array['__storage']); - } elseif (\property_exists('\\SplObjectStorage', 'storage')) { - unset($array['storage']); - } - if (\property_exists('\\SplObjectStorage', '__key')) { - unset($array['__key']); - } - foreach ($value as $key => $val) { - $array[\spl_object_hash($val)] = array('obj' => $val, 'inf' => $value->getInfo()); - } - } - return $array; - } - /** - * Recursive implementation of export - * - * @param mixed $value The value to export - * @param int $indentation The indentation level of the 2nd+ line - * @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects - * @return string - * @see SebastianBergmann\Exporter\Exporter::export - */ - protected static function recursiveExport(&$value, $indentation, $processed = null) - { - if ($value === null) { - return 'null'; - } - if ($value === \true) { - return 'true'; - } - if ($value === \false) { - return 'false'; - } - if (\is_float($value) && \floatval(\intval($value)) === $value) { - return "{$value}.0"; - } - if (\is_resource($value)) { - return \sprintf('resource(%d) of type (%s)', $value, \get_resource_type($value)); - } - if (\is_string($value)) { - // Match for most non printable chars somewhat taking multibyte chars into account - if (\preg_match('/[^\\x09-\\x0d\\x20-\\xff]/', $value)) { - return 'Binary String: 0x' . \bin2hex($value); - } - return "'" . \str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) . "'"; - } - $whitespace = \str_repeat(' ', 4 * $indentation); - if (!$processed) { - $processed = new \PHPUnit\SebastianBergmann\RecursionContext\Context(); - } - if (\is_array($value)) { - if (($key = $processed->contains($value)) !== \false) { - return 'Array &' . $key; - } - $array = $value; - $key = $processed->add($value); - $values = ''; - if (\count($array) > 0) { - foreach ($array as $k => $v) { - $values .= \sprintf('%s %s => %s' . "\n", $whitespace, self::recursiveExport($k, $indentation), self::recursiveExport($value[$k], $indentation + 1, $processed)); - } - $values = "\n" . $values . $whitespace; - } - return \sprintf('Array &%s (%s)', $key, $values); - } - if (\is_object($value)) { - $class = \get_class($value); - if ($hash = $processed->contains($value)) { - return \sprintf('%s:%s Object', $class, $hash); - } - $hash = $processed->add($value); - $values = ''; - $array = self::toArray($value); - if (\count($array) > 0) { - foreach ($array as $k => $v) { - $values .= \sprintf('%s %s => %s' . "\n", $whitespace, self::recursiveExport($k, $indentation), self::recursiveExport($v, $indentation + 1, $processed)); - } - $values = "\n" . $values . $whitespace; - } - return \sprintf('%s:%s Object (%s)', $class, $hash, $values); - } - return \var_export($value, \true); - } } - * Marcello Duarte + * This file is part of sebastian/object-reflector. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Util; +namespace PHPUnit\SebastianBergmann\ObjectReflector; -use Prophecy\Call\Call; -/** - * String utility. +class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} + + * (c) Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -class StringUtil +namespace PHPUnit\SebastianBergmann\Invoker; + +use const SIGALRM; +use function call_user_func_array; +use function function_exists; +use function pcntl_alarm; +use function pcntl_async_signals; +use function pcntl_signal; +use function sprintf; +use Throwable; +final class Invoker { - private $verbose; /** - * @param bool $verbose + * @var int */ - public function __construct($verbose = \true) - { - $this->verbose = $verbose; - } + private $timeout; /** - * Stringifies any provided value. - * - * @param mixed $value - * @param boolean $exportObject - * - * @return string + * @throws Throwable */ - public function stringify($value, $exportObject = \true) + public function invoke(callable $callable, array $arguments, int $timeout) { - if (\is_array($value)) { - if (\range(0, \count($value) - 1) === \array_keys($value)) { - return '[' . \implode(', ', \array_map(array($this, __FUNCTION__), $value)) . ']'; - } - $stringify = array($this, __FUNCTION__); - return '[' . \implode(', ', \array_map(function ($item, $key) use($stringify) { - return (\is_integer($key) ? $key : '"' . $key . '"') . ' => ' . \call_user_func($stringify, $item); - }, $value, \array_keys($value))) . ']'; - } - if (\is_resource($value)) { - return \get_resource_type($value) . ':' . $value; - } - if (\is_object($value)) { - return $exportObject ? \Prophecy\Util\ExportUtil::export($value) : \sprintf('%s:%s', \get_class($value), \spl_object_hash($value)); - } - if (\true === $value || \false === $value) { - return $value ? 'true' : 'false'; - } - if (\is_string($value)) { - $str = \sprintf('"%s"', \str_replace("\n", '\\n', $value)); - if (!$this->verbose && 50 <= \strlen($str)) { - return \substr($str, 0, 50) . '"...'; - } - return $str; + if (!$this->canInvokeWithTimeout()) { + throw new ProcessControlExtensionNotLoadedException('The pcntl (process control) extension for PHP is required'); } - if (null === $value) { - return 'null'; + pcntl_signal(\SIGALRM, function () : void { + throw new TimeoutException(sprintf('Execution aborted after %d second%s', $this->timeout, $this->timeout === 1 ? '' : 's')); + }, \true); + $this->timeout = $timeout; + pcntl_async_signals(\true); + pcntl_alarm($timeout); + try { + return call_user_func_array($callable, $arguments); + } finally { + pcntl_alarm(0); } - return (string) $value; } - /** - * Stringifies provided array of calls. - * - * @param Call[] $calls Array of Call instances - * - * @return string - */ - public function stringifyCalls(array $calls) + public function canInvokeWithTimeout() : bool { - $self = $this; - return \implode(\PHP_EOL, \array_map(function (\Prophecy\Call\Call $call) use($self) { - return \sprintf(' - %s(%s) @ %s', $call->getMethodName(), \implode(', ', \array_map(array($self, 'stringify'), $call->getArguments())), \str_replace(\GETCWD() . \DIRECTORY_SEPARATOR, '', $call->getCallPlace())); - }, $calls)); + return function_exists('pcntl_signal') && function_exists('pcntl_async_signals') && function_exists('pcntl_alarm'); } } - * Marcello Duarte + * This file is part of phpunit/php-invoker. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy; +namespace PHPUnit\SebastianBergmann\Invoker; -use Prophecy\Argument\Token; -/** - * Argument tokens shortcuts. +use RuntimeException; +final class ProcessControlExtensionNotLoadedException extends RuntimeException implements Exception +{ +} + + * (c) Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -class Argument +namespace PHPUnit\SebastianBergmann\Invoker; + +use RuntimeException; +final class TimeoutException extends RuntimeException implements Exception { - /** - * Checks that argument is exact value or object. - * - * @param mixed $value - * - * @return Token\ExactValueToken - */ - public static function exact($value) - { - return new \Prophecy\Argument\Token\ExactValueToken($value); - } - /** - * Checks that argument is of specific type or instance of specific class. - * - * @param string $type Type name (`integer`, `string`) or full class name - * - * @return Token\TypeToken - */ - public static function type($type) - { - return new \Prophecy\Argument\Token\TypeToken($type); - } - /** - * Checks that argument object has specific state. - * - * @param string $methodName - * @param mixed $value - * - * @return Token\ObjectStateToken - */ - public static function which($methodName, $value) - { - return new \Prophecy\Argument\Token\ObjectStateToken($methodName, $value); - } - /** - * Checks that argument matches provided callback. - * - * @param callable $callback - * - * @return Token\CallbackToken - */ - public static function that($callback) - { - return new \Prophecy\Argument\Token\CallbackToken($callback); - } - /** - * Matches any single value. - * - * @return Token\AnyValueToken - */ - public static function any() - { - return new \Prophecy\Argument\Token\AnyValueToken(); - } - /** - * Matches all values to the rest of the signature. - * - * @return Token\AnyValuesToken - */ - public static function cetera() - { - return new \Prophecy\Argument\Token\AnyValuesToken(); - } - /** - * Checks that argument matches all tokens - * - * @param mixed ... a list of tokens - * - * @return Token\LogicalAndToken - */ - public static function allOf() - { - return new \Prophecy\Argument\Token\LogicalAndToken(\func_get_args()); - } - /** - * Checks that argument array or countable object has exact number of elements. - * - * @param integer $value array elements count - * - * @return Token\ArrayCountToken - */ - public static function size($value) - { - return new \Prophecy\Argument\Token\ArrayCountToken($value); - } - /** - * Checks that argument array contains (key, value) pair - * - * @param mixed $key exact value or token - * @param mixed $value exact value or token - * - * @return Token\ArrayEntryToken - */ - public static function withEntry($key, $value) - { - return new \Prophecy\Argument\Token\ArrayEntryToken($key, $value); - } - /** - * Checks that arguments array entries all match value - * - * @param mixed $value - * - * @return Token\ArrayEveryEntryToken - */ - public static function withEveryEntry($value) - { - return new \Prophecy\Argument\Token\ArrayEveryEntryToken($value); - } - /** - * Checks that argument array contains value - * - * @param mixed $value - * - * @return Token\ArrayEntryToken - */ - public static function containing($value) - { - return new \Prophecy\Argument\Token\ArrayEntryToken(self::any(), $value); - } - /** - * Checks that argument array has key - * - * @param mixed $key exact value or token - * - * @return Token\ArrayEntryToken - */ - public static function withKey($key) - { - return new \Prophecy\Argument\Token\ArrayEntryToken($key, self::any()); - } - /** - * Checks that argument does not match the value|token. - * - * @param mixed $value either exact value or argument token - * - * @return Token\LogicalNotToken - */ - public static function not($value) - { - return new \Prophecy\Argument\Token\LogicalNotToken($value); - } - /** - * @param string $value - * - * @return Token\StringContainsToken - */ - public static function containingString($value) - { - return new \Prophecy\Argument\Token\StringContainsToken($value); - } - /** - * Checks that argument is identical value. - * - * @param mixed $value - * - * @return Token\IdenticalValueToken - */ - public static function is($value) - { - return new \Prophecy\Argument\Token\IdenticalValueToken($value); - } - /** - * Check that argument is same value when rounding to the - * given precision. - * - * @param float $value - * @param float $precision - * - * @return Token\ApproximateValueToken - */ - public static function approximate($value, $precision = 0) - { - return new \Prophecy\Argument\Token\ApproximateValueToken($value, $precision); - } - /** - * Checks that argument is in array. - * - * @param array $value - * - * @return Token\InArrayToken - */ - public function in($value) - { - return new \Prophecy\Argument\Token\InArrayToken($value); - } - /** - * Checks that argument is in array. - * - * @param array $value - * - * @return Token\InArrayToken - */ - public function notIn($value) - { - return new \Prophecy\Argument\Token\NotInArrayToken($value); - } } - * Marcello Duarte + * This file is part of phpunit/php-invoker. + * + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Prediction; +namespace PHPUnit\SebastianBergmann\Invoker; -use Prophecy\Call\Call; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Util\StringUtil; -use Prophecy\Exception\Prediction\UnexpectedCallsException; -/** - * No calls prediction. +use Throwable; +interface Exception extends Throwable +{ +} + + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -class NoCallsPrediction implements \Prophecy\Prediction\PredictionInterface +namespace PHPUnit\PharIo\Version; + +class OrVersionConstraintGroup extends AbstractVersionConstraint { - private $util; + /** @var VersionConstraint[] */ + private $constraints = []; /** - * Initializes prediction. - * - * @param null|StringUtil $util + * @param string $originalValue + * @param VersionConstraint[] $constraints */ - public function __construct(\Prophecy\Util\StringUtil $util = null) + public function __construct($originalValue, array $constraints) { - $this->util = $util ?: new \Prophecy\Util\StringUtil(); + parent::__construct($originalValue); + $this->constraints = $constraints; } - /** - * Tests that there were no calls made. - * - * @param Call[] $calls - * @param ObjectProphecy $object - * @param MethodProphecy $method - * - * @throws \Prophecy\Exception\Prediction\UnexpectedCallsException - */ - public function check(array $calls, \Prophecy\Prophecy\ObjectProphecy $object, \Prophecy\Prophecy\MethodProphecy $method) + public function complies(Version $version) : bool { - if (!\count($calls)) { - return; + foreach ($this->constraints as $constraint) { + if ($constraint->complies($version)) { + return \true; + } } - $verb = \count($calls) === 1 ? 'was' : 'were'; - throw new \Prophecy\Exception\Prediction\UnexpectedCallsException(\sprintf("No calls expected that match:\n" . " %s->%s(%s)\n" . "but %d %s made:\n%s", \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), \count($calls), $verb, $this->util->stringifyCalls($calls)), $method, $calls); + return \false; } } - * Marcello Duarte + * This file is part of PharIo\Version. + * + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Prediction; +namespace PHPUnit\PharIo\Version; -use Prophecy\Call\Call; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Argument\ArgumentsWildcard; -use Prophecy\Argument\Token\AnyValuesToken; -use Prophecy\Util\StringUtil; -use Prophecy\Exception\Prediction\UnexpectedCallsCountException; -/** - * Prediction interface. - * Predictions are logical test blocks, tied to `should...` keyword. - * - * @author Konstantin Kudryashov - */ -class CallTimesPrediction implements \Prophecy\Prediction\PredictionInterface +interface VersionConstraint { - private $times; - private $util; - /** - * Initializes prediction. - * - * @param int $times - * @param StringUtil $util - */ - public function __construct($times, \Prophecy\Util\StringUtil $util = null) - { - $this->times = \intval($times); - $this->util = $util ?: new \Prophecy\Util\StringUtil(); - } - /** - * Tests that there was exact amount of calls made. - * - * @param Call[] $calls - * @param ObjectProphecy $object - * @param MethodProphecy $method - * - * @throws \Prophecy\Exception\Prediction\UnexpectedCallsCountException - */ - public function check(array $calls, \Prophecy\Prophecy\ObjectProphecy $object, \Prophecy\Prophecy\MethodProphecy $method) - { - if ($this->times == \count($calls)) { - return; - } - $methodCalls = $object->findProphecyMethodCalls($method->getMethodName(), new \Prophecy\Argument\ArgumentsWildcard(array(new \Prophecy\Argument\Token\AnyValuesToken()))); - if (\count($calls)) { - $message = \sprintf("Expected exactly %d calls that match:\n" . " %s->%s(%s)\n" . "but %d were made:\n%s", $this->times, \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), \count($calls), $this->util->stringifyCalls($calls)); - } elseif (\count($methodCalls)) { - $message = \sprintf("Expected exactly %d calls that match:\n" . " %s->%s(%s)\n" . "but none were made.\n" . "Recorded `%s(...)` calls:\n%s", $this->times, \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), $method->getMethodName(), $this->util->stringifyCalls($methodCalls)); - } else { - $message = \sprintf("Expected exactly %d calls that match:\n" . " %s->%s(%s)\n" . "but none were made.", $this->times, \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard()); - } - throw new \Prophecy\Exception\Prediction\UnexpectedCallsCountException($message, $method, $this->times, $calls); - } + public function complies(Version $version) : bool; + public function asString() : string; } - * Marcello Duarte + * This file is part of PharIo\Version. + * + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Prediction; +namespace PHPUnit\PharIo\Version; -use Prophecy\Call\Call; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -/** - * Prediction interface. - * Predictions are logical test blocks, tied to `should...` keyword. - * - * @author Konstantin Kudryashov - */ -interface PredictionInterface +class ExactVersionConstraint extends AbstractVersionConstraint { - /** - * Tests that double fulfilled prediction. - * - * @param Call[] $calls - * @param ObjectProphecy $object - * @param MethodProphecy $method - * - * @throws object - * @return void - */ - public function check(array $calls, \Prophecy\Prophecy\ObjectProphecy $object, \Prophecy\Prophecy\MethodProphecy $method); + public function complies(Version $version) : bool + { + return $this->asString() === $version->getVersionString(); + } } - * Marcello Duarte + * This file is part of PharIo\Version. + * + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Prediction; +namespace PHPUnit\PharIo\Version; -use Prophecy\Call\Call; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Argument\ArgumentsWildcard; -use Prophecy\Argument\Token\AnyValuesToken; -use Prophecy\Util\StringUtil; -use Prophecy\Exception\Prediction\NoCallsException; -/** - * Call prediction. - * - * @author Konstantin Kudryashov - */ -class CallPrediction implements \Prophecy\Prediction\PredictionInterface +class AnyVersionConstraint implements VersionConstraint { - private $util; - /** - * Initializes prediction. - * - * @param StringUtil $util - */ - public function __construct(\Prophecy\Util\StringUtil $util = null) + public function complies(Version $version) : bool { - $this->util = $util ?: new \Prophecy\Util\StringUtil(); + return \true; } - /** - * Tests that there was at least one call. - * - * @param Call[] $calls - * @param ObjectProphecy $object - * @param MethodProphecy $method - * - * @throws \Prophecy\Exception\Prediction\NoCallsException - */ - public function check(array $calls, \Prophecy\Prophecy\ObjectProphecy $object, \Prophecy\Prophecy\MethodProphecy $method) + public function asString() : string { - if (\count($calls)) { - return; - } - $methodCalls = $object->findProphecyMethodCalls($method->getMethodName(), new \Prophecy\Argument\ArgumentsWildcard(array(new \Prophecy\Argument\Token\AnyValuesToken()))); - if (\count($methodCalls)) { - throw new \Prophecy\Exception\Prediction\NoCallsException(\sprintf("No calls have been made that match:\n" . " %s->%s(%s)\n" . "but expected at least one.\n" . "Recorded `%s(...)` calls:\n%s", \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), $method->getMethodName(), $this->util->stringifyCalls($methodCalls)), $method); - } - throw new \Prophecy\Exception\Prediction\NoCallsException(\sprintf("No calls have been made that match:\n" . " %s->%s(%s)\n" . "but expected at least one.", \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard()), $method); + return '*'; } } - * Marcello Duarte + * This file is part of PharIo\Version. + * + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Prophecy\Prediction; +namespace PHPUnit\PharIo\Version; -use Prophecy\Call\Call; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Exception\InvalidArgumentException; -use Closure; -/** - * Callback prediction. - * - * @author Konstantin Kudryashov - */ -class CallbackPrediction implements \Prophecy\Prediction\PredictionInterface +class SpecificMajorVersionConstraint extends AbstractVersionConstraint { - private $callback; - /** - * Initializes callback prediction. - * - * @param callable $callback Custom callback - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function __construct($callback) + /** @var int */ + private $major; + public function __construct(string $originalValue, int $major) { - if (!\is_callable($callback)) { - throw new \Prophecy\Exception\InvalidArgumentException(\sprintf('Callable expected as an argument to CallbackPrediction, but got %s.', \gettype($callback))); - } - $this->callback = $callback; + parent::__construct($originalValue); + $this->major = $major; } - /** - * Executes preset callback. - * - * @param Call[] $calls - * @param ObjectProphecy $object - * @param MethodProphecy $method - */ - public function check(array $calls, \Prophecy\Prophecy\ObjectProphecy $object, \Prophecy\Prophecy\MethodProphecy $method) + public function complies(Version $version) : bool { - $callback = $this->callback; - if ($callback instanceof \Closure && \method_exists('Closure', 'bind')) { - $callback = \Closure::bind($callback, $object); - } - \call_user_func($callback, $calls, $object, $method); + return $version->getMajor()->getValue() === $this->major; } } -Copyright (c) 2013 Konstantin Kudryashov - Marcello Duarte - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnit\PharIo\Version; -use RuntimeException; -use PHPUnit\SebastianBergmann\Diff\Differ; -use PHPUnit\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; -/** - * Thrown when an assertion for string equality failed. - */ -class ComparisonFailure extends \RuntimeException +class SpecificMajorAndMinorVersionConstraint extends AbstractVersionConstraint { - /** - * Expected value of the retrieval which does not match $actual. - * - * @var mixed - */ - protected $expected; - /** - * Actually retrieved value which does not match $expected. - * - * @var mixed - */ - protected $actual; - /** - * The string representation of the expected value. - * - * @var string - */ - protected $expectedAsString; - /** - * The string representation of the actual value. - * - * @var string - */ - protected $actualAsString; - /** - * @var bool - */ - protected $identical; - /** - * Optional message which is placed in front of the first line - * returned by toString(). - * - * @var string - */ - protected $message; - /** - * Initialises with the expected value and the actual value. - * - * @param mixed $expected expected value retrieved - * @param mixed $actual actual value retrieved - * @param string $expectedAsString - * @param string $actualAsString - * @param bool $identical - * @param string $message a string which is prefixed on all returned lines - * in the difference output - */ - public function __construct($expected, $actual, $expectedAsString, $actualAsString, $identical = \false, $message = '') - { - $this->expected = $expected; - $this->actual = $actual; - $this->expectedAsString = $expectedAsString; - $this->actualAsString = $actualAsString; - $this->message = $message; - } - public function getActual() - { - return $this->actual; - } - public function getExpected() - { - return $this->expected; - } - /** - * @return string - */ - public function getActualAsString() - { - return $this->actualAsString; - } - /** - * @return string - */ - public function getExpectedAsString() + /** @var int */ + private $major; + /** @var int */ + private $minor; + public function __construct(string $originalValue, int $major, int $minor) { - return $this->expectedAsString; + parent::__construct($originalValue); + $this->major = $major; + $this->minor = $minor; } - /** - * @return string - */ - public function getDiff() + public function complies(Version $version) : bool { - if (!$this->actualAsString && !$this->expectedAsString) { - return ''; + if ($version->getMajor()->getValue() !== $this->major) { + return \false; } - $differ = new \PHPUnit\SebastianBergmann\Diff\Differ(new \PHPUnit\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder("\n--- Expected\n+++ Actual\n")); - return $differ->diff($this->expectedAsString, $this->actualAsString); - } - /** - * @return string - */ - public function toString() - { - return $this->message . $this->getDiff(); + return $version->getMinor()->getValue() === $this->minor; } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnit\PharIo\Version; -use function array_key_exists; -use function is_array; -use function sort; -use function sprintf; -use function str_replace; -use function trim; -/** - * Compares arrays for equality. - * - * Arrays are equal if they contain the same key-value pairs. - * The order of the keys does not matter. - * The types of key-value pairs do not matter. - */ -class ArrayComparator extends \PHPUnit\SebastianBergmann\Comparator\Comparator +class GreaterThanOrEqualToVersionConstraint extends AbstractVersionConstraint { - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - public function accepts($expected, $actual) - { - return \is_array($expected) && \is_array($actual); - } - /** - * Asserts that two arrays are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * @param array $processed List of already processed elements (used to prevent infinite recursion) - * - * @throws ComparisonFailure - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false, array &$processed = []) + /** @var Version */ + private $minimalVersion; + public function __construct(string $originalValue, Version $minimalVersion) { - if ($canonicalize) { - \sort($expected); - \sort($actual); - } - $remaining = $actual; - $actualAsString = "Array (\n"; - $expectedAsString = "Array (\n"; - $equal = \true; - foreach ($expected as $key => $value) { - unset($remaining[$key]); - if (!\array_key_exists($key, $actual)) { - $expectedAsString .= \sprintf(" %s => %s\n", $this->exporter->export($key), $this->exporter->shortenedExport($value)); - $equal = \false; - continue; - } - try { - $comparator = $this->factory->getComparatorFor($value, $actual[$key]); - $comparator->assertEquals($value, $actual[$key], $delta, $canonicalize, $ignoreCase, $processed); - $expectedAsString .= \sprintf(" %s => %s\n", $this->exporter->export($key), $this->exporter->shortenedExport($value)); - $actualAsString .= \sprintf(" %s => %s\n", $this->exporter->export($key), $this->exporter->shortenedExport($actual[$key])); - } catch (\PHPUnit\SebastianBergmann\Comparator\ComparisonFailure $e) { - $expectedAsString .= \sprintf(" %s => %s\n", $this->exporter->export($key), $e->getExpectedAsString() ? $this->indent($e->getExpectedAsString()) : $this->exporter->shortenedExport($e->getExpected())); - $actualAsString .= \sprintf(" %s => %s\n", $this->exporter->export($key), $e->getActualAsString() ? $this->indent($e->getActualAsString()) : $this->exporter->shortenedExport($e->getActual())); - $equal = \false; - } - } - foreach ($remaining as $key => $value) { - $actualAsString .= \sprintf(" %s => %s\n", $this->exporter->export($key), $this->exporter->shortenedExport($value)); - $equal = \false; - } - $expectedAsString .= ')'; - $actualAsString .= ')'; - if (!$equal) { - throw new \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure($expected, $actual, $expectedAsString, $actualAsString, \false, 'Failed asserting that two arrays are equal.'); - } + parent::__construct($originalValue); + $this->minimalVersion = $minimalVersion; } - protected function indent($lines) + public function complies(Version $version) : bool { - return \trim(\str_replace("\n", "\n ", $lines)); + return $version->getVersionString() === $this->minimalVersion->getVersionString() || $version->isGreaterThan($this->minimalVersion); } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnit\PharIo\Version; -use function array_unshift; -/** - * Factory for comparators which compare values for equality. - */ -class Factory +abstract class AbstractVersionConstraint implements VersionConstraint { - /** - * @var Factory - */ - private static $instance; - /** - * @var Comparator[] - */ - private $customComparators = []; - /** - * @var Comparator[] - */ - private $defaultComparators = []; - /** - * @return Factory - */ - public static function getInstance() - { - if (self::$instance === null) { - self::$instance = new self(); - // @codeCoverageIgnore - } - return self::$instance; - } - /** - * Constructs a new factory. - */ - public function __construct() + /** @var string */ + private $originalValue; + public function __construct(string $originalValue) { - $this->registerDefaultComparators(); + $this->originalValue = $originalValue; } - /** - * Returns the correct comparator for comparing two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return Comparator - */ - public function getComparatorFor($expected, $actual) + public function asString() : string { - foreach ($this->customComparators as $comparator) { - if ($comparator->accepts($expected, $actual)) { - return $comparator; - } - } - foreach ($this->defaultComparators as $comparator) { - if ($comparator->accepts($expected, $actual)) { - return $comparator; - } - } - throw new \PHPUnit\SebastianBergmann\Comparator\RuntimeException('No suitable Comparator implementation found'); + return $this->originalValue; } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\PharIo\Version; + +class AndVersionConstraintGroup extends AbstractVersionConstraint +{ + /** @var VersionConstraint[] */ + private $constraints = []; /** - * Registers a new comparator. - * - * This comparator will be returned by getComparatorFor() if its accept() method - * returns TRUE for the compared values. It has higher priority than the - * existing comparators, meaning that its accept() method will be invoked - * before those of the other comparators. - * - * @param Comparator $comparator The comparator to be registered + * @param VersionConstraint[] $constraints */ - public function register(\PHPUnit\SebastianBergmann\Comparator\Comparator $comparator) + public function __construct(string $originalValue, array $constraints) { - \array_unshift($this->customComparators, $comparator); - $comparator->setFactory($this); + parent::__construct($originalValue); + $this->constraints = $constraints; } - /** - * Unregisters a comparator. - * - * This comparator will no longer be considered by getComparatorFor(). - * - * @param Comparator $comparator The comparator to be unregistered - */ - public function unregister(\PHPUnit\SebastianBergmann\Comparator\Comparator $comparator) + public function complies(Version $version) : bool { - foreach ($this->customComparators as $key => $_comparator) { - if ($comparator === $_comparator) { - unset($this->customComparators[$key]); + foreach ($this->constraints as $constraint) { + if (!$constraint->complies($version)) { + return \false; } } + return \true; } - /** - * Unregisters all non-default comparators. - */ - public function reset() +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\PharIo\Version; + +class VersionNumber +{ + /** @var ?int */ + private $value; + public function __construct(?int $value) { - $this->customComparators = []; + $this->value = $value; } - private function registerDefaultComparators() : void + public function isAny() : bool { - $this->registerDefaultComparator(new \PHPUnit\SebastianBergmann\Comparator\MockObjectComparator()); - $this->registerDefaultComparator(new \PHPUnit\SebastianBergmann\Comparator\DateTimeComparator()); - $this->registerDefaultComparator(new \PHPUnit\SebastianBergmann\Comparator\DOMNodeComparator()); - $this->registerDefaultComparator(new \PHPUnit\SebastianBergmann\Comparator\SplObjectStorageComparator()); - $this->registerDefaultComparator(new \PHPUnit\SebastianBergmann\Comparator\ExceptionComparator()); - $this->registerDefaultComparator(new \PHPUnit\SebastianBergmann\Comparator\ObjectComparator()); - $this->registerDefaultComparator(new \PHPUnit\SebastianBergmann\Comparator\ResourceComparator()); - $this->registerDefaultComparator(new \PHPUnit\SebastianBergmann\Comparator\ArrayComparator()); - $this->registerDefaultComparator(new \PHPUnit\SebastianBergmann\Comparator\DoubleComparator()); - $this->registerDefaultComparator(new \PHPUnit\SebastianBergmann\Comparator\NumericComparator()); - $this->registerDefaultComparator(new \PHPUnit\SebastianBergmann\Comparator\ScalarComparator()); - $this->registerDefaultComparator(new \PHPUnit\SebastianBergmann\Comparator\TypeComparator()); - } - private function registerDefaultComparator(\PHPUnit\SebastianBergmann\Comparator\Comparator $comparator) : void + return $this->value === null; + } + public function getValue() : ?int { - $this->defaultComparators[] = $comparator; - $comparator->setFactory($this); + return $this->value; } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnit\PharIo\Version; -use function gettype; -use function sprintf; -/** - * Compares values for type equality. - */ -class TypeComparator extends \PHPUnit\SebastianBergmann\Comparator\Comparator +class VersionConstraintParser { /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool + * @throws UnsupportedVersionConstraintException */ - public function accepts($expected, $actual) + public function parse(string $value) : VersionConstraint { - return \true; + if (\strpos($value, '||') !== \false) { + return $this->handleOrGroup($value); + } + if (!\preg_match('/^[\\^~*]?v?[\\d.*]+(?:-.*)?$/i', $value)) { + throw new UnsupportedVersionConstraintException(\sprintf('Version constraint %s is not supported.', $value)); + } + switch ($value[0]) { + case '~': + return $this->handleTildeOperator($value); + case '^': + return $this->handleCaretOperator($value); + } + $constraint = new VersionConstraintValue($value); + if ($constraint->getMajor()->isAny()) { + return new AnyVersionConstraint(); + } + if ($constraint->getMinor()->isAny()) { + return new SpecificMajorVersionConstraint($constraint->getVersionString(), $constraint->getMajor()->getValue() ?? 0); + } + if ($constraint->getPatch()->isAny()) { + return new SpecificMajorAndMinorVersionConstraint($constraint->getVersionString(), $constraint->getMajor()->getValue() ?? 0, $constraint->getMinor()->getValue() ?? 0); + } + return new ExactVersionConstraint($constraint->getVersionString()); } - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * - * @throws ComparisonFailure - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false) + private function handleOrGroup(string $value) : OrVersionConstraintGroup { - if (\gettype($expected) != \gettype($actual)) { - throw new \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure( - $expected, - $actual, - // we don't need a diff - '', - '', - \false, - \sprintf('%s does not match expected type "%s".', $this->exporter->shortenedExport($actual), \gettype($expected)) - ); + $constraints = []; + foreach (\explode('||', $value) as $groupSegment) { + $constraints[] = $this->parse(\trim($groupSegment)); + } + return new OrVersionConstraintGroup($value, $constraints); + } + private function handleTildeOperator(string $value) : AndVersionConstraintGroup + { + $constraintValue = new VersionConstraintValue(\substr($value, 1)); + if ($constraintValue->getPatch()->isAny()) { + return $this->handleCaretOperator($value); + } + $constraints = [new GreaterThanOrEqualToVersionConstraint($value, new Version(\substr($value, 1))), new SpecificMajorAndMinorVersionConstraint($value, $constraintValue->getMajor()->getValue() ?? 0, $constraintValue->getMinor()->getValue() ?? 0)]; + return new AndVersionConstraintGroup($value, $constraints); + } + private function handleCaretOperator(string $value) : AndVersionConstraintGroup + { + $constraintValue = new VersionConstraintValue(\substr($value, 1)); + $constraints = [new GreaterThanOrEqualToVersionConstraint($value, new Version(\substr($value, 1)))]; + if ($constraintValue->getMajor()->getValue() === 0) { + $constraints[] = new SpecificMajorAndMinorVersionConstraint($value, $constraintValue->getMajor()->getValue() ?? 0, $constraintValue->getMinor()->getValue() ?? 0); + } else { + $constraints[] = new SpecificMajorVersionConstraint($value, $constraintValue->getMajor()->getValue() ?? 0); } + return new AndVersionConstraintGroup($value, $constraints); } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnit\PharIo\Version; -use function is_resource; -/** - * Compares resources for equality. - */ -class ResourceComparator extends \PHPUnit\SebastianBergmann\Comparator\Comparator +class Version { + /** @var string */ + private $originalVersionString; + /** @var VersionNumber */ + private $major; + /** @var VersionNumber */ + private $minor; + /** @var VersionNumber */ + private $patch; + /** @var null|PreReleaseSuffix */ + private $preReleaseSuffix; + public function __construct(string $versionString) + { + $this->ensureVersionStringIsValid($versionString); + $this->originalVersionString = $versionString; + } + public function getPreReleaseSuffix() : PreReleaseSuffix + { + if ($this->preReleaseSuffix === null) { + throw new NoPreReleaseSuffixException('No pre-release suffix set'); + } + return $this->preReleaseSuffix; + } + public function getOriginalString() : string + { + return $this->originalVersionString; + } + public function getVersionString() : string + { + $str = \sprintf('%d.%d.%d', $this->getMajor()->getValue() ?? 0, $this->getMinor()->getValue() ?? 0, $this->getPatch()->getValue() ?? 0); + if (!$this->hasPreReleaseSuffix()) { + return $str; + } + return $str . '-' . $this->getPreReleaseSuffix()->asString(); + } + public function hasPreReleaseSuffix() : bool + { + return $this->preReleaseSuffix !== null; + } + public function equals(Version $other) : bool + { + return $this->getVersionString() === $other->getVersionString(); + } + public function isGreaterThan(Version $version) : bool + { + if ($version->getMajor()->getValue() > $this->getMajor()->getValue()) { + return \false; + } + if ($version->getMajor()->getValue() < $this->getMajor()->getValue()) { + return \true; + } + if ($version->getMinor()->getValue() > $this->getMinor()->getValue()) { + return \false; + } + if ($version->getMinor()->getValue() < $this->getMinor()->getValue()) { + return \true; + } + if ($version->getPatch()->getValue() > $this->getPatch()->getValue()) { + return \false; + } + if ($version->getPatch()->getValue() < $this->getPatch()->getValue()) { + return \true; + } + if (!$version->hasPreReleaseSuffix() && !$this->hasPreReleaseSuffix()) { + return \false; + } + if ($version->hasPreReleaseSuffix() && !$this->hasPreReleaseSuffix()) { + return \true; + } + if (!$version->hasPreReleaseSuffix() && $this->hasPreReleaseSuffix()) { + return \false; + } + return $this->getPreReleaseSuffix()->isGreaterThan($version->getPreReleaseSuffix()); + } + public function getMajor() : VersionNumber + { + return $this->major; + } + public function getMinor() : VersionNumber + { + return $this->minor; + } + public function getPatch() : VersionNumber + { + return $this->patch; + } /** - * Returns whether the comparator can compare two values. + * @param string[] $matches * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool + * @throws InvalidPreReleaseSuffixException */ - public function accepts($expected, $actual) + private function parseVersion(array $matches) : void { - return \is_resource($expected) && \is_resource($actual); + $this->major = new VersionNumber((int) $matches['Major']); + $this->minor = new VersionNumber((int) $matches['Minor']); + $this->patch = isset($matches['Patch']) ? new VersionNumber((int) $matches['Patch']) : new VersionNumber(0); + if (isset($matches['PreReleaseSuffix'])) { + $this->preReleaseSuffix = new PreReleaseSuffix($matches['PreReleaseSuffix']); + } } /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true + * @param string $version * - * @throws ComparisonFailure + * @throws InvalidVersionException */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false) + private function ensureVersionStringIsValid($version) : void { - if ($actual != $expected) { - throw new \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure($expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual)); + $regex = '/^v? + (?(0|(?:[1-9]\\d*))) + \\. + (?(0|(?:[1-9]\\d*))) + (\\. + (?(0|(?:[1-9]\\d*))) + )? + (?: + - + (?(?:(dev|beta|b|rc|alpha|a|patch|p)\\.?\\d*)) + )? + $/xi'; + if (\preg_match($regex, $version, $matches) !== 1) { + throw new InvalidVersionException(\sprintf("Version string '%s' does not follow SemVer semantics", $version)); } + $this->parseVersion($matches); } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnit\PharIo\Version; -use function abs; -use function is_float; -use function is_infinite; -use function is_nan; -use function is_numeric; -use function is_string; -use function sprintf; -/** - * Compares numerical values for equality. - */ -class NumericComparator extends \PHPUnit\SebastianBergmann\Comparator\ScalarComparator +final class UnsupportedVersionConstraintException extends \RuntimeException implements Exception { - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - public function accepts($expected, $actual) - { - // all numerical values, but not if one of them is a double - // or both of them are strings - return \is_numeric($expected) && \is_numeric($actual) && !(\is_float($expected) || \is_float($actual)) && !(\is_string($expected) && \is_string($actual)); - } - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * - * @throws ComparisonFailure - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false) - { - if ($this->isInfinite($actual) && $this->isInfinite($expected)) { - return; - } - if (($this->isInfinite($actual) xor $this->isInfinite($expected)) || ($this->isNan($actual) || $this->isNan($expected)) || \abs($actual - $expected) > $delta) { - throw new \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure($expected, $actual, '', '', \false, \sprintf('Failed asserting that %s matches expected %s.', $this->exporter->export($actual), $this->exporter->export($expected))); - } - } - private function isInfinite($value) : bool - { - return \is_float($value) && \is_infinite($value); - } - private function isNan($value) : bool - { - return \is_float($value) && \is_nan($value); - } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnit\PharIo\Version; use Throwable; -interface Exception extends \Throwable +interface Exception extends Throwable { } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnit\PharIo\Version; -final class RuntimeException extends \RuntimeException implements \PHPUnit\SebastianBergmann\Comparator\Exception +class NoPreReleaseSuffixException extends \Exception implements Exception { } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnit\PharIo\Version; -use SplObjectStorage; -/** - * Compares \SplObjectStorage instances for equality. - */ -class SplObjectStorageComparator extends \PHPUnit\SebastianBergmann\Comparator\Comparator +class InvalidVersionException extends \InvalidArgumentException implements Exception +{ +} + 0, 'a' => 1, 'alpha' => 1, 'b' => 2, 'beta' => 2, 'rc' => 3, 'p' => 4, 'patch' => 4]; + /** @var string */ + private $value; + /** @var int */ + private $valueScore; + /** @var int */ + private $number = 0; + /** @var string */ + private $full; /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool + * @throws InvalidPreReleaseSuffixException */ - public function accepts($expected, $actual) + public function __construct(string $value) { - return $expected instanceof \SplObjectStorage && $actual instanceof \SplObjectStorage; + $this->parseValue($value); } - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * - * @throws ComparisonFailure - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false) + public function asString() : string { - foreach ($actual as $object) { - if (!$expected->contains($object)) { - throw new \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure($expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual), \false, 'Failed asserting that two objects are equal.'); - } + return $this->full; + } + public function getValue() : string + { + return $this->value; + } + public function getNumber() : ?int + { + return $this->number; + } + public function isGreaterThan(PreReleaseSuffix $suffix) : bool + { + if ($this->valueScore > $suffix->valueScore) { + return \true; } - foreach ($expected as $object) { - if (!$actual->contains($object)) { - throw new \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure($expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual), \false, 'Failed asserting that two objects are equal.'); - } + if ($this->valueScore < $suffix->valueScore) { + return \false; + } + return $this->getNumber() > $suffix->getNumber(); + } + private function mapValueToScore(string $value) : int + { + $value = \strtolower($value); + if (\array_key_exists($value, self::valueScoreMap)) { + return self::valueScoreMap[$value]; + } + return 0; + } + private function parseValue(string $value) : void + { + $regex = '/-?((dev|beta|b|rc|alpha|a|patch|p)\\.?(\\d*)).*$/i'; + if (\preg_match($regex, $value, $matches) !== 1) { + throw new InvalidPreReleaseSuffixException(\sprintf('Invalid label %s', $value)); + } + $this->full = $matches[1]; + $this->value = $matches[2]; + if ($matches[3] !== '') { + $this->number = (int) $matches[3]; } + $this->valueScore = $this->mapValueToScore($matches[2]); } } +phar-io/version + +Copyright (c) 2016-2017 Arne Blankerts , Sebastian Heuer and contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of Arne Blankerts nor the names of contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnit\PharIo\Version; -use PHPUnit\SebastianBergmann\Exporter\Exporter; -/** - * Abstract base class for comparators which compare values for equality. - */ -abstract class Comparator +class VersionConstraintValue { - /** - * @var Factory - */ - protected $factory; - /** - * @var Exporter - */ - protected $exporter; - public function __construct() + /** @var VersionNumber */ + private $major; + /** @var VersionNumber */ + private $minor; + /** @var VersionNumber */ + private $patch; + /** @var string */ + private $label = ''; + /** @var string */ + private $buildMetaData = ''; + /** @var string */ + private $versionString = ''; + public function __construct(string $versionString) + { + $this->versionString = $versionString; + $this->parseVersion($versionString); + } + public function getLabel() : string { - $this->exporter = new \PHPUnit\SebastianBergmann\Exporter\Exporter(); + return $this->label; } - public function setFactory(\PHPUnit\SebastianBergmann\Comparator\Factory $factory) + public function getBuildMetaData() : string { - $this->factory = $factory; + return $this->buildMetaData; + } + public function getVersionString() : string + { + return $this->versionString; + } + public function getMajor() : VersionNumber + { + return $this->major; + } + public function getMinor() : VersionNumber + { + return $this->minor; + } + public function getPatch() : VersionNumber + { + return $this->patch; + } + private function parseVersion(string $versionString) : void + { + $this->extractBuildMetaData($versionString); + $this->extractLabel($versionString); + $this->stripPotentialVPrefix($versionString); + $versionSegments = \explode('.', $versionString); + $this->major = new VersionNumber(\is_numeric($versionSegments[0]) ? (int) $versionSegments[0] : null); + $minorValue = isset($versionSegments[1]) && \is_numeric($versionSegments[1]) ? (int) $versionSegments[1] : null; + $patchValue = isset($versionSegments[2]) && \is_numeric($versionSegments[2]) ? (int) $versionSegments[2] : null; + $this->minor = new VersionNumber($minorValue); + $this->patch = new VersionNumber($patchValue); + } + private function extractBuildMetaData(string &$versionString) : void + { + if (\preg_match('/\\+(.*)/', $versionString, $matches) === 1) { + $this->buildMetaData = $matches[1]; + $versionString = \str_replace($matches[0], '', $versionString); + } + } + private function extractLabel(string &$versionString) : void + { + if (\preg_match('/-(.*)/', $versionString, $matches) === 1) { + $this->label = $matches[1]; + $versionString = \str_replace($matches[0], '', $versionString); + } + } + private function stripPotentialVPrefix(string &$versionString) : void + { + if ($versionString[0] !== 'v') { + return; + } + $versionString = \substr($versionString, 1); } - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - public abstract function accepts($expected, $actual); - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * - * @throws ComparisonFailure - */ - public abstract function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false); } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnit\SebastianBergmann\ObjectEnumerator; -use function abs; -use function floor; -use function sprintf; -use DateInterval; -use DateTime; -use DateTimeInterface; -use DateTimeZone; -use Exception; +use function array_merge; +use function func_get_args; +use function is_array; +use function is_object; +use PHPUnit\SebastianBergmann\ObjectReflector\ObjectReflector; +use PHPUnit\SebastianBergmann\RecursionContext\Context; /** - * Compares DateTimeInterface instances for equality. + * Traverses array structures and object graphs + * to enumerate all referenced objects. */ -class DateTimeComparator extends \PHPUnit\SebastianBergmann\Comparator\ObjectComparator +class Enumerator { /** - * Returns whether the comparator can compare two values. + * Returns an array of all objects referenced either + * directly or indirectly by a variable. * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare + * @param array|object $variable * - * @return bool - */ - public function accepts($expected, $actual) - { - return ($expected instanceof \DateTime || $expected instanceof \DateTimeInterface) && ($actual instanceof \DateTime || $actual instanceof \DateTimeInterface); - } - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * @param array $processed List of already processed elements (used to prevent infinite recursion) - * - * @throws Exception - * @throws ComparisonFailure + * @return object[] */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false, array &$processed = []) + public function enumerate($variable) { - /** @var DateTimeInterface $expected */ - /** @var DateTimeInterface $actual */ - $absDelta = \abs($delta); - $delta = new \DateInterval(\sprintf('PT%dS', $absDelta)); - $delta->f = $absDelta - \floor($absDelta); - $actualClone = (clone $actual)->setTimezone(new \DateTimeZone('UTC')); - $expectedLower = (clone $expected)->setTimezone(new \DateTimeZone('UTC'))->sub($delta); - $expectedUpper = (clone $expected)->setTimezone(new \DateTimeZone('UTC'))->add($delta); - if ($actualClone < $expectedLower || $actualClone > $expectedUpper) { - throw new \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure($expected, $actual, $this->dateTimeToString($expected), $this->dateTimeToString($actual), \false, 'Failed asserting that two DateTime objects are equal.'); + if (!is_array($variable) && !is_object($variable)) { + throw new InvalidArgumentException(); } - } - /** - * Returns an ISO 8601 formatted string representation of a datetime or - * 'Invalid DateTimeInterface object' if the provided DateTimeInterface was not properly - * initialized. - */ - private function dateTimeToString(\DateTimeInterface $datetime) : string - { - $string = $datetime->format('Y-m-d\\TH:i:s.uO'); - return $string ?: 'Invalid DateTimeInterface object'; + if (isset(func_get_args()[1])) { + if (!func_get_args()[1] instanceof Context) { + throw new InvalidArgumentException(); + } + $processed = func_get_args()[1]; + } else { + $processed = new Context(); + } + $objects = []; + if ($processed->contains($variable)) { + return $objects; + } + $array = $variable; + $processed->add($variable); + if (is_array($variable)) { + foreach ($array as $element) { + if (!is_array($element) && !is_object($element)) { + continue; + } + $objects = array_merge($objects, $this->enumerate($element, $processed)); + } + } else { + $objects[] = $variable; + $reflector = new ObjectReflector(); + foreach ($reflector->getAttributes($variable) as $value) { + if (!is_array($value) && !is_object($value)) { + continue; + } + $objects = array_merge($objects, $this->enumerate($value, $processed)); + } + } + return $objects; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnit\SebastianBergmann\ObjectEnumerator; -use function is_float; -use function is_numeric; -/** - * Compares doubles for equality. - */ -class DoubleComparator extends \PHPUnit\SebastianBergmann\Comparator\NumericComparator +use Throwable; +interface Exception extends Throwable { - /** - * Smallest value available in PHP. - * - * @var float - */ - public const EPSILON = 1.0E-10; - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - public function accepts($expected, $actual) - { - return (\is_float($expected) || \is_float($actual)) && \is_numeric($expected) && \is_numeric($actual); - } - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * - * @throws ComparisonFailure - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false) - { - if ($delta == 0) { - $delta = self::EPSILON; - } - parent::assertEquals($expected, $actual, $delta, $canonicalize, $ignoreCase); - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnit\SebastianBergmann\ObjectEnumerator; -use Exception; -/** - * Compares Exception instances for equality. - */ -class ExceptionComparator extends \PHPUnit\SebastianBergmann\Comparator\ObjectComparator +class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.5 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +BSD 3-Clause License + +Copyright (c) 2011, Nikita Popov +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +dumpComments = !empty($options['dumpComments']); + $this->dumpPositions = !empty($options['dumpPositions']); } /** - * Converts an object to an array containing all of its private, protected - * and public properties. + * Dumps a node or array. * - * @param object $object + * @param array|Node $node Node or array to dump + * @param string|null $code Code corresponding to dumped AST. This only needs to be passed if + * the dumpPositions option is enabled and the dumping of node offsets + * is desired. * - * @return array + * @return string Dumped value */ - protected function toArray($object) + public function dump($node, string $code = null) : string { - $array = parent::toArray($object); - unset($array['file'], $array['line'], $array['trace'], $array['string'], $array['xdebug_message']); - return $array; + $this->code = $code; + return $this->dumpRecursive($node); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Comparator; - -use function is_object; -use function is_scalar; -use function is_string; -use function method_exists; -use function sprintf; -use function strtolower; -/** - * Compares scalar or NULL values for equality. - */ -class ScalarComparator extends \PHPUnit\SebastianBergmann\Comparator\Comparator -{ - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - * - * @since Method available since Release 3.6.0 - */ - public function accepts($expected, $actual) + protected function dumpRecursive($node) + { + if ($node instanceof Node) { + $r = $node->getType(); + if ($this->dumpPositions && null !== ($p = $this->dumpPosition($node))) { + $r .= $p; + } + $r .= '('; + foreach ($node->getSubNodeNames() as $key) { + $r .= "\n " . $key . ': '; + $value = $node->{$key}; + if (null === $value) { + $r .= 'null'; + } elseif (\false === $value) { + $r .= 'false'; + } elseif (\true === $value) { + $r .= 'true'; + } elseif (\is_scalar($value)) { + if ('flags' === $key || 'newModifier' === $key) { + $r .= $this->dumpFlags($value); + } elseif ('type' === $key && $node instanceof Include_) { + $r .= $this->dumpIncludeType($value); + } elseif ('type' === $key && ($node instanceof Use_ || $node instanceof UseUse || $node instanceof GroupUse)) { + $r .= $this->dumpUseType($value); + } else { + $r .= $value; + } + } else { + $r .= \str_replace("\n", "\n ", $this->dumpRecursive($value)); + } + } + if ($this->dumpComments && ($comments = $node->getComments())) { + $r .= "\n comments: " . \str_replace("\n", "\n ", $this->dumpRecursive($comments)); + } + } elseif (\is_array($node)) { + $r = 'array('; + foreach ($node as $key => $value) { + $r .= "\n " . $key . ': '; + if (null === $value) { + $r .= 'null'; + } elseif (\false === $value) { + $r .= 'false'; + } elseif (\true === $value) { + $r .= 'true'; + } elseif (\is_scalar($value)) { + $r .= $value; + } else { + $r .= \str_replace("\n", "\n ", $this->dumpRecursive($value)); + } + } + } elseif ($node instanceof Comment) { + return $node->getReformattedText(); + } else { + throw new \InvalidArgumentException('Can only dump nodes and arrays.'); + } + return $r . "\n)"; + } + protected function dumpFlags($flags) + { + $strs = []; + if ($flags & Class_::MODIFIER_PUBLIC) { + $strs[] = 'MODIFIER_PUBLIC'; + } + if ($flags & Class_::MODIFIER_PROTECTED) { + $strs[] = 'MODIFIER_PROTECTED'; + } + if ($flags & Class_::MODIFIER_PRIVATE) { + $strs[] = 'MODIFIER_PRIVATE'; + } + if ($flags & Class_::MODIFIER_ABSTRACT) { + $strs[] = 'MODIFIER_ABSTRACT'; + } + if ($flags & Class_::MODIFIER_STATIC) { + $strs[] = 'MODIFIER_STATIC'; + } + if ($flags & Class_::MODIFIER_FINAL) { + $strs[] = 'MODIFIER_FINAL'; + } + if ($flags & Class_::MODIFIER_READONLY) { + $strs[] = 'MODIFIER_READONLY'; + } + if ($strs) { + return \implode(' | ', $strs) . ' (' . $flags . ')'; + } else { + return $flags; + } + } + protected function dumpIncludeType($type) + { + $map = [Include_::TYPE_INCLUDE => 'TYPE_INCLUDE', Include_::TYPE_INCLUDE_ONCE => 'TYPE_INCLUDE_ONCE', Include_::TYPE_REQUIRE => 'TYPE_REQUIRE', Include_::TYPE_REQUIRE_ONCE => 'TYPE_REQUIRE_ONCE']; + if (!isset($map[$type])) { + return $type; + } + return $map[$type] . ' (' . $type . ')'; + } + protected function dumpUseType($type) { - return (\is_scalar($expected) xor null === $expected) && (\is_scalar($actual) xor null === $actual) || \is_string($expected) && \is_object($actual) && \method_exists($actual, '__toString') || \is_object($expected) && \method_exists($expected, '__toString') && \is_string($actual); + $map = [Use_::TYPE_UNKNOWN => 'TYPE_UNKNOWN', Use_::TYPE_NORMAL => 'TYPE_NORMAL', Use_::TYPE_FUNCTION => 'TYPE_FUNCTION', Use_::TYPE_CONSTANT => 'TYPE_CONSTANT']; + if (!isset($map[$type])) { + return $type; + } + return $map[$type] . ' (' . $type . ')'; } /** - * Asserts that two values are equal. + * Dump node position, if possible. * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true + * @param Node $node Node for which to dump position * - * @throws ComparisonFailure + * @return string|null Dump of position, or null if position information not available */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false) + protected function dumpPosition(Node $node) { - $expectedToCompare = $expected; - $actualToCompare = $actual; - // always compare as strings to avoid strange behaviour - // otherwise 0 == 'Foobar' - if (\is_string($expected) || \is_string($actual)) { - $expectedToCompare = (string) $expectedToCompare; - $actualToCompare = (string) $actualToCompare; - if ($ignoreCase) { - $expectedToCompare = \strtolower($expectedToCompare); - $actualToCompare = \strtolower($actualToCompare); - } + if (!$node->hasAttribute('startLine') || !$node->hasAttribute('endLine')) { + return null; } - if ($expectedToCompare !== $actualToCompare && \is_string($expected) && \is_string($actual)) { - throw new \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure($expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual), \false, 'Failed asserting that two strings are equal.'); + $start = $node->getStartLine(); + $end = $node->getEndLine(); + if ($node->hasAttribute('startFilePos') && $node->hasAttribute('endFilePos') && null !== $this->code) { + $start .= ':' . $this->toColumn($this->code, $node->getStartFilePos()); + $end .= ':' . $this->toColumn($this->code, $node->getEndFilePos()); } - if ($expectedToCompare != $actualToCompare) { - throw new \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure( - $expected, - $actual, - // no diff is required - '', - '', - \false, - \sprintf('Failed asserting that %s matches expected %s.', $this->exporter->export($actual), $this->exporter->export($expected)) - ); + return "[{$start} - {$end}]"; + } + // Copied from Error class + private function toColumn($code, $pos) + { + if ($pos > \strlen($code)) { + throw new \RuntimeException('Invalid position information'); + } + $lineStartPos = \strrpos($code, "\n", $pos - \strlen($code)); + if (\false === $lineStartPos) { + $lineStartPos = -1; } + return $pos - $lineStartPos; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Comparator; +namespace PHPUnit\PhpParser\Internal; -use function get_class; -use function in_array; -use function is_object; -use function sprintf; -use function substr_replace; /** - * Compares objects for equality. + * Provides operations on token streams, for use by pretty printer. + * + * @internal */ -class ObjectComparator extends \PHPUnit\SebastianBergmann\Comparator\ArrayComparator +class TokenStream { + /** @var array Tokens (in token_get_all format) */ + private $tokens; + /** @var int[] Map from position to indentation */ + private $indentMap; /** - * Returns whether the comparator can compare two values. + * Create token stream instance. * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare + * @param array $tokens Tokens in token_get_all() format + */ + public function __construct(array $tokens) + { + $this->tokens = $tokens; + $this->indentMap = $this->calcIndentMap(); + } + /** + * Whether the given position is immediately surrounded by parenthesis. + * + * @param int $startPos Start position + * @param int $endPos End position * * @return bool */ - public function accepts($expected, $actual) + public function haveParens(int $startPos, int $endPos) : bool { - return \is_object($expected) && \is_object($actual); + return $this->haveTokenImmediatelyBefore($startPos, '(') && $this->haveTokenImmediatelyAfter($endPos, ')'); } /** - * Asserts that two values are equal. + * Whether the given position is immediately surrounded by braces. * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * @param array $processed List of already processed elements (used to prevent infinite recursion) + * @param int $startPos Start position + * @param int $endPos End position * - * @throws ComparisonFailure + * @return bool */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false, array &$processed = []) + public function haveBraces(int $startPos, int $endPos) : bool { - if (\get_class($actual) !== \get_class($expected)) { - throw new \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure($expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual), \false, \sprintf('%s is not instance of expected class "%s".', $this->exporter->export($actual), \get_class($expected))); - } - // don't compare twice to allow for cyclic dependencies - if (\in_array([$actual, $expected], $processed, \true) || \in_array([$expected, $actual], $processed, \true)) { - return; - } - $processed[] = [$actual, $expected]; - // don't compare objects if they are identical - // this helps to avoid the error "maximum function nesting level reached" - // CAUTION: this conditional clause is not tested - if ($actual !== $expected) { - try { - parent::assertEquals($this->toArray($expected), $this->toArray($actual), $delta, $canonicalize, $ignoreCase, $processed); - } catch (\PHPUnit\SebastianBergmann\Comparator\ComparisonFailure $e) { - throw new \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure( - $expected, - $actual, - // replace "Array" with "MyClass object" - \substr_replace($e->getExpectedAsString(), \get_class($expected) . ' Object', 0, 5), - \substr_replace($e->getActualAsString(), \get_class($actual) . ' Object', 0, 5), - \false, - 'Failed asserting that two objects are equal.' - ); + return ($this->haveTokenImmediatelyBefore($startPos, '{') || $this->haveTokenImmediatelyBefore($startPos, \T_CURLY_OPEN)) && $this->haveTokenImmediatelyAfter($endPos, '}'); + } + /** + * Check whether the position is directly preceded by a certain token type. + * + * During this check whitespace and comments are skipped. + * + * @param int $pos Position before which the token should occur + * @param int|string $expectedTokenType Token to check for + * + * @return bool Whether the expected token was found + */ + public function haveTokenImmediatelyBefore(int $pos, $expectedTokenType) : bool + { + $tokens = $this->tokens; + $pos--; + for (; $pos >= 0; $pos--) { + $tokenType = $tokens[$pos][0]; + if ($tokenType === $expectedTokenType) { + return \true; + } + if ($tokenType !== \T_WHITESPACE && $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) { + break; } } + return \false; } /** - * Converts an object to an array containing all of its private, protected - * and public properties. + * Check whether the position is directly followed by a certain token type. * - * @param object $object + * During this check whitespace and comments are skipped. * - * @return array + * @param int $pos Position after which the token should occur + * @param int|string $expectedTokenType Token to check for + * + * @return bool Whether the expected token was found */ - protected function toArray($object) + public function haveTokenImmediatelyAfter(int $pos, $expectedTokenType) : bool { - return $this->exporter->toArray($object); + $tokens = $this->tokens; + $pos++; + for (; $pos < \count($tokens); $pos++) { + $tokenType = $tokens[$pos][0]; + if ($tokenType === $expectedTokenType) { + return \true; + } + if ($tokenType !== \T_WHITESPACE && $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) { + break; + } + } + return \false; + } + public function skipLeft(int $pos, $skipTokenType) + { + $tokens = $this->tokens; + $pos = $this->skipLeftWhitespace($pos); + if ($skipTokenType === \T_WHITESPACE) { + return $pos; + } + if ($tokens[$pos][0] !== $skipTokenType) { + // Shouldn't happen. The skip token MUST be there + throw new \Exception('Encountered unexpected token'); + } + $pos--; + return $this->skipLeftWhitespace($pos); + } + public function skipRight(int $pos, $skipTokenType) + { + $tokens = $this->tokens; + $pos = $this->skipRightWhitespace($pos); + if ($skipTokenType === \T_WHITESPACE) { + return $pos; + } + if ($tokens[$pos][0] !== $skipTokenType) { + // Shouldn't happen. The skip token MUST be there + throw new \Exception('Encountered unexpected token'); + } + $pos++; + return $this->skipRightWhitespace($pos); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Comparator; - -use PHPUnit\Framework\MockObject\MockObject; -/** - * Compares PHPUnit\Framework\MockObject\MockObject instances for equality. - */ -class MockObjectComparator extends \PHPUnit\SebastianBergmann\Comparator\ObjectComparator -{ /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare + * Return first non-whitespace token position smaller or equal to passed position. * - * @return bool + * @param int $pos Token position + * @return int Non-whitespace token position */ - public function accepts($expected, $actual) + public function skipLeftWhitespace(int $pos) { - return $expected instanceof \PHPUnit\Framework\MockObject\MockObject && $actual instanceof \PHPUnit\Framework\MockObject\MockObject; + $tokens = $this->tokens; + for (; $pos >= 0; $pos--) { + $type = $tokens[$pos][0]; + if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) { + break; + } + } + return $pos; } /** - * Converts an object to an array containing all of its private, protected - * and public properties. + * Return first non-whitespace position greater or equal to passed position. * - * @param object $object + * @param int $pos Token position + * @return int Non-whitespace token position + */ + public function skipRightWhitespace(int $pos) + { + $tokens = $this->tokens; + for ($count = \count($tokens); $pos < $count; $pos++) { + $type = $tokens[$pos][0]; + if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) { + break; + } + } + return $pos; + } + public function findRight(int $pos, $findTokenType) + { + $tokens = $this->tokens; + for ($count = \count($tokens); $pos < $count; $pos++) { + $type = $tokens[$pos][0]; + if ($type === $findTokenType) { + return $pos; + } + } + return -1; + } + /** + * Whether the given position range contains a certain token type. * - * @return array + * @param int $startPos Starting position (inclusive) + * @param int $endPos Ending position (exclusive) + * @param int|string $tokenType Token type to look for + * @return bool Whether the token occurs in the given range */ - protected function toArray($object) + public function haveTokenInRange(int $startPos, int $endPos, $tokenType) { - $array = parent::toArray($object); - unset($array['__phpunit_invocationMocker']); - return $array; + $tokens = $this->tokens; + for ($pos = $startPos; $pos < $endPos; $pos++) { + if ($tokens[$pos][0] === $tokenType) { + return \true; + } + } + return \false; + } + public function haveBracesInRange(int $startPos, int $endPos) + { + return $this->haveTokenInRange($startPos, $endPos, '{') || $this->haveTokenInRange($startPos, $endPos, \T_CURLY_OPEN) || $this->haveTokenInRange($startPos, $endPos, '}'); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Comparator; - -use function sprintf; -use function strtolower; -use DOMDocument; -use DOMNode; -use ValueError; -/** - * Compares DOMNode instances for equality. - */ -class DOMNodeComparator extends \PHPUnit\SebastianBergmann\Comparator\ObjectComparator -{ /** - * Returns whether the comparator can compare two values. + * Get indentation before token position. * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare + * @param int $pos Token position * - * @return bool + * @return int Indentation depth (in spaces) */ - public function accepts($expected, $actual) + public function getIndentationBefore(int $pos) : int { - return $expected instanceof \DOMNode && $actual instanceof \DOMNode; + return $this->indentMap[$pos]; } /** - * Asserts that two values are equal. + * Get the code corresponding to a token offset range, optionally adjusted for indentation. * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * @param array $processed List of already processed elements (used to prevent infinite recursion) + * @param int $from Token start position (inclusive) + * @param int $to Token end position (exclusive) + * @param int $indent By how much the code should be indented (can be negative as well) * - * @throws ComparisonFailure + * @return string Code corresponding to token range, adjusted for indentation */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false, array &$processed = []) + public function getTokenCode(int $from, int $to, int $indent) : string { - $expectedAsString = $this->nodeToText($expected, \true, $ignoreCase); - $actualAsString = $this->nodeToText($actual, \true, $ignoreCase); - if ($expectedAsString !== $actualAsString) { - $type = $expected instanceof \DOMDocument ? 'documents' : 'nodes'; - throw new \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure($expected, $actual, $expectedAsString, $actualAsString, \false, \sprintf("Failed asserting that two DOM %s are equal.\n", $type)); + $tokens = $this->tokens; + $result = ''; + for ($pos = $from; $pos < $to; $pos++) { + $token = $tokens[$pos]; + if (\is_array($token)) { + $type = $token[0]; + $content = $token[1]; + if ($type === \T_CONSTANT_ENCAPSED_STRING || $type === \T_ENCAPSED_AND_WHITESPACE) { + $result .= $content; + } else { + // TODO Handle non-space indentation + if ($indent < 0) { + $result .= \str_replace("\n" . \str_repeat(" ", -$indent), "\n", $content); + } elseif ($indent > 0) { + $result .= \str_replace("\n", "\n" . \str_repeat(" ", $indent), $content); + } else { + $result .= $content; + } + } + } else { + $result .= $token; + } } + return $result; } /** - * Returns the normalized, whitespace-cleaned, and indented textual - * representation of a DOMNode. + * Precalculate the indentation at every token position. + * + * @return int[] Token position to indentation map */ - private function nodeToText(\DOMNode $node, bool $canonicalize, bool $ignoreCase) : string + private function calcIndentMap() { - if ($canonicalize) { - $document = new \DOMDocument(); - try { - @$document->loadXML($node->C14N()); - } catch (\ValueError $e) { + $indentMap = []; + $indent = 0; + foreach ($this->tokens as $token) { + $indentMap[] = $indent; + if ($token[0] === \T_WHITESPACE) { + $content = $token[1]; + $newlinePos = \strrpos($content, "\n"); + if (\false !== $newlinePos) { + $indent = \strlen($content) - $newlinePos - 1; + } } - $node = $document; } - $document = $node instanceof \DOMDocument ? $node : $node->ownerDocument; - $document->formatOutput = \true; - $document->normalizeDocument(); - $text = $node instanceof \DOMDocument ? $node->saveXML() : $document->saveXML($node); - return $ignoreCase ? \strtolower($text) : $text; + // Add a sentinel for one past end of the file + $indentMap[] = $indent; + return $indentMap; } } -Comparator - -Copyright (c) 2002-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: +type = $type; + $this->old = $old; + $this->new = $new; + } } getProperties() does not return private properties from ancestor classes. + * Create differ over the given equality relation. * - * @author muratyaman@gmail.com - * @see http://php.net/manual/en/reflectionclass.getproperties.php + * @param callable $isEqual Equality relation with signature function($a, $b) : bool + */ + public function __construct(callable $isEqual) + { + $this->isEqual = $isEqual; + } + /** + * Calculate diff (edit script) from $old to $new. * - * @param ReflectionClass $ref + * @param array $old Original array + * @param array $new New array * - * @return ReflectionProperty[] + * @return DiffElem[] Diff (edit script) */ - public static function getProperties(\ReflectionClass $ref) + public function diff(array $old, array $new) { - $props = $ref->getProperties(); - $propsArr = array(); - foreach ($props as $prop) { - $propertyName = $prop->getName(); - $propsArr[$propertyName] = $prop; - } - if ($parentClass = $ref->getParentClass()) { - $parentPropsArr = self::getProperties($parentClass); - foreach ($propsArr as $key => $property) { - $parentPropsArr[$key] = $property; - } - return $parentPropsArr; - } - return $propsArr; + list($trace, $x, $y) = $this->calculateTrace($old, $new); + return $this->extractDiff($trace, $x, $y, $old, $new); } /** - * Retrieves property by name from object and all its ancestors. + * Calculate diff, including "replace" operations. * - * @param object|string $object - * @param string $name + * If a sequence of remove operations is followed by the same number of add operations, these + * will be coalesced into replace operations. * - * @throws PropertyException - * @throws ReflectionException + * @param array $old Original array + * @param array $new New array * - * @return ReflectionProperty + * @return DiffElem[] Diff (edit script), including replace operations */ - public static function getProperty($object, $name) + public function diffWithReplacements(array $old, array $new) { - $reflection = \is_object($object) ? new \ReflectionObject($object) : new \ReflectionClass($object); - if ($reflection->hasProperty($name)) { - return $reflection->getProperty($name); + return $this->coalesceReplacements($this->diff($old, $new)); + } + private function calculateTrace(array $a, array $b) + { + $n = \count($a); + $m = \count($b); + $max = $n + $m; + $v = [1 => 0]; + $trace = []; + for ($d = 0; $d <= $max; $d++) { + $trace[] = $v; + for ($k = -$d; $k <= $d; $k += 2) { + if ($k === -$d || $k !== $d && $v[$k - 1] < $v[$k + 1]) { + $x = $v[$k + 1]; + } else { + $x = $v[$k - 1] + 1; + } + $y = $x - $k; + while ($x < $n && $y < $m && ($this->isEqual)($a[$x], $b[$y])) { + $x++; + $y++; + } + $v[$k] = $x; + if ($x >= $n && $y >= $m) { + return [$trace, $x, $y]; + } + } } - if ($parentClass = $reflection->getParentClass()) { - return self::getProperty($parentClass->getName(), $name); + throw new \Exception('Should not happen'); + } + private function extractDiff(array $trace, int $x, int $y, array $a, array $b) + { + $result = []; + for ($d = \count($trace) - 1; $d >= 0; $d--) { + $v = $trace[$d]; + $k = $x - $y; + if ($k === -$d || $k !== $d && $v[$k - 1] < $v[$k + 1]) { + $prevK = $k + 1; + } else { + $prevK = $k - 1; + } + $prevX = $v[$prevK]; + $prevY = $prevX - $prevK; + while ($x > $prevX && $y > $prevY) { + $result[] = new DiffElem(DiffElem::TYPE_KEEP, $a[$x - 1], $b[$y - 1]); + $x--; + $y--; + } + if ($d === 0) { + break; + } + while ($x > $prevX) { + $result[] = new DiffElem(DiffElem::TYPE_REMOVE, $a[$x - 1], null); + $x--; + } + while ($y > $prevY) { + $result[] = new DiffElem(DiffElem::TYPE_ADD, null, $b[$y - 1]); + $y--; + } } - throw new \PHPUnit\DeepCopy\Exception\PropertyException(\sprintf('The class "%s" doesn\'t have a property with the given name: "%s".', \is_object($object) ? \get_class($object) : $object, $name)); + return \array_reverse($result); } -} -__load(); + $newDiff = []; + $c = \count($diff); + for ($i = 0; $i < $c; $i++) { + $diffType = $diff[$i]->type; + if ($diffType !== DiffElem::TYPE_REMOVE) { + $newDiff[] = $diff[$i]; + continue; + } + $j = $i; + while ($j < $c && $diff[$j]->type === DiffElem::TYPE_REMOVE) { + $j++; + } + $k = $j; + while ($k < $c && $diff[$k]->type === DiffElem::TYPE_ADD) { + $k++; + } + if ($j - $i === $k - $j) { + $len = $j - $i; + for ($n = 0; $n < $len; $n++) { + $newDiff[] = new DiffElem(DiffElem::TYPE_REPLACE, $diff[$i + $n]->old, $diff[$j + $n]->new); + } + } else { + for (; $i < $k; $i++) { + $newDiff[] = $diff[$i]; + } + } + $i = $k - 1; + } + return $newDiff; } } setAccessible(\true); - $reflectionProperty->setValue($object, new \PHPUnit\Doctrine\Common\Collections\ArrayCollection()); + parent::__construct($attributes); + $this->attrGroups = $attrGroups; + $this->args = $args; + $this->extends = $extends; + $this->implements = $implements; + $this->stmts = $stmts; } -} -setAccessible(\true); - $oldCollection = $reflectionProperty->getValue($object); - $newCollection = $oldCollection->map(function ($item) use($objectCopier) { - return $objectCopier($item); - }); - $reflectionProperty->setValue($object, $newCollection); + $class = $newNode->class; + \assert($class instanceof Node\Stmt\Class_); + // We don't assert that $class->name is null here, to allow consumers to assign unique names + // to anonymous classes for their own purposes. We simplify ignore the name here. + return new self($class->attrGroups, $newNode->args, $class->extends, $class->implements, $class->stmts, $newNode->getAttributes()); + } + public function getType() : string + { + return 'Expr_PrintableNewAnonClass'; + } + public function getSubNodeNames() : array + { + return ['attrGroups', 'args', 'extends', 'implements', 'stmts']; } } [0, 1], + Expr\BitwiseNot::class => [10, 1], + Expr\PreInc::class => [10, 1], + Expr\PreDec::class => [10, 1], + Expr\PostInc::class => [10, -1], + Expr\PostDec::class => [10, -1], + Expr\UnaryPlus::class => [10, 1], + Expr\UnaryMinus::class => [10, 1], + Cast\Int_::class => [10, 1], + Cast\Double::class => [10, 1], + Cast\String_::class => [10, 1], + Cast\Array_::class => [10, 1], + Cast\Object_::class => [10, 1], + Cast\Bool_::class => [10, 1], + Cast\Unset_::class => [10, 1], + Expr\ErrorSuppress::class => [10, 1], + Expr\Instanceof_::class => [20, 0], + Expr\BooleanNot::class => [30, 1], + BinaryOp\Mul::class => [40, -1], + BinaryOp\Div::class => [40, -1], + BinaryOp\Mod::class => [40, -1], + BinaryOp\Plus::class => [50, -1], + BinaryOp\Minus::class => [50, -1], + BinaryOp\Concat::class => [50, -1], + BinaryOp\ShiftLeft::class => [60, -1], + BinaryOp\ShiftRight::class => [60, -1], + BinaryOp\Smaller::class => [70, 0], + BinaryOp\SmallerOrEqual::class => [70, 0], + BinaryOp\Greater::class => [70, 0], + BinaryOp\GreaterOrEqual::class => [70, 0], + BinaryOp\Equal::class => [80, 0], + BinaryOp\NotEqual::class => [80, 0], + BinaryOp\Identical::class => [80, 0], + BinaryOp\NotIdentical::class => [80, 0], + BinaryOp\Spaceship::class => [80, 0], + BinaryOp\BitwiseAnd::class => [90, -1], + BinaryOp\BitwiseXor::class => [100, -1], + BinaryOp\BitwiseOr::class => [110, -1], + BinaryOp\BooleanAnd::class => [120, -1], + BinaryOp\BooleanOr::class => [130, -1], + BinaryOp\Coalesce::class => [140, 1], + Expr\Ternary::class => [150, 0], + // parser uses %left for assignments, but they really behave as %right + Expr\Assign::class => [160, 1], + Expr\AssignRef::class => [160, 1], + AssignOp\Plus::class => [160, 1], + AssignOp\Minus::class => [160, 1], + AssignOp\Mul::class => [160, 1], + AssignOp\Div::class => [160, 1], + AssignOp\Concat::class => [160, 1], + AssignOp\Mod::class => [160, 1], + AssignOp\BitwiseAnd::class => [160, 1], + AssignOp\BitwiseOr::class => [160, 1], + AssignOp\BitwiseXor::class => [160, 1], + AssignOp\ShiftLeft::class => [160, 1], + AssignOp\ShiftRight::class => [160, 1], + AssignOp\Pow::class => [160, 1], + AssignOp\Coalesce::class => [160, 1], + Expr\YieldFrom::class => [165, 1], + Expr\Print_::class => [168, 1], + BinaryOp\LogicalAnd::class => [170, -1], + BinaryOp\LogicalXor::class => [180, -1], + BinaryOp\LogicalOr::class => [190, -1], + Expr\Include_::class => [200, -1], + ]; + /** @var int Current indentation level. */ + protected $indentLevel; + /** @var string Newline including current indentation. */ + protected $nl; + /** @var string Token placed at end of doc string to ensure it is followed by a newline. */ + protected $docStringEndToken; + /** @var bool Whether semicolon namespaces can be used (i.e. no global namespace is used) */ + protected $canUseSemicolonNamespaces; + /** @var array Pretty printer options */ + protected $options; + /** @var TokenStream Original tokens for use in format-preserving pretty print */ + protected $origTokens; + /** @var Internal\Differ Differ for node lists */ + protected $nodeListDiffer; + /** @var bool[] Map determining whether a certain character is a label character */ + protected $labelCharMap; /** - * @var callable + * @var int[][] Map from token classes and subnode names to FIXUP_* constants. This is used + * during format-preserving prints to place additional parens/braces if necessary. */ - protected $callback; + protected $fixupMap; /** - * @param callable $callable Will be called to get the new value for each property to replace + * @var int[][] Map from "{$node->getType()}->{$subNode}" to ['left' => $l, 'right' => $r], + * where $l and $r specify the token type that needs to be stripped when removing + * this node. */ - public function __construct(callable $callable) + protected $removalMap; + /** + * @var mixed[] Map from "{$node->getType()}->{$subNode}" to [$find, $beforeToken, $extraLeft, $extraRight]. + * $find is an optional token after which the insertion occurs. $extraLeft/Right + * are optionally added before/after the main insertions. + */ + protected $insertionMap; + /** + * @var string[] Map From "{$node->getType()}->{$subNode}" to string that should be inserted + * between elements of this list subnode. + */ + protected $listInsertionMap; + protected $emptyListInsertionMap; + /** @var int[] Map from "{$node->getType()}->{$subNode}" to token before which the modifiers + * should be reprinted. */ + protected $modifierChangeMap; + /** + * Creates a pretty printer instance using the given options. + * + * Supported options: + * * bool $shortArraySyntax = false: Whether to use [] instead of array() as the default array + * syntax, if the node does not specify a format. + * + * @param array $options Dictionary of formatting options + */ + public function __construct(array $options = []) { - $this->callback = $callable; + $this->docStringEndToken = '_DOC_STRING_END_' . \mt_rand(); + $defaultOptions = ['shortArraySyntax' => \false]; + $this->options = $options + $defaultOptions; } /** - * Replaces the object property by the result of the callback called with the object property. - * - * {@inheritdoc} + * Reset pretty printing state. */ - public function apply($object, $property, $objectCopier) + protected function resetState() { - $reflectionProperty = \PHPUnit\DeepCopy\Reflection\ReflectionHelper::getProperty($object, $property); - $reflectionProperty->setAccessible(\true); - $value = \call_user_func($this->callback, $reflectionProperty->getValue($object)); - $reflectionProperty->setValue($object, $value); + $this->indentLevel = 0; + $this->nl = "\n"; + $this->origTokens = null; } -} -indentLevel = $level; + $this->nl = "\n" . \str_repeat(' ', $level); } -} -setAccessible(\true); - $reflectionProperty->setValue($object, null); + $this->indentLevel += 4; + $this->nl .= ' '; } -} -indentLevel >= 4); + $this->indentLevel -= 4; + $this->nl = "\n" . \str_repeat(' ', $this->indentLevel); + } /** - * @var string + * Pretty prints an array of statements. + * + * @param Node[] $stmts Array of statements + * + * @return string Pretty printed statements */ - private $type; + public function prettyPrint(array $stmts) : string + { + $this->resetState(); + $this->preprocessNodes($stmts); + return \ltrim($this->handleMagicTokens($this->pStmts($stmts, \false))); + } /** - * @param string $type + * Pretty prints an expression. + * + * @param Expr $node Expression node + * + * @return string Pretty printed node */ - public function __construct($type) + public function prettyPrintExpr(Expr $node) : string { - $this->type = $type; + $this->resetState(); + return $this->handleMagicTokens($this->p($node)); } /** - * @param mixed $element + * Pretty prints a file of statements (includes the opening type) : \gettype($element) === $this->type; + if (!$stmts) { + return "prettyPrint($stmts); + if ($stmts[0] instanceof Stmt\InlineHTML) { + $p = \preg_replace('/^<\\?php\\s+\\?>\\n?/', '', $p); + } + if ($stmts[\count($stmts) - 1] instanceof Stmt\InlineHTML) { + $p = \preg_replace('/<\\?php$/', '', \rtrim($p)); + } + return $p; } -} - Filter, 'matcher' => Matcher] pairs. - */ - private $filters = []; /** - * Type Filters to apply. + * Preprocesses the top-level nodes to initialize pretty printer state. * - * @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs. - */ - private $typeFilters = []; - /** - * @var bool - */ - private $skipUncloneable = \false; - /** - * @var bool - */ - private $useCloneMethod; - /** - * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will be used - * instead of the regular deep cloning. + * @param Node[] $nodes Array of nodes */ - public function __construct($useCloneMethod = \false) + protected function preprocessNodes(array $nodes) { - $this->useCloneMethod = $useCloneMethod; - $this->addTypeFilter(new \PHPUnit\DeepCopy\TypeFilter\Spl\ArrayObjectFilter($this), new \PHPUnit\DeepCopy\TypeMatcher\TypeMatcher(\ArrayObject::class)); - $this->addTypeFilter(new \PHPUnit\DeepCopy\TypeFilter\Date\DateIntervalFilter(), new \PHPUnit\DeepCopy\TypeMatcher\TypeMatcher(\DateInterval::class)); - $this->addTypeFilter(new \PHPUnit\DeepCopy\TypeFilter\Spl\SplDoublyLinkedListFilter($this), new \PHPUnit\DeepCopy\TypeMatcher\TypeMatcher(\SplDoublyLinkedList::class)); + /* We can use semicolon-namespaces unless there is a global namespace declaration */ + $this->canUseSemicolonNamespaces = \true; + foreach ($nodes as $node) { + if ($node instanceof Stmt\Namespace_ && null === $node->name) { + $this->canUseSemicolonNamespaces = \false; + break; + } + } } /** - * If enabled, will not throw an exception when coming across an uncloneable property. - * - * @param $skipUncloneable + * Handles (and removes) no-indent and doc-string-end tokens. * - * @return $this + * @param string $str + * @return string */ - public function skipUncloneable($skipUncloneable = \true) + protected function handleMagicTokens(string $str) : string { - $this->skipUncloneable = $skipUncloneable; - return $this; + // Replace doc-string-end tokens with nothing or a newline + $str = \str_replace($this->docStringEndToken . ";\n", ";\n", $str); + $str = \str_replace($this->docStringEndToken, "\n", $str); + return $str; } /** - * Deep copies the given object. + * Pretty prints an array of nodes (statements) and indents them optionally. * - * @param mixed $object + * @param Node[] $nodes Array of nodes + * @param bool $indent Whether to indent the printed nodes * - * @return mixed + * @return string Pretty printed statements */ - public function copy($object) - { - $this->hashMap = []; - return $this->recursiveCopy($object); - } - public function addFilter(\PHPUnit\DeepCopy\Filter\Filter $filter, \PHPUnit\DeepCopy\Matcher\Matcher $matcher) - { - $this->filters[] = ['matcher' => $matcher, 'filter' => $filter]; - } - public function prependFilter(\PHPUnit\DeepCopy\Filter\Filter $filter, \PHPUnit\DeepCopy\Matcher\Matcher $matcher) - { - \array_unshift($this->filters, ['matcher' => $matcher, 'filter' => $filter]); - } - public function addTypeFilter(\PHPUnit\DeepCopy\TypeFilter\TypeFilter $filter, \PHPUnit\DeepCopy\TypeMatcher\TypeMatcher $matcher) - { - $this->typeFilters[] = ['matcher' => $matcher, 'filter' => $filter]; - } - private function recursiveCopy($var) + protected function pStmts(array $nodes, bool $indent = \true) : string { - // Matches Type Filter - if ($filter = $this->getFirstMatchedTypeFilter($this->typeFilters, $var)) { - return $filter->apply($var); - } - // Resource - if (\is_resource($var)) { - return $var; + if ($indent) { + $this->indent(); } - // Array - if (\is_array($var)) { - return $this->copyArray($var); + $result = ''; + foreach ($nodes as $node) { + $comments = $node->getComments(); + if ($comments) { + $result .= $this->nl . $this->pComments($comments); + if ($node instanceof Stmt\Nop) { + continue; + } + } + $result .= $this->nl . $this->p($node); } - // Scalar - if (!\is_object($var)) { - return $var; + if ($indent) { + $this->outdent(); } - // Object - return $this->copyObject($var); + return $result; } /** - * Copy an array - * @param array $array - * @return array + * Pretty-print an infix operation while taking precedence into account. + * + * @param string $class Node class of operator + * @param Node $leftNode Left-hand side node + * @param string $operatorString String representation of the operator + * @param Node $rightNode Right-hand side node + * + * @return string Pretty printed infix operation */ - private function copyArray(array $array) + protected function pInfixOp(string $class, Node $leftNode, string $operatorString, Node $rightNode) : string { - foreach ($array as $key => $value) { - $array[$key] = $this->recursiveCopy($value); - } - return $array; + list($precedence, $associativity) = $this->precedenceMap[$class]; + return $this->pPrec($leftNode, $precedence, $associativity, -1) . $operatorString . $this->pPrec($rightNode, $precedence, $associativity, 1); } /** - * Copies an object. - * - * @param object $object + * Pretty-print a prefix operation while taking precedence into account. * - * @throws CloneException + * @param string $class Node class of operator + * @param string $operatorString String representation of the operator + * @param Node $node Node * - * @return object + * @return string Pretty printed prefix operation */ - private function copyObject($object) - { - $objectHash = \spl_object_hash($object); - if (isset($this->hashMap[$objectHash])) { - return $this->hashMap[$objectHash]; - } - $reflectedObject = new \ReflectionObject($object); - $isCloneable = $reflectedObject->isCloneable(); - if (\false === $isCloneable) { - if ($this->skipUncloneable) { - $this->hashMap[$objectHash] = $object; - return $object; - } - throw new \PHPUnit\DeepCopy\Exception\CloneException(\sprintf('The class "%s" is not cloneable.', $reflectedObject->getName())); - } - $newObject = clone $object; - $this->hashMap[$objectHash] = $newObject; - if ($this->useCloneMethod && $reflectedObject->hasMethod('__clone')) { - return $newObject; - } - if ($newObject instanceof \DateTimeInterface || $newObject instanceof \DateTimeZone) { - return $newObject; - } - foreach (\PHPUnit\DeepCopy\Reflection\ReflectionHelper::getProperties($reflectedObject) as $property) { - $this->copyObjectProperty($newObject, $property); - } - return $newObject; - } - private function copyObjectProperty($object, \ReflectionProperty $property) + protected function pPrefixOp(string $class, string $operatorString, Node $node) : string { - // Ignore static properties - if ($property->isStatic()) { - return; - } - // Apply the filters - foreach ($this->filters as $item) { - /** @var Matcher $matcher */ - $matcher = $item['matcher']; - /** @var Filter $filter */ - $filter = $item['filter']; - if ($matcher->matches($object, $property->getName())) { - $filter->apply($object, $property->getName(), function ($object) { - return $this->recursiveCopy($object); - }); - // If a filter matches, we stop processing this property - return; - } - } - $property->setAccessible(\true); - // Ignore uninitialized properties (for PHP >7.4) - if (\method_exists($property, 'isInitialized') && !$property->isInitialized($object)) { - return; - } - $propertyValue = $property->getValue($object); - // Copy the property - $property->setValue($object, $this->recursiveCopy($propertyValue)); + list($precedence, $associativity) = $this->precedenceMap[$class]; + return $operatorString . $this->pPrec($node, $precedence, $associativity, 1); } /** - * Returns first filter that matches variable, `null` if no such filter found. + * Pretty-print a postfix operation while taking precedence into account. * - * @param array $filterRecords Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and - * 'matcher' with value of type {@see TypeMatcher} - * @param mixed $var + * @param string $class Node class of operator + * @param string $operatorString String representation of the operator + * @param Node $node Node * - * @return TypeFilter|null + * @return string Pretty printed postfix operation */ - private function getFirstMatchedTypeFilter(array $filterRecords, $var) + protected function pPostfixOp(string $class, Node $node, string $operatorString) : string { - $matched = $this->first($filterRecords, function (array $record) use($var) { - /* @var TypeMatcher $matcher */ - $matcher = $record['matcher']; - return $matcher->matches($var); - }); - return isset($matched) ? $matched['filter'] : null; + list($precedence, $associativity) = $this->precedenceMap[$class]; + return $this->pPrec($node, $precedence, $associativity, -1) . $operatorString; } /** - * Returns first element that matches predicate, `null` if no such element found. + * Prints an expression node with the least amount of parentheses necessary to preserve the meaning. * - * @param array $elements Array of ['filter' => Filter, 'matcher' => Matcher] pairs. - * @param callable $predicate Predicate arguments are: element. + * @param Node $node Node to pretty print + * @param int $parentPrecedence Precedence of the parent operator + * @param int $parentAssociativity Associativity of parent operator + * (-1 is left, 0 is nonassoc, 1 is right) + * @param int $childPosition Position of the node relative to the operator + * (-1 is left, 1 is right) * - * @return array|null Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and 'matcher' - * with value of type {@see TypeMatcher} or `null`. + * @return string The pretty printed node */ - private function first(array $elements, callable $predicate) + protected function pPrec(Node $node, int $parentPrecedence, int $parentAssociativity, int $childPosition) : string { - foreach ($elements as $element) { - if (\call_user_func($predicate, $element)) { - return $element; + $class = \get_class($node); + if (isset($this->precedenceMap[$class])) { + $childPrecedence = $this->precedenceMap[$class][0]; + if ($childPrecedence > $parentPrecedence || $parentPrecedence === $childPrecedence && $parentAssociativity !== $childPosition) { + return '(' . $this->p($node) . ')'; } } - return null; - } -} -p($node); } -} - $propertyValue) { - $copy->{$propertyName} = $propertyValue; + $pNodes = []; + foreach ($nodes as $node) { + if (null === $node) { + $pNodes[] = ''; + } else { + $pNodes[] = $this->p($node); + } } - return $copy; + return \implode($glue, $pNodes); } -} -copier = $copier; + return $this->pImplode($nodes, ', '); } /** - * {@inheritdoc} + * Pretty prints a comma-separated list of nodes in multiline style, including comments. + * + * The result includes a leading newline and one level of indentation (same as pStmts). + * + * @param Node[] $nodes Array of Nodes to be printed + * @param bool $trailingComma Whether to use a trailing comma + * + * @return string Comma separated pretty printed nodes in multiline style */ - public function apply($arrayObject) + protected function pCommaSeparatedMultiline(array $nodes, bool $trailingComma) : string { - $clone = clone $arrayObject; - foreach ($arrayObject->getArrayCopy() as $k => $v) { - $clone->offsetSet($k, $this->copier->copy($v)); + $this->indent(); + $result = ''; + $lastIdx = \count($nodes) - 1; + foreach ($nodes as $idx => $node) { + if ($node !== null) { + $comments = $node->getComments(); + if ($comments) { + $result .= $this->nl . $this->pComments($comments); + } + $result .= $this->nl . $this->p($node); + } else { + $result .= $this->nl; + } + if ($trailingComma || $idx !== $lastIdx) { + $result .= ','; + } } - return $clone; - } -} -copier = $copier; + $this->outdent(); + return $result; } /** - * {@inheritdoc} + * Prints reformatted text of the passed comments. + * + * @param Comment[] $comments List of comments + * + * @return string Reformatted text of comments */ - public function apply($element) - { - $newElement = clone $element; - $copy = $this->createCopyClosure(); - return $copy($newElement); - } - private function createCopyClosure() + protected function pComments(array $comments) : string { - $copier = $this->copier; - $copy = function (\SplDoublyLinkedList $list) use($copier) { - // Replace each element in the list with a deep copy of itself - for ($i = 1; $i <= $list->count(); $i++) { - $copy = $copier->recursiveCopy($list->shift()); - $list->push($copy); - } - return $list; - }; - return \Closure::bind($copy, null, \PHPUnit\DeepCopy\DeepCopy::class); + $formattedComments = []; + foreach ($comments as $comment) { + $formattedComments[] = \str_replace("\n", $this->nl, $comment->getReformattedText()); + } + return \implode($this->nl, $formattedComments); } -} -callback = $callable; + $this->initializeNodeListDiffer(); + $this->initializeLabelCharMap(); + $this->initializeFixupMap(); + $this->initializeRemovalMap(); + $this->initializeInsertionMap(); + $this->initializeListInsertionMap(); + $this->initializeEmptyListInsertionMap(); + $this->initializeModifierChangeMap(); + $this->resetState(); + $this->origTokens = new TokenStream($origTokens); + $this->preprocessNodes($stmts); + $pos = 0; + $result = $this->pArray($stmts, $origStmts, $pos, 0, 'File', 'stmts', null); + if (null !== $result) { + $result .= $this->origTokens->getTokenCode($pos, \count($origTokens), 0); + } else { + // Fallback + // TODO Add pStmts($stmts, \false); + } + return \ltrim($this->handleMagicTokens($result)); } - /** - * {@inheritdoc} - */ - public function apply($element) + protected function pFallback(Node $node) { - return \call_user_func($this->callback, $element); + return $this->{'p' . $node->getType()}($node); } -} -property = $property; + // No orig tokens means this is a normal pretty print without preservation of formatting + if (!$this->origTokens) { + return $this->{'p' . $node->getType()}($node); + } + /** @var Node $origNode */ + $origNode = $node->getAttribute('origNode'); + if (null === $origNode) { + return $this->pFallback($node); + } + $class = \get_class($node); + \assert($class === \get_class($origNode)); + $startPos = $origNode->getStartTokenPos(); + $endPos = $origNode->getEndTokenPos(); + \assert($startPos >= 0 && $endPos >= 0); + $fallbackNode = $node; + if ($node instanceof Expr\New_ && $node->class instanceof Stmt\Class_) { + // Normalize node structure of anonymous classes + $node = PrintableNewAnonClassNode::fromNewNode($node); + $origNode = PrintableNewAnonClassNode::fromNewNode($origNode); + } + // InlineHTML node does not contain closing and opening PHP tags. If the parent formatting + // is not preserved, then we need to use the fallback code to make sure the tags are + // printed. + if ($node instanceof Stmt\InlineHTML && !$parentFormatPreserved) { + return $this->pFallback($fallbackNode); + } + $indentAdjustment = $this->indentLevel - $this->origTokens->getIndentationBefore($startPos); + $type = $node->getType(); + $fixupInfo = $this->fixupMap[$class] ?? null; + $result = ''; + $pos = $startPos; + foreach ($node->getSubNodeNames() as $subNodeName) { + $subNode = $node->{$subNodeName}; + $origSubNode = $origNode->{$subNodeName}; + if (!$subNode instanceof Node && $subNode !== null || !$origSubNode instanceof Node && $origSubNode !== null) { + if ($subNode === $origSubNode) { + // Unchanged, can reuse old code + continue; + } + if (\is_array($subNode) && \is_array($origSubNode)) { + // Array subnode changed, we might be able to reconstruct it + $listResult = $this->pArray($subNode, $origSubNode, $pos, $indentAdjustment, $type, $subNodeName, $fixupInfo[$subNodeName] ?? null); + if (null === $listResult) { + return $this->pFallback($fallbackNode); + } + $result .= $listResult; + continue; + } + if (\is_int($subNode) && \is_int($origSubNode)) { + // Check if this is a modifier change + $key = $type . '->' . $subNodeName; + if (!isset($this->modifierChangeMap[$key])) { + return $this->pFallback($fallbackNode); + } + $findToken = $this->modifierChangeMap[$key]; + $result .= $this->pModifiers($subNode); + $pos = $this->origTokens->findRight($pos, $findToken); + continue; + } + // If a non-node, non-array subnode changed, we don't be able to do a partial + // reconstructions, as we don't have enough offset information. Pretty print the + // whole node instead. + return $this->pFallback($fallbackNode); + } + $extraLeft = ''; + $extraRight = ''; + if ($origSubNode !== null) { + $subStartPos = $origSubNode->getStartTokenPos(); + $subEndPos = $origSubNode->getEndTokenPos(); + \assert($subStartPos >= 0 && $subEndPos >= 0); + } else { + if ($subNode === null) { + // Both null, nothing to do + continue; + } + // A node has been inserted, check if we have insertion information for it + $key = $type . '->' . $subNodeName; + if (!isset($this->insertionMap[$key])) { + return $this->pFallback($fallbackNode); + } + list($findToken, $beforeToken, $extraLeft, $extraRight) = $this->insertionMap[$key]; + if (null !== $findToken) { + $subStartPos = $this->origTokens->findRight($pos, $findToken) + (int) (!$beforeToken); + } else { + $subStartPos = $pos; + } + if (null === $extraLeft && null !== $extraRight) { + // If inserting on the right only, skipping whitespace looks better + $subStartPos = $this->origTokens->skipRightWhitespace($subStartPos); + } + $subEndPos = $subStartPos - 1; + } + if (null === $subNode) { + // A node has been removed, check if we have removal information for it + $key = $type . '->' . $subNodeName; + if (!isset($this->removalMap[$key])) { + return $this->pFallback($fallbackNode); + } + // Adjust positions to account for additional tokens that must be skipped + $removalInfo = $this->removalMap[$key]; + if (isset($removalInfo['left'])) { + $subStartPos = $this->origTokens->skipLeft($subStartPos - 1, $removalInfo['left']) + 1; + } + if (isset($removalInfo['right'])) { + $subEndPos = $this->origTokens->skipRight($subEndPos + 1, $removalInfo['right']) - 1; + } + } + $result .= $this->origTokens->getTokenCode($pos, $subStartPos, $indentAdjustment); + if (null !== $subNode) { + $result .= $extraLeft; + $origIndentLevel = $this->indentLevel; + $this->setIndentLevel($this->origTokens->getIndentationBefore($subStartPos) + $indentAdjustment); + // If it's the same node that was previously in this position, it certainly doesn't + // need fixup. It's important to check this here, because our fixup checks are more + // conservative than strictly necessary. + if (isset($fixupInfo[$subNodeName]) && $subNode->getAttribute('origNode') !== $origSubNode) { + $fixup = $fixupInfo[$subNodeName]; + $res = $this->pFixup($fixup, $subNode, $class, $subStartPos, $subEndPos); + } else { + $res = $this->p($subNode, \true); + } + $this->safeAppend($result, $res); + $this->setIndentLevel($origIndentLevel); + $result .= $extraRight; + } + $pos = $subEndPos + 1; + } + $result .= $this->origTokens->getTokenCode($pos, $endPos + 1, $indentAdjustment); + return $result; } /** - * Matches a property by its name. + * Perform a format-preserving pretty print of an array. * - * {@inheritdoc} + * @param array $nodes New nodes + * @param array $origNodes Original nodes + * @param int $pos Current token position (updated by reference) + * @param int $indentAdjustment Adjustment for indentation + * @param string $parentNodeType Type of the containing node. + * @param string $subNodeName Name of array subnode. + * @param null|int $fixup Fixup information for array item nodes + * + * @return null|string Result of pretty print or null if cannot preserve formatting */ - public function matches($object, $property) + protected function pArray(array $nodes, array $origNodes, int &$pos, int $indentAdjustment, string $parentNodeType, string $subNodeName, $fixup) { - return $property == $this->property; + $diff = $this->nodeListDiffer->diffWithReplacements($origNodes, $nodes); + $mapKey = $parentNodeType . '->' . $subNodeName; + $insertStr = $this->listInsertionMap[$mapKey] ?? null; + $isStmtList = $subNodeName === 'stmts'; + $beforeFirstKeepOrReplace = \true; + $skipRemovedNode = \false; + $delayedAdd = []; + $lastElemIndentLevel = $this->indentLevel; + $insertNewline = \false; + if ($insertStr === "\n") { + $insertStr = ''; + $insertNewline = \true; + } + if ($isStmtList && \count($origNodes) === 1 && \count($nodes) !== 1) { + $startPos = $origNodes[0]->getStartTokenPos(); + $endPos = $origNodes[0]->getEndTokenPos(); + \assert($startPos >= 0 && $endPos >= 0); + if (!$this->origTokens->haveBraces($startPos, $endPos)) { + // This was a single statement without braces, but either additional statements + // have been added, or the single statement has been removed. This requires the + // addition of braces. For now fall back. + // TODO: Try to preserve formatting + return null; + } + } + $result = ''; + foreach ($diff as $i => $diffElem) { + $diffType = $diffElem->type; + /** @var Node|null $arrItem */ + $arrItem = $diffElem->new; + /** @var Node|null $origArrItem */ + $origArrItem = $diffElem->old; + if ($diffType === DiffElem::TYPE_KEEP || $diffType === DiffElem::TYPE_REPLACE) { + $beforeFirstKeepOrReplace = \false; + if ($origArrItem === null || $arrItem === null) { + // We can only handle the case where both are null + if ($origArrItem === $arrItem) { + continue; + } + return null; + } + if (!$arrItem instanceof Node || !$origArrItem instanceof Node) { + // We can only deal with nodes. This can occur for Names, which use string arrays. + return null; + } + $itemStartPos = $origArrItem->getStartTokenPos(); + $itemEndPos = $origArrItem->getEndTokenPos(); + \assert($itemStartPos >= 0 && $itemEndPos >= 0 && $itemStartPos >= $pos); + $origIndentLevel = $this->indentLevel; + $lastElemIndentLevel = $this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment; + $this->setIndentLevel($lastElemIndentLevel); + $comments = $arrItem->getComments(); + $origComments = $origArrItem->getComments(); + $commentStartPos = $origComments ? $origComments[0]->getStartTokenPos() : $itemStartPos; + \assert($commentStartPos >= 0); + if ($commentStartPos < $pos) { + // Comments may be assigned to multiple nodes if they start at the same position. + // Make sure we don't try to print them multiple times. + $commentStartPos = $itemStartPos; + } + if ($skipRemovedNode) { + if ($isStmtList && $this->origTokens->haveBracesInRange($pos, $itemStartPos)) { + // We'd remove the brace of a code block. + // TODO: Preserve formatting. + $this->setIndentLevel($origIndentLevel); + return null; + } + } else { + $result .= $this->origTokens->getTokenCode($pos, $commentStartPos, $indentAdjustment); + } + if (!empty($delayedAdd)) { + /** @var Node $delayedAddNode */ + foreach ($delayedAdd as $delayedAddNode) { + if ($insertNewline) { + $delayedAddComments = $delayedAddNode->getComments(); + if ($delayedAddComments) { + $result .= $this->pComments($delayedAddComments) . $this->nl; + } + } + $this->safeAppend($result, $this->p($delayedAddNode, \true)); + if ($insertNewline) { + $result .= $insertStr . $this->nl; + } else { + $result .= $insertStr; + } + } + $delayedAdd = []; + } + if ($comments !== $origComments) { + if ($comments) { + $result .= $this->pComments($comments) . $this->nl; + } + } else { + $result .= $this->origTokens->getTokenCode($commentStartPos, $itemStartPos, $indentAdjustment); + } + // If we had to remove anything, we have done so now. + $skipRemovedNode = \false; + } elseif ($diffType === DiffElem::TYPE_ADD) { + if (null === $insertStr) { + // We don't have insertion information for this list type + return null; + } + // We go multiline if the original code was multiline, + // or if it's an array item with a comment above it. + if ($insertStr === ', ' && ($this->isMultiline($origNodes) || $arrItem->getComments())) { + $insertStr = ','; + $insertNewline = \true; + } + if ($beforeFirstKeepOrReplace) { + // Will be inserted at the next "replace" or "keep" element + $delayedAdd[] = $arrItem; + continue; + } + $itemStartPos = $pos; + $itemEndPos = $pos - 1; + $origIndentLevel = $this->indentLevel; + $this->setIndentLevel($lastElemIndentLevel); + if ($insertNewline) { + $result .= $insertStr . $this->nl; + $comments = $arrItem->getComments(); + if ($comments) { + $result .= $this->pComments($comments) . $this->nl; + } + } else { + $result .= $insertStr; + } + } elseif ($diffType === DiffElem::TYPE_REMOVE) { + if (!$origArrItem instanceof Node) { + // We only support removal for nodes + return null; + } + $itemStartPos = $origArrItem->getStartTokenPos(); + $itemEndPos = $origArrItem->getEndTokenPos(); + \assert($itemStartPos >= 0 && $itemEndPos >= 0); + // Consider comments part of the node. + $origComments = $origArrItem->getComments(); + if ($origComments) { + $itemStartPos = $origComments[0]->getStartTokenPos(); + } + if ($i === 0) { + // If we're removing from the start, keep the tokens before the node and drop those after it, + // instead of the other way around. + $result .= $this->origTokens->getTokenCode($pos, $itemStartPos, $indentAdjustment); + $skipRemovedNode = \true; + } else { + if ($isStmtList && $this->origTokens->haveBracesInRange($pos, $itemStartPos)) { + // We'd remove the brace of a code block. + // TODO: Preserve formatting. + return null; + } + } + $pos = $itemEndPos + 1; + continue; + } else { + throw new \Exception("Shouldn't happen"); + } + if (null !== $fixup && $arrItem->getAttribute('origNode') !== $origArrItem) { + $res = $this->pFixup($fixup, $arrItem, null, $itemStartPos, $itemEndPos); + } else { + $res = $this->p($arrItem, \true); + } + $this->safeAppend($result, $res); + $this->setIndentLevel($origIndentLevel); + $pos = $itemEndPos + 1; + } + if ($skipRemovedNode) { + // TODO: Support removing single node. + return null; + } + if (!empty($delayedAdd)) { + if (!isset($this->emptyListInsertionMap[$mapKey])) { + return null; + } + list($findToken, $extraLeft, $extraRight) = $this->emptyListInsertionMap[$mapKey]; + if (null !== $findToken) { + $insertPos = $this->origTokens->findRight($pos, $findToken) + 1; + $result .= $this->origTokens->getTokenCode($pos, $insertPos, $indentAdjustment); + $pos = $insertPos; + } + $first = \true; + $result .= $extraLeft; + foreach ($delayedAdd as $delayedAddNode) { + if (!$first) { + $result .= $insertStr; + } + $result .= $this->p($delayedAddNode, \true); + $first = \false; + } + $result .= $extraRight; + } + return $result; } -} -propertyType = $propertyType; + switch ($fixup) { + case self::FIXUP_PREC_LEFT: + case self::FIXUP_PREC_RIGHT: + if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) { + list($precedence, $associativity) = $this->precedenceMap[$parentClass]; + return $this->pPrec($subNode, $precedence, $associativity, $fixup === self::FIXUP_PREC_LEFT ? -1 : 1); + } + break; + case self::FIXUP_CALL_LHS: + if ($this->callLhsRequiresParens($subNode) && !$this->origTokens->haveParens($subStartPos, $subEndPos)) { + return '(' . $this->p($subNode) . ')'; + } + break; + case self::FIXUP_DEREF_LHS: + if ($this->dereferenceLhsRequiresParens($subNode) && !$this->origTokens->haveParens($subStartPos, $subEndPos)) { + return '(' . $this->p($subNode) . ')'; + } + break; + case self::FIXUP_BRACED_NAME: + case self::FIXUP_VAR_BRACED_NAME: + if ($subNode instanceof Expr && !$this->origTokens->haveBraces($subStartPos, $subEndPos)) { + return ($fixup === self::FIXUP_VAR_BRACED_NAME ? '$' : '') . '{' . $this->p($subNode) . '}'; + } + break; + case self::FIXUP_ENCAPSED: + if (!$subNode instanceof Scalar\EncapsedStringPart && !$this->origTokens->haveBraces($subStartPos, $subEndPos)) { + return '{' . $this->p($subNode) . '}'; + } + break; + default: + throw new \Exception('Cannot happen'); + } + // Nothing special to do + return $this->p($subNode); } /** - * {@inheritdoc} + * Appends to a string, ensuring whitespace between label characters. + * + * Example: "echo" and "$x" result in "echo$x", but "echo" and "x" result in "echo x". + * Without safeAppend the result would be "echox", which does not preserve semantics. + * + * @param string $str + * @param string $append */ - public function matches($object, $property) + protected function safeAppend(string &$str, string $append) { - try { - $reflectionProperty = \PHPUnit\DeepCopy\Reflection\ReflectionHelper::getProperty($object, $property); - } catch (\ReflectionException $exception) { - return \false; + if ($str === "") { + $str = $append; + return; } - $reflectionProperty->setAccessible(\true); - // Uninitialized properties (for PHP >7.4) - if (\method_exists($reflectionProperty, 'isInitialized') && !$reflectionProperty->isInitialized($object)) { - // null instanceof $this->propertyType - return \false; + if ($append === "") { + return; + } + if (!$this->labelCharMap[$append[0]] || !$this->labelCharMap[$str[\strlen($str) - 1]]) { + $str .= $append; + } else { + $str .= " " . $append; } - return $reflectionProperty->getValue($object) instanceof $this->propertyType; } -} -class = $class; - $this->property = $property; + return !($node instanceof Expr\Variable || $node instanceof Node\Name || $node instanceof Expr\ArrayDimFetch || $node instanceof Expr\PropertyFetch || $node instanceof Expr\NullsafePropertyFetch || $node instanceof Expr\StaticPropertyFetch || $node instanceof Expr\FuncCall || $node instanceof Expr\MethodCall || $node instanceof Expr\NullsafeMethodCall || $node instanceof Expr\StaticCall || $node instanceof Expr\Array_ || $node instanceof Scalar\String_ || $node instanceof Expr\ConstFetch || $node instanceof Expr\ClassConstFetch); } /** - * Matches a specific property of a specific class. + * Print modifiers, including trailing whitespace. * - * {@inheritdoc} + * @param int $modifiers Modifier mask to print + * + * @return string Printed modifiers */ - public function matches($object, $property) + protected function pModifiers(int $modifiers) { - return $object instanceof $this->class && $property == $this->property; + return ($modifiers & Stmt\Class_::MODIFIER_PUBLIC ? 'public ' : '') . ($modifiers & Stmt\Class_::MODIFIER_PROTECTED ? 'protected ' : '') . ($modifiers & Stmt\Class_::MODIFIER_PRIVATE ? 'private ' : '') . ($modifiers & Stmt\Class_::MODIFIER_STATIC ? 'static ' : '') . ($modifiers & Stmt\Class_::MODIFIER_ABSTRACT ? 'abstract ' : '') . ($modifiers & Stmt\Class_::MODIFIER_FINAL ? 'final ' : '') . ($modifiers & Stmt\Class_::MODIFIER_READONLY ? 'readonly ' : ''); } -} -copy($value); - } -} -The MIT License (MIT) - -Copyright (c) 2013 My C-Sense - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Template; - -use Throwable; -interface Exception extends \Throwable -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Template; - -final class InvalidArgumentException extends \InvalidArgumentException implements \PHPUnit\SebastianBergmann\Template\Exception -{ + if (\count($nodes) < 2) { + return \false; + } + $pos = -1; + foreach ($nodes as $node) { + if (null === $node) { + continue; + } + $endPos = $node->getEndTokenPos() + 1; + if ($pos >= 0) { + $text = $this->origTokens->getTokenCode($pos, $endPos, 0); + if (\false === \strpos($text, "\n")) { + // We require that a newline is present between *every* item. If the formatting + // is inconsistent, with only some items having newlines, we don't consider it + // as multiline + return \false; + } + } + $pos = $endPos; + } + return \true; + } + /** + * Lazily initializes label char map. + * + * The label char map determines whether a certain character may occur in a label. + */ + protected function initializeLabelCharMap() + { + if ($this->labelCharMap) { + return; + } + $this->labelCharMap = []; + for ($i = 0; $i < 256; $i++) { + // Since PHP 7.1 The lower range is 0x80. However, we also want to support code for + // older versions. + $chr = \chr($i); + $this->labelCharMap[$chr] = $i >= 0x7f || \ctype_alnum($chr); + } + } + /** + * Lazily initializes node list differ. + * + * The node list differ is used to determine differences between two array subnodes. + */ + protected function initializeNodeListDiffer() + { + if ($this->nodeListDiffer) { + return; + } + $this->nodeListDiffer = new Internal\Differ(function ($a, $b) { + if ($a instanceof Node && $b instanceof Node) { + return $a === $b->getAttribute('origNode'); + } + // Can happen for array destructuring + return $a === null && $b === null; + }); + } + /** + * Lazily initializes fixup map. + * + * The fixup map is used to determine whether a certain subnode of a certain node may require + * some kind of "fixup" operation, e.g. the addition of parenthesis or braces. + */ + protected function initializeFixupMap() + { + if ($this->fixupMap) { + return; + } + $this->fixupMap = [ + Expr\PreInc::class => ['var' => self::FIXUP_PREC_RIGHT], + Expr\PreDec::class => ['var' => self::FIXUP_PREC_RIGHT], + Expr\PostInc::class => ['var' => self::FIXUP_PREC_LEFT], + Expr\PostDec::class => ['var' => self::FIXUP_PREC_LEFT], + Expr\Instanceof_::class => ['expr' => self::FIXUP_PREC_LEFT, 'class' => self::FIXUP_PREC_RIGHT], + Expr\Ternary::class => ['cond' => self::FIXUP_PREC_LEFT, 'else' => self::FIXUP_PREC_RIGHT], + Expr\FuncCall::class => ['name' => self::FIXUP_CALL_LHS], + Expr\StaticCall::class => ['class' => self::FIXUP_DEREF_LHS], + Expr\ArrayDimFetch::class => ['var' => self::FIXUP_DEREF_LHS], + Expr\ClassConstFetch::class => ['var' => self::FIXUP_DEREF_LHS], + Expr\New_::class => ['class' => self::FIXUP_DEREF_LHS], + // TODO: FIXUP_NEW_VARIABLE + Expr\MethodCall::class => ['var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], + Expr\NullsafeMethodCall::class => ['var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], + Expr\StaticPropertyFetch::class => ['class' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_VAR_BRACED_NAME], + Expr\PropertyFetch::class => ['var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], + Expr\NullsafePropertyFetch::class => ['var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], + Scalar\Encapsed::class => ['parts' => self::FIXUP_ENCAPSED], + ]; + $binaryOps = [BinaryOp\Pow::class, BinaryOp\Mul::class, BinaryOp\Div::class, BinaryOp\Mod::class, BinaryOp\Plus::class, BinaryOp\Minus::class, BinaryOp\Concat::class, BinaryOp\ShiftLeft::class, BinaryOp\ShiftRight::class, BinaryOp\Smaller::class, BinaryOp\SmallerOrEqual::class, BinaryOp\Greater::class, BinaryOp\GreaterOrEqual::class, BinaryOp\Equal::class, BinaryOp\NotEqual::class, BinaryOp\Identical::class, BinaryOp\NotIdentical::class, BinaryOp\Spaceship::class, BinaryOp\BitwiseAnd::class, BinaryOp\BitwiseXor::class, BinaryOp\BitwiseOr::class, BinaryOp\BooleanAnd::class, BinaryOp\BooleanOr::class, BinaryOp\Coalesce::class, BinaryOp\LogicalAnd::class, BinaryOp\LogicalXor::class, BinaryOp\LogicalOr::class]; + foreach ($binaryOps as $binaryOp) { + $this->fixupMap[$binaryOp] = ['left' => self::FIXUP_PREC_LEFT, 'right' => self::FIXUP_PREC_RIGHT]; + } + $assignOps = [Expr\Assign::class, Expr\AssignRef::class, AssignOp\Plus::class, AssignOp\Minus::class, AssignOp\Mul::class, AssignOp\Div::class, AssignOp\Concat::class, AssignOp\Mod::class, AssignOp\BitwiseAnd::class, AssignOp\BitwiseOr::class, AssignOp\BitwiseXor::class, AssignOp\ShiftLeft::class, AssignOp\ShiftRight::class, AssignOp\Pow::class, AssignOp\Coalesce::class]; + foreach ($assignOps as $assignOp) { + $this->fixupMap[$assignOp] = ['var' => self::FIXUP_PREC_LEFT, 'expr' => self::FIXUP_PREC_RIGHT]; + } + $prefixOps = [Expr\BitwiseNot::class, Expr\BooleanNot::class, Expr\UnaryPlus::class, Expr\UnaryMinus::class, Cast\Int_::class, Cast\Double::class, Cast\String_::class, Cast\Array_::class, Cast\Object_::class, Cast\Bool_::class, Cast\Unset_::class, Expr\ErrorSuppress::class, Expr\YieldFrom::class, Expr\Print_::class, Expr\Include_::class]; + foreach ($prefixOps as $prefixOp) { + $this->fixupMap[$prefixOp] = ['expr' => self::FIXUP_PREC_RIGHT]; + } + } + /** + * Lazily initializes the removal map. + * + * The removal map is used to determine which additional tokens should be removed when a + * certain node is replaced by null. + */ + protected function initializeRemovalMap() + { + if ($this->removalMap) { + return; + } + $stripBoth = ['left' => \T_WHITESPACE, 'right' => \T_WHITESPACE]; + $stripLeft = ['left' => \T_WHITESPACE]; + $stripRight = ['right' => \T_WHITESPACE]; + $stripDoubleArrow = ['right' => \T_DOUBLE_ARROW]; + $stripColon = ['left' => ':']; + $stripEquals = ['left' => '=']; + $this->removalMap = ['Expr_ArrayDimFetch->dim' => $stripBoth, 'Expr_ArrayItem->key' => $stripDoubleArrow, 'Expr_ArrowFunction->returnType' => $stripColon, 'Expr_Closure->returnType' => $stripColon, 'Expr_Exit->expr' => $stripBoth, 'Expr_Ternary->if' => $stripBoth, 'Expr_Yield->key' => $stripDoubleArrow, 'Expr_Yield->value' => $stripBoth, 'Param->type' => $stripRight, 'Param->default' => $stripEquals, 'Stmt_Break->num' => $stripBoth, 'Stmt_Catch->var' => $stripLeft, 'Stmt_ClassMethod->returnType' => $stripColon, 'Stmt_Class->extends' => ['left' => \T_EXTENDS], 'Stmt_Enum->scalarType' => $stripColon, 'Stmt_EnumCase->expr' => $stripEquals, 'Expr_PrintableNewAnonClass->extends' => ['left' => \T_EXTENDS], 'Stmt_Continue->num' => $stripBoth, 'Stmt_Foreach->keyVar' => $stripDoubleArrow, 'Stmt_Function->returnType' => $stripColon, 'Stmt_If->else' => $stripLeft, 'Stmt_Namespace->name' => $stripLeft, 'Stmt_Property->type' => $stripRight, 'Stmt_PropertyProperty->default' => $stripEquals, 'Stmt_Return->expr' => $stripBoth, 'Stmt_StaticVar->default' => $stripEquals, 'Stmt_TraitUseAdaptation_Alias->newName' => $stripLeft, 'Stmt_TryCatch->finally' => $stripLeft]; + } + protected function initializeInsertionMap() + { + if ($this->insertionMap) { + return; + } + // TODO: "yield" where both key and value are inserted doesn't work + // [$find, $beforeToken, $extraLeft, $extraRight] + $this->insertionMap = [ + 'Expr_ArrayDimFetch->dim' => ['[', \false, null, null], + 'Expr_ArrayItem->key' => [null, \false, null, ' => '], + 'Expr_ArrowFunction->returnType' => [')', \false, ' : ', null], + 'Expr_Closure->returnType' => [')', \false, ' : ', null], + 'Expr_Ternary->if' => ['?', \false, ' ', ' '], + 'Expr_Yield->key' => [\T_YIELD, \false, null, ' => '], + 'Expr_Yield->value' => [\T_YIELD, \false, ' ', null], + 'Param->type' => [null, \false, null, ' '], + 'Param->default' => [null, \false, ' = ', null], + 'Stmt_Break->num' => [\T_BREAK, \false, ' ', null], + 'Stmt_Catch->var' => [null, \false, ' ', null], + 'Stmt_ClassMethod->returnType' => [')', \false, ' : ', null], + 'Stmt_Class->extends' => [null, \false, ' extends ', null], + 'Stmt_Enum->scalarType' => [null, \false, ' : ', null], + 'Stmt_EnumCase->expr' => [null, \false, ' = ', null], + 'Expr_PrintableNewAnonClass->extends' => [null, ' extends ', null], + 'Stmt_Continue->num' => [\T_CONTINUE, \false, ' ', null], + 'Stmt_Foreach->keyVar' => [\T_AS, \false, null, ' => '], + 'Stmt_Function->returnType' => [')', \false, ' : ', null], + 'Stmt_If->else' => [null, \false, ' ', null], + 'Stmt_Namespace->name' => [\T_NAMESPACE, \false, ' ', null], + 'Stmt_Property->type' => [\T_VARIABLE, \true, null, ' '], + 'Stmt_PropertyProperty->default' => [null, \false, ' = ', null], + 'Stmt_Return->expr' => [\T_RETURN, \false, ' ', null], + 'Stmt_StaticVar->default' => [null, \false, ' = ', null], + //'Stmt_TraitUseAdaptation_Alias->newName' => [T_AS, false, ' ', null], // TODO + 'Stmt_TryCatch->finally' => [null, \false, ' ', null], + ]; + } + protected function initializeListInsertionMap() + { + if ($this->listInsertionMap) { + return; + } + $this->listInsertionMap = [ + // special + //'Expr_ShellExec->parts' => '', // TODO These need to be treated more carefully + //'Scalar_Encapsed->parts' => '', + 'Stmt_Catch->types' => '|', + 'UnionType->types' => '|', + 'IntersectionType->types' => '&', + 'Stmt_If->elseifs' => ' ', + 'Stmt_TryCatch->catches' => ' ', + // comma-separated lists + 'Expr_Array->items' => ', ', + 'Expr_ArrowFunction->params' => ', ', + 'Expr_Closure->params' => ', ', + 'Expr_Closure->uses' => ', ', + 'Expr_FuncCall->args' => ', ', + 'Expr_Isset->vars' => ', ', + 'Expr_List->items' => ', ', + 'Expr_MethodCall->args' => ', ', + 'Expr_NullsafeMethodCall->args' => ', ', + 'Expr_New->args' => ', ', + 'Expr_PrintableNewAnonClass->args' => ', ', + 'Expr_StaticCall->args' => ', ', + 'Stmt_ClassConst->consts' => ', ', + 'Stmt_ClassMethod->params' => ', ', + 'Stmt_Class->implements' => ', ', + 'Stmt_Enum->implements' => ', ', + 'Expr_PrintableNewAnonClass->implements' => ', ', + 'Stmt_Const->consts' => ', ', + 'Stmt_Declare->declares' => ', ', + 'Stmt_Echo->exprs' => ', ', + 'Stmt_For->init' => ', ', + 'Stmt_For->cond' => ', ', + 'Stmt_For->loop' => ', ', + 'Stmt_Function->params' => ', ', + 'Stmt_Global->vars' => ', ', + 'Stmt_GroupUse->uses' => ', ', + 'Stmt_Interface->extends' => ', ', + 'Stmt_Match->arms' => ', ', + 'Stmt_Property->props' => ', ', + 'Stmt_StaticVar->vars' => ', ', + 'Stmt_TraitUse->traits' => ', ', + 'Stmt_TraitUseAdaptation_Precedence->insteadof' => ', ', + 'Stmt_Unset->vars' => ', ', + 'Stmt_Use->uses' => ', ', + 'MatchArm->conds' => ', ', + 'AttributeGroup->attrs' => ', ', + // statement lists + 'Expr_Closure->stmts' => "\n", + 'Stmt_Case->stmts' => "\n", + 'Stmt_Catch->stmts' => "\n", + 'Stmt_Class->stmts' => "\n", + 'Stmt_Enum->stmts' => "\n", + 'Expr_PrintableNewAnonClass->stmts' => "\n", + 'Stmt_Interface->stmts' => "\n", + 'Stmt_Trait->stmts' => "\n", + 'Stmt_ClassMethod->stmts' => "\n", + 'Stmt_Declare->stmts' => "\n", + 'Stmt_Do->stmts' => "\n", + 'Stmt_ElseIf->stmts' => "\n", + 'Stmt_Else->stmts' => "\n", + 'Stmt_Finally->stmts' => "\n", + 'Stmt_Foreach->stmts' => "\n", + 'Stmt_For->stmts' => "\n", + 'Stmt_Function->stmts' => "\n", + 'Stmt_If->stmts' => "\n", + 'Stmt_Namespace->stmts' => "\n", + 'Stmt_Class->attrGroups' => "\n", + 'Stmt_Enum->attrGroups' => "\n", + 'Stmt_EnumCase->attrGroups' => "\n", + 'Stmt_Interface->attrGroups' => "\n", + 'Stmt_Trait->attrGroups' => "\n", + 'Stmt_Function->attrGroups' => "\n", + 'Stmt_ClassMethod->attrGroups' => "\n", + 'Stmt_ClassConst->attrGroups' => "\n", + 'Stmt_Property->attrGroups' => "\n", + 'Expr_PrintableNewAnonClass->attrGroups' => ' ', + 'Expr_Closure->attrGroups' => ' ', + 'Expr_ArrowFunction->attrGroups' => ' ', + 'Param->attrGroups' => ' ', + 'Stmt_Switch->cases' => "\n", + 'Stmt_TraitUse->adaptations' => "\n", + 'Stmt_TryCatch->stmts' => "\n", + 'Stmt_While->stmts' => "\n", + // dummy for top-level context + 'File->stmts' => "\n", + ]; + } + protected function initializeEmptyListInsertionMap() + { + if ($this->emptyListInsertionMap) { + return; + } + // TODO Insertion into empty statement lists. + // [$find, $extraLeft, $extraRight] + $this->emptyListInsertionMap = ['Expr_ArrowFunction->params' => ['(', '', ''], 'Expr_Closure->uses' => [')', ' use(', ')'], 'Expr_Closure->params' => ['(', '', ''], 'Expr_FuncCall->args' => ['(', '', ''], 'Expr_MethodCall->args' => ['(', '', ''], 'Expr_NullsafeMethodCall->args' => ['(', '', ''], 'Expr_New->args' => ['(', '', ''], 'Expr_PrintableNewAnonClass->args' => ['(', '', ''], 'Expr_PrintableNewAnonClass->implements' => [null, ' implements ', ''], 'Expr_StaticCall->args' => ['(', '', ''], 'Stmt_Class->implements' => [null, ' implements ', ''], 'Stmt_Enum->implements' => [null, ' implements ', ''], 'Stmt_ClassMethod->params' => ['(', '', ''], 'Stmt_Interface->extends' => [null, ' extends ', ''], 'Stmt_Function->params' => ['(', '', '']]; + } + protected function initializeModifierChangeMap() + { + if ($this->modifierChangeMap) { + return; + } + $this->modifierChangeMap = ['Stmt_ClassConst->flags' => \T_CONST, 'Stmt_ClassMethod->flags' => \T_FUNCTION, 'Stmt_Class->flags' => \T_CLASS, 'Stmt_Property->flags' => \T_VARIABLE, 'Param->flags' => \T_VARIABLE]; + // List of integer subnodes that are not modifiers: + // Expr_Include->type + // Stmt_GroupUse->type + // Stmt_Use->type + // Stmt_UseUse->type + } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Template; +namespace PHPUnit\PhpParser\Comment; -use InvalidArgumentException; -final class RuntimeException extends \InvalidArgumentException implements \PHPUnit\SebastianBergmann\Template\Exception +class Doc extends \PHPUnit\PhpParser\Comment { } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Template; +namespace PHPUnit\PhpParser\Builder; -use function array_merge; -use function file_exists; -use function file_get_contents; -use function file_put_contents; -use function sprintf; -use function str_replace; -final class Template +use PHPUnit\PhpParser; +use PHPUnit\PhpParser\BuilderHelpers; +use PHPUnit\PhpParser\Node; +use PHPUnit\PhpParser\Node\Stmt; +class Function_ extends FunctionLike { + protected $name; + protected $stmts = []; + /** @var Node\AttributeGroup[] */ + protected $attributeGroups = []; /** - * @var string + * Creates a function builder. + * + * @param string $name Name of the function */ - private $template = ''; + public function __construct(string $name) + { + $this->name = $name; + } /** - * @var string + * Adds a statement. + * + * @param Node|PhpParser\Builder $stmt The statement to add + * + * @return $this The builder instance (for fluid interface) */ - private $openDelimiter; + public function addStmt($stmt) + { + $this->stmts[] = BuilderHelpers::normalizeStmt($stmt); + return $this; + } /** - * @var string + * Adds an attribute group. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) */ - private $closeDelimiter; + public function addAttribute($attribute) + { + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + return $this; + } /** - * @var array + * Returns the built function node. + * + * @return Stmt\Function_ The built function node */ - private $values = []; + public function getNode() : Node + { + return new Stmt\Function_($this->name, ['byRef' => $this->returnByRef, 'params' => $this->params, 'returnType' => $this->returnType, 'stmts' => $this->stmts, 'attrGroups' => $this->attributeGroups], $this->attributes); + } +} +setFile($file); - $this->openDelimiter = $openDelimiter; - $this->closeDelimiter = $closeDelimiter; + $this->name = $name; } /** - * @throws InvalidArgumentException + * Extends one or more interfaces. + * + * @param Name|string ...$interfaces Names of interfaces to extend + * + * @return $this The builder instance (for fluid interface) */ - public function setFile(string $file) : void + public function extend(...$interfaces) { - $distFile = $file . '.dist'; - if (\file_exists($file)) { - $this->template = \file_get_contents($file); - } elseif (\file_exists($distFile)) { - $this->template = \file_get_contents($distFile); - } else { - throw new \PHPUnit\SebastianBergmann\Template\InvalidArgumentException(\sprintf('Failed to load template "%s"', $file)); + foreach ($interfaces as $interface) { + $this->extends[] = BuilderHelpers::normalizeName($interface); } + return $this; } - public function setVar(array $values, bool $merge = \true) : void + /** + * Adds a statement. + * + * @param Stmt|PhpParser\Builder $stmt The statement to add + * + * @return $this The builder instance (for fluid interface) + */ + public function addStmt($stmt) { - if (!$merge || empty($this->values)) { - $this->values = $values; + $stmt = BuilderHelpers::normalizeNode($stmt); + if ($stmt instanceof Stmt\ClassConst) { + $this->constants[] = $stmt; + } elseif ($stmt instanceof Stmt\ClassMethod) { + // we erase all statements in the body of an interface method + $stmt->stmts = null; + $this->methods[] = $stmt; } else { - $this->values = \array_merge($this->values, $values); + throw new \LogicException(\sprintf('Unexpected node of type "%s"', $stmt->getType())); } + return $this; } - public function render() : string + /** + * Adds an attribute group. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) + */ + public function addAttribute($attribute) { - $keys = []; - foreach ($this->values as $key => $value) { - $keys[] = $this->openDelimiter . $key . $this->closeDelimiter; - } - return \str_replace($keys, $this->values, $this->template); + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + return $this; } /** - * @codeCoverageIgnore + * Returns the built interface node. + * + * @return Stmt\Interface_ The built interface node */ - public function renderTo(string $target) : void + public function getNode() : PhpParser\Node { - if (!\file_put_contents($target, $this->render())) { - throw new \PHPUnit\SebastianBergmann\Template\RuntimeException(\sprintf('Writing rendered result to "%s" failed', $target)); - } + return new Stmt\Interface_($this->name, ['extends' => $this->extends, 'stmts' => \array_merge($this->constants, $this->methods), 'attrGroups' => $this->attributeGroups], $this->attributes); } } -phpunit/php-text-template - -Copyright (c) 2009-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\GlobalState; +namespace PHPUnit\PhpParser\Builder; -use function array_diff; -use function array_key_exists; -use function array_keys; -use function array_merge; -use function function_exists; -use function get_defined_functions; -use function in_array; -use function is_array; -use ReflectionClass; -use ReflectionProperty; -/** - * Restorer of snapshots of global state. - */ -class Restorer +use PHPUnit\PhpParser; +use PHPUnit\PhpParser\BuilderHelpers; +use PHPUnit\PhpParser\Node; +use PHPUnit\PhpParser\Node\Stmt; +class Method extends FunctionLike { + protected $name; + protected $flags = 0; + /** @var array|null */ + protected $stmts = []; + /** @var Node\AttributeGroup[] */ + protected $attributeGroups = []; /** - * Deletes function definitions that are not defined in a snapshot. - * - * @throws RuntimeException when the uopz_delete() function is not available + * Creates a method builder. * - * @see https://github.com/krakjoe/uopz + * @param string $name Name of the method */ - public function restoreFunctions(\PHPUnit\SebastianBergmann\GlobalState\Snapshot $snapshot) : void + public function __construct(string $name) { - if (!\function_exists('PHPUnit\\uopz_delete')) { - throw new \PHPUnit\SebastianBergmann\GlobalState\RuntimeException('The uopz_delete() function is required for this operation'); - } - $functions = \get_defined_functions(); - foreach (\array_diff($functions['user'], $snapshot->functions()) as $function) { - uopz_delete($function); - } + $this->name = $name; } /** - * Restores all global and super-global variables from a snapshot. + * Makes the method public. + * + * @return $this The builder instance (for fluid interface) */ - public function restoreGlobalVariables(\PHPUnit\SebastianBergmann\GlobalState\Snapshot $snapshot) : void + public function makePublic() { - $superGlobalArrays = $snapshot->superGlobalArrays(); - foreach ($superGlobalArrays as $superGlobalArray) { - $this->restoreSuperGlobalArray($snapshot, $superGlobalArray); - } - $globalVariables = $snapshot->globalVariables(); - foreach (\array_keys($GLOBALS) as $key) { - if ($key !== 'GLOBALS' && !\in_array($key, $superGlobalArrays) && !$snapshot->excludeList()->isGlobalVariableExcluded($key)) { - if (\array_key_exists($key, $globalVariables)) { - $GLOBALS[$key] = $globalVariables[$key]; - } else { - unset($GLOBALS[$key]); - } - } - } + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC); + return $this; } /** - * Restores all static attributes in user-defined classes from this snapshot. - */ - public function restoreStaticAttributes(\PHPUnit\SebastianBergmann\GlobalState\Snapshot $snapshot) : void - { - $current = new \PHPUnit\SebastianBergmann\GlobalState\Snapshot($snapshot->excludeList(), \false, \false, \false, \false, \true, \false, \false, \false, \false); - $newClasses = \array_diff($current->classes(), $snapshot->classes()); - unset($current); - foreach ($snapshot->staticAttributes() as $className => $staticAttributes) { - foreach ($staticAttributes as $name => $value) { - $reflector = new \ReflectionProperty($className, $name); - $reflector->setAccessible(\true); - $reflector->setValue($value); - } - } - foreach ($newClasses as $className) { - $class = new \ReflectionClass($className); - $defaults = $class->getDefaultProperties(); - foreach ($class->getProperties() as $attribute) { - if (!$attribute->isStatic()) { - continue; - } - $name = $attribute->getName(); - if ($snapshot->excludeList()->isStaticAttributeExcluded($className, $name)) { - continue; - } - if (!isset($defaults[$name])) { - continue; - } - $attribute->setAccessible(\true); - $attribute->setValue($defaults[$name]); - } - } + * Makes the method protected. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeProtected() + { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED); + return $this; } /** - * Restores a super-global variable array from this snapshot. + * Makes the method private. + * + * @return $this The builder instance (for fluid interface) */ - private function restoreSuperGlobalArray(\PHPUnit\SebastianBergmann\GlobalState\Snapshot $snapshot, string $superGlobalArray) : void + public function makePrivate() { - $superGlobalVariables = $snapshot->superGlobalVariables(); - if (isset($GLOBALS[$superGlobalArray]) && \is_array($GLOBALS[$superGlobalArray]) && isset($superGlobalVariables[$superGlobalArray])) { - $keys = \array_keys(\array_merge($GLOBALS[$superGlobalArray], $superGlobalVariables[$superGlobalArray])); - foreach ($keys as $key) { - if (isset($superGlobalVariables[$superGlobalArray][$key])) { - $GLOBALS[$superGlobalArray][$key] = $superGlobalVariables[$superGlobalArray][$key]; - } else { - unset($GLOBALS[$superGlobalArray][$key]); - } - } - } + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE); + return $this; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\GlobalState; - -use Throwable; -interface Exception extends \Throwable -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\GlobalState; - -final class RuntimeException extends \RuntimeException implements \PHPUnit\SebastianBergmann\GlobalState\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\GlobalState; - -use function in_array; -use function strpos; -use ReflectionClass; -final class ExcludeList -{ /** - * @var array + * Makes the method static. + * + * @return $this The builder instance (for fluid interface) */ - private $globalVariables = []; + public function makeStatic() + { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC); + return $this; + } /** - * @var string[] + * Makes the method abstract. + * + * @return $this The builder instance (for fluid interface) */ - private $classes = []; + public function makeAbstract() + { + if (!empty($this->stmts)) { + throw new \LogicException('Cannot make method with statements abstract'); + } + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT); + $this->stmts = null; + // abstract methods don't have statements + return $this; + } /** - * @var string[] + * Makes the method final. + * + * @return $this The builder instance (for fluid interface) */ - private $classNamePrefixes = []; + public function makeFinal() + { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL); + return $this; + } /** - * @var string[] + * Adds a statement. + * + * @param Node|PhpParser\Builder $stmt The statement to add + * + * @return $this The builder instance (for fluid interface) */ - private $parentClasses = []; + public function addStmt($stmt) + { + if (null === $this->stmts) { + throw new \LogicException('Cannot add statements to an abstract method'); + } + $this->stmts[] = BuilderHelpers::normalizeStmt($stmt); + return $this; + } /** - * @var string[] + * Adds an attribute group. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) */ - private $interfaces = []; + public function addAttribute($attribute) + { + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + return $this; + } /** - * @var array + * Returns the built method node. + * + * @return Stmt\ClassMethod The built method node */ - private $staticAttributes = []; - public function addGlobalVariable(string $variableName) : void + public function getNode() : Node { - $this->globalVariables[$variableName] = \true; + return new Stmt\ClassMethod($this->name, ['flags' => $this->flags, 'byRef' => $this->returnByRef, 'params' => $this->params, 'returnType' => $this->returnType, 'stmts' => $this->stmts, 'attrGroups' => $this->attributeGroups], $this->attributes); } - public function addClass(string $className) : void +} +classes[] = $className; + $this->name = $name; } - public function addSubclassesOf(string $className) : void + /** + * Extends a class. + * + * @param Name|string $class Name of class to extend + * + * @return $this The builder instance (for fluid interface) + */ + public function extend($class) { - $this->parentClasses[] = $className; + $this->extends = BuilderHelpers::normalizeName($class); + return $this; } - public function addImplementorsOf(string $interfaceName) : void + /** + * Implements one or more interfaces. + * + * @param Name|string ...$interfaces Names of interfaces to implement + * + * @return $this The builder instance (for fluid interface) + */ + public function implement(...$interfaces) { - $this->interfaces[] = $interfaceName; + foreach ($interfaces as $interface) { + $this->implements[] = BuilderHelpers::normalizeName($interface); + } + return $this; } - public function addClassNamePrefix(string $classNamePrefix) : void + /** + * Makes the class abstract. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeAbstract() { - $this->classNamePrefixes[] = $classNamePrefix; + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT); + return $this; } - public function addStaticAttribute(string $className, string $attributeName) : void + /** + * Makes the class final. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeFinal() { - if (!isset($this->staticAttributes[$className])) { - $this->staticAttributes[$className] = []; + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL); + return $this; + } + /** + * Adds a statement. + * + * @param Stmt|PhpParser\Builder $stmt The statement to add + * + * @return $this The builder instance (for fluid interface) + */ + public function addStmt($stmt) + { + $stmt = BuilderHelpers::normalizeNode($stmt); + $targets = [Stmt\TraitUse::class => &$this->uses, Stmt\ClassConst::class => &$this->constants, Stmt\Property::class => &$this->properties, Stmt\ClassMethod::class => &$this->methods]; + $class = \get_class($stmt); + if (!isset($targets[$class])) { + throw new \LogicException(\sprintf('Unexpected node of type "%s"', $stmt->getType())); } - $this->staticAttributes[$className][$attributeName] = \true; + $targets[$class][] = $stmt; + return $this; } - public function isGlobalVariableExcluded(string $variableName) : bool + /** + * Adds an attribute group. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) + */ + public function addAttribute($attribute) { - return isset($this->globalVariables[$variableName]); + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + return $this; } - public function isStaticAttributeExcluded(string $className, string $attributeName) : bool + /** + * Returns the built class node. + * + * @return Stmt\Class_ The built class node + */ + public function getNode() : PhpParser\Node { - if (\in_array($className, $this->classes)) { - return \true; - } - foreach ($this->classNamePrefixes as $prefix) { - if (\strpos($className, $prefix) === 0) { - return \true; - } - } - $class = new \ReflectionClass($className); - foreach ($this->parentClasses as $type) { - if ($class->isSubclassOf($type)) { - return \true; - } - } - foreach ($this->interfaces as $type) { - if ($class->implementsInterface($type)) { - return \true; - } - } - if (isset($this->staticAttributes[$className][$attributeName])) { - return \true; - } - return \false; + return new Stmt\Class_($this->name, ['flags' => $this->flags, 'extends' => $this->extends, 'implements' => $this->implements, 'stmts' => \array_merge($this->uses, $this->constants, $this->properties, $this->methods), 'attrGroups' => $this->attributeGroups], $this->attributes); } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\GlobalState; +namespace PHPUnit\PhpParser\Builder; -use const PHP_EOL; -use function is_array; -use function is_scalar; -use function serialize; -use function sprintf; -use function var_export; -/** - * Exports parts of a Snapshot as PHP code. - */ -final class CodeExporter +use PHPUnit\PhpParser; +use PHPUnit\PhpParser\BuilderHelpers; +use PHPUnit\PhpParser\Node; +use PHPUnit\PhpParser\Node\Stmt; +class Trait_ extends Declaration { - public function constants(\PHPUnit\SebastianBergmann\GlobalState\Snapshot $snapshot) : string - { - $result = ''; - foreach ($snapshot->constants() as $name => $value) { - $result .= \sprintf('if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", $name, $name, $this->exportVariable($value)); - } - return $result; - } - public function globalVariables(\PHPUnit\SebastianBergmann\GlobalState\Snapshot $snapshot) : string + protected $name; + protected $uses = []; + protected $properties = []; + protected $methods = []; + /** @var Node\AttributeGroup[] */ + protected $attributeGroups = []; + /** + * Creates an interface builder. + * + * @param string $name Name of the interface + */ + public function __construct(string $name) { - $result = '$GLOBALS = [];' . \PHP_EOL; - foreach ($snapshot->globalVariables() as $name => $value) { - $result .= \sprintf('$GLOBALS[%s] = %s;' . \PHP_EOL, $this->exportVariable($name), $this->exportVariable($value)); - } - return $result; + $this->name = $name; } - public function iniSettings(\PHPUnit\SebastianBergmann\GlobalState\Snapshot $snapshot) : string + /** + * Adds a statement. + * + * @param Stmt|PhpParser\Builder $stmt The statement to add + * + * @return $this The builder instance (for fluid interface) + */ + public function addStmt($stmt) { - $result = ''; - foreach ($snapshot->iniSettings() as $key => $value) { - $result .= \sprintf('@ini_set(%s, %s);' . "\n", $this->exportVariable($key), $this->exportVariable($value)); + $stmt = BuilderHelpers::normalizeNode($stmt); + if ($stmt instanceof Stmt\Property) { + $this->properties[] = $stmt; + } elseif ($stmt instanceof Stmt\ClassMethod) { + $this->methods[] = $stmt; + } elseif ($stmt instanceof Stmt\TraitUse) { + $this->uses[] = $stmt; + } else { + throw new \LogicException(\sprintf('Unexpected node of type "%s"', $stmt->getType())); } - return $result; + return $this; } - private function exportVariable($variable) : string + /** + * Adds an attribute group. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) + */ + public function addAttribute($attribute) { - if (\is_scalar($variable) || null === $variable || \is_array($variable) && $this->arrayOnlyContainsScalars($variable)) { - return \var_export($variable, \true); - } - return 'unserialize(' . \var_export(\serialize($variable), \true) . ')'; + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + return $this; } - private function arrayOnlyContainsScalars(array $array) : bool + /** + * Returns the built trait node. + * + * @return Stmt\Trait_ The built interface node + */ + public function getNode() : PhpParser\Node { - $result = \true; - foreach ($array as $element) { - if (\is_array($element)) { - $result = $this->arrayOnlyContainsScalars($element); - } elseif (!\is_scalar($element) && null !== $element) { - $result = \false; - } - if ($result === \false) { - break; - } - } - return $result; + return new Stmt\Trait_($this->name, ['stmts' => \array_merge($this->uses, $this->properties, $this->methods), 'attrGroups' => $this->attributeGroups], $this->attributes); } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\GlobalState; +namespace PHPUnit\PhpParser\Builder; -use function array_keys; -use function array_merge; -use function array_reverse; -use function func_get_args; -use function get_declared_classes; -use function get_declared_interfaces; -use function get_declared_traits; -use function get_defined_constants; -use function get_defined_functions; -use function get_included_files; -use function in_array; -use function ini_get_all; -use function is_array; -use function is_object; -use function is_resource; -use function is_scalar; -use function serialize; -use function unserialize; -use ReflectionClass; -use PHPUnit\SebastianBergmann\ObjectReflector\ObjectReflector; -use PHPUnit\SebastianBergmann\RecursionContext\Context; -use Throwable; -/** - * A snapshot of global state. - */ -class Snapshot +use PHPUnit\PhpParser\BuilderHelpers; +use PHPUnit\PhpParser\Node; +abstract class FunctionLike extends Declaration { + protected $returnByRef = \false; + protected $params = []; + /** @var string|Node\Name|Node\NullableType|null */ + protected $returnType = null; /** - * @var ExcludeList + * Make the function return by reference. + * + * @return $this The builder instance (for fluid interface) */ - private $excludeList; + public function makeReturnByRef() + { + $this->returnByRef = \true; + return $this; + } /** - * @var array + * Adds a parameter. + * + * @param Node\Param|Param $param The parameter to add + * + * @return $this The builder instance (for fluid interface) */ - private $globalVariables = []; + public function addParam($param) + { + $param = BuilderHelpers::normalizeNode($param); + if (!$param instanceof Node\Param) { + throw new \LogicException(\sprintf('Expected parameter node, got "%s"', $param->getType())); + } + $this->params[] = $param; + return $this; + } /** - * @var array + * Adds multiple parameters. + * + * @param array $params The parameters to add + * + * @return $this The builder instance (for fluid interface) */ - private $superGlobalArrays = []; + public function addParams(array $params) + { + foreach ($params as $param) { + $this->addParam($param); + } + return $this; + } /** - * @var array + * Sets the return type for PHP 7. + * + * @param string|Node\Name|Node\Identifier|Node\ComplexType $type + * + * @return $this The builder instance (for fluid interface) */ - private $superGlobalVariables = []; + public function setReturnType($type) + { + $this->returnType = BuilderHelpers::normalizeType($type); + return $this; + } +} +and($trait); + } + } /** - * @var array + * Adds used trait. + * + * @param Node\Name|string $trait Trait name + * + * @return $this The builder instance (for fluid interface) */ - private $iniSettings = []; + public function and($trait) + { + $this->traits[] = BuilderHelpers::normalizeName($trait); + return $this; + } /** - * @var array + * Adds trait adaptation. + * + * @param Stmt\TraitUseAdaptation|Builder\TraitUseAdaptation $adaptation Trait adaptation + * + * @return $this The builder instance (for fluid interface) */ - private $includedFiles = []; + public function with($adaptation) + { + $adaptation = BuilderHelpers::normalizeNode($adaptation); + if (!$adaptation instanceof Stmt\TraitUseAdaptation) { + throw new \LogicException('Adaptation must have type TraitUseAdaptation'); + } + $this->adaptations[] = $adaptation; + return $this; + } /** - * @var array + * Returns the built node. + * + * @return Node The built node */ - private $constants = []; - /** - * @var array - */ - private $functions = []; - /** - * @var array - */ - private $interfaces = []; - /** - * @var array - */ - private $classes = []; - /** - * @var array - */ - private $traits = []; - /** - * Creates a snapshot of the current global state. - */ - public function __construct(\PHPUnit\SebastianBergmann\GlobalState\ExcludeList $excludeList = null, bool $includeGlobalVariables = \true, bool $includeStaticAttributes = \true, bool $includeConstants = \true, bool $includeFunctions = \true, bool $includeClasses = \true, bool $includeInterfaces = \true, bool $includeTraits = \true, bool $includeIniSettings = \true, bool $includeIncludedFiles = \true) - { - $this->excludeList = $excludeList ?: new \PHPUnit\SebastianBergmann\GlobalState\ExcludeList(); - if ($includeConstants) { - $this->snapshotConstants(); - } - if ($includeFunctions) { - $this->snapshotFunctions(); - } - if ($includeClasses || $includeStaticAttributes) { - $this->snapshotClasses(); - } - if ($includeInterfaces) { - $this->snapshotInterfaces(); - } - if ($includeGlobalVariables) { - $this->setupSuperGlobalArrays(); - $this->snapshotGlobals(); - } - if ($includeStaticAttributes) { - $this->snapshotStaticAttributes(); - } - if ($includeIniSettings) { - $this->iniSettings = \ini_get_all(null, \false); - } - if ($includeIncludedFiles) { - $this->includedFiles = \get_included_files(); - } - $this->traits = \get_declared_traits(); - } - public function excludeList() : \PHPUnit\SebastianBergmann\GlobalState\ExcludeList - { - return $this->excludeList; - } - public function globalVariables() : array - { - return $this->globalVariables; - } - public function superGlobalVariables() : array - { - return $this->superGlobalVariables; - } - public function superGlobalArrays() : array - { - return $this->superGlobalArrays; - } - public function staticAttributes() : array - { - return $this->staticAttributes; - } - public function iniSettings() : array - { - return $this->iniSettings; - } - public function includedFiles() : array - { - return $this->includedFiles; - } - public function constants() : array - { - return $this->constants; - } - public function functions() : array - { - return $this->functions; - } - public function interfaces() : array - { - return $this->interfaces; - } - public function classes() : array - { - return $this->classes; - } - public function traits() : array + public function getNode() : Node { - return $this->traits; + return new Stmt\TraitUse($this->traits, $this->adaptations); } +} +constants = $constants['user']; + foreach ($stmts as $stmt) { + $this->addStmt($stmt); } + return $this; } /** - * Creates a snapshot user-defined functions. + * Sets doc comment for the declaration. + * + * @param PhpParser\Comment\Doc|string $docComment Doc comment to set + * + * @return $this The builder instance (for fluid interface) */ - private function snapshotFunctions() : void + public function setDocComment($docComment) { - $functions = \get_defined_functions(); - $this->functions = $functions['user']; + $this->attributes['comments'] = [BuilderHelpers::normalizeDocComment($docComment)]; + return $this; } +} +isUserDefined()) { - break; - } - $this->classes[] = $className; - } - $this->classes = \array_reverse($this->classes); + $this->name = $name; } /** - * Creates a snapshot user-defined interfaces. + * Sets default value for the parameter. + * + * @param mixed $value Default value to use + * + * @return $this The builder instance (for fluid interface) */ - private function snapshotInterfaces() : void + public function setDefault($value) { - foreach (\array_reverse(\get_declared_interfaces()) as $interfaceName) { - $class = new \ReflectionClass($interfaceName); - if (!$class->isUserDefined()) { - break; - } - $this->interfaces[] = $interfaceName; - } - $this->interfaces = \array_reverse($this->interfaces); + $this->default = BuilderHelpers::normalizeValue($value); + return $this; } /** - * Creates a snapshot of all global and super-global variables. + * Sets type for the parameter. + * + * @param string|Node\Name|Node\Identifier|Node\ComplexType $type Parameter type + * + * @return $this The builder instance (for fluid interface) */ - private function snapshotGlobals() : void + public function setType($type) { - $superGlobalArrays = $this->superGlobalArrays(); - foreach ($superGlobalArrays as $superGlobalArray) { - $this->snapshotSuperGlobalArray($superGlobalArray); - } - foreach (\array_keys($GLOBALS) as $key) { - if ($key !== 'GLOBALS' && !\in_array($key, $superGlobalArrays) && $this->canBeSerialized($GLOBALS[$key]) && !$this->excludeList->isGlobalVariableExcluded($key)) { - /* @noinspection UnserializeExploitsInspection */ - $this->globalVariables[$key] = \unserialize(\serialize($GLOBALS[$key])); - } + $this->type = BuilderHelpers::normalizeType($type); + if ($this->type == 'void') { + throw new \LogicException('Parameter type cannot be void'); } + return $this; } /** - * Creates a snapshot a super-global variable array. + * Sets type for the parameter. + * + * @param string|Node\Name|Node\Identifier|Node\ComplexType $type Parameter type + * + * @return $this The builder instance (for fluid interface) + * + * @deprecated Use setType() instead */ - private function snapshotSuperGlobalArray(string $superGlobalArray) : void + public function setTypeHint($type) { - $this->superGlobalVariables[$superGlobalArray] = []; - if (isset($GLOBALS[$superGlobalArray]) && \is_array($GLOBALS[$superGlobalArray])) { - foreach ($GLOBALS[$superGlobalArray] as $key => $value) { - /* @noinspection UnserializeExploitsInspection */ - $this->superGlobalVariables[$superGlobalArray][$key] = \unserialize(\serialize($value)); - } - } + return $this->setType($type); } /** - * Creates a snapshot of all static attributes in user-defined classes. + * Make the parameter accept the value by reference. + * + * @return $this The builder instance (for fluid interface) */ - private function snapshotStaticAttributes() : void + public function makeByRef() { - foreach ($this->classes as $className) { - $class = new \ReflectionClass($className); - $snapshot = []; - foreach ($class->getProperties() as $attribute) { - if ($attribute->isStatic()) { - $name = $attribute->getName(); - if ($this->excludeList->isStaticAttributeExcluded($className, $name)) { - continue; - } - $attribute->setAccessible(\true); - $value = $attribute->getValue(); - if ($this->canBeSerialized($value)) { - /* @noinspection UnserializeExploitsInspection */ - $snapshot[$name] = \unserialize(\serialize($value)); - } - } - } - if (!empty($snapshot)) { - $this->staticAttributes[$className] = $snapshot; - } - } + $this->byRef = \true; + return $this; } /** - * Returns a list of all super-global variable arrays. + * Make the parameter variadic + * + * @return $this The builder instance (for fluid interface) */ - private function setupSuperGlobalArrays() : void + public function makeVariadic() { - $this->superGlobalArrays = ['_ENV', '_POST', '_GET', '_COOKIE', '_SERVER', '_FILES', '_REQUEST']; + $this->variadic = \true; + return $this; } - private function canBeSerialized($variable) : bool + /** + * Adds an attribute group. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) + */ + public function addAttribute($attribute) { - if (\is_scalar($variable) || $variable === null) { - return \true; - } - if (\is_resource($variable)) { - return \false; - } - foreach ($this->enumerateObjectsAndResources($variable) as $value) { - if (\is_resource($value)) { - return \false; - } - if (\is_object($value)) { - $class = new \ReflectionClass($value); - if ($class->isAnonymous()) { - return \false; - } - try { - @\serialize($value); - } catch (\Throwable $t) { - return \false; - } - } - } - return \true; + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + return $this; } - private function enumerateObjectsAndResources($variable) : array + /** + * Returns the built parameter node. + * + * @return Node\Param The built parameter node + */ + public function getNode() : Node { - if (isset(\func_get_args()[1])) { - $processed = \func_get_args()[1]; - } else { - $processed = new \PHPUnit\SebastianBergmann\RecursionContext\Context(); - } - $result = []; - if ($processed->contains($variable)) { - return $result; - } - $array = $variable; - $processed->add($variable); - if (\is_array($variable)) { - foreach ($array as $element) { - if (!\is_array($element) && !\is_object($element) && !\is_resource($element)) { - continue; - } - if (!\is_resource($element)) { - /** @noinspection SlowArrayOperationsInLoopInspection */ - $result = \array_merge($result, $this->enumerateObjectsAndResources($element, $processed)); - } else { - $result[] = $element; - } - } - } else { - $result[] = $variable; - foreach ((new \PHPUnit\SebastianBergmann\ObjectReflector\ObjectReflector())->getAttributes($variable) as $value) { - if (!\is_array($value) && !\is_object($value) && !\is_resource($value)) { - continue; - } - if (!\is_resource($value)) { - /** @noinspection SlowArrayOperationsInLoopInspection */ - $result = \array_merge($result, $this->enumerateObjectsAndResources($value, $processed)); - } else { - $result[] = $value; - } - } - } - return $result; + return new Node\Param(new Node\Expr\Variable($this->name), $this->default, $this->type, $this->byRef, $this->variadic, [], 0, $this->attributeGroups); } } -sebastian/global-state - -Copyright (c) 2001-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. name = BuilderHelpers::normalizeName($name); + $this->type = $type; + } /** - * Array of objects that can directly be cloned, indexed by class name. + * Sets alias for used name. * - * @var object[] + * @param string $alias Alias to use (last component of full name by default) + * + * @return $this The builder instance (for fluid interface) */ - private static $cachedCloneables = []; + public function as(string $alias) + { + $this->alias = $alias; + return $this; + } /** - * {@inheritDoc} + * Returns the built node. + * + * @return Stmt\Use_ The built node */ - public function instantiate($className) + public function getNode() : Node { - if (isset(self::$cachedCloneables[$className])) { - return clone self::$cachedCloneables[$className]; - } - if (isset(self::$cachedInstantiators[$className])) { - $factory = self::$cachedInstantiators[$className]; - return $factory(); - } - return $this->buildAndCacheFromFactory($className); + return new Stmt\Use_([new Stmt\UseUse($this->name, $this->alias)], $this->type); } +} + $className + * Creates a namespace builder. * - * @phpstan-return T + * @param Node\Name|string|null $name Name of the namespace */ - private function buildAndCacheFromFactory(string $className) + public function __construct($name) { - $factory = self::$cachedInstantiators[$className] = $this->buildFactory($className); - $instance = $factory(); - if ($this->isSafeToClone(new \ReflectionClass($instance))) { - self::$cachedCloneables[$className] = clone $instance; - } - return $instance; + $this->name = null !== $name ? BuilderHelpers::normalizeName($name) : null; } /** - * Builds a callable capable of instantiating the given $className without - * invoking its constructor. - * - * @throws InvalidArgumentException - * @throws UnexpectedValueException - * @throws ReflectionException + * Adds a statement. * - * @template T of object - * @phpstan-param class-string $className + * @param Node|PhpParser\Builder $stmt The statement to add * - * @phpstan-return callable(): T + * @return $this The builder instance (for fluid interface) */ - private function buildFactory(string $className) : callable + public function addStmt($stmt) { - $reflectionClass = $this->getReflectionClass($className); - if ($this->isInstantiableViaReflection($reflectionClass)) { - return [$reflectionClass, 'newInstanceWithoutConstructor']; - } - $serializedString = \sprintf('%s:%d:"%s":0:{}', \is_subclass_of($className, \Serializable::class) ? self::SERIALIZATION_FORMAT_USE_UNSERIALIZER : self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER, \strlen($className), $className); - $this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString); - return static function () use($serializedString) { - return \unserialize($serializedString); - }; + $this->stmts[] = BuilderHelpers::normalizeStmt($stmt); + return $this; } /** - * @throws InvalidArgumentException - * @throws ReflectionException - * - * @template T of object - * @phpstan-param class-string $className + * Returns the built node. * - * @phpstan-return ReflectionClass + * @return Stmt\Namespace_ The built node */ - private function getReflectionClass(string $className) : \ReflectionClass + public function getNode() : Node { - if (!\class_exists($className)) { - throw \PHPUnit\Doctrine\Instantiator\Exception\InvalidArgumentException::fromNonExistingClass($className); - } - $reflection = new \ReflectionClass($className); - if ($reflection->isAbstract()) { - throw \PHPUnit\Doctrine\Instantiator\Exception\InvalidArgumentException::fromAbstractClass($reflection); - } - return $reflection; + return new Stmt\Namespace_($this->name, $this->stmts, $this->attributes); } +} + $reflectionClass + * @param string|Identifier $name Name + * @param Node\Expr|bool|null|int|float|string|array $value Value */ - private function checkIfUnSerializationIsSupported(\ReflectionClass $reflectionClass, string $serializedString) : void + public function __construct($name, $value) { - \set_error_handler(static function (int $code, string $message, string $file, int $line) use($reflectionClass, &$error) : bool { - $error = \PHPUnit\Doctrine\Instantiator\Exception\UnexpectedValueException::fromUncleanUnSerialization($reflectionClass, $message, $code, $file, $line); - return \true; - }); - try { - $this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString); - } finally { - \restore_error_handler(); - } - if ($error) { - throw $error; - } + $this->constants = [new Const_($name, BuilderHelpers::normalizeValue($value))]; } /** - * @throws UnexpectedValueException + * Add another constant to const group * - * @template T of object - * @phpstan-param ReflectionClass $reflectionClass + * @param string|Identifier $name Name + * @param Node\Expr|bool|null|int|float|string|array $value Value + * + * @return $this The builder instance (for fluid interface) */ - private function attemptInstantiationViaUnSerialization(\ReflectionClass $reflectionClass, string $serializedString) : void + public function addConst($name, $value) { - try { - \unserialize($serializedString); - } catch (\Exception $exception) { - throw \PHPUnit\Doctrine\Instantiator\Exception\UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $exception); - } + $this->constants[] = new Const_($name, BuilderHelpers::normalizeValue($value)); + return $this; } /** - * @template T of object - * @phpstan-param ReflectionClass $reflectionClass + * Makes the constant public. + * + * @return $this The builder instance (for fluid interface) */ - private function isInstantiableViaReflection(\ReflectionClass $reflectionClass) : bool + public function makePublic() { - return !($this->hasInternalAncestors($reflectionClass) && $reflectionClass->isFinal()); + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC); + return $this; } /** - * Verifies whether the given class is to be considered internal + * Makes the constant protected. * - * @template T of object - * @phpstan-param ReflectionClass $reflectionClass + * @return $this The builder instance (for fluid interface) */ - private function hasInternalAncestors(\ReflectionClass $reflectionClass) : bool + public function makeProtected() { - do { - if ($reflectionClass->isInternal()) { - return \true; - } - $reflectionClass = $reflectionClass->getParentClass(); - } while ($reflectionClass); - return \false; + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED); + return $this; } /** - * Checks if a class is cloneable - * - * Classes implementing `__clone` cannot be safely cloned, as that may cause side-effects. + * Makes the constant private. * - * @template T of object - * @phpstan-param ReflectionClass $reflectionClass + * @return $this The builder instance (for fluid interface) */ - private function isSafeToClone(\ReflectionClass $reflectionClass) : bool + public function makePrivate() { - return $reflectionClass->isCloneable() && !$reflectionClass->hasMethod('__clone') && !$reflectionClass->isSubclassOf(\ArrayIterator::class); - } -} -flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE); + return $this; } /** - * @template T of object - * @phpstan-param ReflectionClass $reflectionClass + * Makes the constant final. + * + * @return $this The builder instance (for fluid interface) */ - public static function fromAbstractClass(\ReflectionClass $reflectionClass) : self + public function makeFinal() { - return new self(\sprintf('The provided class "%s" is abstract, and can not be instantiated', $reflectionClass->getName())); + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL); + return $this; } -} - $reflectionClass + * Sets doc comment for the constant. + * + * @param PhpParser\Comment\Doc|string $docComment Doc comment to set + * + * @return $this The builder instance (for fluid interface) */ - public static function fromSerializationTriggeredException(\ReflectionClass $reflectionClass, \Exception $exception) : self + public function setDocComment($docComment) { - return new self(\sprintf('An exception was raised while trying to instantiate an instance of "%s" via un-serialization', $reflectionClass->getName()), 0, $exception); + $this->attributes = ['comments' => [BuilderHelpers::normalizeDocComment($docComment)]]; + return $this; } /** - * @template T of object - * @phpstan-param ReflectionClass $reflectionClass + * Adds an attribute group. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) */ - public static function fromUncleanUnSerialization(\ReflectionClass $reflectionClass, string $errorString, int $errorCode, string $errorFile, int $errorLine) : self + public function addAttribute($attribute) { - return new self(\sprintf('Could not produce an instance of "%s" via un-serialization, since an error was triggered ' . 'in file "%s" at line "%d"', $reflectionClass->getName(), $errorFile, $errorLine), 0, new \Exception($errorString, $errorCode)); + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + return $this; } -} - $className + * @return Stmt\ClassConst The built constant node */ - public function instantiate($className); + public function getNode() : PhpParser\Node + { + return new Stmt\ClassConst($this->constants, $this->flags, $this->attributes, $this->attributeGroups); + } } -Copyright (c) 2014 Doctrine Project - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. summary = $summary; - $this->description = $description ?: new \PHPUnit\phpDocumentor\Reflection\DocBlock\Description(''); - foreach ($tags as $tag) { - $this->addTag($tag); - } - $this->context = $context; - $this->location = $location; - $this->isTemplateEnd = $isTemplateEnd; - $this->isTemplateStart = $isTemplateStart; - } - public function getSummary() : string + public function __construct(string $name) { - return $this->summary; + $this->name = $name; } - public function getDescription() : \PHPUnit\phpDocumentor\Reflection\DocBlock\Description + /** + * Makes the property public. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePublic() { - return $this->description; + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC); + return $this; } /** - * Returns the current context. + * Makes the property protected. + * + * @return $this The builder instance (for fluid interface) */ - public function getContext() : ?\PHPUnit\phpDocumentor\Reflection\Types\Context + public function makeProtected() { - return $this->context; + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED); + return $this; } /** - * Returns the current location. + * Makes the property private. + * + * @return $this The builder instance (for fluid interface) */ - public function getLocation() : ?\PHPUnit\phpDocumentor\Reflection\Location + public function makePrivate() { - return $this->location; + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE); + return $this; } /** - * Returns whether this DocBlock is the start of a Template section. - * - * A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker - * (`#@+`) that is appended directly after the opening `/**` of a DocBlock. - * - * An example of such an opening is: - * - * ``` - * /**#@+ - * * My DocBlock - * * / - * ``` - * - * The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all - * elements that follow until another DocBlock is found that contains the closing marker (`#@-`). + * Makes the property static. * - * @see self::isTemplateEnd() for the check whether a closing marker was provided. + * @return $this The builder instance (for fluid interface) */ - public function isTemplateStart() : bool + public function makeStatic() { - return $this->isTemplateStart; + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC); + return $this; } /** - * Returns whether this DocBlock is the end of a Template section. + * Makes the property readonly. * - * @see self::isTemplateStart() for a more complete description of the Docblock Template functionality. + * @return $this The builder instance (for fluid interface) */ - public function isTemplateEnd() : bool + public function makeReadonly() { - return $this->isTemplateEnd; + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_READONLY); + return $this; } /** - * Returns the tags for this DocBlock. + * Sets default value for the property. * - * @return Tag[] + * @param mixed $value Default value to use + * + * @return $this The builder instance (for fluid interface) */ - public function getTags() : array + public function setDefault($value) { - return $this->tags; + $this->default = BuilderHelpers::normalizeValue($value); + return $this; } /** - * Returns an array of tags matching the given name. If no tags are found - * an empty array is returned. + * Sets doc comment for the property. * - * @param string $name String to search by. + * @param PhpParser\Comment\Doc|string $docComment Doc comment to set * - * @return Tag[] + * @return $this The builder instance (for fluid interface) */ - public function getTagsByName(string $name) : array + public function setDocComment($docComment) { - $result = []; - foreach ($this->getTags() as $tag) { - if ($tag->getName() !== $name) { - continue; - } - $result[] = $tag; - } - return $result; + $this->attributes = ['comments' => [BuilderHelpers::normalizeDocComment($docComment)]]; + return $this; } /** - * Checks if a tag of a certain type is present in this DocBlock. + * Sets the property type for PHP 7.4+. * - * @param string $name Tag name to check for. + * @param string|Name|Identifier|ComplexType $type + * + * @return $this */ - public function hasTag(string $name) : bool + public function setType($type) { - foreach ($this->getTags() as $tag) { - if ($tag->getName() === $name) { - return \true; - } - } - return \false; + $this->type = BuilderHelpers::normalizeType($type); + return $this; } /** - * Remove a tag from this DocBlock. + * Adds an attribute group. * - * @param Tag $tagToRemove The tag to remove. + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) */ - public function removeTag(\PHPUnit\phpDocumentor\Reflection\DocBlock\Tag $tagToRemove) : void + public function addAttribute($attribute) { - foreach ($this->tags as $key => $tag) { - if ($tag === $tagToRemove) { - unset($this->tags[$key]); - break; - } - } + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + return $this; } /** - * Adds a tag to this DocBlock. + * Returns the built class node. * - * @param Tag $tag The tag to add. + * @return Stmt\Property The built property node */ - private function addTag(\PHPUnit\phpDocumentor\Reflection\DocBlock\Tag $tag) : void + public function getNode() : PhpParser\Node { - $this->tags[] = $tag; + return new Stmt\Property($this->flags !== 0 ? $this->flags : Stmt\Class_::MODIFIER_PUBLIC, [new Stmt\PropertyProperty($this->name, $this->default)], $this->attributes, $this->type, $this->attributeGroups); } } create('This is a {@see Description}', $context); - * - * The description factory will interpret the given body and create a body template and list of tags from them, and pass - * that onto the constructor if this class. - * - * > The $context variable is a class of type {@see \phpDocumentor\Reflection\Types\Context} and contains the namespace - * > and the namespace aliases that apply to this DocBlock. These are used by the Factory to resolve and expand partial - * > type names and FQSENs. - * - * If you do not want to use the DescriptionFactory you can pass a body template and tag listing like this: - * - * $description = new Description( - * 'This is a %1$s', - * [ new See(new Fqsen('\phpDocumentor\Reflection\DocBlock\Description')) ] - * ); - * - * It is generally recommended to use the Factory as that will also apply escaping rules, while the Description object - * is mainly responsible for rendering. - * - * @see DescriptionFactory to create a new Description. - * @see Description\Formatter for the formatting of the body and tags. - */ -class Description +use PHPUnit\PhpParser\Builder; +use PHPUnit\PhpParser\BuilderHelpers; +use PHPUnit\PhpParser\Node; +use PHPUnit\PhpParser\Node\Stmt; +class TraitUseAdaptation implements Builder { - /** @var string */ - private $bodyTemplate; - /** @var Tag[] */ - private $tags; + const TYPE_UNDEFINED = 0; + const TYPE_ALIAS = 1; + const TYPE_PRECEDENCE = 2; + /** @var int Type of building adaptation */ + protected $type; + protected $trait; + protected $method; + protected $modifier = null; + protected $alias = null; + protected $insteadof = []; /** - * Initializes a Description with its body (template) and a listing of the tags used in the body template. + * Creates a trait use adaptation builder. * - * @param Tag[] $tags + * @param Node\Name|string|null $trait Name of adaptated trait + * @param Node\Identifier|string $method Name of adaptated method */ - public function __construct(string $bodyTemplate, array $tags = []) + public function __construct($trait, $method) { - $this->bodyTemplate = $bodyTemplate; - $this->tags = $tags; + $this->type = self::TYPE_UNDEFINED; + $this->trait = \is_null($trait) ? null : BuilderHelpers::normalizeName($trait); + $this->method = BuilderHelpers::normalizeIdentifier($method); } /** - * Returns the body template. + * Sets alias of method. + * + * @param Node\Identifier|string $alias Alias for adaptated method + * + * @return $this The builder instance (for fluid interface) */ - public function getBodyTemplate() : string + public function as($alias) { - return $this->bodyTemplate; + if ($this->type === self::TYPE_UNDEFINED) { + $this->type = self::TYPE_ALIAS; + } + if ($this->type !== self::TYPE_ALIAS) { + throw new \LogicException('Cannot set alias for not alias adaptation buider'); + } + $this->alias = $alias; + return $this; } /** - * Returns the tags for this DocBlock. + * Sets adaptated method public. * - * @return Tag[] + * @return $this The builder instance (for fluid interface) */ - public function getTags() : array + public function makePublic() { - return $this->tags; + $this->setModifier(Stmt\Class_::MODIFIER_PUBLIC); + return $this; } /** - * Renders this description as a string where the provided formatter will format the tags in the expected string - * format. + * Sets adaptated method protected. + * + * @return $this The builder instance (for fluid interface) */ - public function render(?\PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter $formatter = null) : string + public function makeProtected() { - if ($formatter === null) { - $formatter = new \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter(); - } - $tags = []; - foreach ($this->tags as $tag) { - $tags[] = '{' . $formatter->format($tag) . '}'; - } - return \vsprintf($this->bodyTemplate, $tags); + $this->setModifier(Stmt\Class_::MODIFIER_PROTECTED); + return $this; } /** - * Returns a plain string representation of this description. + * Sets adaptated method private. + * + * @return $this The builder instance (for fluid interface) */ - public function __toString() : string + public function makePrivate() { - return $this->render(); + $this->setModifier(Stmt\Class_::MODIFIER_PRIVATE); + return $this; } -} -uri = $uri; + if ($this->type === self::TYPE_UNDEFINED) { + if (\is_null($this->trait)) { + throw new \LogicException('Precedence adaptation must have trait'); + } + $this->type = self::TYPE_PRECEDENCE; + } + if ($this->type !== self::TYPE_PRECEDENCE) { + throw new \LogicException('Cannot add overwritten traits for not precedence adaptation buider'); + } + foreach ($traits as $trait) { + $this->insteadof[] = BuilderHelpers::normalizeName($trait); + } + return $this; } - public function __toString() : string + protected function setModifier(int $modifier) { - return $this->uri; + if ($this->type === self::TYPE_UNDEFINED) { + $this->type = self::TYPE_ALIAS; + } + if ($this->type !== self::TYPE_ALIAS) { + throw new \LogicException('Cannot set access modifier for not alias adaptation buider'); + } + if (\is_null($this->modifier)) { + $this->modifier = $modifier; + } else { + throw new \LogicException('Multiple access type modifiers are not allowed'); + } + } + /** + * Returns the built node. + * + * @return Node The built node + */ + public function getNode() : Node + { + switch ($this->type) { + case self::TYPE_ALIAS: + return new Stmt\TraitUseAdaptation\Alias($this->trait, $this->method, $this->modifier, $this->alias); + case self::TYPE_PRECEDENCE: + return new Stmt\TraitUseAdaptation\Precedence($this->trait, $this->method, $this->insteadof); + default: + throw new \LogicException('Type of adaptation is not defined'); + } } } fqsen = $fqsen; - } + protected $rawMessage; + protected $attributes; /** - * @return string string representation of the referenced fqsen + * Creates an Exception signifying a parse error. + * + * @param string $message Error message + * @param array|int $attributes Attributes of node/token where error occurred + * (or start line of error -- deprecated) */ - public function __toString() : string + public function __construct(string $message, $attributes = []) { - return (string) $this->fqsen; - } -} -name = 'property-read'; - $this->variableName = $variableName; - $this->type = $type; - $this->description = $description; - } - public static function create(string $body, ?\PHPUnit\phpDocumentor\Reflection\TypeResolver $typeResolver = null, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory $descriptionFactory = null, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : self - { - \PHPUnit\Webmozart\Assert\Assert::stringNotEmpty($body); - \PHPUnit\Webmozart\Assert\Assert::notNull($typeResolver); - \PHPUnit\Webmozart\Assert\Assert::notNull($descriptionFactory); - [$firstPart, $body] = self::extractTypeFromBody($body); - $type = null; - $parts = \PHPUnit\phpDocumentor\Reflection\Utils::pregSplit('/(\\s+)/Su', $body, 2, \PREG_SPLIT_DELIM_CAPTURE); - $variableName = ''; - // if the first item that is encountered is not a variable; it is a type - if ($firstPart && $firstPart[0] !== '$') { - $type = $typeResolver->resolve($firstPart, $context); + $this->rawMessage = $message; + if (\is_array($attributes)) { + $this->attributes = $attributes; } else { - // first part is not a type; we should prepend it to the parts array for further processing - \array_unshift($parts, $firstPart); - } - // if the next item starts with a $ it must be the variable name - if (isset($parts[0]) && \strpos($parts[0], '$') === 0) { - $variableName = \array_shift($parts); - if ($type) { - \array_shift($parts); - } - \PHPUnit\Webmozart\Assert\Assert::notNull($variableName); - $variableName = \substr($variableName, 1); + $this->attributes = ['startLine' => $attributes]; } - $description = $descriptionFactory->create(\implode('', $parts), $context); - return new static($variableName, $type, $description); + $this->updateMessage(); } /** - * Returns the variable's name. + * Gets the error message + * + * @return string Error message */ - public function getVariableName() : ?string + public function getRawMessage() : string { - return $this->variableName; + return $this->rawMessage; } /** - * Returns a string representation for this tag. + * Gets the line the error starts in. + * + * @return int Error start line */ - public function __toString() : string - { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - if ($this->variableName) { - $variableName = '$' . $this->variableName; - } else { - $variableName = ''; - } - $type = (string) $this->type; - return $type . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); - } -} -name = 'return'; - $this->type = $type; - $this->description = $description; - } - public static function create(string $body, ?\PHPUnit\phpDocumentor\Reflection\TypeResolver $typeResolver = null, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory $descriptionFactory = null, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : self - { - \PHPUnit\Webmozart\Assert\Assert::notNull($typeResolver); - \PHPUnit\Webmozart\Assert\Assert::notNull($descriptionFactory); - [$type, $description] = self::extractTypeFromBody($body); - $type = $typeResolver->resolve($type, $context); - $description = $descriptionFactory->create($description, $context); - return new static($type, $description); - } - public function __toString() : string - { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $type = $this->type ? '' . $this->type : 'mixed'; - return $type . ($description !== '' ? ($type !== '' ? ' ' : '') . $description : ''); - } -} -name = $name; - $this->body = $body; + return $this->attributes['startLine'] ?? -1; } - public function getException() : ?\Throwable + /** + * Gets the line the error ends in. + * + * @return int Error end line + */ + public function getEndLine() : int { - return $this->throwable; + return $this->attributes['endLine'] ?? -1; } - public function getName() : string + /** + * Gets the attributes of the node/token the error occurred at. + * + * @return array + */ + public function getAttributes() : array { - return $this->name; + return $this->attributes; } - public static function create(string $body, string $name = '') : self + /** + * Sets the attributes of the node/token the error occurred at. + * + * @param array $attributes + */ + public function setAttributes(array $attributes) { - return new self($name, $body); + $this->attributes = $attributes; + $this->updateMessage(); } - public function withError(\Throwable $exception) : self + /** + * Sets the line of the PHP file the error occurred in. + * + * @param string $message Error message + */ + public function setRawMessage(string $message) { - $this->flattenExceptionBacktrace($exception); - $tag = new self($this->name, $this->body); - $tag->throwable = $exception; - return $tag; + $this->rawMessage = $message; + $this->updateMessage(); } /** - * Removes all complex types from backtrace + * Sets the line the error starts in. * - * Not all objects are serializable. So we need to remove them from the - * stored exception to be sure that we do not break existing library usage. + * @param int $line Error start line */ - private function flattenExceptionBacktrace(\Throwable $exception) : void + public function setStartLine(int $line) { - $traceProperty = (new \ReflectionClass(\Exception::class))->getProperty('trace'); - $traceProperty->setAccessible(\true); - do { - $trace = $exception->getTrace(); - if (isset($trace[0]['args'])) { - $trace = \array_map(function (array $call) : array { - $call['args'] = \array_map([$this, 'flattenArguments'], $call['args']); - return $call; - }, $trace); - } - $traceProperty->setValue($exception, $trace); - $exception = $exception->getPrevious(); - } while ($exception !== null); - $traceProperty->setAccessible(\false); + $this->attributes['startLine'] = $line; + $this->updateMessage(); } /** - * @param mixed $value + * Returns whether the error has start and end column information. * - * @return mixed + * For column information enable the startFilePos and endFilePos in the lexer options. * - * @throws ReflectionException + * @return bool */ - private function flattenArguments($value) + public function hasColumnInfo() : bool { - if ($value instanceof \Closure) { - $closureReflection = new \ReflectionFunction($value); - $value = \sprintf('(Closure at %s:%s)', $closureReflection->getFileName(), $closureReflection->getStartLine()); - } elseif (\is_object($value)) { - $value = \sprintf('object(%s)', \get_class($value)); - } elseif (\is_resource($value)) { - $value = \sprintf('resource(%s)', \get_resource_type($value)); - } elseif (\is_array($value)) { - $value = \array_map([$this, 'flattenArguments'], $value); - } - return $value; + return isset($this->attributes['startFilePos'], $this->attributes['endFilePos']); } - public function render(?\PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter $formatter = null) : string + /** + * Gets the start column (1-based) into the line where the error started. + * + * @param string $code Source code of the file + * @return int + */ + public function getStartColumn(string $code) : int { - if ($formatter === null) { - $formatter = new \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter(); + if (!$this->hasColumnInfo()) { + throw new \RuntimeException('Error does not have column information'); } - return $formatter->format($this); - } - public function __toString() : string - { - return $this->body; + return $this->toColumn($code, $this->attributes['startFilePos']); } -} -version = $version; - $this->description = $description; - } - public static function create(?string $body, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory $descriptionFactory = null, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : ?self + public function getEndColumn(string $code) : int { - if (empty($body)) { - return new static(); - } - $matches = []; - if (!\preg_match('/^(' . self::REGEX_VECTOR . ')\\s*(.+)?$/sux', $body, $matches)) { - return null; - } - $description = null; - if ($descriptionFactory !== null) { - $description = $descriptionFactory->create($matches[2] ?? '', $context); + if (!$this->hasColumnInfo()) { + throw new \RuntimeException('Error does not have column information'); } - return new static($matches[1], $description); + return $this->toColumn($code, $this->attributes['endFilePos']); } /** - * Gets the version section of the tag. + * Formats message including line and column information. + * + * @param string $code Source code associated with the error, for calculation of the columns + * + * @return string Formatted message */ - public function getVersion() : ?string + public function getMessageWithColumnInfo(string $code) : string { - return $this->version; + return \sprintf('%s from %d:%d to %d:%d', $this->getRawMessage(), $this->getStartLine(), $this->getStartColumn($code), $this->getEndLine(), $this->getEndColumn($code)); } /** - * Returns a string representation for this tag. + * Converts a file offset into a column. + * + * @param string $code Source code that $pos indexes into + * @param int $pos 0-based position in $code + * + * @return int 1-based column (relative to start of line) */ - public function __toString() : string + private function toColumn(string $code, int $pos) : int { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; + if ($pos > \strlen($code)) { + throw new \RuntimeException('Invalid position information'); } - $version = (string) $this->version; - return $version . ($description !== '' ? ($version !== '' ? ' ' : '') . $description : ''); + $lineStartPos = \strrpos($code, "\n", $pos - \strlen($code)); + if (\false === $lineStartPos) { + $lineStartPos = -1; + } + return $pos - $lineStartPos; } -} -getName() . ' ' . $tag); + $this->message = $this->rawMessage; + if (-1 === $this->getStartLine()) { + $this->message .= ' on unknown line'; + } else { + $this->message .= ' on line ' . $this->getStartLine(); + } } } maxLen = \max($this->maxLen, \strlen($tag->getName())); - } + $this->nameContext = new NameContext($errorHandler ?? new ErrorHandler\Throwing()); + $this->preserveOriginalNames = $options['preserveOriginalNames'] ?? \false; + $this->replaceNodes = $options['replaceNodes'] ?? \true; } /** - * Formats the given tag to return a simple plain text version. + * Get name resolution context. + * + * @return NameContext */ - public function format(\PHPUnit\phpDocumentor\Reflection\DocBlock\Tag $tag) : string + public function getNameContext() : NameContext { - return '@' . $tag->getName() . \str_repeat(' ', $this->maxLen - \strlen($tag->getName()) + 1) . $tag; + return $this->nameContext; } -} -name = 'property'; - $this->variableName = $variableName; - $this->type = $type; - $this->description = $description; + $this->nameContext->startNamespace(); + return null; } - public static function create(string $body, ?\PHPUnit\phpDocumentor\Reflection\TypeResolver $typeResolver = null, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory $descriptionFactory = null, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : self + public function enterNode(Node $node) { - \PHPUnit\Webmozart\Assert\Assert::stringNotEmpty($body); - \PHPUnit\Webmozart\Assert\Assert::notNull($typeResolver); - \PHPUnit\Webmozart\Assert\Assert::notNull($descriptionFactory); - [$firstPart, $body] = self::extractTypeFromBody($body); - $type = null; - $parts = \PHPUnit\phpDocumentor\Reflection\Utils::pregSplit('/(\\s+)/Su', $body, 2, \PREG_SPLIT_DELIM_CAPTURE); - $variableName = ''; - // if the first item that is encountered is not a variable; it is a type - if ($firstPart && $firstPart[0] !== '$') { - $type = $typeResolver->resolve($firstPart, $context); - } else { - // first part is not a type; we should prepend it to the parts array for further processing - \array_unshift($parts, $firstPart); - } - // if the next item starts with a $ it must be the variable name - if (isset($parts[0]) && \strpos($parts[0], '$') === 0) { - $variableName = \array_shift($parts); - if ($type) { - \array_shift($parts); + if ($node instanceof Stmt\Namespace_) { + $this->nameContext->startNamespace($node->name); + } elseif ($node instanceof Stmt\Use_) { + foreach ($node->uses as $use) { + $this->addAlias($use, $node->type, null); } - \PHPUnit\Webmozart\Assert\Assert::notNull($variableName); - $variableName = \substr($variableName, 1); - } - $description = $descriptionFactory->create(\implode('', $parts), $context); - return new static($variableName, $type, $description); - } - /** - * Returns the variable's name. - */ - public function getVariableName() : ?string - { - return $this->variableName; - } - /** - * Returns a string representation for this tag. - */ - public function __toString() : string - { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - if ($this->variableName) { - $variableName = '$' . $this->variableName; - } else { - $variableName = ''; - } - $type = (string) $this->type; - return $type . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); + } elseif ($node instanceof Stmt\GroupUse) { + foreach ($node->uses as $use) { + $this->addAlias($use, $node->type, $node->prefix); + } + } elseif ($node instanceof Stmt\Class_) { + if (null !== $node->extends) { + $node->extends = $this->resolveClassName($node->extends); + } + foreach ($node->implements as &$interface) { + $interface = $this->resolveClassName($interface); + } + $this->resolveAttrGroups($node); + if (null !== $node->name) { + $this->addNamespacedName($node); + } + } elseif ($node instanceof Stmt\Interface_) { + foreach ($node->extends as &$interface) { + $interface = $this->resolveClassName($interface); + } + $this->resolveAttrGroups($node); + $this->addNamespacedName($node); + } elseif ($node instanceof Stmt\Enum_) { + foreach ($node->implements as &$interface) { + $interface = $this->resolveClassName($interface); + } + $this->resolveAttrGroups($node); + if (null !== $node->name) { + $this->addNamespacedName($node); + } + } elseif ($node instanceof Stmt\Trait_) { + $this->resolveAttrGroups($node); + $this->addNamespacedName($node); + } elseif ($node instanceof Stmt\Function_) { + $this->resolveSignature($node); + $this->resolveAttrGroups($node); + $this->addNamespacedName($node); + } elseif ($node instanceof Stmt\ClassMethod || $node instanceof Expr\Closure || $node instanceof Expr\ArrowFunction) { + $this->resolveSignature($node); + $this->resolveAttrGroups($node); + } elseif ($node instanceof Stmt\Property) { + if (null !== $node->type) { + $node->type = $this->resolveType($node->type); + } + $this->resolveAttrGroups($node); + } elseif ($node instanceof Stmt\Const_) { + foreach ($node->consts as $const) { + $this->addNamespacedName($const); + } + } else { + if ($node instanceof Stmt\ClassConst) { + $this->resolveAttrGroups($node); + } else { + if ($node instanceof Stmt\EnumCase) { + $this->resolveAttrGroups($node); + } elseif ($node instanceof Expr\StaticCall || $node instanceof Expr\StaticPropertyFetch || $node instanceof Expr\ClassConstFetch || $node instanceof Expr\New_ || $node instanceof Expr\Instanceof_) { + if ($node->class instanceof Name) { + $node->class = $this->resolveClassName($node->class); + } + } elseif ($node instanceof Stmt\Catch_) { + foreach ($node->types as &$type) { + $type = $this->resolveClassName($type); + } + } elseif ($node instanceof Expr\FuncCall) { + if ($node->name instanceof Name) { + $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_FUNCTION); + } + } elseif ($node instanceof Expr\ConstFetch) { + $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_CONSTANT); + } elseif ($node instanceof Stmt\TraitUse) { + foreach ($node->traits as &$trait) { + $trait = $this->resolveClassName($trait); + } + foreach ($node->adaptations as $adaptation) { + if (null !== $adaptation->trait) { + $adaptation->trait = $this->resolveClassName($adaptation->trait); + } + if ($adaptation instanceof Stmt\TraitUseAdaptation\Precedence) { + foreach ($adaptation->insteadof as &$insteadof) { + $insteadof = $this->resolveClassName($insteadof); + } + } + } + } + } + } + return null; } -} -startingLine = (int) $startingLine; - $this->lineCount = $lineCount !== null ? (int) $lineCount : null; - $this->description = $description; + // Add prefix for group uses + $name = $prefix ? Name::concat($prefix, $use->name) : $use->name; + // Type is determined either by individual element or whole use declaration + $type |= $use->type; + $this->nameContext->addAlias($name, (string) $use->getAlias(), $type, $use->getAttributes()); + } + /** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure $node */ + private function resolveSignature($node) + { + foreach ($node->params as $param) { + $param->type = $this->resolveType($param->type); + $this->resolveAttrGroups($param); + } + $node->returnType = $this->resolveType($node->returnType); } - public static function create(string $body, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory $descriptionFactory = null, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : self + private function resolveType($node) { - \PHPUnit\Webmozart\Assert\Assert::stringNotEmpty($body); - \PHPUnit\Webmozart\Assert\Assert::notNull($descriptionFactory); - $startingLine = 1; - $lineCount = null; - $description = null; - // Starting line / Number of lines / Description - if (\preg_match('/^([1-9]\\d*)\\s*(?:((?1))\\s+)?(.*)$/sux', $body, $matches)) { - $startingLine = (int) $matches[1]; - if (isset($matches[2]) && $matches[2] !== '') { - $lineCount = (int) $matches[2]; + if ($node instanceof Name) { + return $this->resolveClassName($node); + } + if ($node instanceof Node\NullableType) { + $node->type = $this->resolveType($node->type); + return $node; + } + if ($node instanceof Node\UnionType || $node instanceof Node\IntersectionType) { + foreach ($node->types as &$type) { + $type = $this->resolveType($type); } - $description = $matches[3]; + return $node; } - return new static($startingLine, $lineCount, $descriptionFactory->create($description ?? '', $context)); + return $node; } /** - * Gets the starting line. + * Resolve name, according to name resolver options. * - * @return int The starting line, relative to the structural element's - * location. + * @param Name $name Function or constant name to resolve + * @param int $type One of Stmt\Use_::TYPE_* + * + * @return Name Resolved name, or original name with attribute */ - public function getStartingLine() : int + protected function resolveName(Name $name, int $type) : Name { - return $this->startingLine; + if (!$this->replaceNodes) { + $resolvedName = $this->nameContext->getResolvedName($name, $type); + if (null !== $resolvedName) { + $name->setAttribute('resolvedName', $resolvedName); + } else { + $name->setAttribute('namespacedName', FullyQualified::concat($this->nameContext->getNamespace(), $name, $name->getAttributes())); + } + return $name; + } + if ($this->preserveOriginalNames) { + // Save the original name + $originalName = $name; + $name = clone $originalName; + $name->setAttribute('originalName', $originalName); + } + $resolvedName = $this->nameContext->getResolvedName($name, $type); + if (null !== $resolvedName) { + return $resolvedName; + } + // unqualified names inside a namespace cannot be resolved at compile-time + // add the namespaced version of the name as an attribute + $name->setAttribute('namespacedName', FullyQualified::concat($this->nameContext->getNamespace(), $name, $name->getAttributes())); + return $name; } - /** - * Returns the number of lines. - * - * @return int|null The number of lines, relative to the starting line. NULL - * means "to the end". - */ - public function getLineCount() : ?int + protected function resolveClassName(Name $name) { - return $this->lineCount; + return $this->resolveName($name, Stmt\Use_::TYPE_NORMAL); } - public function __toString() : string + protected function addNamespacedName(Node $node) { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; + $node->namespacedName = Name::concat($this->nameContext->getNamespace(), (string) $node->name); + } + protected function resolveAttrGroups(Node $node) + { + foreach ($node->attrGroups as $attrGroup) { + foreach ($attrGroup->attrs as $attr) { + $attr->name = $this->resolveClassName($attr->name); + } } - $startingLine = (string) $this->startingLine; - $lineCount = $this->lineCount !== null ? '' . $this->lineCount : ''; - return $startingLine . ($lineCount !== '' ? ($startingLine || $startingLine === '0' ? ' ' : '') . $lineCount : '') . ($description !== '' ? ($startingLine || $startingLine === '0' || $lineCount !== '' ? ' ' : '') . $description : ''); } } $node->getAttribute('parent'). */ -final class Author extends \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\BaseTag implements \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod +final class ParentConnectingVisitor extends NodeVisitorAbstract { - /** @var string register that this is the author tag. */ - protected $name = 'author'; - /** @var string The name of the author */ - private $authorName; - /** @var string The email of the author */ - private $authorEmail; - /** - * Initializes this tag with the author name and e-mail. - */ - public function __construct(string $authorName, string $authorEmail) - { - if ($authorEmail && !\filter_var($authorEmail, \FILTER_VALIDATE_EMAIL)) { - throw new \InvalidArgumentException('The author tag does not have a valid e-mail address'); - } - $this->authorName = $authorName; - $this->authorEmail = $authorEmail; - } - /** - * Gets the author's name. - * - * @return string The author's name. - */ - public function getAuthorName() : string - { - return $this->authorName; - } /** - * Returns the author's email. - * - * @return string The author's email. + * @var Node[] */ - public function getEmail() : string + private $stack = []; + public function beforeTraverse(array $nodes) { - return $this->authorEmail; + $this->stack = []; } - /** - * Returns this tag in string form. - */ - public function __toString() : string + public function enterNode(Node $node) { - if ($this->authorEmail) { - $authorEmail = '<' . $this->authorEmail . '>'; - } else { - $authorEmail = ''; + if (!empty($this->stack)) { + $node->setAttribute('parent', $this->stack[count($this->stack) - 1]); } - $authorName = (string) $this->authorName; - return $authorName . ($authorEmail !== '' ? ($authorName !== '' ? ' ' : '') . $authorEmail : ''); + $this->stack[] = $node; } - /** - * Attempts to create a new Author object based on †he tag body. - */ - public static function create(string $body) : ?self + public function leaveNode(Node $node) { - $splitTagContent = \preg_match('/^([^\\<]*)(?:\\<([^\\>]*)\\>)?$/u', $body, $matches); - if (!$splitTagContent) { - return null; - } - $authorName = \trim($matches[1]); - $email = isset($matches[2]) ? \trim($matches[2]) : ''; - return new static($authorName, $email); + array_pop($this->stack); } } setAttribute('origNode', $origNode); + return $node; + } +} +name = 'var'; - $this->variableName = $variableName; - $this->type = $type; - $this->description = $description; - } - public static function create(string $body, ?\PHPUnit\phpDocumentor\Reflection\TypeResolver $typeResolver = null, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory $descriptionFactory = null, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : self + /** @var callable Filter callback */ + protected $filterCallback; + /** @var Node[] Found nodes */ + protected $foundNodes; + public function __construct(callable $filterCallback) { - \PHPUnit\Webmozart\Assert\Assert::stringNotEmpty($body); - \PHPUnit\Webmozart\Assert\Assert::notNull($typeResolver); - \PHPUnit\Webmozart\Assert\Assert::notNull($descriptionFactory); - [$firstPart, $body] = self::extractTypeFromBody($body); - $parts = \PHPUnit\phpDocumentor\Reflection\Utils::pregSplit('/(\\s+)/Su', $body, 2, \PREG_SPLIT_DELIM_CAPTURE); - $type = null; - $variableName = ''; - // if the first item that is encountered is not a variable; it is a type - if ($firstPart && $firstPart[0] !== '$') { - $type = $typeResolver->resolve($firstPart, $context); - } else { - // first part is not a type; we should prepend it to the parts array for further processing - \array_unshift($parts, $firstPart); - } - // if the next item starts with a $ it must be the variable name - if (isset($parts[0]) && \strpos($parts[0], '$') === 0) { - $variableName = \array_shift($parts); - if ($type) { - \array_shift($parts); - } - \PHPUnit\Webmozart\Assert\Assert::notNull($variableName); - $variableName = \substr($variableName, 1); - } - $description = $descriptionFactory->create(\implode('', $parts), $context); - return new static($variableName, $type, $description); + $this->filterCallback = $filterCallback; } /** - * Returns the variable's name. + * Get found nodes satisfying the filter callback. + * + * Nodes are returned in pre-order. + * + * @return Node[] Found nodes */ - public function getVariableName() : ?string + public function getFoundNodes() : array { - return $this->variableName; + return $this->foundNodes; } - /** - * Returns a string representation for this tag. - */ - public function __toString() : string + public function beforeTraverse(array $nodes) { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - if ($this->variableName) { - $variableName = '$' . $this->variableName; - } else { - $variableName = ''; + $this->foundNodes = []; + return null; + } + public function enterNode(Node $node) + { + $filterCallback = $this->filterCallback; + if ($filterCallback($node)) { + $this->foundNodes[] = $node; } - $type = (string) $this->type; - return $type . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); + return null; } } filePath = $filePath; - $this->startingLine = $startingLine; - $this->lineCount = $lineCount; - if ($content !== null) { - $this->content = \trim($content); - } - $this->isURI = $isURI; - } - public function getContent() : string - { - if ($this->content === null || $this->content === '') { - $filePath = $this->filePath; - if ($this->isURI) { - $filePath = $this->isUriRelative($this->filePath) ? \str_replace('%2F', '/', \rawurlencode($this->filePath)) : $this->filePath; - } - return \trim($filePath); - } - return $this->content; - } - public function getDescription() : ?string - { - return $this->content; - } - public static function create(string $body) : ?\PHPUnit\phpDocumentor\Reflection\DocBlock\Tag + /** @var callable Filter callback */ + protected $filterCallback; + /** @var null|Node Found node */ + protected $foundNode; + public function __construct(callable $filterCallback) { - // File component: File path in quotes or File URI / Source information - if (!\preg_match('/^\\s*(?:(\\"[^\\"]+\\")|(\\S+))(?:\\s+(.*))?$/sux', $body, $matches)) { - return null; - } - $filePath = null; - $fileUri = null; - if ($matches[1] !== '') { - $filePath = $matches[1]; - } else { - $fileUri = $matches[2]; - } - $startingLine = 1; - $lineCount = 0; - $description = null; - if (\array_key_exists(3, $matches)) { - $description = $matches[3]; - // Starting line / Number of lines / Description - if (\preg_match('/^([1-9]\\d*)(?:\\s+((?1))\\s*)?(.*)$/sux', $matches[3], $contentMatches)) { - $startingLine = (int) $contentMatches[1]; - if (isset($contentMatches[2])) { - $lineCount = (int) $contentMatches[2]; - } - if (\array_key_exists(3, $contentMatches)) { - $description = $contentMatches[3]; - } - } - } - return new static($filePath ?? $fileUri ?? '', $fileUri !== null, $startingLine, $lineCount, $description); + $this->filterCallback = $filterCallback; } /** - * Returns the file path. + * Get found node satisfying the filter callback. * - * @return string Path to a file to use as an example. - * May also be an absolute URI. - */ - public function getFilePath() : string - { - return \trim($this->filePath, '"'); - } - /** - * Returns a string representation for this tag. - */ - public function __toString() : string - { - $filePath = (string) $this->filePath; - $isDefaultLine = $this->startingLine === 1 && $this->lineCount === 0; - $startingLine = !$isDefaultLine ? (string) $this->startingLine : ''; - $lineCount = !$isDefaultLine ? (string) $this->lineCount : ''; - $content = (string) $this->content; - return $filePath . ($startingLine !== '' ? ($filePath !== '' ? ' ' : '') . $startingLine : '') . ($lineCount !== '' ? ($filePath !== '' || $startingLine !== '' ? ' ' : '') . $lineCount : '') . ($content !== '' ? ($filePath !== '' || $startingLine !== '' || $lineCount !== '' ? ' ' : '') . $content : ''); - } - /** - * Returns true if the provided URI is relative or contains a complete scheme (and thus is absolute). + * Returns null if no node satisfies the filter callback. + * + * @return null|Node Found node (or null if not found) */ - private function isUriRelative(string $uri) : bool - { - return \strpos($uri, ':') === \false; - } - public function getStartingLine() : int - { - return $this->startingLine; - } - public function getLineCount() : int + public function getFoundNode() { - return $this->lineCount; + return $this->foundNode; } - public function getName() : string + public function beforeTraverse(array $nodes) { - return 'example'; + $this->foundNode = null; + return null; } - public function render(?\PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter $formatter = null) : string + public function enterNode(Node $node) { - if ($formatter === null) { - $formatter = new \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter(); + $filterCallback = $this->filterCallback; + if ($filterCallback($node)) { + $this->foundNode = $node; + return NodeTraverser::STOP_TRAVERSAL; } - return $formatter->format($this); + return null; } } $node->getAttribute('parent'), the previous + * node can be accessed through $node->getAttribute('previous'), + * and the next node can be accessed through $node->getAttribute('next'). */ -namespace PHPUnit\phpDocumentor\Reflection\DocBlock\Tags; - -use PHPUnit\phpDocumentor\Reflection\DocBlock\Description; -use PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use PHPUnit\phpDocumentor\Reflection\Type; -use PHPUnit\phpDocumentor\Reflection\TypeResolver; -use PHPUnit\phpDocumentor\Reflection\Types\Context as TypeContext; -use PHPUnit\phpDocumentor\Reflection\Utils; -use PHPUnit\Webmozart\Assert\Assert; -use function array_shift; -use function array_unshift; -use function implode; -use function strpos; -use function substr; -use const PREG_SPLIT_DELIM_CAPTURE; -/** - * Reflection class for a {@}property-write tag in a Docblock. - */ -final class PropertyWrite extends \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\TagWithType implements \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod +final class NodeConnectingVisitor extends NodeVisitorAbstract { - /** @var string */ - protected $variableName; - public function __construct(?string $variableName, ?\PHPUnit\phpDocumentor\Reflection\Type $type = null, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\Description $description = null) + /** + * @var Node[] + */ + private $stack = []; + /** + * @var ?Node + */ + private $previous; + public function beforeTraverse(array $nodes) { - \PHPUnit\Webmozart\Assert\Assert::string($variableName); - $this->name = 'property-write'; - $this->variableName = $variableName; - $this->type = $type; - $this->description = $description; + $this->stack = []; + $this->previous = null; } - public static function create(string $body, ?\PHPUnit\phpDocumentor\Reflection\TypeResolver $typeResolver = null, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory $descriptionFactory = null, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : self + public function enterNode(Node $node) { - \PHPUnit\Webmozart\Assert\Assert::stringNotEmpty($body); - \PHPUnit\Webmozart\Assert\Assert::notNull($typeResolver); - \PHPUnit\Webmozart\Assert\Assert::notNull($descriptionFactory); - [$firstPart, $body] = self::extractTypeFromBody($body); - $type = null; - $parts = \PHPUnit\phpDocumentor\Reflection\Utils::pregSplit('/(\\s+)/Su', $body, 2, \PREG_SPLIT_DELIM_CAPTURE); - $variableName = ''; - // if the first item that is encountered is not a variable; it is a type - if ($firstPart && $firstPart[0] !== '$') { - $type = $typeResolver->resolve($firstPart, $context); - } else { - // first part is not a type; we should prepend it to the parts array for further processing - \array_unshift($parts, $firstPart); + if (!empty($this->stack)) { + $node->setAttribute('parent', $this->stack[\count($this->stack) - 1]); } - // if the next item starts with a $ it must be the variable name - if (isset($parts[0]) && \strpos($parts[0], '$') === 0) { - $variableName = \array_shift($parts); - if ($type) { - \array_shift($parts); - } - \PHPUnit\Webmozart\Assert\Assert::notNull($variableName); - $variableName = \substr($variableName, 1); + if ($this->previous !== null && $this->previous->getAttribute('parent') === $node->getAttribute('parent')) { + $node->setAttribute('previous', $this->previous); + $this->previous->setAttribute('next', $node); } - $description = $descriptionFactory->create(\implode('', $parts), $context); - return new static($variableName, $type, $description); + $this->stack[] = $node; } - /** - * Returns the variable's name. - */ - public function getVariableName() : ?string + public function leaveNode(Node $node) { - return $this->variableName; + $this->previous = $node; + \array_pop($this->stack); } +} +description) { - $description = $this->description->render(); - } else { - $description = ''; + if (null === $lexer) { + $lexer = new Lexer\Emulative(); } - if ($this->variableName) { - $variableName = '$' . $this->variableName; - } else { - $variableName = ''; + switch ($kind) { + case self::PREFER_PHP7: + return new Parser\Multiple([new Parser\Php7($lexer, $parserOptions), new Parser\Php5($lexer, $parserOptions)]); + case self::PREFER_PHP5: + return new Parser\Multiple([new Parser\Php5($lexer, $parserOptions), new Parser\Php7($lexer, $parserOptions)]); + case self::ONLY_PHP7: + return new Parser\Php7($lexer, $parserOptions); + case self::ONLY_PHP5: + return new Parser\Php5($lexer, $parserOptions); + default: + throw new \LogicException('Kind must be one of ::PREFER_PHP7, ::PREFER_PHP5, ::ONLY_PHP7 or ::ONLY_PHP5'); } - $type = (string) $this->type; - return $type . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); } } version = $version; - $this->description = $description; + $this->fallbackEvaluator = $fallbackEvaluator ?? function (Expr $expr) { + throw new ConstExprEvaluationException("Expression of type {$expr->getType()} cannot be evaluated"); + }; } /** - * @return static + * Silently evaluates a constant expression into a PHP value. + * + * Thrown Errors, warnings or notices will be converted into a ConstExprEvaluationException. + * The original source of the exception is available through getPrevious(). + * + * If some part of the expression cannot be evaluated, the fallback evaluator passed to the + * constructor will be invoked. By default, if no fallback is provided, an exception of type + * ConstExprEvaluationException is thrown. + * + * See class doc comment for caveats and limitations. + * + * @param Expr $expr Constant expression to evaluate + * @return mixed Result of evaluation + * + * @throws ConstExprEvaluationException if the expression cannot be evaluated or an error occurred */ - public static function create(?string $body, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory $descriptionFactory = null, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : self + public function evaluateSilently(Expr $expr) { - if (empty($body)) { - return new static(); - } - $matches = []; - if (!\preg_match('/^(' . self::REGEX_VECTOR . ')\\s*(.+)?$/sux', $body, $matches)) { - return new static(null, $descriptionFactory !== null ? $descriptionFactory->create($body, $context) : null); + \set_error_handler(function ($num, $str, $file, $line) { + throw new \ErrorException($str, 0, $num, $file, $line); + }); + try { + return $this->evaluate($expr); + } catch (\Throwable $e) { + if (!$e instanceof ConstExprEvaluationException) { + $e = new ConstExprEvaluationException("An error occurred during constant expression evaluation", 0, $e); + } + throw $e; + } finally { + \restore_error_handler(); } - \PHPUnit\Webmozart\Assert\Assert::notNull($descriptionFactory); - return new static($matches[1], $descriptionFactory->create($matches[2] ?? '', $context)); } /** - * Gets the version section of the tag. + * Directly evaluates a constant expression into a PHP value. + * + * May generate Error exceptions, warnings or notices. Use evaluateSilently() to convert these + * into a ConstExprEvaluationException. + * + * If some part of the expression cannot be evaluated, the fallback evaluator passed to the + * constructor will be invoked. By default, if no fallback is provided, an exception of type + * ConstExprEvaluationException is thrown. + * + * See class doc comment for caveats and limitations. + * + * @param Expr $expr Constant expression to evaluate + * @return mixed Result of evaluation + * + * @throws ConstExprEvaluationException if the expression cannot be evaluated */ - public function getVersion() : ?string + public function evaluateDirectly(Expr $expr) { - return $this->version; + return $this->evaluate($expr); } - /** - * Returns a string representation for this tag. - */ - public function __toString() : string + private function evaluate(Expr $expr) { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; + if ($expr instanceof Scalar\LNumber || $expr instanceof Scalar\DNumber || $expr instanceof Scalar\String_) { + return $expr->value; } - $version = (string) $this->version; - return $version . ($description !== '' ? ($version !== '' ? ' ' : '') . $description : ''); + if ($expr instanceof Expr\Array_) { + return $this->evaluateArray($expr); + } + // Unary operators + if ($expr instanceof Expr\UnaryPlus) { + return +$this->evaluate($expr->expr); + } + if ($expr instanceof Expr\UnaryMinus) { + return -$this->evaluate($expr->expr); + } + if ($expr instanceof Expr\BooleanNot) { + return !$this->evaluate($expr->expr); + } + if ($expr instanceof Expr\BitwiseNot) { + return ~$this->evaluate($expr->expr); + } + if ($expr instanceof Expr\BinaryOp) { + return $this->evaluateBinaryOp($expr); + } + if ($expr instanceof Expr\Ternary) { + return $this->evaluateTernary($expr); + } + if ($expr instanceof Expr\ArrayDimFetch && null !== $expr->dim) { + return $this->evaluate($expr->var)[$this->evaluate($expr->dim)]; + } + if ($expr instanceof Expr\ConstFetch) { + return $this->evaluateConstFetch($expr); + } + return ($this->fallbackEvaluator)($expr); + } + private function evaluateArray(Expr\Array_ $expr) + { + $array = []; + foreach ($expr->items as $item) { + if (null !== $item->key) { + $array[$this->evaluate($item->key)] = $this->evaluate($item->value); + } else { + $array[] = $this->evaluate($item->value); + } + } + return $array; + } + private function evaluateTernary(Expr\Ternary $expr) + { + if (null === $expr->if) { + return $this->evaluate($expr->cond) ?: $this->evaluate($expr->else); + } + return $this->evaluate($expr->cond) ? $this->evaluate($expr->if) : $this->evaluate($expr->else); + } + private function evaluateBinaryOp(Expr\BinaryOp $expr) + { + if ($expr instanceof Expr\BinaryOp\Coalesce && $expr->left instanceof Expr\ArrayDimFetch) { + // This needs to be special cased to respect BP_VAR_IS fetch semantics + return $this->evaluate($expr->left->var)[$this->evaluate($expr->left->dim)] ?? $this->evaluate($expr->right); + } + // The evaluate() calls are repeated in each branch, because some of the operators are + // short-circuiting and evaluating the RHS in advance may be illegal in that case + $l = $expr->left; + $r = $expr->right; + switch ($expr->getOperatorSigil()) { + case '&': + return $this->evaluate($l) & $this->evaluate($r); + case '|': + return $this->evaluate($l) | $this->evaluate($r); + case '^': + return $this->evaluate($l) ^ $this->evaluate($r); + case '&&': + return $this->evaluate($l) && $this->evaluate($r); + case '||': + return $this->evaluate($l) || $this->evaluate($r); + case '??': + return $this->evaluate($l) ?? $this->evaluate($r); + case '.': + return $this->evaluate($l) . $this->evaluate($r); + case '/': + return $this->evaluate($l) / $this->evaluate($r); + case '==': + return $this->evaluate($l) == $this->evaluate($r); + case '>': + return $this->evaluate($l) > $this->evaluate($r); + case '>=': + return $this->evaluate($l) >= $this->evaluate($r); + case '===': + return $this->evaluate($l) === $this->evaluate($r); + case 'and': + return $this->evaluate($l) and $this->evaluate($r); + case 'or': + return $this->evaluate($l) or $this->evaluate($r); + case 'xor': + return $this->evaluate($l) xor $this->evaluate($r); + case '-': + return $this->evaluate($l) - $this->evaluate($r); + case '%': + return $this->evaluate($l) % $this->evaluate($r); + case '*': + return $this->evaluate($l) * $this->evaluate($r); + case '!=': + return $this->evaluate($l) != $this->evaluate($r); + case '!==': + return $this->evaluate($l) !== $this->evaluate($r); + case '+': + return $this->evaluate($l) + $this->evaluate($r); + case '**': + return $this->evaluate($l) ** $this->evaluate($r); + case '<<': + return $this->evaluate($l) << $this->evaluate($r); + case '>>': + return $this->evaluate($l) >> $this->evaluate($r); + case '<': + return $this->evaluate($l) < $this->evaluate($r); + case '<=': + return $this->evaluate($l) <= $this->evaluate($r); + case '<=>': + return $this->evaluate($l) <=> $this->evaluate($r); + } + throw new \Exception('Should not happen'); + } + private function evaluateConstFetch(Expr\ConstFetch $expr) + { + $name = $expr->name->toLowerString(); + switch ($name) { + case 'null': + return null; + case 'false': + return \false; + case 'true': + return \true; + } + return ($this->fallbackEvaluator)($expr); } } name = 'param'; - $this->variableName = $variableName; - $this->type = $type; - $this->isVariadic = $isVariadic; - $this->description = $description; - $this->isReference = $isReference; + return Emulative::PHP_8_1; } - public static function create(string $body, ?\PHPUnit\phpDocumentor\Reflection\TypeResolver $typeResolver = null, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory $descriptionFactory = null, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : self + public function isEmulationNeeded(string $code) : bool { - \PHPUnit\Webmozart\Assert\Assert::stringNotEmpty($body); - \PHPUnit\Webmozart\Assert\Assert::notNull($typeResolver); - \PHPUnit\Webmozart\Assert\Assert::notNull($descriptionFactory); - [$firstPart, $body] = self::extractTypeFromBody($body); - $type = null; - $parts = \PHPUnit\phpDocumentor\Reflection\Utils::pregSplit('/(\\s+)/Su', $body, 2, \PREG_SPLIT_DELIM_CAPTURE); - $variableName = ''; - $isVariadic = \false; - $isReference = \false; - // if the first item that is encountered is not a variable; it is a type - if ($firstPart && !self::strStartsWithVariable($firstPart)) { - $type = $typeResolver->resolve($firstPart, $context); - } else { - // first part is not a type; we should prepend it to the parts array for further processing - \array_unshift($parts, $firstPart); - } - // if the next item starts with a $ or ...$ or &$ or &...$ it must be the variable name - if (isset($parts[0]) && self::strStartsWithVariable($parts[0])) { - $variableName = \array_shift($parts); - if ($type) { - \array_shift($parts); - } - \PHPUnit\Webmozart\Assert\Assert::notNull($variableName); - if (\strpos($variableName, '$') === 0) { - $variableName = \substr($variableName, 1); - } elseif (\strpos($variableName, '&$') === 0) { - $isReference = \true; - $variableName = \substr($variableName, 2); - } elseif (\strpos($variableName, '...$') === 0) { - $isVariadic = \true; - $variableName = \substr($variableName, 4); - } elseif (\strpos($variableName, '&...$') === 0) { - $isVariadic = \true; - $isReference = \true; - $variableName = \substr($variableName, 5); + return \strpos($code, '0o') !== \false || \strpos($code, '0O') !== \false; + } + public function emulate(string $code, array $tokens) : array + { + for ($i = 0, $c = \count($tokens); $i < $c; ++$i) { + if ($tokens[$i][0] == \T_LNUMBER && $tokens[$i][1] === '0' && isset($tokens[$i + 1]) && $tokens[$i + 1][0] == \T_STRING && \preg_match('/[oO][0-7]+(?:_[0-7]+)*/', $tokens[$i + 1][1])) { + $tokenKind = $this->resolveIntegerOrFloatToken($tokens[$i + 1][1]); + \array_splice($tokens, $i, 2, [[$tokenKind, '0' . $tokens[$i + 1][1], $tokens[$i][2]]]); + $c--; } } - $description = $descriptionFactory->create(\implode('', $parts), $context); - return new static($variableName, $type, $isVariadic, $description, $isReference); + return $tokens; } - /** - * Returns the variable's name. - */ - public function getVariableName() : ?string + private function resolveIntegerOrFloatToken(string $str) : int { - return $this->variableName; + $str = \substr($str, 1); + $str = \str_replace('_', '', $str); + $num = \octdec($str); + return \is_float($num) ? \T_DNUMBER : \T_LNUMBER; } - /** - * Returns whether this tag is variadic. - */ - public function isVariadic() : bool + public function reverseEmulate(string $code, array $tokens) : array { - return $this->isVariadic; + // Explicit octals were not legal code previously, don't bother. + return $tokens; } - /** - * Returns whether this tag is passed by reference. - */ - public function isReference() : bool +} +isReference; + return Emulative::PHP_8_1; } - /** - * Returns a string representation for this tag. - */ - public function __toString() : string + public function getKeywordString() : string { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $variableName = ''; - if ($this->variableName) { - $variableName .= ($this->isReference ? '&' : '') . ($this->isVariadic ? '...' : ''); - $variableName .= '$' . $this->variableName; - } - $type = (string) $this->type; - return $type . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); + return 'enum'; } - private static function strStartsWithVariable(string $str) : bool + public function getKeywordToken() : int { - return \strpos($str, '$') === 0 || \strpos($str, '...$') === 0 || \strpos($str, '&$') === 0 || \strpos($str, '&...$') === 0; + return \T_ENUM; + } + protected function isKeywordContext(array $tokens, int $pos) : bool + { + return parent::isKeywordContext($tokens, $pos) && isset($tokens[$pos + 2]) && $tokens[$pos + 1][0] === \T_WHITESPACE && $tokens[$pos + 2][0] === \T_STRING; } } - * @var array> - */ - private $arguments; - /** @var bool */ - private $isStatic; - /** @var Type */ - private $returnType; - /** - * @param array> $arguments - * - * @phpstan-param array $arguments - */ - public function __construct(string $methodName, array $arguments = [], ?\PHPUnit\phpDocumentor\Reflection\Type $returnType = null, bool $static = \false, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\Description $description = null) + public function getPhpVersion() : string { - \PHPUnit\Webmozart\Assert\Assert::stringNotEmpty($methodName); - if ($returnType === null) { - $returnType = new \PHPUnit\phpDocumentor\Reflection\Types\Void_(); - } - $this->methodName = $methodName; - $this->arguments = $this->filterArguments($arguments); - $this->returnType = $returnType; - $this->isStatic = $static; - $this->description = $description; + return Emulative::PHP_7_4; } - public static function create(string $body, ?\PHPUnit\phpDocumentor\Reflection\TypeResolver $typeResolver = null, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory $descriptionFactory = null, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : ?self + public function isEmulationNeeded(string $code) : bool { - \PHPUnit\Webmozart\Assert\Assert::stringNotEmpty($body); - \PHPUnit\Webmozart\Assert\Assert::notNull($typeResolver); - \PHPUnit\Webmozart\Assert\Assert::notNull($descriptionFactory); - // 1. none or more whitespace - // 2. optionally the keyword "static" followed by whitespace - // 3. optionally a word with underscores followed by whitespace : as - // type for the return value - // 4. then optionally a word with underscores followed by () and - // whitespace : as method name as used by phpDocumentor - // 5. then a word with underscores, followed by ( and any character - // until a ) and whitespace : as method name with signature - // 6. any remaining text : as description - if (!\preg_match('/^ - # Static keyword - # Declares a static method ONLY if type is also present - (?: - (static) - \\s+ - )? - # Return type - (?: - ( - (?:[\\w\\|_\\\\]*\\$this[\\w\\|_\\\\]*) - | - (?: - (?:[\\w\\|_\\\\]+) - # array notation - (?:\\[\\])* - )*+ - ) - \\s+ - )? - # Method name - ([\\w_]+) - # Arguments - (?: - \\(([^\\)]*)\\) - )? - \\s* - # Description - (.*) - $/sux', $body, $matches)) { - return null; - } - [, $static, $returnType, $methodName, $argumentLines, $description] = $matches; - $static = $static === 'static'; - if ($returnType === '') { - $returnType = 'void'; - } - $returnType = $typeResolver->resolve($returnType, $context); - $description = $descriptionFactory->create($description, $context); - /** @phpstan-var array $arguments */ - $arguments = []; - if ($argumentLines !== '') { - $argumentsExploded = \explode(',', $argumentLines); - foreach ($argumentsExploded as $argument) { - $argument = \explode(' ', self::stripRestArg(\trim($argument)), 2); - if (\strpos($argument[0], '$') === 0) { - $argumentName = \substr($argument[0], 1); - $argumentType = new \PHPUnit\phpDocumentor\Reflection\Types\Mixed_(); - } else { - $argumentType = $typeResolver->resolve($argument[0], $context); - $argumentName = ''; - if (isset($argument[1])) { - $argument[1] = self::stripRestArg($argument[1]); - $argumentName = \substr($argument[1], 1); - } + return \strpos($code, '??=') !== \false; + } + public function emulate(string $code, array $tokens) : array + { + // We need to manually iterate and manage a count because we'll change + // the tokens array on the way + $line = 1; + for ($i = 0, $c = \count($tokens); $i < $c; ++$i) { + if (isset($tokens[$i + 1])) { + if ($tokens[$i][0] === \T_COALESCE && $tokens[$i + 1] === '=') { + \array_splice($tokens, $i, 2, [[\T_COALESCE_EQUAL, '??=', $line]]); + $c--; + continue; } - $arguments[] = ['name' => $argumentName, 'type' => $argumentType]; + } + if (\is_array($tokens[$i])) { + $line += \substr_count($tokens[$i][1], "\n"); } } - return new static($methodName, $arguments, $returnType, $static, $description); + return $tokens; } - /** - * Retrieves the method name. - */ - public function getMethodName() : string + public function reverseEmulate(string $code, array $tokens) : array { - return $this->methodName; + // ??= was not valid code previously, don't bother. + return $tokens; } +} +> - * - * @phpstan-return array + * @return array Modified Tokens */ - public function getArguments() : array - { - return $this->arguments; - } + public abstract function emulate(string $code, array $tokens) : array; /** - * Checks whether the method tag describes a static method or not. - * - * @return bool TRUE if the method declaration is for a static method, FALSE otherwise. + * @return array Modified Tokens */ - public function isStatic() : bool + public abstract function reverseEmulate(string $code, array $tokens) : array; + public function preprocessCode(string $code, array &$patches) : string { - return $this->isStatic; + return $code; } - public function getReturnType() : \PHPUnit\phpDocumentor\Reflection\Type +} +returnType; + return Emulative::PHP_8_0; } - public function __toString() : string + public function isEmulationNeeded(string $code) : bool { - $arguments = []; - foreach ($this->arguments as $argument) { - $arguments[] = $argument['type'] . ' $' . $argument['name']; - } - $argumentStr = '(' . \implode(', ', $arguments) . ')'; - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $static = $this->isStatic ? 'static' : ''; - $returnType = (string) $this->returnType; - $methodName = (string) $this->methodName; - return $static . ($returnType !== '' ? ($static !== '' ? ' ' : '') . $returnType : '') . ($methodName !== '' ? ($static !== '' || $returnType !== '' ? ' ' : '') . $methodName : '') . $argumentStr . ($description !== '' ? ' ' . $description : ''); + return \strpos($code, '#[') !== \false; } - /** - * @param mixed[][]|string[] $arguments - * - * @return mixed[][] - * - * @phpstan-param array $arguments - * @phpstan-return array - */ - private function filterArguments(array $arguments = []) : array + public function emulate(string $code, array $tokens) : array { - $result = []; - foreach ($arguments as $argument) { - if (\is_string($argument)) { - $argument = ['name' => $argument]; - } - if (!isset($argument['type'])) { - $argument['type'] = new \PHPUnit\phpDocumentor\Reflection\Types\Mixed_(); + // We need to manually iterate and manage a count because we'll change + // the tokens array on the way. + $line = 1; + for ($i = 0, $c = \count($tokens); $i < $c; ++$i) { + if ($tokens[$i] === '#' && isset($tokens[$i + 1]) && $tokens[$i + 1] === '[') { + \array_splice($tokens, $i, 2, [[\T_ATTRIBUTE, '#[', $line]]); + $c--; + continue; } - $keys = \array_keys($argument); - \sort($keys); - if ($keys !== ['name', 'type']) { - throw new \InvalidArgumentException('Arguments can only have the "name" and "type" fields, found: ' . \var_export($keys, \true)); + if (\is_array($tokens[$i])) { + $line += \substr_count($tokens[$i][1], "\n"); } - $result[] = $argument; } - return $result; + return $tokens; } - private static function stripRestArg(string $argument) : string + public function reverseEmulate(string $code, array $tokens) : array { - if (\strpos($argument, '...') === 0) { - $argument = \trim(\substr($argument, 3)); + // TODO + return $tokens; + } + public function preprocessCode(string $code, array &$patches) : string + { + $pos = 0; + while (\false !== ($pos = \strpos($code, '#[', $pos))) { + // Replace #[ with %[ + $code[$pos] = '%'; + $patches[] = [$pos, 'replace', '#']; + $pos += 2; } - return $argument; + return $code; } } name = 'throws'; - $this->type = $type; - $this->description = $description; - } - public static function create(string $body, ?\PHPUnit\phpDocumentor\Reflection\TypeResolver $typeResolver = null, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory $descriptionFactory = null, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : self + /** @var TokenEmulator Inner emulator */ + private $emulator; + public function __construct(TokenEmulator $emulator) { - \PHPUnit\Webmozart\Assert\Assert::notNull($typeResolver); - \PHPUnit\Webmozart\Assert\Assert::notNull($descriptionFactory); - [$type, $description] = self::extractTypeFromBody($body); - $type = $typeResolver->resolve($type, $context); - $description = $descriptionFactory->create($description, $context); - return new static($type, $description); + $this->emulator = $emulator; } - public function __toString() : string + public function getPhpVersion() : string { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $type = (string) $this->type; - return $type . ($description !== '' ? ($type !== '' ? ' ' : '') . $description : ''); + return $this->emulator->getPhpVersion(); } -} -version = $version; - $this->description = $description; + return $this->emulator->isEmulationNeeded($code); } - public static function create(?string $body, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory $descriptionFactory = null, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : ?self + public function emulate(string $code, array $tokens) : array { - if (empty($body)) { - return new static(); - } - $matches = []; - if (!\preg_match('/^(' . self::REGEX_VECTOR . ')\\s*(.+)?$/sux', $body, $matches)) { - return null; - } - \PHPUnit\Webmozart\Assert\Assert::notNull($descriptionFactory); - return new static($matches[1], $descriptionFactory->create($matches[2] ?? '', $context)); + return $this->emulator->reverseEmulate($code, $tokens); } - /** - * Gets the version section of the tag. - */ - public function getVersion() : ?string + public function reverseEmulate(string $code, array $tokens) : array { - return $this->version; + return $this->emulator->emulate($code, $tokens); } - /** - * Returns a string representation for this tag. - */ - public function __toString() : string + public function preprocessCode(string $code, array &$patches) : string { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $version = (string) $this->version; - return $version . ($description !== '' ? ($version !== '' ? ' ' : '') . $description : ''); + return $code; } } validateTagName($name); - $this->name = $name; - $this->description = $description; - } - /** - * Creates a new tag that represents any unknown tag type. - * - * @return static - */ - public static function create(string $body, string $name = '', ?\PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory $descriptionFactory = null, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : self + public function getPhpVersion() : string { - \PHPUnit\Webmozart\Assert\Assert::stringNotEmpty($name); - \PHPUnit\Webmozart\Assert\Assert::notNull($descriptionFactory); - $description = $body !== '' ? $descriptionFactory->create($body, $context) : null; - return new static($name, $description); + return Emulative::PHP_7_4; } - /** - * Returns the tag as a serialized string - */ - public function __toString() : string + public function getKeywordString() : string { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - return $description; + return 'fn'; } - /** - * Validates if the tag name matches the expected format, otherwise throws an exception. - */ - private function validateTagName(string $name) : void + public function getKeywordToken() : int { - if (!\preg_match('/^' . \PHPUnit\phpDocumentor\Reflection\DocBlock\StandardTagFactory::REGEX_TAGNAME . '$/u', $name)) { - throw new \InvalidArgumentException('The tag name "' . $name . '" is not wellformed. Tags may only consist of letters, underscores, ' . 'hyphens and backslashes.'); - } + return \T_FN; } } link = $link; - $this->description = $description; - } - public static function create(string $body, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory $descriptionFactory = null, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : self + public function getPhpVersion() : string { - \PHPUnit\Webmozart\Assert\Assert::notNull($descriptionFactory); - $parts = \PHPUnit\phpDocumentor\Reflection\Utils::pregSplit('/\\s+/Su', $body, 2); - $description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null; - return new static($parts[0], $description); + return Emulative::PHP_8_1; } - /** - * Gets the link - */ - public function getLink() : string + public function getKeywordString() : string { - return $this->link; + return 'readonly'; } - /** - * Returns a string representation for this tag. - */ - public function __toString() : string + public function getKeywordToken() : int { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $link = (string) $this->link; - return $link . ($description !== '' ? ($link !== '' ? ' ' : '') . $description : ''); + return \T_READONLY; } } type; + return Emulative::PHP_8_0; } - /** - * @return string[] - */ - protected static function extractTypeFromBody(string $body) : array + public function getKeywordString() : string { - $type = ''; - $nestingLevel = 0; - for ($i = 0, $iMax = \strlen($body); $i < $iMax; $i++) { - $character = $body[$i]; - if ($nestingLevel === 0 && \trim($character) === '') { - break; - } - $type .= $character; - if (\in_array($character, ['<', '(', '[', '{'])) { - $nestingLevel++; - continue; - } - if (\in_array($character, ['>', ')', ']', '}'])) { - $nestingLevel--; - continue; - } - } - $description = \trim(\substr($body, \strlen($type))); - return [$type, $description]; + return 'match'; + } + public function getKeywordToken() : int + { + return \T_MATCH; } } \h*)\2(?![a-zA-Z0-9_\x80-\xff])(?(?:;?[\r\n])?)/x +REGEX; + public function getPhpVersion() : string { - $this->refers = $refers; - $this->description = $description; + return Emulative::PHP_7_3; } - public static function create(string $body, ?\PHPUnit\phpDocumentor\Reflection\FqsenResolver $resolver = null, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory $descriptionFactory = null, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : self + public function isEmulationNeeded(string $code) : bool { - \PHPUnit\Webmozart\Assert\Assert::notNull($resolver); - \PHPUnit\Webmozart\Assert\Assert::notNull($descriptionFactory); - $parts = \PHPUnit\phpDocumentor\Reflection\Utils::pregSplit('/\\s+/Su', $body, 2); - return new static(self::resolveFqsen($parts[0], $resolver, $context), $descriptionFactory->create($parts[1] ?? '', $context)); + return \strpos($code, '<<<') !== \false; } - private static function resolveFqsen(string $parts, ?\PHPUnit\phpDocumentor\Reflection\FqsenResolver $fqsenResolver, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context) : \PHPUnit\phpDocumentor\Reflection\Fqsen + public function emulate(string $code, array $tokens) : array { - \PHPUnit\Webmozart\Assert\Assert::notNull($fqsenResolver); - $fqsenParts = \explode('::', $parts); - $resolved = $fqsenResolver->resolve($fqsenParts[0], $context); - if (!\array_key_exists(1, $fqsenParts)) { - return $resolved; - } - return new \PHPUnit\phpDocumentor\Reflection\Fqsen($resolved . '::' . $fqsenParts[1]); + // Handled by preprocessing + fixup. + return $tokens; } - /** - * Returns the structural element this tag refers to. - */ - public function getReference() : \PHPUnit\phpDocumentor\Reflection\Fqsen + public function reverseEmulate(string $code, array $tokens) : array { - return $this->refers; + // Not supported. + return $tokens; } - /** - * Returns a string representation of this tag. - */ - public function __toString() : string + public function preprocessCode(string $code, array &$patches) : string { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; + if (!\preg_match_all(self::FLEXIBLE_DOC_STRING_REGEX, $code, $matches, \PREG_SET_ORDER | \PREG_OFFSET_CAPTURE)) { + // No heredoc/nowdoc found + return $code; } - $refers = (string) $this->refers; - return $refers . ($description !== '' ? ($refers !== '' ? ' ' : '') . $description : ''); + // Keep track of how much we need to adjust string offsets due to the modifications we + // already made + $posDelta = 0; + foreach ($matches as $match) { + $indentation = $match['indentation'][0]; + $indentationStart = $match['indentation'][1]; + $separator = $match['separator'][0]; + $separatorStart = $match['separator'][1]; + if ($indentation === '' && $separator !== '') { + // Ordinary heredoc/nowdoc + continue; + } + if ($indentation !== '') { + // Remove indentation + $indentationLen = \strlen($indentation); + $code = \substr_replace($code, '', $indentationStart + $posDelta, $indentationLen); + $patches[] = [$indentationStart + $posDelta, 'add', $indentation]; + $posDelta -= $indentationLen; + } + if ($separator === '') { + // Insert newline as separator + $code = \substr_replace($code, "\n", $separatorStart + $posDelta, 0); + $patches[] = [$separatorStart + $posDelta, 'remove', "\n"]; + $posDelta += 1; + } + } + return $code; } } name; + return Emulative::PHP_7_4; } - public function getDescription() : ?\PHPUnit\phpDocumentor\Reflection\DocBlock\Description + public function isEmulationNeeded(string $code) : bool { - return $this->description; + return \preg_match('~[0-9]_[0-9]~', $code) || \preg_match('~0x[0-9a-f]+_[0-9a-f]~i', $code); } - public function render(?\PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter $formatter = null) : string + public function emulate(string $code, array $tokens) : array { - if ($formatter === null) { - $formatter = new \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter(); + // We need to manually iterate and manage a count because we'll change + // the tokens array on the way + $codeOffset = 0; + for ($i = 0, $c = \count($tokens); $i < $c; ++$i) { + $token = $tokens[$i]; + $tokenLen = \strlen(\is_array($token) ? $token[1] : $token); + if ($token[0] !== \T_LNUMBER && $token[0] !== \T_DNUMBER) { + $codeOffset += $tokenLen; + continue; + } + $res = \preg_match(self::NUMBER, $code, $matches, 0, $codeOffset); + \assert($res, "No number at number token position"); + $match = $matches[0]; + $matchLen = \strlen($match); + if ($matchLen === $tokenLen) { + // Original token already holds the full number. + $codeOffset += $tokenLen; + continue; + } + $tokenKind = $this->resolveIntegerOrFloatToken($match); + $newTokens = [[$tokenKind, $match, $token[2]]]; + $numTokens = 1; + $len = $tokenLen; + while ($matchLen > $len) { + $nextToken = $tokens[$i + $numTokens]; + $nextTokenText = \is_array($nextToken) ? $nextToken[1] : $nextToken; + $nextTokenLen = \strlen($nextTokenText); + $numTokens++; + if ($matchLen < $len + $nextTokenLen) { + // Split trailing characters into a partial token. + \assert(\is_array($nextToken), "Partial token should be an array token"); + $partialText = \substr($nextTokenText, $matchLen - $len); + $newTokens[] = [$nextToken[0], $partialText, $nextToken[2]]; + break; + } + $len += $nextTokenLen; + } + \array_splice($tokens, $i, $numTokens, $newTokens); + $c -= $numTokens - \count($newTokens); + $codeOffset += $matchLen; } - return $formatter->format($this); + return $tokens; + } + private function resolveIntegerOrFloatToken(string $str) : int + { + $str = \str_replace('_', '', $str); + if (\stripos($str, '0b') === 0) { + $num = \bindec($str); + } elseif (\stripos($str, '0x') === 0) { + $num = \hexdec($str); + } elseif (\stripos($str, '0') === 0 && \ctype_digit($str)) { + $num = \octdec($str); + } else { + $num = +$str; + } + return \is_float($num) ? \T_DNUMBER : \T_LNUMBER; + } + public function reverseEmulate(string $code, array $tokens) : array + { + // Numeric separators were not legal code previously, don't bother. + return $tokens; } } refers = $refers; - $this->description = $description; + return \strpos(\strtolower($code), $this->getKeywordString()) !== \false; } - public static function create(string $body, ?\PHPUnit\phpDocumentor\Reflection\FqsenResolver $typeResolver = null, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory $descriptionFactory = null, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : self + protected function isKeywordContext(array $tokens, int $pos) : bool { - \PHPUnit\Webmozart\Assert\Assert::notNull($descriptionFactory); - $parts = \PHPUnit\phpDocumentor\Reflection\Utils::pregSplit('/\\s+/Su', $body, 2); - $description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null; - // https://tools.ietf.org/html/rfc2396#section-3 - if (\preg_match('/\\w:\\/\\/\\w/i', $parts[0])) { - return new static(new \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Reference\Url($parts[0]), $description); - } - return new static(new \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Reference\Fqsen(self::resolveFqsen($parts[0], $typeResolver, $context)), $description); + $previousNonSpaceToken = $this->getPreviousNonSpaceToken($tokens, $pos); + return $previousNonSpaceToken === null || $previousNonSpaceToken[0] !== \T_OBJECT_OPERATOR; } - private static function resolveFqsen(string $parts, ?\PHPUnit\phpDocumentor\Reflection\FqsenResolver $fqsenResolver, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context) : \PHPUnit\phpDocumentor\Reflection\Fqsen + public function emulate(string $code, array $tokens) : array { - \PHPUnit\Webmozart\Assert\Assert::notNull($fqsenResolver); - $fqsenParts = \explode('::', $parts); - $resolved = $fqsenResolver->resolve($fqsenParts[0], $context); - if (!\array_key_exists(1, $fqsenParts)) { - return $resolved; + $keywordString = $this->getKeywordString(); + foreach ($tokens as $i => $token) { + if ($token[0] === \T_STRING && \strtolower($token[1]) === $keywordString && $this->isKeywordContext($tokens, $i)) { + $tokens[$i][0] = $this->getKeywordToken(); + } } - return new \PHPUnit\phpDocumentor\Reflection\Fqsen($resolved . '::' . $fqsenParts[1]); + return $tokens; } /** - * Returns the ref of this tag. + * @param mixed[] $tokens + * @return mixed[]|null */ - public function getReference() : \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Reference\Reference + private function getPreviousNonSpaceToken(array $tokens, int $start) { - return $this->refers; + for ($i = $start - 1; $i >= 0; --$i) { + if ($tokens[$i][0] === \T_WHITESPACE) { + continue; + } + return $tokens[$i]; + } + return null; } - /** - * Returns a string representation of this tag. - */ - public function __toString() : string + public function reverseEmulate(string $code, array $tokens) : array { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; + $keywordToken = $this->getKeywordToken(); + foreach ($tokens as $i => $token) { + if ($token[0] === $keywordToken) { + $tokens[$i][0] = \T_STRING; + } } - $refers = (string) $this->refers; - return $refers . ($description !== '' ? ($refers !== '' ? ' ' : '') . $description : ''); + return $tokens; } } refers = $refers; - $this->description = $description; + return Emulative::PHP_8_0; } - public static function create(string $body, ?\PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory $descriptionFactory = null, ?\PHPUnit\phpDocumentor\Reflection\FqsenResolver $resolver = null, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : self + public function isEmulationNeeded(string $code) : bool { - \PHPUnit\Webmozart\Assert\Assert::stringNotEmpty($body); - \PHPUnit\Webmozart\Assert\Assert::notNull($descriptionFactory); - \PHPUnit\Webmozart\Assert\Assert::notNull($resolver); - $parts = \PHPUnit\phpDocumentor\Reflection\Utils::pregSplit('/\\s+/Su', $body, 2); - return new static(self::resolveFqsen($parts[0], $resolver, $context), $descriptionFactory->create($parts[1] ?? '', $context)); + return \strpos($code, '?->') !== \false; } - private static function resolveFqsen(string $parts, ?\PHPUnit\phpDocumentor\Reflection\FqsenResolver $fqsenResolver, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context) : \PHPUnit\phpDocumentor\Reflection\Fqsen + public function emulate(string $code, array $tokens) : array { - \PHPUnit\Webmozart\Assert\Assert::notNull($fqsenResolver); - $fqsenParts = \explode('::', $parts); - $resolved = $fqsenResolver->resolve($fqsenParts[0], $context); - if (!\array_key_exists(1, $fqsenParts)) { - return $resolved; + // We need to manually iterate and manage a count because we'll change + // the tokens array on the way + $line = 1; + for ($i = 0, $c = \count($tokens); $i < $c; ++$i) { + if ($tokens[$i] === '?' && isset($tokens[$i + 1]) && $tokens[$i + 1][0] === \T_OBJECT_OPERATOR) { + \array_splice($tokens, $i, 2, [[\T_NULLSAFE_OBJECT_OPERATOR, '?->', $line]]); + $c--; + continue; + } + // Handle ?-> inside encapsed string. + if ($tokens[$i][0] === \T_ENCAPSED_AND_WHITESPACE && isset($tokens[$i - 1]) && $tokens[$i - 1][0] === \T_VARIABLE && \preg_match('/^\\?->([a-zA-Z_\\x80-\\xff][a-zA-Z0-9_\\x80-\\xff]*)/', $tokens[$i][1], $matches)) { + $replacement = [[\T_NULLSAFE_OBJECT_OPERATOR, '?->', $line], [\T_STRING, $matches[1], $line]]; + if (\strlen($matches[0]) !== \strlen($tokens[$i][1])) { + $replacement[] = [\T_ENCAPSED_AND_WHITESPACE, \substr($tokens[$i][1], \strlen($matches[0])), $line]; + } + \array_splice($tokens, $i, 1, $replacement); + $c += \count($replacement) - 1; + continue; + } + if (\is_array($tokens[$i])) { + $line += \substr_count($tokens[$i][1], "\n"); + } } - return new \PHPUnit\phpDocumentor\Reflection\Fqsen($resolved . '::' . $fqsenParts[1]); - } - /** - * Returns the structural element this tag refers to. - */ - public function getReference() : \PHPUnit\phpDocumentor\Reflection\Fqsen - { - return $this->refers; + return $tokens; } - /** - * Returns a string representation of this tag. - */ - public function __toString() : string + public function reverseEmulate(string $code, array $tokens) : array { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - $refers = (string) $this->refers; - return $refers . ($description !== '' ? ($refers !== '' ? ' ' : '') . $description : ''); + // ?-> was not valid code previously, don't bother. + return $tokens; } } tagFactory = $tagFactory; + $this->targetPhpVersion = $options['phpVersion'] ?? Emulative::PHP_8_1; + unset($options['phpVersion']); + parent::__construct($options); + $emulators = [new FlexibleDocStringEmulator(), new FnTokenEmulator(), new MatchTokenEmulator(), new CoaleseEqualTokenEmulator(), new NumericLiteralSeparatorEmulator(), new NullsafeTokenEmulator(), new AttributeEmulator(), new EnumTokenEmulator(), new ReadonlyTokenEmulator(), new ExplicitOctalEmulator()]; + // Collect emulators that are relevant for the PHP version we're running + // and the PHP version we're targeting for emulation. + foreach ($emulators as $emulator) { + $emulatorPhpVersion = $emulator->getPhpVersion(); + if ($this->isForwardEmulationNeeded($emulatorPhpVersion)) { + $this->emulators[] = $emulator; + } else { + if ($this->isReverseEmulationNeeded($emulatorPhpVersion)) { + $this->emulators[] = new ReverseEmulator($emulator); + } + } + } } - /** - * Returns the parsed text of this description. - */ - public function create(string $contents, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : \PHPUnit\phpDocumentor\Reflection\DocBlock\Description + public function startLexing(string $code, ErrorHandler $errorHandler = null) { - $tokens = $this->lex($contents); - $count = \count($tokens); - $tagCount = 0; - $tags = []; - for ($i = 1; $i < $count; $i += 2) { - $tags[] = $this->tagFactory->create($tokens[$i], $context); - $tokens[$i] = '%' . ++$tagCount . '$s'; + $emulators = \array_filter($this->emulators, function ($emulator) use($code) { + return $emulator->isEmulationNeeded($code); + }); + if (empty($emulators)) { + // Nothing to emulate, yay + parent::startLexing($code, $errorHandler); + return; } - //In order to allow "literal" inline tags, the otherwise invalid - //sequence "{@}" is changed to "@", and "{}" is changed to "}". - //"%" is escaped to "%%" because of vsprintf. - //See unit tests for examples. - for ($i = 0; $i < $count; $i += 2) { - $tokens[$i] = \str_replace(['{@}', '{}', '%'], ['@', '}', '%%'], $tokens[$i]); + $this->patches = []; + foreach ($emulators as $emulator) { + $code = $emulator->preprocessCode($code, $this->patches); + } + $collector = new ErrorHandler\Collecting(); + parent::startLexing($code, $collector); + $this->sortPatches(); + $this->fixupTokens(); + $errors = $collector->getErrors(); + if (!empty($errors)) { + $this->fixupErrors($errors); + foreach ($errors as $error) { + $errorHandler->handleError($error); + } + } + foreach ($emulators as $emulator) { + $this->tokens = $emulator->emulate($code, $this->tokens); } - return new \PHPUnit\phpDocumentor\Reflection\DocBlock\Description(\implode('', $tokens), $tags); } - /** - * Strips the contents from superfluous whitespace and splits the description into a series of tokens. - * - * @return string[] A series of tokens of which the description text is composed. - */ - private function lex(string $contents) : array + private function isForwardEmulationNeeded(string $emulatorPhpVersion) : bool { - $contents = $this->removeSuperfluousStartingWhitespace($contents); - // performance optimalization; if there is no inline tag, don't bother splitting it up. - if (\strpos($contents, '{@') === \false) { - return [$contents]; - } - return \PHPUnit\phpDocumentor\Reflection\Utils::pregSplit('/\\{ - # "{@}" is not a valid inline tag. This ensures that we do not treat it as one, but treat it literally. - (?!@\\}) - # We want to capture the whole tag line, but without the inline tag delimiters. - (\\@ - # Match everything up to the next delimiter. - [^{}]* - # Nested inline tag content should not be captured, or it will appear in the result separately. - (?: - # Match nested inline tags. - (?: - # Because we did not catch the tag delimiters earlier, we must be explicit with them here. - # Notice that this also matches "{}", as a way to later introduce it as an escape sequence. - \\{(?1)?\\} - | - # Make sure we match hanging "{". - \\{ - ) - # Match content after the nested inline tag. - [^{}]* - )* # If there are more inline tags, match them as well. We use "*" since there may not be any - # nested inline tags. - ) - \\}/Sux', $contents, 0, \PREG_SPLIT_DELIM_CAPTURE); + return \version_compare(\PHP_VERSION, $emulatorPhpVersion, '<') && \version_compare($this->targetPhpVersion, $emulatorPhpVersion, '>='); } - /** - * Removes the superfluous from a multi-line description. - * - * When a description has more than one line then it can happen that the second and subsequent lines have an - * additional indentation. This is commonly in use with tags like this: - * - * {@}since 1.1.0 This is an example - * description where we have an - * indentation in the second and - * subsequent lines. - * - * If we do not normalize the indentation then we have superfluous whitespace on the second and subsequent - * lines and this may cause rendering issues when, for example, using a Markdown converter. - */ - private function removeSuperfluousStartingWhitespace(string $contents) : string + private function isReverseEmulationNeeded(string $emulatorPhpVersion) : bool { - $lines = \explode("\n", $contents); - // if there is only one line then we don't have lines with superfluous whitespace and - // can use the contents as-is - if (\count($lines) <= 1) { - return $contents; + return \version_compare(\PHP_VERSION, $emulatorPhpVersion, '>=') && \version_compare($this->targetPhpVersion, $emulatorPhpVersion, '<'); + } + private function sortPatches() + { + // Patches may be contributed by different emulators. + // Make sure they are sorted by increasing patch position. + \usort($this->patches, function ($p1, $p2) { + return $p1[0] <=> $p2[0]; + }); + } + private function fixupTokens() + { + if (\count($this->patches) === 0) { + return; } - // determine how many whitespace characters need to be stripped - $startingSpaceCount = 9999999; - for ($i = 1, $iMax = \count($lines); $i < $iMax; ++$i) { - // lines with a no length do not count as they are not indented at all - if (\trim($lines[$i]) === '') { + // Load first patch + $patchIdx = 0; + list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; + // We use a manual loop over the tokens, because we modify the array on the fly + $pos = 0; + for ($i = 0, $c = \count($this->tokens); $i < $c; $i++) { + $token = $this->tokens[$i]; + if (\is_string($token)) { + if ($patchPos === $pos) { + // Only support replacement for string tokens. + \assert($patchType === 'replace'); + $this->tokens[$i] = $patchText; + // Fetch the next patch + $patchIdx++; + if ($patchIdx >= \count($this->patches)) { + // No more patches, we're done + return; + } + list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; + } + $pos += \strlen($token); continue; } - // determine the number of prefixing spaces by checking the difference in line length before and after - // an ltrim - $startingSpaceCount = \min($startingSpaceCount, \strlen($lines[$i]) - \strlen(\ltrim($lines[$i]))); + $len = \strlen($token[1]); + $posDelta = 0; + while ($patchPos >= $pos && $patchPos < $pos + $len) { + $patchTextLen = \strlen($patchText); + if ($patchType === 'remove') { + if ($patchPos === $pos && $patchTextLen === $len) { + // Remove token entirely + \array_splice($this->tokens, $i, 1, []); + $i--; + $c--; + } else { + // Remove from token string + $this->tokens[$i][1] = \substr_replace($token[1], '', $patchPos - $pos + $posDelta, $patchTextLen); + $posDelta -= $patchTextLen; + } + } elseif ($patchType === 'add') { + // Insert into the token string + $this->tokens[$i][1] = \substr_replace($token[1], $patchText, $patchPos - $pos + $posDelta, 0); + $posDelta += $patchTextLen; + } else { + if ($patchType === 'replace') { + // Replace inside the token string + $this->tokens[$i][1] = \substr_replace($token[1], $patchText, $patchPos - $pos + $posDelta, $patchTextLen); + } else { + \assert(\false); + } + } + // Fetch the next patch + $patchIdx++; + if ($patchIdx >= \count($this->patches)) { + // No more patches, we're done + return; + } + list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; + // Multiple patches may apply to the same token. Reload the current one to check + // If the new patch applies + $token = $this->tokens[$i]; + } + $pos += $len; } - // strip the number of spaces from each line - if ($startingSpaceCount > 0) { - for ($i = 1, $iMax = \count($lines); $i < $iMax; ++$i) { - $lines[$i] = \substr($lines[$i], $startingSpaceCount); + // A patch did not apply + \assert(\false); + } + /** + * Fixup line and position information in errors. + * + * @param Error[] $errors + */ + private function fixupErrors(array $errors) + { + foreach ($errors as $error) { + $attrs = $error->getAttributes(); + $posDelta = 0; + $lineDelta = 0; + foreach ($this->patches as $patch) { + list($patchPos, $patchType, $patchText) = $patch; + if ($patchPos >= $attrs['startFilePos']) { + // No longer relevant + break; + } + if ($patchType === 'add') { + $posDelta += \strlen($patchText); + $lineDelta += \substr_count($patchText, "\n"); + } else { + if ($patchType === 'remove') { + $posDelta -= \strlen($patchText); + $lineDelta -= \substr_count($patchText, "\n"); + } + } } + $attrs['startFilePos'] += $posDelta; + $attrs['endFilePos'] += $posDelta; + $attrs['startLine'] += $lineDelta; + $attrs['endLine'] += $lineDelta; + $error->setAttributes($attrs); } - return \implode("\n", $lines); } } [aliasName => originalName]] */ + protected $aliases = []; + /** @var Name[][] Same as $aliases but preserving original case */ + protected $origAliases = []; + /** @var ErrorHandler Error handler */ + protected $errorHandler; /** - * Attempts to find the example contents for the given descriptor. + * Create a name context. + * + * @param ErrorHandler $errorHandler Error handling used to report errors */ - public function find(\PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Example $example) : string + public function __construct(ErrorHandler $errorHandler) { - $filename = $example->getFilePath(); - $file = $this->getExampleFileContents($filename); - if (!$file) { - return \sprintf('** File not found : %s **', $filename); - } - return \implode('', \array_slice($file, $example->getStartingLine() - 1, $example->getLineCount())); + $this->errorHandler = $errorHandler; } /** - * Registers the project's root directory where an 'examples' folder can be expected. + * Start a new namespace. + * + * This also resets the alias table. + * + * @param Name|null $namespace Null is the global namespace */ - public function setSourceDirectory(string $directory = '') : void + public function startNamespace(Name $namespace = null) { - $this->sourceDirectory = $directory; + $this->namespace = $namespace; + $this->origAliases = $this->aliases = [Stmt\Use_::TYPE_NORMAL => [], Stmt\Use_::TYPE_FUNCTION => [], Stmt\Use_::TYPE_CONSTANT => []]; } /** - * Returns the project's root directory where an 'examples' folder can be expected. + * Add an alias / import. + * + * @param Name $name Original name + * @param string $aliasName Aliased name + * @param int $type One of Stmt\Use_::TYPE_* + * @param array $errorAttrs Attributes to use to report an error */ - public function getSourceDirectory() : string + public function addAlias(Name $name, string $aliasName, int $type, array $errorAttrs = []) { - return $this->sourceDirectory; + // Constant names are case sensitive, everything else case insensitive + if ($type === Stmt\Use_::TYPE_CONSTANT) { + $aliasLookupName = $aliasName; + } else { + $aliasLookupName = \strtolower($aliasName); + } + if (isset($this->aliases[$type][$aliasLookupName])) { + $typeStringMap = [Stmt\Use_::TYPE_NORMAL => '', Stmt\Use_::TYPE_FUNCTION => 'function ', Stmt\Use_::TYPE_CONSTANT => 'const ']; + $this->errorHandler->handleError(new Error(\sprintf('Cannot use %s%s as %s because the name is already in use', $typeStringMap[$type], $name, $aliasName), $errorAttrs)); + return; + } + $this->aliases[$type][$aliasLookupName] = $name; + $this->origAliases[$type][$aliasName] = $name; } /** - * Registers a series of directories that may contain examples. + * Get current namespace. * - * @param string[] $directories + * @return null|Name Namespace (or null if global namespace) */ - public function setExampleDirectories(array $directories) : void + public function getNamespace() { - $this->exampleDirectories = $directories; + return $this->namespace; } /** - * Returns a series of directories that may contain examples. + * Get resolved name. * - * @return string[] + * @param Name $name Name to resolve + * @param int $type One of Stmt\Use_::TYPE_{FUNCTION|CONSTANT} + * + * @return null|Name Resolved name, or null if static resolution is not possible */ - public function getExampleDirectories() : array + public function getResolvedName(Name $name, int $type) { - return $this->exampleDirectories; + // don't resolve special class names + if ($type === Stmt\Use_::TYPE_NORMAL && $name->isSpecialClassName()) { + if (!$name->isUnqualified()) { + $this->errorHandler->handleError(new Error(\sprintf("'\\%s' is an invalid class name", $name->toString()), $name->getAttributes())); + } + return $name; + } + // fully qualified names are already resolved + if ($name->isFullyQualified()) { + return $name; + } + // Try to resolve aliases + if (null !== ($resolvedName = $this->resolveAlias($name, $type))) { + return $resolvedName; + } + if ($type !== Stmt\Use_::TYPE_NORMAL && $name->isUnqualified()) { + if (null === $this->namespace) { + // outside of a namespace unaliased unqualified is same as fully qualified + return new FullyQualified($name, $name->getAttributes()); + } + // Cannot resolve statically + return null; + } + // if no alias exists prepend current namespace + return FullyQualified::concat($this->namespace, $name, $name->getAttributes()); } /** - * Attempts to find the requested example file and returns its contents or null if no file was found. + * Get resolved class name. * - * This method will try several methods in search of the given example file, the first one it encounters is - * returned: + * @param Name $name Class ame to resolve * - * 1. Iterates through all examples folders for the given filename - * 2. Checks the source folder for the given filename - * 3. Checks the 'examples' folder in the current working directory for examples - * 4. Checks the path relative to the current working directory for the given filename + * @return Name Resolved name + */ + public function getResolvedClassName(Name $name) : Name + { + return $this->getResolvedName($name, Stmt\Use_::TYPE_NORMAL); + } + /** + * Get possible ways of writing a fully qualified name (e.g., by making use of aliases). * - * @return string[] all lines of the example file + * @param string $name Fully-qualified name (without leading namespace separator) + * @param int $type One of Stmt\Use_::TYPE_* + * + * @return Name[] Possible representations of the name */ - private function getExampleFileContents(string $filename) : ?array + public function getPossibleNames(string $name, int $type) : array { - $normalizedPath = null; - foreach ($this->exampleDirectories as $directory) { - $exampleFileFromConfig = $this->constructExamplePath($directory, $filename); - if (\is_readable($exampleFileFromConfig)) { - $normalizedPath = $exampleFileFromConfig; - break; + $lcName = \strtolower($name); + if ($type === Stmt\Use_::TYPE_NORMAL) { + // self, parent and static must always be unqualified + if ($lcName === "self" || $lcName === "parent" || $lcName === "static") { + return [new Name($name)]; } } - if (!$normalizedPath) { - if (\is_readable($this->getExamplePathFromSource($filename))) { - $normalizedPath = $this->getExamplePathFromSource($filename); - } elseif (\is_readable($this->getExamplePathFromExampleDirectory($filename))) { - $normalizedPath = $this->getExamplePathFromExampleDirectory($filename); - } elseif (\is_readable($filename)) { - $normalizedPath = $filename; + // Collect possible ways to write this name, starting with the fully-qualified name + $possibleNames = [new FullyQualified($name)]; + if (null !== ($nsRelativeName = $this->getNamespaceRelativeName($name, $lcName, $type))) { + // Make sure there is no alias that makes the normally namespace-relative name + // into something else + if (null === $this->resolveAlias($nsRelativeName, $type)) { + $possibleNames[] = $nsRelativeName; } } - $lines = $normalizedPath && \is_readable($normalizedPath) ? \file($normalizedPath) : \false; - return $lines !== \false ? $lines : null; + // Check for relevant namespace use statements + foreach ($this->origAliases[Stmt\Use_::TYPE_NORMAL] as $alias => $orig) { + $lcOrig = $orig->toLowerString(); + if (0 === \strpos($lcName, $lcOrig . '\\')) { + $possibleNames[] = new Name($alias . \substr($name, \strlen($lcOrig))); + } + } + // Check for relevant type-specific use statements + foreach ($this->origAliases[$type] as $alias => $orig) { + if ($type === Stmt\Use_::TYPE_CONSTANT) { + // Constants are are complicated-sensitive + $normalizedOrig = $this->normalizeConstName($orig->toString()); + if ($normalizedOrig === $this->normalizeConstName($name)) { + $possibleNames[] = new Name($alias); + } + } else { + // Everything else is case-insensitive + if ($orig->toLowerString() === $lcName) { + $possibleNames[] = new Name($alias); + } + } + } + return $possibleNames; } /** - * Get example filepath based on the example directory inside your project. + * Get shortest representation of this fully-qualified name. + * + * @param string $name Fully-qualified name (without leading namespace separator) + * @param int $type One of Stmt\Use_::TYPE_* + * + * @return Name Shortest representation */ - private function getExamplePathFromExampleDirectory(string $file) : string + public function getShortName(string $name, int $type) : Name { - return \getcwd() . \DIRECTORY_SEPARATOR . 'examples' . \DIRECTORY_SEPARATOR . $file; + $possibleNames = $this->getPossibleNames($name, $type); + // Find shortest name + $shortestName = null; + $shortestLength = \INF; + foreach ($possibleNames as $possibleName) { + $length = \strlen($possibleName->toCodeString()); + if ($length < $shortestLength) { + $shortestName = $possibleName; + $shortestLength = $length; + } + } + return $shortestName; } - /** - * Returns a path to the example file in the given directory.. - */ - private function constructExamplePath(string $directory, string $file) : string + private function resolveAlias(Name $name, $type) + { + $firstPart = $name->getFirst(); + if ($name->isQualified()) { + // resolve aliases for qualified names, always against class alias table + $checkName = \strtolower($firstPart); + if (isset($this->aliases[Stmt\Use_::TYPE_NORMAL][$checkName])) { + $alias = $this->aliases[Stmt\Use_::TYPE_NORMAL][$checkName]; + return FullyQualified::concat($alias, $name->slice(1), $name->getAttributes()); + } + } elseif ($name->isUnqualified()) { + // constant aliases are case-sensitive, function aliases case-insensitive + $checkName = $type === Stmt\Use_::TYPE_CONSTANT ? $firstPart : \strtolower($firstPart); + if (isset($this->aliases[$type][$checkName])) { + // resolve unqualified aliases + return new FullyQualified($this->aliases[$type][$checkName], $name->getAttributes()); + } + } + // No applicable aliases + return null; + } + private function getNamespaceRelativeName(string $name, string $lcName, int $type) { - return \rtrim($directory, '\\/') . \DIRECTORY_SEPARATOR . $file; + if (null === $this->namespace) { + return new Name($name); + } + if ($type === Stmt\Use_::TYPE_CONSTANT) { + // The constants true/false/null always resolve to the global symbols, even inside a + // namespace, so they may be used without qualification + if ($lcName === "true" || $lcName === "false" || $lcName === "null") { + return new Name($name); + } + } + $namespacePrefix = \strtolower($this->namespace . '\\'); + if (0 === \strpos($lcName, $namespacePrefix)) { + return new Name(\substr($name, \strlen($namespacePrefix))); + } + return null; } - /** - * Get example filepath based on sourcecode. - */ - private function getExamplePathFromSource(string $file) : string + private function normalizeConstName(string $name) { - return \sprintf('%s%s%s', \trim($this->getSourceDirectory(), '\\/'), \DIRECTORY_SEPARATOR, \trim($file, '"')); + $nsSep = \strrpos($name, '\\'); + if (\false === $nsSep) { + return $name; + } + // Constants have case-insensitive namespace and case-sensitive short-name + $ns = \substr($name, 0, $nsSep); + $shortName = \substr($name, $nsSep + 1); + return \strtolower($ns) . '\\' . $shortName; } } Important: each parameter in addition to the body variable for the `create` method must default to null, otherwise - * > it violates the constraint with the interface; it is recommended to use the {@see Assert::notNull()} method to - * > verify that a dependency is actually passed. - * - * This Factory also features a Service Locator component that is used to pass the right dependencies to the - * `create` method of a tag; each dependency should be registered as a service or as a parameter. - * - * When you want to use a Tag of your own with custom handling you need to call the `registerTagHandler` method, pass - * the name of the tag and a Fully Qualified Class Name pointing to a class that implements the Tag interface. - */ -final class StandardTagFactory implements \PHPUnit\phpDocumentor\Reflection\DocBlock\TagFactory -{ - /** PCRE regular expression matching a tag name. */ - public const REGEX_TAGNAME = '[\\w\\-\\_\\\\:]+'; - /** - * @var array> An array with a tag as a key, and an - * FQCN to a class that handles it as an array value. + * @param string $text Comment text (including comment delimiters like /*) + * @param int $startLine Line number the comment started on + * @param int $startFilePos File offset the comment started on + * @param int $startTokenPos Token offset the comment started on */ - private $tagHandlerMappings = [ - 'author' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Author::class, - 'covers' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Covers::class, - 'deprecated' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Deprecated::class, - // 'example' => '\phpDocumentor\Reflection\DocBlock\Tags\Example', - 'link' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Link::class, - 'method' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Method::class, - 'param' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Param::class, - 'property-read' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\PropertyRead::class, - 'property' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Property::class, - 'property-write' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite::class, - 'return' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Return_::class, - 'see' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\See::class, - 'since' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Since::class, - 'source' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Source::class, - 'throw' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Throws::class, - 'throws' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Throws::class, - 'uses' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Uses::class, - 'var' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Var_::class, - 'version' => \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Version::class, - ]; + public function __construct(string $text, int $startLine = -1, int $startFilePos = -1, int $startTokenPos = -1, int $endLine = -1, int $endFilePos = -1, int $endTokenPos = -1) + { + $this->text = $text; + $this->startLine = $startLine; + $this->startFilePos = $startFilePos; + $this->startTokenPos = $startTokenPos; + $this->endLine = $endLine; + $this->endFilePos = $endFilePos; + $this->endTokenPos = $endTokenPos; + } /** - * @var array> An array with a anotation s a key, and an - * FQCN to a class that handles it as an array value. + * Gets the comment text. + * + * @return string The comment text (including comment delimiters like /*) */ - private $annotationMappings = []; + public function getText() : string + { + return $this->text; + } /** - * @var ReflectionParameter[][] a lazy-loading cache containing parameters - * for each tagHandler that has been used. + * Gets the line number the comment started on. + * + * @return int Line number (or -1 if not available) */ - private $tagHandlerParameterCache = []; - /** @var FqsenResolver */ - private $fqsenResolver; + public function getStartLine() : int + { + return $this->startLine; + } /** - * @var mixed[] an array representing a simple Service Locator where we can store parameters and - * services that can be inserted into the Factory Methods of Tag Handlers. + * Gets the file offset the comment started on. + * + * @return int File offset (or -1 if not available) */ - private $serviceLocator = []; + public function getStartFilePos() : int + { + return $this->startFilePos; + } /** - * Initialize this tag factory with the means to resolve an FQSEN and optionally a list of tag handlers. - * - * If no tag handlers are provided than the default list in the {@see self::$tagHandlerMappings} property - * is used. - * - * @see self::registerTagHandler() to add a new tag handler to the existing default list. + * Gets the token offset the comment started on. * - * @param array> $tagHandlers + * @return int Token offset (or -1 if not available) */ - public function __construct(\PHPUnit\phpDocumentor\Reflection\FqsenResolver $fqsenResolver, ?array $tagHandlers = null) + public function getStartTokenPos() : int { - $this->fqsenResolver = $fqsenResolver; - if ($tagHandlers !== null) { - $this->tagHandlerMappings = $tagHandlers; - } - $this->addService($fqsenResolver, \PHPUnit\phpDocumentor\Reflection\FqsenResolver::class); - } - public function create(string $tagLine, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : \PHPUnit\phpDocumentor\Reflection\DocBlock\Tag - { - if (!$context) { - $context = new \PHPUnit\phpDocumentor\Reflection\Types\Context(''); - } - [$tagName, $tagBody] = $this->extractTagParts($tagLine); - return $this->createTag(\trim($tagBody), $tagName, $context); + return $this->startTokenPos; } /** - * @param mixed $value + * Gets the line number the comment ends on. + * + * @return int Line number (or -1 if not available) */ - public function addParameter(string $name, $value) : void + public function getEndLine() : int { - $this->serviceLocator[$name] = $value; + return $this->endLine; } - public function addService(object $service, ?string $alias = null) : void + /** + * Gets the file offset the comment ends on. + * + * @return int File offset (or -1 if not available) + */ + public function getEndFilePos() : int { - $this->serviceLocator[$alias ?: \get_class($service)] = $service; + return $this->endFilePos; } - public function registerTagHandler(string $tagName, string $handler) : void + /** + * Gets the token offset the comment ends on. + * + * @return int Token offset (or -1 if not available) + */ + public function getEndTokenPos() : int { - \PHPUnit\Webmozart\Assert\Assert::stringNotEmpty($tagName); - \PHPUnit\Webmozart\Assert\Assert::classExists($handler); - \PHPUnit\Webmozart\Assert\Assert::implementsInterface($handler, \PHPUnit\phpDocumentor\Reflection\DocBlock\Tag::class); - if (\strpos($tagName, '\\') && $tagName[0] !== '\\') { - throw new \InvalidArgumentException('A namespaced tag must have a leading backslash as it must be fully qualified'); - } - $this->tagHandlerMappings[$tagName] = $handler; + return $this->endTokenPos; } /** - * Extracts all components for a tag. + * Gets the line number the comment started on. * - * @return string[] + * @deprecated Use getStartLine() instead + * + * @return int Line number */ - private function extractTagParts(string $tagLine) : array + public function getLine() : int { - $matches = []; - if (!\preg_match('/^@(' . self::REGEX_TAGNAME . ')((?:[\\s\\(\\{])\\s*([^\\s].*)|$)/us', $tagLine, $matches)) { - throw new \InvalidArgumentException('The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors'); - } - if (\count($matches) < 3) { - $matches[] = ''; - } - return \array_slice($matches, 1); + return $this->startLine; } /** - * Creates a new tag object with the given name and body or returns null if the tag name was recognized but the - * body was invalid. + * Gets the file offset the comment started on. + * + * @deprecated Use getStartFilePos() instead + * + * @return int File offset */ - private function createTag(string $body, string $name, \PHPUnit\phpDocumentor\Reflection\Types\Context $context) : \PHPUnit\phpDocumentor\Reflection\DocBlock\Tag + public function getFilePos() : int { - $handlerClassName = $this->findHandlerClassName($name, $context); - $arguments = $this->getArgumentsForParametersFromWiring($this->fetchParametersForHandlerFactoryMethod($handlerClassName), $this->getServiceLocatorWithDynamicParameters($context, $name, $body)); - try { - $callable = [$handlerClassName, 'create']; - \PHPUnit\Webmozart\Assert\Assert::isCallable($callable); - /** @phpstan-var callable(string): ?Tag $callable */ - $tag = \call_user_func_array($callable, $arguments); - return $tag ?? \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\InvalidTag::create($body, $name); - } catch (\InvalidArgumentException $e) { - return \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\InvalidTag::create($body, $name)->withError($e); - } + return $this->startFilePos; } /** - * Determines the Fully Qualified Class Name of the Factory or Tag (containing a Factory Method `create`). + * Gets the token offset the comment started on. * - * @return class-string + * @deprecated Use getStartTokenPos() instead + * + * @return int Token offset */ - private function findHandlerClassName(string $tagName, \PHPUnit\phpDocumentor\Reflection\Types\Context $context) : string + public function getTokenPos() : int { - $handlerClassName = \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Generic::class; - if (isset($this->tagHandlerMappings[$tagName])) { - $handlerClassName = $this->tagHandlerMappings[$tagName]; - } elseif ($this->isAnnotation($tagName)) { - // TODO: Annotation support is planned for a later stage and as such is disabled for now - $tagName = (string) $this->fqsenResolver->resolve($tagName, $context); - if (isset($this->annotationMappings[$tagName])) { - $handlerClassName = $this->annotationMappings[$tagName]; - } - } - return $handlerClassName; + return $this->startTokenPos; } /** - * Retrieves the arguments that need to be passed to the Factory Method with the given Parameters. - * - * @param ReflectionParameter[] $parameters - * @param mixed[] $locator + * Gets the comment text. * - * @return mixed[] A series of values that can be passed to the Factory Method of the tag whose parameters - * is provided with this method. + * @return string The comment text (including comment delimiters like /*) */ - private function getArgumentsForParametersFromWiring(array $parameters, array $locator) : array + public function __toString() : string { - $arguments = []; - foreach ($parameters as $parameter) { - $type = $parameter->getType(); - $typeHint = null; - if ($type instanceof \ReflectionNamedType) { - $typeHint = $type->getName(); - if ($typeHint === 'self') { - $declaringClass = $parameter->getDeclaringClass(); - if ($declaringClass !== null) { - $typeHint = $declaringClass->getName(); - } - } - } - if (isset($locator[$typeHint])) { - $arguments[] = $locator[$typeHint]; - continue; - } - $parameterName = $parameter->getName(); - if (isset($locator[$parameterName])) { - $arguments[] = $locator[$parameterName]; - continue; - } - $arguments[] = null; - } - return $arguments; + return $this->text; } /** - * Retrieves a series of ReflectionParameter objects for the static 'create' method of the given - * tag handler class name. + * Gets the reformatted comment text. * - * @param class-string $handlerClassName + * "Reformatted" here means that we try to clean up the whitespace at the + * starts of the lines. This is necessary because we receive the comments + * without trailing whitespace on the first line, but with trailing whitespace + * on all subsequent lines. * - * @return ReflectionParameter[] + * @return mixed|string */ - private function fetchParametersForHandlerFactoryMethod(string $handlerClassName) : array + public function getReformattedText() { - if (!isset($this->tagHandlerParameterCache[$handlerClassName])) { - $methodReflection = new \ReflectionMethod($handlerClassName, 'create'); - $this->tagHandlerParameterCache[$handlerClassName] = $methodReflection->getParameters(); + $text = \trim($this->text); + $newlinePos = \strpos($text, "\n"); + if (\false === $newlinePos) { + // Single line comments don't need further processing + return $text; + } elseif (\preg_match('((*BSR_ANYCRLF)(*ANYCRLF)^.*(?:\\R\\s+\\*.*)+$)', $text)) { + // Multi line comment of the type + // + // /* + // * Some text. + // * Some more text. + // */ + // + // is handled by replacing the whitespace sequences before the * by a single space + return \preg_replace('(^\\s+\\*)m', ' *', $this->text); + } elseif (\preg_match('(^/\\*\\*?\\s*[\\r\\n])', $text) && \preg_match('(\\n(\\s*)\\*/$)', $text, $matches)) { + // Multi line comment of the type + // + // /* + // Some text. + // Some more text. + // */ + // + // is handled by removing the whitespace sequence on the line before the closing + // */ on all lines. So if the last line is " */", then " " is removed at the + // start of all lines. + return \preg_replace('(^' . \preg_quote($matches[1]) . ')m', '', $text); + } elseif (\preg_match('(^/\\*\\*?\\s*(?!\\s))', $text, $matches)) { + // Multi line comment of the type + // + // /* Some text. + // Some more text. + // Indented text. + // Even more text. */ + // + // is handled by removing the difference between the shortest whitespace prefix on all + // lines and the length of the "/* " opening sequence. + $prefixLen = $this->getShortestWhitespacePrefixLen(\substr($text, $newlinePos + 1)); + $removeLen = $prefixLen - \strlen($matches[0]); + return \preg_replace('(^\\s{' . $removeLen . '})m', '', $text); } - return $this->tagHandlerParameterCache[$handlerClassName]; + // No idea how to format this comment, so simply return as is + return $text; } /** - * Returns a copy of this class' Service Locator with added dynamic parameters, - * such as the tag's name, body and Context. + * Get length of shortest whitespace prefix (at the start of a line). * - * @param TypeContext $context The Context (namespace and aliasses) that may be - * passed and is used to resolve FQSENs. - * @param string $tagName The name of the tag that may be - * passed onto the factory method of the Tag class. - * @param string $tagBody The body of the tag that may be - * passed onto the factory method of the Tag class. + * If there is a line with no prefix whitespace, 0 is a valid return value. * - * @return mixed[] + * @param string $str String to check + * @return int Length in characters. Tabs count as single characters. */ - private function getServiceLocatorWithDynamicParameters(\PHPUnit\phpDocumentor\Reflection\Types\Context $context, string $tagName, string $tagBody) : array + private function getShortestWhitespacePrefixLen(string $str) : int { - return \array_merge($this->serviceLocator, ['name' => $tagName, 'body' => $tagBody, \PHPUnit\phpDocumentor\Reflection\Types\Context::class => $context]); + $lines = \explode("\n", $str); + $shortestPrefixLen = \INF; + foreach ($lines as $line) { + \preg_match('(^\\s*)', $line, $matches); + $prefixLen = \strlen($matches[0]); + if ($prefixLen < $shortestPrefixLen) { + $shortestPrefixLen = $prefixLen; + } + } + return $shortestPrefixLen; } /** - * Returns whether the given tag belongs to an annotation. - * - * @todo this method should be populated once we implement Annotation notation support. + * @return array + * @psalm-return array{nodeType:string, text:mixed, line:mixed, filePos:mixed} */ - private function isAnnotation(string $tagContent) : bool + public function jsonSerialize() : array { - // 1. Contains a namespace separator - // 2. Contains parenthesis - // 3. Is present in a list of known annotations (make the algorithm smart by first checking is the last part - // of the annotation class name matches the found tag name - return \false; + // Technically not a node, but we make it look like one anyway + $type = $this instanceof Comment\Doc ? 'Comment_Doc' : 'Comment'; + return [ + 'nodeType' => $type, + 'text' => $this->text, + // TODO: Rename these to include "start". + 'line' => $this->startLine, + 'filePos' => $this->startFilePos, + 'tokenPos' => $this->startTokenPos, + 'endLine' => $this->endLine, + 'endFilePos' => $this->endFilePos, + 'endTokenPos' => $this->endTokenPos, + ]; } } $handler FQCN of handler. + * Creates a parser instance. * - * @throws InvalidArgumentException If the tag name is not a string. - * @throws InvalidArgumentException If the tag name is namespaced (contains backslashes) but - * does not start with a backslash. - * @throws InvalidArgumentException If the handler is not a string. - * @throws InvalidArgumentException If the handler is not an existing class. - * @throws InvalidArgumentException If the handler does not implement the {@see Tag} interface. - */ - public function registerTagHandler(string $tagName, string $handler) : void; -} -indent = $indent; - $this->indentString = $indentString; - $this->isFirstLineIndented = $indentFirstLine; - $this->lineLength = $lineLength; - $this->tagFormatter = $tagFormatter ?: new \PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter(); + $this->lexer = $lexer; + if (isset($options['throwOnError'])) { + throw new \LogicException('"throwOnError" is no longer supported, use "errorHandler" instead'); + } + $this->initReduceCallbacks(); } /** - * Generate a DocBlock comment. + * Parses PHP code into a node tree. * - * @param DocBlock $docblock The DocBlock to serialize. + * If a non-throwing error handler is used, the parser will continue parsing after an error + * occurred and attempt to build a partial AST. * - * @return string The serialized doc block. + * @param string $code The source code to parse + * @param ErrorHandler|null $errorHandler Error handler to use for lexer/parser errors, defaults + * to ErrorHandler\Throwing. + * + * @return Node\Stmt[]|null Array of statements (or null non-throwing error handler is used and + * the parser was unable to recover from an error). */ - public function getDocComment(\PHPUnit\phpDocumentor\Reflection\DocBlock $docblock) : string - { - $indent = \str_repeat($this->indentString, $this->indent); - $firstIndent = $this->isFirstLineIndented ? $indent : ''; - // 3 === strlen(' * ') - $wrapLength = $this->lineLength ? $this->lineLength - \strlen($indent) - 3 : null; - $text = $this->removeTrailingSpaces($indent, $this->addAsterisksForEachLine($indent, $this->getSummaryAndDescriptionTextBlock($docblock, $wrapLength))); - $comment = $firstIndent . "/**\n"; - if ($text) { - $comment .= $indent . ' * ' . $text . "\n"; - $comment .= $indent . " *\n"; - } - $comment = $this->addTagBlock($docblock, $wrapLength, $indent, $comment); - return $comment . $indent . ' */'; - } - private function removeTrailingSpaces(string $indent, string $text) : string - { - return \str_replace(\sprintf("\n%s * \n", $indent), \sprintf("\n%s *\n", $indent), $text); - } - private function addAsterisksForEachLine(string $indent, string $text) : string - { - return \str_replace("\n", \sprintf("\n%s * ", $indent), $text); - } - private function getSummaryAndDescriptionTextBlock(\PHPUnit\phpDocumentor\Reflection\DocBlock $docblock, ?int $wrapLength) : string + public function parse(string $code, ErrorHandler $errorHandler = null) { - $text = $docblock->getSummary() . ((string) $docblock->getDescription() ? "\n\n" . $docblock->getDescription() : ''); - if ($wrapLength !== null) { - $text = \wordwrap($text, $wrapLength); - return $text; - } - return $text; + $this->errorHandler = $errorHandler ?: new ErrorHandler\Throwing(); + $this->lexer->startLexing($code, $this->errorHandler); + $result = $this->doParse(); + // Clear out some of the interior state, so we don't hold onto unnecessary + // memory between uses of the parser + $this->startAttributeStack = []; + $this->endAttributeStack = []; + $this->semStack = []; + $this->semValue = null; + return $result; } - private function addTagBlock(\PHPUnit\phpDocumentor\Reflection\DocBlock $docblock, ?int $wrapLength, string $indent, string $comment) : string + protected function doParse() { - foreach ($docblock->getTags() as $tag) { - $tagText = $this->tagFormatter->format($tag); - if ($wrapLength !== null) { - $tagText = \wordwrap($tagText, $wrapLength); - } - $tagText = \str_replace("\n", \sprintf("\n%s * ", $indent), $tagText); - $comment .= \sprintf("%s * %s\n", $indent, $tagText); - } - return $comment; - } -} -endAttributes = $endAttributes; + // Keep stack of start and end attributes + $this->startAttributeStack = []; + $this->endAttributeStack = [$endAttributes]; + // Start off in the initial state and keep a stack of previous states + $state = 0; + $stateStack = [$state]; + // Semantic value stack (contains values of tokens and semantic action results) + $this->semStack = []; + // Current position in the stack(s) + $stackPos = 0; + $this->errorState = 0; + for (;;) { + //$this->traceNewState($state, $symbol); + if ($this->actionBase[$state] === 0) { + $rule = $this->actionDefault[$state]; + } else { + if ($symbol === self::SYMBOL_NONE) { + // Fetch the next token id from the lexer and fetch additional info by-ref. + // The end attributes are fetched into a temporary variable and only set once the token is really + // shifted (not during read). Otherwise you would sometimes get off-by-one errors, when a rule is + // reduced after a token was read but not yet shifted. + $tokenId = $this->lexer->getNextToken($tokenValue, $startAttributes, $endAttributes); + // map the lexer token id to the internally used symbols + $symbol = $tokenId >= 0 && $tokenId < $this->tokenToSymbolMapSize ? $this->tokenToSymbol[$tokenId] : $this->invalidSymbol; + if ($symbol === $this->invalidSymbol) { + throw new \RangeException(\sprintf('The lexer returned an invalid token (id=%d, value=%s)', $tokenId, $tokenValue)); + } + // Allow productions to access the start attributes of the lookahead token. + $this->lookaheadStartAttributes = $startAttributes; + //$this->traceRead($symbol); + } + $idx = $this->actionBase[$state] + $symbol; + if (($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol || $state < $this->YY2TBLSTATE && ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $symbol) >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol) && ($action = $this->action[$idx]) !== $this->defaultAction) { + /* + * >= numNonLeafStates: shift and reduce + * > 0: shift + * = 0: accept + * < 0: reduce + * = -YYUNEXPECTED: error + */ + if ($action > 0) { + /* shift */ + //$this->traceShift($symbol); + ++$stackPos; + $stateStack[$stackPos] = $state = $action; + $this->semStack[$stackPos] = $tokenValue; + $this->startAttributeStack[$stackPos] = $startAttributes; + $this->endAttributeStack[$stackPos] = $endAttributes; + $this->endAttributes = $endAttributes; + $symbol = self::SYMBOL_NONE; + if ($this->errorState) { + --$this->errorState; + } + if ($action < $this->numNonLeafStates) { + continue; + } + /* $yyn >= numNonLeafStates means shift-and-reduce */ + $rule = $action - $this->numNonLeafStates; + } else { + $rule = -$action; + } + } else { + $rule = $this->actionDefault[$state]; + } + } + for (;;) { + if ($rule === 0) { + /* accept */ + //$this->traceAccept(); + return $this->semValue; + } elseif ($rule !== $this->unexpectedTokenRule) { + /* reduce */ + //$this->traceReduce($rule); + try { + $this->reduceCallbacks[$rule]($stackPos); + } catch (Error $e) { + if (-1 === $e->getStartLine() && isset($startAttributes['startLine'])) { + $e->setStartLine($startAttributes['startLine']); + } + $this->emitError($e); + // Can't recover from this type of error + return null; + } + /* Goto - shift nonterminal */ + $lastEndAttributes = $this->endAttributeStack[$stackPos]; + $ruleLength = $this->ruleToLength[$rule]; + $stackPos -= $ruleLength; + $nonTerminal = $this->ruleToNonTerminal[$rule]; + $idx = $this->gotoBase[$nonTerminal] + $stateStack[$stackPos]; + if ($idx >= 0 && $idx < $this->gotoTableSize && $this->gotoCheck[$idx] === $nonTerminal) { + $state = $this->goto[$idx]; + } else { + $state = $this->gotoDefault[$nonTerminal]; + } + ++$stackPos; + $stateStack[$stackPos] = $state; + $this->semStack[$stackPos] = $this->semValue; + $this->endAttributeStack[$stackPos] = $lastEndAttributes; + if ($ruleLength === 0) { + // Empty productions use the start attributes of the lookahead token. + $this->startAttributeStack[$stackPos] = $this->lookaheadStartAttributes; + } + } else { + /* error */ + switch ($this->errorState) { + case 0: + $msg = $this->getErrorMessage($symbol, $state); + $this->emitError(new Error($msg, $startAttributes + $endAttributes)); + // Break missing intentionally + case 1: + case 2: + $this->errorState = 3; + // Pop until error-expecting state uncovered + while (!(($idx = $this->actionBase[$state] + $this->errorSymbol) >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $this->errorSymbol || $state < $this->YY2TBLSTATE && ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $this->errorSymbol) >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $this->errorSymbol) || ($action = $this->action[$idx]) === $this->defaultAction) { + // Not totally sure about this + if ($stackPos <= 0) { + // Could not recover from error + return null; + } + $state = $stateStack[--$stackPos]; + //$this->tracePop($state); + } + //$this->traceShift($this->errorSymbol); + ++$stackPos; + $stateStack[$stackPos] = $state = $action; + // We treat the error symbol as being empty, so we reset the end attributes + // to the end attributes of the last non-error symbol + $this->startAttributeStack[$stackPos] = $this->lookaheadStartAttributes; + $this->endAttributeStack[$stackPos] = $this->endAttributeStack[$stackPos - 1]; + $this->endAttributes = $this->endAttributeStack[$stackPos - 1]; + break; + case 3: + if ($symbol === 0) { + // Reached EOF without recovering from error + return null; + } + //$this->traceDiscard($symbol); + $symbol = self::SYMBOL_NONE; + break 2; + } + } + if ($state < $this->numNonLeafStates) { + break; + } + /* >= numNonLeafStates means shift-and-reduce */ + $rule = $state - $this->numNonLeafStates; + } } - return $parts; + throw new \RuntimeException('Reached end of parser loop'); } -} -errorHandler->handleError($error); } -} -> $additionalTags - */ - public static function createInstance(array $additionalTags = []) : \PHPUnit\phpDocumentor\Reflection\DocBlockFactory; - /** - * @param string|object $docblock - */ - public function create($docblock, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null, ?\PHPUnit\phpDocumentor\Reflection\Location $location = null) : \PHPUnit\phpDocumentor\Reflection\DocBlock; -} -The MIT License (MIT) - -Copyright (c) 2010 Mike van Riel - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -descriptionFactory = $descriptionFactory; - $this->tagFactory = $tagFactory; + $expectedString = ''; + if ($expected = $this->getExpectedTokens($state)) { + $expectedString = ', expecting ' . \implode(' or ', $expected); + } + return 'Syntax error, unexpected ' . $this->symbolToName[$symbol] . $expectedString; } /** - * Factory method for easy instantiation. + * Get limited number of expected tokens in given state. * - * @param array> $additionalTags + * @param int $state State + * + * @return string[] Expected tokens. If too many, an empty array is returned. */ - public static function createInstance(array $additionalTags = []) : self + protected function getExpectedTokens(int $state) : array { - $fqsenResolver = new \PHPUnit\phpDocumentor\Reflection\FqsenResolver(); - $tagFactory = new \PHPUnit\phpDocumentor\Reflection\DocBlock\StandardTagFactory($fqsenResolver); - $descriptionFactory = new \PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory($tagFactory); - $tagFactory->addService($descriptionFactory); - $tagFactory->addService(new \PHPUnit\phpDocumentor\Reflection\TypeResolver($fqsenResolver)); - $docBlockFactory = new self($descriptionFactory, $tagFactory); - foreach ($additionalTags as $tagName => $tagHandler) { - $docBlockFactory->registerTagHandler($tagName, $tagHandler); + $expected = []; + $base = $this->actionBase[$state]; + foreach ($this->symbolToName as $symbol => $name) { + $idx = $base + $symbol; + if ($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol || $state < $this->YY2TBLSTATE && ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $symbol) >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol) { + if ($this->action[$idx] !== $this->unexpectedTokenRule && $this->action[$idx] !== $this->defaultAction && $symbol !== $this->errorSymbol) { + if (\count($expected) === 4) { + /* Too many expected tokens */ + return []; + } + $expected[] = $name; + } + } } - return $docBlockFactory; + return $expected; + } + /* + * Tracing functions used for debugging the parser. + */ + /* + protected function traceNewState($state, $symbol) { + echo '% State ' . $state + . ', Lookahead ' . ($symbol == self::SYMBOL_NONE ? '--none--' : $this->symbolToName[$symbol]) . "\n"; + } + + protected function traceRead($symbol) { + echo '% Reading ' . $this->symbolToName[$symbol] . "\n"; + } + + protected function traceShift($symbol) { + echo '% Shift ' . $this->symbolToName[$symbol] . "\n"; + } + + protected function traceAccept() { + echo "% Accepted.\n"; + } + + protected function traceReduce($n) { + echo '% Reduce by (' . $n . ') ' . $this->productions[$n] . "\n"; + } + + protected function tracePop($state) { + echo '% Recovering, uncovered state ' . $state . "\n"; + } + + protected function traceDiscard($symbol) { + echo '% Discard ' . $this->symbolToName[$symbol] . "\n"; } + */ + /* + * Helper functions invoked by semantic actions + */ /** - * @param object|string $docblock A string containing the DocBlock to parse or an object supporting the - * getDocComment method (such as a ReflectionClass object). + * Moves statements of semicolon-style namespaces into $ns->stmts and checks various error conditions. + * + * @param Node\Stmt[] $stmts + * @return Node\Stmt[] */ - public function create($docblock, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null, ?\PHPUnit\phpDocumentor\Reflection\Location $location = null) : \PHPUnit\phpDocumentor\Reflection\DocBlock + protected function handleNamespaces(array $stmts) : array { - if (\is_object($docblock)) { - if (!\method_exists($docblock, 'getDocComment')) { - $exceptionMessage = 'Invalid object passed; the given object must support the getDocComment method'; - throw new \InvalidArgumentException($exceptionMessage); + $hasErrored = \false; + $style = $this->getNamespacingStyle($stmts); + if (null === $style) { + // not namespaced, nothing to do + return $stmts; + } elseif ('brace' === $style) { + // For braced namespaces we only have to check that there are no invalid statements between the namespaces + $afterFirstNamespace = \false; + foreach ($stmts as $stmt) { + if ($stmt instanceof Node\Stmt\Namespace_) { + $afterFirstNamespace = \true; + } elseif (!$stmt instanceof Node\Stmt\HaltCompiler && !$stmt instanceof Node\Stmt\Nop && $afterFirstNamespace && !$hasErrored) { + $this->emitError(new Error('No code may exist outside of namespace {}', $stmt->getAttributes())); + $hasErrored = \true; + // Avoid one error for every statement + } } - $docblock = $docblock->getDocComment(); - \PHPUnit\Webmozart\Assert\Assert::string($docblock); - } - \PHPUnit\Webmozart\Assert\Assert::stringNotEmpty($docblock); - if ($context === null) { - $context = new \PHPUnit\phpDocumentor\Reflection\Types\Context(''); + return $stmts; + } else { + // For semicolon namespaces we have to move the statements after a namespace declaration into ->stmts + $resultStmts = []; + $targetStmts =& $resultStmts; + $lastNs = null; + foreach ($stmts as $stmt) { + if ($stmt instanceof Node\Stmt\Namespace_) { + if ($lastNs !== null) { + $this->fixupNamespaceAttributes($lastNs); + } + if ($stmt->stmts === null) { + $stmt->stmts = []; + $targetStmts =& $stmt->stmts; + $resultStmts[] = $stmt; + } else { + // This handles the invalid case of mixed style namespaces + $resultStmts[] = $stmt; + $targetStmts =& $resultStmts; + } + $lastNs = $stmt; + } elseif ($stmt instanceof Node\Stmt\HaltCompiler) { + // __halt_compiler() is not moved into the namespace + $resultStmts[] = $stmt; + } else { + $targetStmts[] = $stmt; + } + } + if ($lastNs !== null) { + $this->fixupNamespaceAttributes($lastNs); + } + return $resultStmts; } - $parts = $this->splitDocBlock($this->stripDocComment($docblock)); - [$templateMarker, $summary, $description, $tags] = $parts; - return new \PHPUnit\phpDocumentor\Reflection\DocBlock($summary, $description ? $this->descriptionFactory->create($description, $context) : null, $this->parseTagBlock($tags, $context), $context, $location, $templateMarker === '#@+', $templateMarker === '#@-'); } - /** - * @param class-string $handler - */ - public function registerTagHandler(string $tagName, string $handler) : void + private function fixupNamespaceAttributes(Node\Stmt\Namespace_ $stmt) { - $this->tagFactory->registerTagHandler($tagName, $handler); + // We moved the statements into the namespace node, as such the end of the namespace node + // needs to be extended to the end of the statements. + if (empty($stmt->stmts)) { + return; + } + // We only move the builtin end attributes here. This is the best we can do with the + // knowledge we have. + $endAttributes = ['endLine', 'endFilePos', 'endTokenPos']; + $lastStmt = $stmt->stmts[\count($stmt->stmts) - 1]; + foreach ($endAttributes as $endAttribute) { + if ($lastStmt->hasAttribute($endAttribute)) { + $stmt->setAttribute($endAttribute, $lastStmt->getAttribute($endAttribute)); + } + } } /** - * Strips the asterisks from the DocBlock comment. + * Determine namespacing style (semicolon or brace) * - * @param string $comment String containing the comment text. + * @param Node[] $stmts Top-level statements. + * + * @return null|string One of "semicolon", "brace" or null (no namespaces) */ - private function stripDocComment(string $comment) : string + private function getNamespacingStyle(array $stmts) { - $comment = \preg_replace('#[ \\t]*(?:\\/\\*\\*|\\*\\/|\\*)?[ \\t]?(.*)?#u', '$1', $comment); - \PHPUnit\Webmozart\Assert\Assert::string($comment); - $comment = \trim($comment); - // reg ex above is not able to remove */ from a single line docblock - if (\substr($comment, -2) === '*/') { - $comment = \trim(\substr($comment, 0, -2)); + $style = null; + $hasNotAllowedStmts = \false; + foreach ($stmts as $i => $stmt) { + if ($stmt instanceof Node\Stmt\Namespace_) { + $currentStyle = null === $stmt->stmts ? 'semicolon' : 'brace'; + if (null === $style) { + $style = $currentStyle; + if ($hasNotAllowedStmts) { + $this->emitError(new Error('Namespace declaration statement has to be the very first statement in the script', $stmt->getLine())); + } + } elseif ($style !== $currentStyle) { + $this->emitError(new Error('Cannot mix bracketed namespace declarations with unbracketed namespace declarations', $stmt->getLine())); + // Treat like semicolon style for namespace normalization + return 'semicolon'; + } + continue; + } + /* declare(), __halt_compiler() and nops can be used before a namespace declaration */ + if ($stmt instanceof Node\Stmt\Declare_ || $stmt instanceof Node\Stmt\HaltCompiler || $stmt instanceof Node\Stmt\Nop) { + continue; + } + /* There may be a hashbang line at the very start of the file */ + if ($i === 0 && $stmt instanceof Node\Stmt\InlineHTML && \preg_match('/\\A#!.*\\r?\\n\\z/', $stmt->value)) { + continue; + } + /* Everything else if forbidden before namespace declarations */ + $hasNotAllowedStmts = \true; } - return \str_replace(["\r\n", "\r"], "\n", $comment); + return $style; } - // phpcs:disable /** - * Splits the DocBlock into a template marker, summary, description and block of tags. - * - * @param string $comment Comment to split into the sub-parts. + * Fix up parsing of static property calls in PHP 5. * - * @return string[] containing the template marker (if any), summary, description and a string containing the tags. + * In PHP 5 A::$b[c][d] and A::$b[c][d]() have very different interpretation. The former is + * interpreted as (A::$b)[c][d], while the latter is the same as A::{$b[c][d]}(). We parse the + * latter as the former initially and this method fixes the AST into the correct form when we + * encounter the "()". * - * @author Mike van Riel for extending the regex with template marker support. + * @param Node\Expr\StaticPropertyFetch|Node\Expr\ArrayDimFetch $prop + * @param Node\Arg[] $args + * @param array $attributes * - * @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split. + * @return Expr\StaticCall */ - private function splitDocBlock(string $comment) : array + protected function fixupPhp5StaticPropCall($prop, array $args, array $attributes) : Expr\StaticCall { - // phpcs:enable - // Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This - // method does not split tags so we return this verbatim as the fourth result (tags). This saves us the - // performance impact of running a regular expression - if (\strpos($comment, '@') === 0) { - return ['', '', '', $comment]; + if ($prop instanceof Node\Expr\StaticPropertyFetch) { + $name = $prop->name instanceof VarLikeIdentifier ? $prop->name->toString() : $prop->name; + $var = new Expr\Variable($name, $prop->name->getAttributes()); + return new Expr\StaticCall($prop->class, $var, $args, $attributes); + } elseif ($prop instanceof Node\Expr\ArrayDimFetch) { + $tmp = $prop; + while ($tmp->var instanceof Node\Expr\ArrayDimFetch) { + $tmp = $tmp->var; + } + /** @var Expr\StaticPropertyFetch $staticProp */ + $staticProp = $tmp->var; + // Set start attributes to attributes of innermost node + $tmp = $prop; + $this->fixupStartAttributes($tmp, $staticProp->name); + while ($tmp->var instanceof Node\Expr\ArrayDimFetch) { + $tmp = $tmp->var; + $this->fixupStartAttributes($tmp, $staticProp->name); + } + $name = $staticProp->name instanceof VarLikeIdentifier ? $staticProp->name->toString() : $staticProp->name; + $tmp->var = new Expr\Variable($name, $staticProp->name->getAttributes()); + return new Expr\StaticCall($staticProp->class, $prop, $args, $attributes); + } else { + throw new \Exception(); } - // clears all extra horizontal whitespace from the line endings to prevent parsing issues - $comment = \preg_replace('/\\h*$/Sum', '', $comment); - \PHPUnit\Webmozart\Assert\Assert::string($comment); - /* - * Splits the docblock into a template marker, summary, description and tags section. - * - * - The template marker is empty, #@+ or #@- if the DocBlock starts with either of those (a newline may - * occur after it and will be stripped). - * - The short description is started from the first character until a dot is encountered followed by a - * newline OR two consecutive newlines (horizontal whitespace is taken into account to consider spacing - * errors). This is optional. - * - The long description, any character until a new line is encountered followed by an @ and word - * characters (a tag). This is optional. - * - Tags; the remaining characters - * - * Big thanks to RichardJ for contributing this Regular Expression - */ - \preg_match('/ - \\A - # 1. Extract the template marker - (?:(\\#\\@\\+|\\#\\@\\-)\\n?)? - - # 2. Extract the summary - (?: - (?! @\\pL ) # The summary may not start with an @ - ( - [^\\n.]+ - (?: - (?! \\. \\n | \\n{2} ) # End summary upon a dot followed by newline or two newlines - [\\n.]* (?! [ \\t]* @\\pL ) # End summary when an @ is found as first character on a new line - [^\\n.]+ # Include anything else - )* - \\.? - )? - ) - - # 3. Extract the description - (?: - \\s* # Some form of whitespace _must_ precede a description because a summary must be there - (?! @\\pL ) # The description may not start with an @ - ( - [^\\n]+ - (?: \\n+ - (?! [ \\t]* @\\pL ) # End description when an @ is found as first character on a new line - [^\\n]+ # Include anything else - )* - ) - )? - - # 4. Extract the tags (anything that follows) - (\\s+ [\\s\\S]*)? # everything that follows - /ux', $comment, $matches); - \array_shift($matches); - while (\count($matches) < 4) { - $matches[] = ''; + } + protected function fixupStartAttributes(Node $to, Node $from) + { + $startAttributes = ['startLine', 'startFilePos', 'startTokenPos']; + foreach ($startAttributes as $startAttribute) { + if ($from->hasAttribute($startAttribute)) { + $to->setAttribute($startAttribute, $from->getAttribute($startAttribute)); + } } - return $matches; + } + protected function handleBuiltinTypes(Name $name) + { + $builtinTypes = ['bool' => \true, 'int' => \true, 'float' => \true, 'string' => \true, 'iterable' => \true, 'void' => \true, 'object' => \true, 'null' => \true, 'false' => \true, 'mixed' => \true, 'never' => \true]; + if (!$name->isUnqualified()) { + return $name; + } + $lowerName = $name->toLowerString(); + if (!isset($builtinTypes[$lowerName])) { + return $name; + } + return new Node\Identifier($lowerName, $name->getAttributes()); } /** - * Creates the tag objects. + * Get combined start and end attributes at a stack location * - * @param string $tags Tag block to parse. - * @param Types\Context $context Context of the parsed Tag + * @param int $pos Stack location * - * @return DocBlock\Tag[] + * @return array Combined start and end attributes */ - private function parseTagBlock(string $tags, \PHPUnit\phpDocumentor\Reflection\Types\Context $context) : array + protected function getAttributesAt(int $pos) : array { - $tags = $this->filterTagBlock($tags); - if ($tags === null) { - return []; + return $this->startAttributeStack[$pos] + $this->endAttributeStack[$pos]; + } + protected function getFloatCastKind(string $cast) : int + { + $cast = \strtolower($cast); + if (\strpos($cast, 'float') !== \false) { + return Double::KIND_FLOAT; } - $result = []; - $lines = $this->splitTagBlockIntoTagLines($tags); - foreach ($lines as $key => $tagLine) { - $result[$key] = $this->tagFactory->create(\trim($tagLine), $context); + if (\strpos($cast, 'real') !== \false) { + return Double::KIND_REAL; + } + return Double::KIND_DOUBLE; + } + protected function parseLNumber($str, $attributes, $allowInvalidOctal = \false) + { + try { + return LNumber::fromString($str, $attributes, $allowInvalidOctal); + } catch (Error $error) { + $this->emitError($error); + // Use dummy value + return new LNumber(0, $attributes); } - return $result; } /** - * @return string[] + * Parse a T_NUM_STRING token into either an integer or string node. + * + * @param string $str Number string + * @param array $attributes Attributes + * + * @return LNumber|String_ Integer or string node. */ - private function splitTagBlockIntoTagLines(string $tags) : array + protected function parseNumString(string $str, array $attributes) { - $result = []; - foreach (\explode("\n", $tags) as $tagLine) { - if ($tagLine !== '' && \strpos($tagLine, '@') === 0) { - $result[] = $tagLine; - } else { - $result[\count($result) - 1] .= "\n" . $tagLine; - } + if (!\preg_match('/^(?:0|-?[1-9][0-9]*)$/', $str)) { + return new String_($str, $attributes); } - return $result; + $num = +$str; + if (!\is_int($num)) { + return new String_($str, $attributes); + } + return new LNumber($num, $attributes); } - private function filterTagBlock(string $tags) : ?string + protected function stripIndentation(string $string, int $indentLen, string $indentChar, bool $newlineAtStart, bool $newlineAtEnd, array $attributes) { - $tags = \trim($tags); - if (!$tags) { - return null; + if ($indentLen === 0) { + return $string; } - if ($tags[0] !== '@') { - // @codeCoverageIgnoreStart - // Can't simulate this; this only happens if there is an error with the parsing of the DocBlock that - // we didn't foresee. - throw new \LogicException('A tag block started with text instead of an at-sign(@): ' . $tags); - // @codeCoverageIgnoreEnd + $start = $newlineAtStart ? '(?:(?<=\\n)|\\A)' : '(?<=\\n)'; + $end = $newlineAtEnd ? '(?:(?=[\\r\\n])|\\z)' : '(?=[\\r\\n])'; + $regex = '/' . $start . '([ \\t]*)(' . $end . ')?/'; + return \preg_replace_callback($regex, function ($matches) use($indentLen, $indentChar, $attributes) { + $prefix = \substr($matches[1], 0, $indentLen); + if (\false !== \strpos($prefix, $indentChar === " " ? "\t" : " ")) { + $this->emitError(new Error('Invalid indentation - tabs and spaces cannot be mixed', $attributes)); + } elseif (\strlen($prefix) < $indentLen && !isset($matches[2])) { + $this->emitError(new Error('Invalid body indentation level ' . '(expecting an indentation level of at least ' . $indentLen . ')', $attributes)); + } + return \substr($matches[0], \strlen($prefix)); + }, $string); + } + protected function parseDocString(string $startToken, $contents, string $endToken, array $attributes, array $endTokenAttributes, bool $parseUnicodeEscape) + { + $kind = \strpos($startToken, "'") === \false ? String_::KIND_HEREDOC : String_::KIND_NOWDOC; + $regex = '/\\A[bB]?<<<[ \\t]*[\'"]?([a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*)[\'"]?(?:\\r\\n|\\n|\\r)\\z/'; + $result = \preg_match($regex, $startToken, $matches); + \assert($result === 1); + $label = $matches[1]; + $result = \preg_match('/\\A[ \\t]*/', $endToken, $matches); + \assert($result === 1); + $indentation = $matches[0]; + $attributes['kind'] = $kind; + $attributes['docLabel'] = $label; + $attributes['docIndentation'] = $indentation; + $indentHasSpaces = \false !== \strpos($indentation, " "); + $indentHasTabs = \false !== \strpos($indentation, "\t"); + if ($indentHasSpaces && $indentHasTabs) { + $this->emitError(new Error('Invalid indentation - tabs and spaces cannot be mixed', $endTokenAttributes)); + // Proceed processing as if this doc string is not indented + $indentation = ''; + } + $indentLen = \strlen($indentation); + $indentChar = $indentHasSpaces ? " " : "\t"; + if (\is_string($contents)) { + if ($contents === '') { + return new String_('', $attributes); + } + $contents = $this->stripIndentation($contents, $indentLen, $indentChar, \true, \true, $attributes); + $contents = \preg_replace('~(\\r\\n|\\n|\\r)\\z~', '', $contents); + if ($kind === String_::KIND_HEREDOC) { + $contents = String_::parseEscapeSequences($contents, null, $parseUnicodeEscape); + } + return new String_($contents, $attributes); + } else { + \assert(\count($contents) > 0); + if (!$contents[0] instanceof Node\Scalar\EncapsedStringPart) { + // If there is no leading encapsed string part, pretend there is an empty one + $this->stripIndentation('', $indentLen, $indentChar, \true, \false, $contents[0]->getAttributes()); + } + $newContents = []; + foreach ($contents as $i => $part) { + if ($part instanceof Node\Scalar\EncapsedStringPart) { + $isLast = $i === \count($contents) - 1; + $part->value = $this->stripIndentation($part->value, $indentLen, $indentChar, $i === 0, $isLast, $part->getAttributes()); + $part->value = String_::parseEscapeSequences($part->value, null, $parseUnicodeEscape); + if ($isLast) { + $part->value = \preg_replace('~(\\r\\n|\\n|\\r)\\z~', '', $part->value); + } + if ('' === $part->value) { + continue; + } + } + $newContents[] = $part; + } + return new Encapsed($newContents, $attributes); } - return $tags; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Timer; - -use function array_pop; -use function hrtime; -final class Timer -{ /** - * @psalm-var list + * Create attributes for a zero-length common-capturing nop. + * + * @param Comment[] $comments + * @return array */ - private $startTimes = []; - public function start() : void + protected function createCommentNopAttributes(array $comments) { - $this->startTimes[] = (float) \hrtime(\true); + $comment = $comments[\count($comments) - 1]; + $commentEndLine = $comment->getEndLine(); + $commentEndFilePos = $comment->getEndFilePos(); + $commentEndTokenPos = $comment->getEndTokenPos(); + $attributes = ['comments' => $comments]; + if (-1 !== $commentEndLine) { + $attributes['startLine'] = $commentEndLine; + $attributes['endLine'] = $commentEndLine; + } + if (-1 !== $commentEndFilePos) { + $attributes['startFilePos'] = $commentEndFilePos + 1; + $attributes['endFilePos'] = $commentEndFilePos; + } + if (-1 !== $commentEndTokenPos) { + $attributes['startTokenPos'] = $commentEndTokenPos + 1; + $attributes['endTokenPos'] = $commentEndTokenPos; + } + return $attributes; } - /** - * @throws NoActiveTimerException - */ - public function stop() : \PHPUnit\SebastianBergmann\Timer\Duration + protected function checkModifier($a, $b, $modifierPos) { - if (empty($this->startTimes)) { - throw new \PHPUnit\SebastianBergmann\Timer\NoActiveTimerException('Timer::start() has to be called before Timer::stop()'); + // Jumping through some hoops here because verifyModifier() is also used elsewhere + try { + Class_::verifyModifier($a, $b); + } catch (Error $error) { + $error->setAttributes($this->getAttributesAt($modifierPos)); + $this->emitError($error); + } + } + protected function checkParam(Param $node) + { + if ($node->variadic && null !== $node->default) { + $this->emitError(new Error('Variadic parameter cannot have a default value', $node->default->getAttributes())); + } + } + protected function checkTryCatch(TryCatch $node) + { + if (empty($node->catches) && null === $node->finally) { + $this->emitError(new Error('Cannot use try without catch or finally', $node->getAttributes())); + } + } + protected function checkNamespace(Namespace_ $node) + { + if (null !== $node->stmts) { + foreach ($node->stmts as $stmt) { + if ($stmt instanceof Namespace_) { + $this->emitError(new Error('Namespace declarations cannot be nested', $stmt->getAttributes())); + } + } + } + } + private function checkClassName($name, $namePos) + { + if (null !== $name && $name->isSpecialClassName()) { + $this->emitError(new Error(\sprintf('Cannot use \'%s\' as class name as it is reserved', $name), $this->getAttributesAt($namePos))); + } + } + private function checkImplementedInterfaces(array $interfaces) + { + foreach ($interfaces as $interface) { + if ($interface->isSpecialClassName()) { + $this->emitError(new Error(\sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface), $interface->getAttributes())); + } + } + } + protected function checkClass(Class_ $node, $namePos) + { + $this->checkClassName($node->name, $namePos); + if ($node->extends && $node->extends->isSpecialClassName()) { + $this->emitError(new Error(\sprintf('Cannot use \'%s\' as class name as it is reserved', $node->extends), $node->extends->getAttributes())); + } + $this->checkImplementedInterfaces($node->implements); + } + protected function checkInterface(Interface_ $node, $namePos) + { + $this->checkClassName($node->name, $namePos); + $this->checkImplementedInterfaces($node->extends); + } + protected function checkEnum(Enum_ $node, $namePos) + { + $this->checkClassName($node->name, $namePos); + $this->checkImplementedInterfaces($node->implements); + } + protected function checkClassMethod(ClassMethod $node, $modifierPos) + { + if ($node->flags & Class_::MODIFIER_STATIC) { + switch ($node->name->toLowerString()) { + case '__construct': + $this->emitError(new Error(\sprintf('Constructor %s() cannot be static', $node->name), $this->getAttributesAt($modifierPos))); + break; + case '__destruct': + $this->emitError(new Error(\sprintf('Destructor %s() cannot be static', $node->name), $this->getAttributesAt($modifierPos))); + break; + case '__clone': + $this->emitError(new Error(\sprintf('Clone method %s() cannot be static', $node->name), $this->getAttributesAt($modifierPos))); + break; + } + } + if ($node->flags & Class_::MODIFIER_READONLY) { + $this->emitError(new Error(\sprintf('Method %s() cannot be readonly', $node->name), $this->getAttributesAt($modifierPos))); + } + } + protected function checkClassConst(ClassConst $node, $modifierPos) + { + if ($node->flags & Class_::MODIFIER_STATIC) { + $this->emitError(new Error("Cannot use 'static' as constant modifier", $this->getAttributesAt($modifierPos))); + } + if ($node->flags & Class_::MODIFIER_ABSTRACT) { + $this->emitError(new Error("Cannot use 'abstract' as constant modifier", $this->getAttributesAt($modifierPos))); + } + if ($node->flags & Class_::MODIFIER_READONLY) { + $this->emitError(new Error("Cannot use 'readonly' as constant modifier", $this->getAttributesAt($modifierPos))); + } + } + protected function checkProperty(Property $node, $modifierPos) + { + if ($node->flags & Class_::MODIFIER_ABSTRACT) { + $this->emitError(new Error('Properties cannot be declared abstract', $this->getAttributesAt($modifierPos))); + } + if ($node->flags & Class_::MODIFIER_FINAL) { + $this->emitError(new Error('Properties cannot be declared final', $this->getAttributesAt($modifierPos))); + } + } + protected function checkUseUse(UseUse $node, $namePos) + { + if ($node->alias && $node->alias->isSpecialClassName()) { + $this->emitError(new Error(\sprintf('Cannot use %s as %s because \'%2$s\' is a special class name', $node->name, $node->alias), $this->getAttributesAt($namePos))); } - return \PHPUnit\SebastianBergmann\Timer\Duration::fromNanoseconds((float) \hrtime(\true) - \array_pop($this->startTimes)); } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Timer; +namespace PHPUnit\PhpParser; -use Throwable; -interface Exception extends \Throwable +class ConstExprEvaluationException extends \Exception { } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Timer; - -use LogicException; -final class NoActiveTimerException extends \LogicException implements \PHPUnit\SebastianBergmann\Timer\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. +/** + * @codeCoverageIgnore */ -namespace PHPUnit\SebastianBergmann\Timer; - -use RuntimeException; -final class TimeSinceStartOfRequestNotAvailableException extends \RuntimeException implements \PHPUnit\SebastianBergmann\Timer\Exception +class NodeVisitorAbstract implements NodeVisitor { + public function beforeTraverse(array $nodes) + { + return null; + } + public function enterNode(Node $node) + { + return null; + } + public function leaveNode(Node $node) + { + return null; + } + public function afterTraverse(array $nodes) + { + return null; + } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Timer; +namespace PHPUnit\PhpParser; -use function floor; -use function sprintf; -/** - * @psalm-immutable - */ -final class Duration +use PHPUnit\PhpParser\Node\Arg; +use PHPUnit\PhpParser\Node\Expr; +use PHPUnit\PhpParser\Node\Expr\BinaryOp\Concat; +use PHPUnit\PhpParser\Node\Identifier; +use PHPUnit\PhpParser\Node\Name; +use PHPUnit\PhpParser\Node\Scalar\String_; +use PHPUnit\PhpParser\Node\Stmt\Use_; +class BuilderFactory { /** - * @var float - */ - private $nanoseconds; - /** - * @var int - */ - private $hours; - /** - * @var int + * Creates an attribute node. + * + * @param string|Name $name Name of the attribute + * @param array $args Attribute named arguments + * + * @return Node\Attribute */ - private $minutes; + public function attribute($name, array $args = []) : Node\Attribute + { + return new Node\Attribute(BuilderHelpers::normalizeName($name), $this->args($args)); + } /** - * @var int + * Creates a namespace builder. + * + * @param null|string|Node\Name $name Name of the namespace + * + * @return Builder\Namespace_ The created namespace builder */ - private $seconds; + public function namespace($name) : Builder\Namespace_ + { + return new Builder\Namespace_($name); + } /** - * @var int + * Creates a class builder. + * + * @param string $name Name of the class + * + * @return Builder\Class_ The created class builder */ - private $milliseconds; - public static function fromMicroseconds(float $microseconds) : self + public function class(string $name) : Builder\Class_ { - return new self($microseconds * 1000); + return new Builder\Class_($name); } - public static function fromNanoseconds(float $nanoseconds) : self + /** + * Creates an interface builder. + * + * @param string $name Name of the interface + * + * @return Builder\Interface_ The created interface builder + */ + public function interface(string $name) : Builder\Interface_ { - return new self($nanoseconds); + return new Builder\Interface_($name); } - private function __construct(float $nanoseconds) + /** + * Creates a trait builder. + * + * @param string $name Name of the trait + * + * @return Builder\Trait_ The created trait builder + */ + public function trait(string $name) : Builder\Trait_ { - $this->nanoseconds = $nanoseconds; - $timeInMilliseconds = $nanoseconds / 1000000; - $hours = \floor($timeInMilliseconds / 60 / 60 / 1000); - $hoursInMilliseconds = $hours * 60 * 60 * 1000; - $minutes = \floor($timeInMilliseconds / 60 / 1000) % 60; - $minutesInMilliseconds = $minutes * 60 * 1000; - $seconds = \floor(($timeInMilliseconds - $hoursInMilliseconds - $minutesInMilliseconds) / 1000); - $secondsInMilliseconds = $seconds * 1000; - $milliseconds = $timeInMilliseconds - $hoursInMilliseconds - $minutesInMilliseconds - $secondsInMilliseconds; - $this->hours = (int) $hours; - $this->minutes = $minutes; - $this->seconds = (int) $seconds; - $this->milliseconds = (int) $milliseconds; + return new Builder\Trait_($name); } - public function asNanoseconds() : float + /** + * Creates a trait use builder. + * + * @param Node\Name|string ...$traits Trait names + * + * @return Builder\TraitUse The create trait use builder + */ + public function useTrait(...$traits) : Builder\TraitUse { - return $this->nanoseconds; + return new Builder\TraitUse(...$traits); } - public function asMicroseconds() : float + /** + * Creates a trait use adaptation builder. + * + * @param Node\Name|string|null $trait Trait name + * @param Node\Identifier|string $method Method name + * + * @return Builder\TraitUseAdaptation The create trait use adaptation builder + */ + public function traitUseAdaptation($trait, $method = null) : Builder\TraitUseAdaptation { - return $this->nanoseconds / 1000; + if ($method === null) { + $method = $trait; + $trait = null; + } + return new Builder\TraitUseAdaptation($trait, $method); } - public function asMilliseconds() : float + /** + * Creates a method builder. + * + * @param string $name Name of the method + * + * @return Builder\Method The created method builder + */ + public function method(string $name) : Builder\Method { - return $this->nanoseconds / 1000000; + return new Builder\Method($name); } - public function asSeconds() : float + /** + * Creates a parameter builder. + * + * @param string $name Name of the parameter + * + * @return Builder\Param The created parameter builder + */ + public function param(string $name) : Builder\Param { - return $this->nanoseconds / 1000000000; + return new Builder\Param($name); } - public function asString() : string + /** + * Creates a property builder. + * + * @param string $name Name of the property + * + * @return Builder\Property The created property builder + */ + public function property(string $name) : Builder\Property { - $result = ''; - if ($this->hours > 0) { - $result = \sprintf('%02d', $this->hours) . ':'; - } - $result .= \sprintf('%02d', $this->minutes) . ':'; - $result .= \sprintf('%02d', $this->seconds); - if ($this->milliseconds > 0) { - $result .= '.' . \sprintf('%03d', $this->milliseconds); - } - return $result; + return new Builder\Property($name); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Timer; - -use function is_float; -use function memory_get_peak_usage; -use function microtime; -use function sprintf; -final class ResourceUsageFormatter -{ /** - * @psalm-var array + * Creates a function builder. + * + * @param string $name Name of the function + * + * @return Builder\Function_ The created function builder */ - private const SIZES = ['GB' => 1073741824, 'MB' => 1048576, 'KB' => 1024]; - public function resourceUsage(\PHPUnit\SebastianBergmann\Timer\Duration $duration) : string + public function function(string $name) : Builder\Function_ { - return \sprintf('Time: %s, Memory: %s', $duration->asString(), $this->bytesToString(\memory_get_peak_usage(\true))); + return new Builder\Function_($name); } /** - * @throws TimeSinceStartOfRequestNotAvailableException + * Creates a namespace/class use builder. + * + * @param Node\Name|string $name Name of the entity (namespace or class) to alias + * + * @return Builder\Use_ The created use builder */ - public function resourceUsageSinceStartOfRequest() : string + public function use($name) : Builder\Use_ { - if (!isset($_SERVER['REQUEST_TIME_FLOAT'])) { - throw new \PHPUnit\SebastianBergmann\Timer\TimeSinceStartOfRequestNotAvailableException('Cannot determine time at which the request started because $_SERVER[\'REQUEST_TIME_FLOAT\'] is not available'); - } - if (!\is_float($_SERVER['REQUEST_TIME_FLOAT'])) { - throw new \PHPUnit\SebastianBergmann\Timer\TimeSinceStartOfRequestNotAvailableException('Cannot determine time at which the request started because $_SERVER[\'REQUEST_TIME_FLOAT\'] is not of type float'); - } - return $this->resourceUsage(\PHPUnit\SebastianBergmann\Timer\Duration::fromMicroseconds(1000000 * (\microtime(\true) - $_SERVER['REQUEST_TIME_FLOAT']))); + return new Builder\Use_($name, Use_::TYPE_NORMAL); } - private function bytesToString(int $bytes) : string + /** + * Creates a function use builder. + * + * @param Node\Name|string $name Name of the function to alias + * + * @return Builder\Use_ The created use function builder + */ + public function useFunction($name) : Builder\Use_ { - foreach (self::SIZES as $unit => $value) { - if ($bytes >= $value) { - return \sprintf('%.2f %s', $bytes >= 1024 ? $bytes / $value : $bytes, $unit); - } - } - // @codeCoverageIgnoreStart - return $bytes . ' byte' . ($bytes !== 1 ? 's' : ''); - // @codeCoverageIgnoreEnd + return new Builder\Use_($name, Use_::TYPE_FUNCTION); } -} -phpunit/php-timer - -Copyright (c) 2010-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\ResourceOperations; - -final class ResourceOperations -{ /** - * @return string[] + * Creates a constant use builder. + * + * @param Node\Name|string $name Name of the const to alias + * + * @return Builder\Use_ The created use const builder */ - public static function getFunctions() : array + public function useConst($name) : Builder\Use_ { - return ['Directory::close', 'Directory::read', 'Directory::rewind', 'DirectoryIterator::openFile', 'FilesystemIterator::openFile', 'Gmagick::readimagefile', 'HttpResponse::getRequestBodyStream', 'HttpResponse::getStream', 'HttpResponse::setStream', 'Imagick::pingImageFile', 'Imagick::readImageFile', 'Imagick::writeImageFile', 'Imagick::writeImagesFile', 'MongoGridFSCursor::__construct', 'MongoGridFSFile::getResource', 'MysqlndUhConnection::stmtInit', 'MysqlndUhConnection::storeResult', 'MysqlndUhConnection::useResult', 'PDF_activate_item', 'PDF_add_launchlink', 'PDF_add_locallink', 'PDF_add_nameddest', 'PDF_add_note', 'PDF_add_pdflink', 'PDF_add_table_cell', 'PDF_add_textflow', 'PDF_add_thumbnail', 'PDF_add_weblink', 'PDF_arc', 'PDF_arcn', 'PDF_attach_file', 'PDF_begin_document', 'PDF_begin_font', 'PDF_begin_glyph', 'PDF_begin_item', 'PDF_begin_layer', 'PDF_begin_page', 'PDF_begin_page_ext', 'PDF_begin_pattern', 'PDF_begin_template', 'PDF_begin_template_ext', 'PDF_circle', 'PDF_clip', 'PDF_close', 'PDF_close_image', 'PDF_close_pdi', 'PDF_close_pdi_page', 'PDF_closepath', 'PDF_closepath_fill_stroke', 'PDF_closepath_stroke', 'PDF_concat', 'PDF_continue_text', 'PDF_create_3dview', 'PDF_create_action', 'PDF_create_annotation', 'PDF_create_bookmark', 'PDF_create_field', 'PDF_create_fieldgroup', 'PDF_create_gstate', 'PDF_create_pvf', 'PDF_create_textflow', 'PDF_curveto', 'PDF_define_layer', 'PDF_delete', 'PDF_delete_pvf', 'PDF_delete_table', 'PDF_delete_textflow', 'PDF_encoding_set_char', 'PDF_end_document', 'PDF_end_font', 'PDF_end_glyph', 'PDF_end_item', 'PDF_end_layer', 'PDF_end_page', 'PDF_end_page_ext', 'PDF_end_pattern', 'PDF_end_template', 'PDF_endpath', 'PDF_fill', 'PDF_fill_imageblock', 'PDF_fill_pdfblock', 'PDF_fill_stroke', 'PDF_fill_textblock', 'PDF_findfont', 'PDF_fit_image', 'PDF_fit_pdi_page', 'PDF_fit_table', 'PDF_fit_textflow', 'PDF_fit_textline', 'PDF_get_apiname', 'PDF_get_buffer', 'PDF_get_errmsg', 'PDF_get_errnum', 'PDF_get_parameter', 'PDF_get_pdi_parameter', 'PDF_get_pdi_value', 'PDF_get_value', 'PDF_info_font', 'PDF_info_matchbox', 'PDF_info_table', 'PDF_info_textflow', 'PDF_info_textline', 'PDF_initgraphics', 'PDF_lineto', 'PDF_load_3ddata', 'PDF_load_font', 'PDF_load_iccprofile', 'PDF_load_image', 'PDF_makespotcolor', 'PDF_moveto', 'PDF_new', 'PDF_open_ccitt', 'PDF_open_file', 'PDF_open_image', 'PDF_open_image_file', 'PDF_open_memory_image', 'PDF_open_pdi', 'PDF_open_pdi_document', 'PDF_open_pdi_page', 'PDF_pcos_get_number', 'PDF_pcos_get_stream', 'PDF_pcos_get_string', 'PDF_place_image', 'PDF_place_pdi_page', 'PDF_process_pdi', 'PDF_rect', 'PDF_restore', 'PDF_resume_page', 'PDF_rotate', 'PDF_save', 'PDF_scale', 'PDF_set_border_color', 'PDF_set_border_dash', 'PDF_set_border_style', 'PDF_set_gstate', 'PDF_set_info', 'PDF_set_layer_dependency', 'PDF_set_parameter', 'PDF_set_text_pos', 'PDF_set_value', 'PDF_setcolor', 'PDF_setdash', 'PDF_setdashpattern', 'PDF_setflat', 'PDF_setfont', 'PDF_setgray', 'PDF_setgray_fill', 'PDF_setgray_stroke', 'PDF_setlinecap', 'PDF_setlinejoin', 'PDF_setlinewidth', 'PDF_setmatrix', 'PDF_setmiterlimit', 'PDF_setrgbcolor', 'PDF_setrgbcolor_fill', 'PDF_setrgbcolor_stroke', 'PDF_shading', 'PDF_shading_pattern', 'PDF_shfill', 'PDF_show', 'PDF_show_boxed', 'PDF_show_xy', 'PDF_skew', 'PDF_stringwidth', 'PDF_stroke', 'PDF_suspend_page', 'PDF_translate', 'PDF_utf16_to_utf8', 'PDF_utf32_to_utf16', 'PDF_utf8_to_utf16', 'PDO::pgsqlLOBOpen', 'RarEntry::getStream', 'SQLite3::openBlob', 'SWFMovie::saveToFile', 'SplFileInfo::openFile', 'SplFileObject::openFile', 'SplTempFileObject::openFile', 'V8Js::compileString', 'V8Js::executeScript', 'Vtiful\\Kernel\\Excel::setColumn', 'Vtiful\\Kernel\\Excel::setRow', 'Vtiful\\Kernel\\Format::align', 'Vtiful\\Kernel\\Format::bold', 'Vtiful\\Kernel\\Format::italic', 'Vtiful\\Kernel\\Format::underline', 'XMLWriter::openMemory', 'XMLWriter::openURI', 'ZipArchive::getStream', 'Zookeeper::setLogStream', 'apc_bin_dumpfile', 'apc_bin_loadfile', 'bbcode_add_element', 'bbcode_add_smiley', 'bbcode_create', 'bbcode_destroy', 'bbcode_parse', 'bbcode_set_arg_parser', 'bbcode_set_flags', 'bcompiler_read', 'bcompiler_write_class', 'bcompiler_write_constant', 'bcompiler_write_exe_footer', 'bcompiler_write_file', 'bcompiler_write_footer', 'bcompiler_write_function', 'bcompiler_write_functions_from_file', 'bcompiler_write_header', 'bcompiler_write_included_filename', 'bzclose', 'bzerrno', 'bzerror', 'bzerrstr', 'bzflush', 'bzopen', 'bzread', 'bzwrite', 'cairo_surface_write_to_png', 'closedir', 'copy', 'crack_closedict', 'crack_opendict', 'cubrid_bind', 'cubrid_close_prepare', 'cubrid_close_request', 'cubrid_col_get', 'cubrid_col_size', 'cubrid_column_names', 'cubrid_column_types', 'cubrid_commit', 'cubrid_connect', 'cubrid_connect_with_url', 'cubrid_current_oid', 'cubrid_db_parameter', 'cubrid_disconnect', 'cubrid_drop', 'cubrid_fetch', 'cubrid_free_result', 'cubrid_get', 'cubrid_get_autocommit', 'cubrid_get_charset', 'cubrid_get_class_name', 'cubrid_get_db_parameter', 'cubrid_get_query_timeout', 'cubrid_get_server_info', 'cubrid_insert_id', 'cubrid_is_instance', 'cubrid_lob2_bind', 'cubrid_lob2_close', 'cubrid_lob2_export', 'cubrid_lob2_import', 'cubrid_lob2_new', 'cubrid_lob2_read', 'cubrid_lob2_seek', 'cubrid_lob2_seek64', 'cubrid_lob2_size', 'cubrid_lob2_size64', 'cubrid_lob2_tell', 'cubrid_lob2_tell64', 'cubrid_lob2_write', 'cubrid_lob_export', 'cubrid_lob_get', 'cubrid_lob_send', 'cubrid_lob_size', 'cubrid_lock_read', 'cubrid_lock_write', 'cubrid_move_cursor', 'cubrid_next_result', 'cubrid_num_cols', 'cubrid_num_rows', 'cubrid_pconnect', 'cubrid_pconnect_with_url', 'cubrid_prepare', 'cubrid_put', 'cubrid_query', 'cubrid_rollback', 'cubrid_schema', 'cubrid_seq_add', 'cubrid_seq_drop', 'cubrid_seq_insert', 'cubrid_seq_put', 'cubrid_set_add', 'cubrid_set_autocommit', 'cubrid_set_db_parameter', 'cubrid_set_drop', 'cubrid_set_query_timeout', 'cubrid_unbuffered_query', 'curl_close', 'curl_copy_handle', 'curl_errno', 'curl_error', 'curl_escape', 'curl_exec', 'curl_getinfo', 'curl_multi_add_handle', 'curl_multi_close', 'curl_multi_errno', 'curl_multi_exec', 'curl_multi_getcontent', 'curl_multi_info_read', 'curl_multi_remove_handle', 'curl_multi_select', 'curl_multi_setopt', 'curl_pause', 'curl_reset', 'curl_setopt', 'curl_setopt_array', 'curl_share_close', 'curl_share_errno', 'curl_share_init', 'curl_share_setopt', 'curl_unescape', 'cyrus_authenticate', 'cyrus_bind', 'cyrus_close', 'cyrus_connect', 'cyrus_query', 'cyrus_unbind', 'db2_autocommit', 'db2_bind_param', 'db2_client_info', 'db2_close', 'db2_column_privileges', 'db2_columns', 'db2_commit', 'db2_conn_error', 'db2_conn_errormsg', 'db2_connect', 'db2_cursor_type', 'db2_exec', 'db2_execute', 'db2_fetch_array', 'db2_fetch_assoc', 'db2_fetch_both', 'db2_fetch_object', 'db2_fetch_row', 'db2_field_display_size', 'db2_field_name', 'db2_field_num', 'db2_field_precision', 'db2_field_scale', 'db2_field_type', 'db2_field_width', 'db2_foreign_keys', 'db2_free_result', 'db2_free_stmt', 'db2_get_option', 'db2_last_insert_id', 'db2_lob_read', 'db2_next_result', 'db2_num_fields', 'db2_num_rows', 'db2_pclose', 'db2_pconnect', 'db2_prepare', 'db2_primary_keys', 'db2_procedure_columns', 'db2_procedures', 'db2_result', 'db2_rollback', 'db2_server_info', 'db2_set_option', 'db2_special_columns', 'db2_statistics', 'db2_stmt_error', 'db2_stmt_errormsg', 'db2_table_privileges', 'db2_tables', 'dba_close', 'dba_delete', 'dba_exists', 'dba_fetch', 'dba_firstkey', 'dba_insert', 'dba_nextkey', 'dba_open', 'dba_optimize', 'dba_popen', 'dba_replace', 'dba_sync', 'dbplus_add', 'dbplus_aql', 'dbplus_close', 'dbplus_curr', 'dbplus_find', 'dbplus_first', 'dbplus_flush', 'dbplus_freelock', 'dbplus_freerlocks', 'dbplus_getlock', 'dbplus_getunique', 'dbplus_info', 'dbplus_last', 'dbplus_lockrel', 'dbplus_next', 'dbplus_open', 'dbplus_prev', 'dbplus_rchperm', 'dbplus_rcreate', 'dbplus_rcrtexact', 'dbplus_rcrtlike', 'dbplus_restorepos', 'dbplus_rkeys', 'dbplus_ropen', 'dbplus_rquery', 'dbplus_rrename', 'dbplus_rsecindex', 'dbplus_runlink', 'dbplus_rzap', 'dbplus_savepos', 'dbplus_setindex', 'dbplus_setindexbynumber', 'dbplus_sql', 'dbplus_tremove', 'dbplus_undo', 'dbplus_undoprepare', 'dbplus_unlockrel', 'dbplus_unselect', 'dbplus_update', 'dbplus_xlockrel', 'dbplus_xunlockrel', 'deflate_add', 'dio_close', 'dio_fcntl', 'dio_open', 'dio_read', 'dio_seek', 'dio_stat', 'dio_tcsetattr', 'dio_truncate', 'dio_write', 'dir', 'eio_busy', 'eio_cancel', 'eio_chmod', 'eio_chown', 'eio_close', 'eio_custom', 'eio_dup2', 'eio_fallocate', 'eio_fchmod', 'eio_fchown', 'eio_fdatasync', 'eio_fstat', 'eio_fstatvfs', 'eio_fsync', 'eio_ftruncate', 'eio_futime', 'eio_get_last_error', 'eio_grp', 'eio_grp_add', 'eio_grp_cancel', 'eio_grp_limit', 'eio_link', 'eio_lstat', 'eio_mkdir', 'eio_mknod', 'eio_nop', 'eio_open', 'eio_read', 'eio_readahead', 'eio_readdir', 'eio_readlink', 'eio_realpath', 'eio_rename', 'eio_rmdir', 'eio_seek', 'eio_sendfile', 'eio_stat', 'eio_statvfs', 'eio_symlink', 'eio_sync', 'eio_sync_file_range', 'eio_syncfs', 'eio_truncate', 'eio_unlink', 'eio_utime', 'eio_write', 'enchant_broker_describe', 'enchant_broker_dict_exists', 'enchant_broker_free', 'enchant_broker_free_dict', 'enchant_broker_get_dict_path', 'enchant_broker_get_error', 'enchant_broker_init', 'enchant_broker_list_dicts', 'enchant_broker_request_dict', 'enchant_broker_request_pwl_dict', 'enchant_broker_set_dict_path', 'enchant_broker_set_ordering', 'enchant_dict_add_to_personal', 'enchant_dict_add_to_session', 'enchant_dict_check', 'enchant_dict_describe', 'enchant_dict_get_error', 'enchant_dict_is_in_session', 'enchant_dict_quick_check', 'enchant_dict_store_replacement', 'enchant_dict_suggest', 'event_add', 'event_base_free', 'event_base_loop', 'event_base_loopbreak', 'event_base_loopexit', 'event_base_new', 'event_base_priority_init', 'event_base_reinit', 'event_base_set', 'event_buffer_base_set', 'event_buffer_disable', 'event_buffer_enable', 'event_buffer_fd_set', 'event_buffer_free', 'event_buffer_new', 'event_buffer_priority_set', 'event_buffer_read', 'event_buffer_set_callback', 'event_buffer_timeout_set', 'event_buffer_watermark_set', 'event_buffer_write', 'event_del', 'event_free', 'event_new', 'event_priority_set', 'event_set', 'event_timer_add', 'event_timer_del', 'event_timer_pending', 'event_timer_set', 'expect_expectl', 'expect_popen', 'fam_cancel_monitor', 'fam_close', 'fam_monitor_collection', 'fam_monitor_directory', 'fam_monitor_file', 'fam_next_event', 'fam_open', 'fam_pending', 'fam_resume_monitor', 'fam_suspend_monitor', 'fann_cascadetrain_on_data', 'fann_cascadetrain_on_file', 'fann_clear_scaling_params', 'fann_copy', 'fann_create_from_file', 'fann_create_shortcut_array', 'fann_create_standard', 'fann_create_standard_array', 'fann_create_train', 'fann_create_train_from_callback', 'fann_descale_input', 'fann_descale_output', 'fann_descale_train', 'fann_destroy', 'fann_destroy_train', 'fann_duplicate_train_data', 'fann_get_MSE', 'fann_get_activation_function', 'fann_get_activation_steepness', 'fann_get_bias_array', 'fann_get_bit_fail', 'fann_get_bit_fail_limit', 'fann_get_cascade_activation_functions', 'fann_get_cascade_activation_functions_count', 'fann_get_cascade_activation_steepnesses', 'fann_get_cascade_activation_steepnesses_count', 'fann_get_cascade_candidate_change_fraction', 'fann_get_cascade_candidate_limit', 'fann_get_cascade_candidate_stagnation_epochs', 'fann_get_cascade_max_cand_epochs', 'fann_get_cascade_max_out_epochs', 'fann_get_cascade_min_cand_epochs', 'fann_get_cascade_min_out_epochs', 'fann_get_cascade_num_candidate_groups', 'fann_get_cascade_num_candidates', 'fann_get_cascade_output_change_fraction', 'fann_get_cascade_output_stagnation_epochs', 'fann_get_cascade_weight_multiplier', 'fann_get_connection_array', 'fann_get_connection_rate', 'fann_get_errno', 'fann_get_errstr', 'fann_get_layer_array', 'fann_get_learning_momentum', 'fann_get_learning_rate', 'fann_get_network_type', 'fann_get_num_input', 'fann_get_num_layers', 'fann_get_num_output', 'fann_get_quickprop_decay', 'fann_get_quickprop_mu', 'fann_get_rprop_decrease_factor', 'fann_get_rprop_delta_max', 'fann_get_rprop_delta_min', 'fann_get_rprop_delta_zero', 'fann_get_rprop_increase_factor', 'fann_get_sarprop_step_error_shift', 'fann_get_sarprop_step_error_threshold_factor', 'fann_get_sarprop_temperature', 'fann_get_sarprop_weight_decay_shift', 'fann_get_total_connections', 'fann_get_total_neurons', 'fann_get_train_error_function', 'fann_get_train_stop_function', 'fann_get_training_algorithm', 'fann_init_weights', 'fann_length_train_data', 'fann_merge_train_data', 'fann_num_input_train_data', 'fann_num_output_train_data', 'fann_randomize_weights', 'fann_read_train_from_file', 'fann_reset_errno', 'fann_reset_errstr', 'fann_run', 'fann_save', 'fann_save_train', 'fann_scale_input', 'fann_scale_input_train_data', 'fann_scale_output', 'fann_scale_output_train_data', 'fann_scale_train', 'fann_scale_train_data', 'fann_set_activation_function', 'fann_set_activation_function_hidden', 'fann_set_activation_function_layer', 'fann_set_activation_function_output', 'fann_set_activation_steepness', 'fann_set_activation_steepness_hidden', 'fann_set_activation_steepness_layer', 'fann_set_activation_steepness_output', 'fann_set_bit_fail_limit', 'fann_set_callback', 'fann_set_cascade_activation_functions', 'fann_set_cascade_activation_steepnesses', 'fann_set_cascade_candidate_change_fraction', 'fann_set_cascade_candidate_limit', 'fann_set_cascade_candidate_stagnation_epochs', 'fann_set_cascade_max_cand_epochs', 'fann_set_cascade_max_out_epochs', 'fann_set_cascade_min_cand_epochs', 'fann_set_cascade_min_out_epochs', 'fann_set_cascade_num_candidate_groups', 'fann_set_cascade_output_change_fraction', 'fann_set_cascade_output_stagnation_epochs', 'fann_set_cascade_weight_multiplier', 'fann_set_error_log', 'fann_set_input_scaling_params', 'fann_set_learning_momentum', 'fann_set_learning_rate', 'fann_set_output_scaling_params', 'fann_set_quickprop_decay', 'fann_set_quickprop_mu', 'fann_set_rprop_decrease_factor', 'fann_set_rprop_delta_max', 'fann_set_rprop_delta_min', 'fann_set_rprop_delta_zero', 'fann_set_rprop_increase_factor', 'fann_set_sarprop_step_error_shift', 'fann_set_sarprop_step_error_threshold_factor', 'fann_set_sarprop_temperature', 'fann_set_sarprop_weight_decay_shift', 'fann_set_scaling_params', 'fann_set_train_error_function', 'fann_set_train_stop_function', 'fann_set_training_algorithm', 'fann_set_weight', 'fann_set_weight_array', 'fann_shuffle_train_data', 'fann_subset_train_data', 'fann_test', 'fann_test_data', 'fann_train', 'fann_train_epoch', 'fann_train_on_data', 'fann_train_on_file', 'fbsql_affected_rows', 'fbsql_autocommit', 'fbsql_blob_size', 'fbsql_change_user', 'fbsql_clob_size', 'fbsql_close', 'fbsql_commit', 'fbsql_connect', 'fbsql_create_blob', 'fbsql_create_clob', 'fbsql_create_db', 'fbsql_data_seek', 'fbsql_database', 'fbsql_database_password', 'fbsql_db_query', 'fbsql_db_status', 'fbsql_drop_db', 'fbsql_errno', 'fbsql_error', 'fbsql_fetch_array', 'fbsql_fetch_assoc', 'fbsql_fetch_field', 'fbsql_fetch_lengths', 'fbsql_fetch_object', 'fbsql_fetch_row', 'fbsql_field_flags', 'fbsql_field_len', 'fbsql_field_name', 'fbsql_field_seek', 'fbsql_field_table', 'fbsql_field_type', 'fbsql_free_result', 'fbsql_get_autostart_info', 'fbsql_hostname', 'fbsql_insert_id', 'fbsql_list_dbs', 'fbsql_list_fields', 'fbsql_list_tables', 'fbsql_next_result', 'fbsql_num_fields', 'fbsql_num_rows', 'fbsql_password', 'fbsql_pconnect', 'fbsql_query', 'fbsql_read_blob', 'fbsql_read_clob', 'fbsql_result', 'fbsql_rollback', 'fbsql_rows_fetched', 'fbsql_select_db', 'fbsql_set_characterset', 'fbsql_set_lob_mode', 'fbsql_set_password', 'fbsql_set_transaction', 'fbsql_start_db', 'fbsql_stop_db', 'fbsql_table_name', 'fbsql_username', 'fclose', 'fdf_add_doc_javascript', 'fdf_add_template', 'fdf_close', 'fdf_create', 'fdf_enum_values', 'fdf_get_ap', 'fdf_get_attachment', 'fdf_get_encoding', 'fdf_get_file', 'fdf_get_flags', 'fdf_get_opt', 'fdf_get_status', 'fdf_get_value', 'fdf_get_version', 'fdf_next_field_name', 'fdf_open', 'fdf_open_string', 'fdf_remove_item', 'fdf_save', 'fdf_save_string', 'fdf_set_ap', 'fdf_set_encoding', 'fdf_set_file', 'fdf_set_flags', 'fdf_set_javascript_action', 'fdf_set_on_import_javascript', 'fdf_set_opt', 'fdf_set_status', 'fdf_set_submit_form_action', 'fdf_set_target_frame', 'fdf_set_value', 'fdf_set_version', 'feof', 'fflush', 'ffmpeg_frame::__construct', 'ffmpeg_frame::toGDImage', 'fgetc', 'fgetcsv', 'fgets', 'fgetss', 'file', 'file_get_contents', 'file_put_contents', 'finfo::buffer', 'finfo::file', 'finfo_buffer', 'finfo_close', 'finfo_file', 'finfo_open', 'finfo_set_flags', 'flock', 'fopen', 'fpassthru', 'fprintf', 'fputcsv', 'fputs', 'fread', 'fscanf', 'fseek', 'fstat', 'ftell', 'ftp_alloc', 'ftp_append', 'ftp_cdup', 'ftp_chdir', 'ftp_chmod', 'ftp_close', 'ftp_delete', 'ftp_exec', 'ftp_fget', 'ftp_fput', 'ftp_get', 'ftp_get_option', 'ftp_login', 'ftp_mdtm', 'ftp_mkdir', 'ftp_mlsd', 'ftp_nb_continue', 'ftp_nb_fget', 'ftp_nb_fput', 'ftp_nb_get', 'ftp_nb_put', 'ftp_nlist', 'ftp_pasv', 'ftp_put', 'ftp_pwd', 'ftp_quit', 'ftp_raw', 'ftp_rawlist', 'ftp_rename', 'ftp_rmdir', 'ftp_set_option', 'ftp_site', 'ftp_size', 'ftp_systype', 'ftruncate', 'fwrite', 'get_resource_type', 'gmp_div', 'gnupg::init', 'gnupg_adddecryptkey', 'gnupg_addencryptkey', 'gnupg_addsignkey', 'gnupg_cleardecryptkeys', 'gnupg_clearencryptkeys', 'gnupg_clearsignkeys', 'gnupg_decrypt', 'gnupg_decryptverify', 'gnupg_encrypt', 'gnupg_encryptsign', 'gnupg_export', 'gnupg_geterror', 'gnupg_getprotocol', 'gnupg_import', 'gnupg_init', 'gnupg_keyinfo', 'gnupg_setarmor', 'gnupg_seterrormode', 'gnupg_setsignmode', 'gnupg_sign', 'gnupg_verify', 'gupnp_context_get_host_ip', 'gupnp_context_get_port', 'gupnp_context_get_subscription_timeout', 'gupnp_context_host_path', 'gupnp_context_new', 'gupnp_context_set_subscription_timeout', 'gupnp_context_timeout_add', 'gupnp_context_unhost_path', 'gupnp_control_point_browse_start', 'gupnp_control_point_browse_stop', 'gupnp_control_point_callback_set', 'gupnp_control_point_new', 'gupnp_device_action_callback_set', 'gupnp_device_info_get', 'gupnp_device_info_get_service', 'gupnp_root_device_get_available', 'gupnp_root_device_get_relative_location', 'gupnp_root_device_new', 'gupnp_root_device_set_available', 'gupnp_root_device_start', 'gupnp_root_device_stop', 'gupnp_service_action_get', 'gupnp_service_action_return', 'gupnp_service_action_return_error', 'gupnp_service_action_set', 'gupnp_service_freeze_notify', 'gupnp_service_info_get', 'gupnp_service_info_get_introspection', 'gupnp_service_introspection_get_state_variable', 'gupnp_service_notify', 'gupnp_service_proxy_action_get', 'gupnp_service_proxy_action_set', 'gupnp_service_proxy_add_notify', 'gupnp_service_proxy_callback_set', 'gupnp_service_proxy_get_subscribed', 'gupnp_service_proxy_remove_notify', 'gupnp_service_proxy_send_action', 'gupnp_service_proxy_set_subscribed', 'gupnp_service_thaw_notify', 'gzclose', 'gzeof', 'gzgetc', 'gzgets', 'gzgetss', 'gzpassthru', 'gzputs', 'gzread', 'gzrewind', 'gzseek', 'gztell', 'gzwrite', 'hash_update_stream', 'http\\Env\\Response::send', 'http_get_request_body_stream', 'ibase_add_user', 'ibase_affected_rows', 'ibase_backup', 'ibase_blob_add', 'ibase_blob_cancel', 'ibase_blob_close', 'ibase_blob_create', 'ibase_blob_get', 'ibase_blob_open', 'ibase_close', 'ibase_commit', 'ibase_commit_ret', 'ibase_connect', 'ibase_db_info', 'ibase_delete_user', 'ibase_drop_db', 'ibase_execute', 'ibase_fetch_assoc', 'ibase_fetch_object', 'ibase_fetch_row', 'ibase_field_info', 'ibase_free_event_handler', 'ibase_free_query', 'ibase_free_result', 'ibase_gen_id', 'ibase_maintain_db', 'ibase_modify_user', 'ibase_name_result', 'ibase_num_fields', 'ibase_num_params', 'ibase_param_info', 'ibase_pconnect', 'ibase_prepare', 'ibase_query', 'ibase_restore', 'ibase_rollback', 'ibase_rollback_ret', 'ibase_server_info', 'ibase_service_attach', 'ibase_service_detach', 'ibase_set_event_handler', 'ibase_trans', 'ifx_affected_rows', 'ifx_close', 'ifx_connect', 'ifx_do', 'ifx_error', 'ifx_fetch_row', 'ifx_fieldproperties', 'ifx_fieldtypes', 'ifx_free_result', 'ifx_getsqlca', 'ifx_htmltbl_result', 'ifx_num_fields', 'ifx_num_rows', 'ifx_pconnect', 'ifx_prepare', 'ifx_query', 'image2wbmp', 'imageaffine', 'imagealphablending', 'imageantialias', 'imagearc', 'imagebmp', 'imagechar', 'imagecharup', 'imagecolorallocate', 'imagecolorallocatealpha', 'imagecolorat', 'imagecolorclosest', 'imagecolorclosestalpha', 'imagecolorclosesthwb', 'imagecolordeallocate', 'imagecolorexact', 'imagecolorexactalpha', 'imagecolormatch', 'imagecolorresolve', 'imagecolorresolvealpha', 'imagecolorset', 'imagecolorsforindex', 'imagecolorstotal', 'imagecolortransparent', 'imageconvolution', 'imagecopy', 'imagecopymerge', 'imagecopymergegray', 'imagecopyresampled', 'imagecopyresized', 'imagecrop', 'imagecropauto', 'imagedashedline', 'imagedestroy', 'imageellipse', 'imagefill', 'imagefilledarc', 'imagefilledellipse', 'imagefilledpolygon', 'imagefilledrectangle', 'imagefilltoborder', 'imagefilter', 'imageflip', 'imagefttext', 'imagegammacorrect', 'imagegd', 'imagegd2', 'imagegetclip', 'imagegif', 'imagegrabscreen', 'imagegrabwindow', 'imageinterlace', 'imageistruecolor', 'imagejpeg', 'imagelayereffect', 'imageline', 'imageopenpolygon', 'imagepalettecopy', 'imagepalettetotruecolor', 'imagepng', 'imagepolygon', 'imagepsencodefont', 'imagepsextendfont', 'imagepsfreefont', 'imagepsloadfont', 'imagepsslantfont', 'imagepstext', 'imagerectangle', 'imageresolution', 'imagerotate', 'imagesavealpha', 'imagescale', 'imagesetbrush', 'imagesetclip', 'imagesetinterpolation', 'imagesetpixel', 'imagesetstyle', 'imagesetthickness', 'imagesettile', 'imagestring', 'imagestringup', 'imagesx', 'imagesy', 'imagetruecolortopalette', 'imagettftext', 'imagewbmp', 'imagewebp', 'imagexbm', 'imap_append', 'imap_body', 'imap_bodystruct', 'imap_check', 'imap_clearflag_full', 'imap_close', 'imap_create', 'imap_createmailbox', 'imap_delete', 'imap_deletemailbox', 'imap_expunge', 'imap_fetch_overview', 'imap_fetchbody', 'imap_fetchheader', 'imap_fetchmime', 'imap_fetchstructure', 'imap_fetchtext', 'imap_gc', 'imap_get_quota', 'imap_get_quotaroot', 'imap_getacl', 'imap_getmailboxes', 'imap_getsubscribed', 'imap_header', 'imap_headerinfo', 'imap_headers', 'imap_list', 'imap_listmailbox', 'imap_listscan', 'imap_listsubscribed', 'imap_lsub', 'imap_mail_copy', 'imap_mail_move', 'imap_mailboxmsginfo', 'imap_msgno', 'imap_num_msg', 'imap_num_recent', 'imap_ping', 'imap_rename', 'imap_renamemailbox', 'imap_reopen', 'imap_savebody', 'imap_scan', 'imap_scanmailbox', 'imap_search', 'imap_set_quota', 'imap_setacl', 'imap_setflag_full', 'imap_sort', 'imap_status', 'imap_subscribe', 'imap_thread', 'imap_uid', 'imap_undelete', 'imap_unsubscribe', 'inflate_add', 'inflate_get_read_len', 'inflate_get_status', 'ingres_autocommit', 'ingres_autocommit_state', 'ingres_charset', 'ingres_close', 'ingres_commit', 'ingres_connect', 'ingres_cursor', 'ingres_errno', 'ingres_error', 'ingres_errsqlstate', 'ingres_escape_string', 'ingres_execute', 'ingres_fetch_array', 'ingres_fetch_assoc', 'ingres_fetch_object', 'ingres_fetch_proc_return', 'ingres_fetch_row', 'ingres_field_length', 'ingres_field_name', 'ingres_field_nullable', 'ingres_field_precision', 'ingres_field_scale', 'ingres_field_type', 'ingres_free_result', 'ingres_next_error', 'ingres_num_fields', 'ingres_num_rows', 'ingres_pconnect', 'ingres_prepare', 'ingres_query', 'ingres_result_seek', 'ingres_rollback', 'ingres_set_environment', 'ingres_unbuffered_query', 'inotify_add_watch', 'inotify_init', 'inotify_queue_len', 'inotify_read', 'inotify_rm_watch', 'kadm5_chpass_principal', 'kadm5_create_principal', 'kadm5_delete_principal', 'kadm5_destroy', 'kadm5_flush', 'kadm5_get_policies', 'kadm5_get_principal', 'kadm5_get_principals', 'kadm5_init_with_password', 'kadm5_modify_principal', 'ldap_add', 'ldap_bind', 'ldap_close', 'ldap_compare', 'ldap_control_paged_result', 'ldap_control_paged_result_response', 'ldap_count_entries', 'ldap_delete', 'ldap_errno', 'ldap_error', 'ldap_exop', 'ldap_exop_passwd', 'ldap_exop_refresh', 'ldap_exop_whoami', 'ldap_first_attribute', 'ldap_first_entry', 'ldap_first_reference', 'ldap_free_result', 'ldap_get_attributes', 'ldap_get_dn', 'ldap_get_entries', 'ldap_get_option', 'ldap_get_values', 'ldap_get_values_len', 'ldap_mod_add', 'ldap_mod_del', 'ldap_mod_replace', 'ldap_modify', 'ldap_modify_batch', 'ldap_next_attribute', 'ldap_next_entry', 'ldap_next_reference', 'ldap_parse_exop', 'ldap_parse_reference', 'ldap_parse_result', 'ldap_rename', 'ldap_sasl_bind', 'ldap_set_option', 'ldap_set_rebind_proc', 'ldap_sort', 'ldap_start_tls', 'ldap_unbind', 'libxml_set_streams_context', 'm_checkstatus', 'm_completeauthorizations', 'm_connect', 'm_connectionerror', 'm_deletetrans', 'm_destroyconn', 'm_getcell', 'm_getcellbynum', 'm_getcommadelimited', 'm_getheader', 'm_initconn', 'm_iscommadelimited', 'm_maxconntimeout', 'm_monitor', 'm_numcolumns', 'm_numrows', 'm_parsecommadelimited', 'm_responsekeys', 'm_responseparam', 'm_returnstatus', 'm_setblocking', 'm_setdropfile', 'm_setip', 'm_setssl', 'm_setssl_cafile', 'm_setssl_files', 'm_settimeout', 'm_transactionssent', 'm_transinqueue', 'm_transkeyval', 'm_transnew', 'm_transsend', 'm_validateidentifier', 'm_verifyconnection', 'm_verifysslcert', 'mailparse_determine_best_xfer_encoding', 'mailparse_msg_create', 'mailparse_msg_extract_part', 'mailparse_msg_extract_part_file', 'mailparse_msg_extract_whole_part_file', 'mailparse_msg_free', 'mailparse_msg_get_part', 'mailparse_msg_get_part_data', 'mailparse_msg_get_structure', 'mailparse_msg_parse', 'mailparse_msg_parse_file', 'mailparse_stream_encode', 'mailparse_uudecode_all', 'maxdb::use_result', 'maxdb_affected_rows', 'maxdb_connect', 'maxdb_disable_rpl_parse', 'maxdb_dump_debug_info', 'maxdb_embedded_connect', 'maxdb_enable_reads_from_master', 'maxdb_enable_rpl_parse', 'maxdb_errno', 'maxdb_error', 'maxdb_fetch_lengths', 'maxdb_field_tell', 'maxdb_get_host_info', 'maxdb_get_proto_info', 'maxdb_get_server_info', 'maxdb_get_server_version', 'maxdb_info', 'maxdb_init', 'maxdb_insert_id', 'maxdb_master_query', 'maxdb_more_results', 'maxdb_next_result', 'maxdb_num_fields', 'maxdb_num_rows', 'maxdb_rpl_parse_enabled', 'maxdb_rpl_probe', 'maxdb_select_db', 'maxdb_sqlstate', 'maxdb_stmt::result_metadata', 'maxdb_stmt_affected_rows', 'maxdb_stmt_errno', 'maxdb_stmt_error', 'maxdb_stmt_num_rows', 'maxdb_stmt_param_count', 'maxdb_stmt_result_metadata', 'maxdb_stmt_sqlstate', 'maxdb_thread_id', 'maxdb_use_result', 'maxdb_warning_count', 'mcrypt_enc_get_algorithms_name', 'mcrypt_enc_get_block_size', 'mcrypt_enc_get_iv_size', 'mcrypt_enc_get_key_size', 'mcrypt_enc_get_modes_name', 'mcrypt_enc_get_supported_key_sizes', 'mcrypt_enc_is_block_algorithm', 'mcrypt_enc_is_block_algorithm_mode', 'mcrypt_enc_is_block_mode', 'mcrypt_enc_self_test', 'mcrypt_generic', 'mcrypt_generic_deinit', 'mcrypt_generic_end', 'mcrypt_generic_init', 'mcrypt_module_close', 'mcrypt_module_open', 'mdecrypt_generic', 'mkdir', 'mqseries_back', 'mqseries_begin', 'mqseries_close', 'mqseries_cmit', 'mqseries_conn', 'mqseries_connx', 'mqseries_disc', 'mqseries_get', 'mqseries_inq', 'mqseries_open', 'mqseries_put', 'mqseries_put1', 'mqseries_set', 'msg_get_queue', 'msg_receive', 'msg_remove_queue', 'msg_send', 'msg_set_queue', 'msg_stat_queue', 'msql_affected_rows', 'msql_close', 'msql_connect', 'msql_create_db', 'msql_data_seek', 'msql_db_query', 'msql_drop_db', 'msql_fetch_array', 'msql_fetch_field', 'msql_fetch_object', 'msql_fetch_row', 'msql_field_flags', 'msql_field_len', 'msql_field_name', 'msql_field_seek', 'msql_field_table', 'msql_field_type', 'msql_free_result', 'msql_list_dbs', 'msql_list_fields', 'msql_list_tables', 'msql_num_fields', 'msql_num_rows', 'msql_pconnect', 'msql_query', 'msql_result', 'msql_select_db', 'mssql_bind', 'mssql_close', 'mssql_connect', 'mssql_data_seek', 'mssql_execute', 'mssql_fetch_array', 'mssql_fetch_assoc', 'mssql_fetch_batch', 'mssql_fetch_field', 'mssql_fetch_object', 'mssql_fetch_row', 'mssql_field_length', 'mssql_field_name', 'mssql_field_seek', 'mssql_field_type', 'mssql_free_result', 'mssql_free_statement', 'mssql_init', 'mssql_next_result', 'mssql_num_fields', 'mssql_num_rows', 'mssql_pconnect', 'mssql_query', 'mssql_result', 'mssql_rows_affected', 'mssql_select_db', 'mysql_affected_rows', 'mysql_client_encoding', 'mysql_close', 'mysql_connect', 'mysql_create_db', 'mysql_data_seek', 'mysql_db_name', 'mysql_db_query', 'mysql_drop_db', 'mysql_errno', 'mysql_error', 'mysql_fetch_array', 'mysql_fetch_assoc', 'mysql_fetch_field', 'mysql_fetch_lengths', 'mysql_fetch_object', 'mysql_fetch_row', 'mysql_field_flags', 'mysql_field_len', 'mysql_field_name', 'mysql_field_seek', 'mysql_field_table', 'mysql_field_type', 'mysql_free_result', 'mysql_get_host_info', 'mysql_get_proto_info', 'mysql_get_server_info', 'mysql_info', 'mysql_insert_id', 'mysql_list_dbs', 'mysql_list_fields', 'mysql_list_processes', 'mysql_list_tables', 'mysql_num_fields', 'mysql_num_rows', 'mysql_pconnect', 'mysql_ping', 'mysql_query', 'mysql_real_escape_string', 'mysql_result', 'mysql_select_db', 'mysql_set_charset', 'mysql_stat', 'mysql_tablename', 'mysql_thread_id', 'mysql_unbuffered_query', 'mysqlnd_uh_convert_to_mysqlnd', 'ncurses_bottom_panel', 'ncurses_del_panel', 'ncurses_delwin', 'ncurses_getmaxyx', 'ncurses_getyx', 'ncurses_hide_panel', 'ncurses_keypad', 'ncurses_meta', 'ncurses_move_panel', 'ncurses_mvwaddstr', 'ncurses_new_panel', 'ncurses_newpad', 'ncurses_newwin', 'ncurses_panel_above', 'ncurses_panel_below', 'ncurses_panel_window', 'ncurses_pnoutrefresh', 'ncurses_prefresh', 'ncurses_replace_panel', 'ncurses_show_panel', 'ncurses_top_panel', 'ncurses_waddch', 'ncurses_waddstr', 'ncurses_wattroff', 'ncurses_wattron', 'ncurses_wattrset', 'ncurses_wborder', 'ncurses_wclear', 'ncurses_wcolor_set', 'ncurses_werase', 'ncurses_wgetch', 'ncurses_whline', 'ncurses_wmouse_trafo', 'ncurses_wmove', 'ncurses_wnoutrefresh', 'ncurses_wrefresh', 'ncurses_wstandend', 'ncurses_wstandout', 'ncurses_wvline', 'newt_button', 'newt_button_bar', 'newt_checkbox', 'newt_checkbox_get_value', 'newt_checkbox_set_flags', 'newt_checkbox_set_value', 'newt_checkbox_tree', 'newt_checkbox_tree_add_item', 'newt_checkbox_tree_find_item', 'newt_checkbox_tree_get_current', 'newt_checkbox_tree_get_entry_value', 'newt_checkbox_tree_get_multi_selection', 'newt_checkbox_tree_get_selection', 'newt_checkbox_tree_multi', 'newt_checkbox_tree_set_current', 'newt_checkbox_tree_set_entry', 'newt_checkbox_tree_set_entry_value', 'newt_checkbox_tree_set_width', 'newt_compact_button', 'newt_component_add_callback', 'newt_component_takes_focus', 'newt_create_grid', 'newt_draw_form', 'newt_entry', 'newt_entry_get_value', 'newt_entry_set', 'newt_entry_set_filter', 'newt_entry_set_flags', 'newt_form', 'newt_form_add_component', 'newt_form_add_components', 'newt_form_add_hot_key', 'newt_form_destroy', 'newt_form_get_current', 'newt_form_run', 'newt_form_set_background', 'newt_form_set_height', 'newt_form_set_size', 'newt_form_set_timer', 'newt_form_set_width', 'newt_form_watch_fd', 'newt_grid_add_components_to_form', 'newt_grid_basic_window', 'newt_grid_free', 'newt_grid_get_size', 'newt_grid_h_close_stacked', 'newt_grid_h_stacked', 'newt_grid_place', 'newt_grid_set_field', 'newt_grid_simple_window', 'newt_grid_v_close_stacked', 'newt_grid_v_stacked', 'newt_grid_wrapped_window', 'newt_grid_wrapped_window_at', 'newt_label', 'newt_label_set_text', 'newt_listbox', 'newt_listbox_append_entry', 'newt_listbox_clear', 'newt_listbox_clear_selection', 'newt_listbox_delete_entry', 'newt_listbox_get_current', 'newt_listbox_get_selection', 'newt_listbox_insert_entry', 'newt_listbox_item_count', 'newt_listbox_select_item', 'newt_listbox_set_current', 'newt_listbox_set_current_by_key', 'newt_listbox_set_data', 'newt_listbox_set_entry', 'newt_listbox_set_width', 'newt_listitem', 'newt_listitem_get_data', 'newt_listitem_set', 'newt_radio_get_current', 'newt_radiobutton', 'newt_run_form', 'newt_scale', 'newt_scale_set', 'newt_scrollbar_set', 'newt_textbox', 'newt_textbox_get_num_lines', 'newt_textbox_reflowed', 'newt_textbox_set_height', 'newt_textbox_set_text', 'newt_vertical_scrollbar', 'oci_bind_array_by_name', 'oci_bind_by_name', 'oci_cancel', 'oci_close', 'oci_commit', 'oci_connect', 'oci_define_by_name', 'oci_error', 'oci_execute', 'oci_fetch', 'oci_fetch_all', 'oci_fetch_array', 'oci_fetch_assoc', 'oci_fetch_object', 'oci_fetch_row', 'oci_field_is_null', 'oci_field_name', 'oci_field_precision', 'oci_field_scale', 'oci_field_size', 'oci_field_type', 'oci_field_type_raw', 'oci_free_cursor', 'oci_free_statement', 'oci_get_implicit_resultset', 'oci_new_collection', 'oci_new_connect', 'oci_new_cursor', 'oci_new_descriptor', 'oci_num_fields', 'oci_num_rows', 'oci_parse', 'oci_pconnect', 'oci_register_taf_callback', 'oci_result', 'oci_rollback', 'oci_server_version', 'oci_set_action', 'oci_set_client_identifier', 'oci_set_client_info', 'oci_set_module_name', 'oci_set_prefetch', 'oci_statement_type', 'oci_unregister_taf_callback', 'odbc_autocommit', 'odbc_close', 'odbc_columnprivileges', 'odbc_columns', 'odbc_commit', 'odbc_connect', 'odbc_cursor', 'odbc_data_source', 'odbc_do', 'odbc_error', 'odbc_errormsg', 'odbc_exec', 'odbc_execute', 'odbc_fetch_array', 'odbc_fetch_into', 'odbc_fetch_row', 'odbc_field_len', 'odbc_field_name', 'odbc_field_num', 'odbc_field_precision', 'odbc_field_scale', 'odbc_field_type', 'odbc_foreignkeys', 'odbc_free_result', 'odbc_gettypeinfo', 'odbc_next_result', 'odbc_num_fields', 'odbc_num_rows', 'odbc_pconnect', 'odbc_prepare', 'odbc_primarykeys', 'odbc_procedurecolumns', 'odbc_procedures', 'odbc_result', 'odbc_result_all', 'odbc_rollback', 'odbc_setoption', 'odbc_specialcolumns', 'odbc_statistics', 'odbc_tableprivileges', 'odbc_tables', 'openal_buffer_create', 'openal_buffer_data', 'openal_buffer_destroy', 'openal_buffer_get', 'openal_buffer_loadwav', 'openal_context_create', 'openal_context_current', 'openal_context_destroy', 'openal_context_process', 'openal_context_suspend', 'openal_device_close', 'openal_device_open', 'openal_source_create', 'openal_source_destroy', 'openal_source_get', 'openal_source_pause', 'openal_source_play', 'openal_source_rewind', 'openal_source_set', 'openal_source_stop', 'openal_stream', 'opendir', 'openssl_csr_new', 'openssl_dh_compute_key', 'openssl_free_key', 'openssl_pkey_export', 'openssl_pkey_free', 'openssl_pkey_get_details', 'openssl_spki_new', 'openssl_x509_free', 'pclose', 'pfsockopen', 'pg_affected_rows', 'pg_cancel_query', 'pg_client_encoding', 'pg_close', 'pg_connect_poll', 'pg_connection_busy', 'pg_connection_reset', 'pg_connection_status', 'pg_consume_input', 'pg_convert', 'pg_copy_from', 'pg_copy_to', 'pg_dbname', 'pg_delete', 'pg_end_copy', 'pg_escape_bytea', 'pg_escape_identifier', 'pg_escape_literal', 'pg_escape_string', 'pg_execute', 'pg_fetch_all', 'pg_fetch_all_columns', 'pg_fetch_array', 'pg_fetch_assoc', 'pg_fetch_row', 'pg_field_name', 'pg_field_num', 'pg_field_size', 'pg_field_table', 'pg_field_type', 'pg_field_type_oid', 'pg_flush', 'pg_free_result', 'pg_get_notify', 'pg_get_pid', 'pg_get_result', 'pg_host', 'pg_insert', 'pg_last_error', 'pg_last_notice', 'pg_last_oid', 'pg_lo_close', 'pg_lo_create', 'pg_lo_export', 'pg_lo_import', 'pg_lo_open', 'pg_lo_read', 'pg_lo_read_all', 'pg_lo_seek', 'pg_lo_tell', 'pg_lo_truncate', 'pg_lo_unlink', 'pg_lo_write', 'pg_meta_data', 'pg_num_fields', 'pg_num_rows', 'pg_options', 'pg_parameter_status', 'pg_ping', 'pg_port', 'pg_prepare', 'pg_put_line', 'pg_query', 'pg_query_params', 'pg_result_error', 'pg_result_error_field', 'pg_result_seek', 'pg_result_status', 'pg_select', 'pg_send_execute', 'pg_send_prepare', 'pg_send_query', 'pg_send_query_params', 'pg_set_client_encoding', 'pg_set_error_verbosity', 'pg_socket', 'pg_trace', 'pg_transaction_status', 'pg_tty', 'pg_untrace', 'pg_update', 'pg_version', 'php_user_filter::filter', 'proc_close', 'proc_get_status', 'proc_terminate', 'ps_add_bookmark', 'ps_add_launchlink', 'ps_add_locallink', 'ps_add_note', 'ps_add_pdflink', 'ps_add_weblink', 'ps_arc', 'ps_arcn', 'ps_begin_page', 'ps_begin_pattern', 'ps_begin_template', 'ps_circle', 'ps_clip', 'ps_close', 'ps_close_image', 'ps_closepath', 'ps_closepath_stroke', 'ps_continue_text', 'ps_curveto', 'ps_delete', 'ps_end_page', 'ps_end_pattern', 'ps_end_template', 'ps_fill', 'ps_fill_stroke', 'ps_findfont', 'ps_get_buffer', 'ps_get_parameter', 'ps_get_value', 'ps_hyphenate', 'ps_include_file', 'ps_lineto', 'ps_makespotcolor', 'ps_moveto', 'ps_new', 'ps_open_file', 'ps_open_image', 'ps_open_image_file', 'ps_open_memory_image', 'ps_place_image', 'ps_rect', 'ps_restore', 'ps_rotate', 'ps_save', 'ps_scale', 'ps_set_border_color', 'ps_set_border_dash', 'ps_set_border_style', 'ps_set_info', 'ps_set_parameter', 'ps_set_text_pos', 'ps_set_value', 'ps_setcolor', 'ps_setdash', 'ps_setflat', 'ps_setfont', 'ps_setgray', 'ps_setlinecap', 'ps_setlinejoin', 'ps_setlinewidth', 'ps_setmiterlimit', 'ps_setoverprintmode', 'ps_setpolydash', 'ps_shading', 'ps_shading_pattern', 'ps_shfill', 'ps_show', 'ps_show2', 'ps_show_boxed', 'ps_show_xy', 'ps_show_xy2', 'ps_string_geometry', 'ps_stringwidth', 'ps_stroke', 'ps_symbol', 'ps_symbol_name', 'ps_symbol_width', 'ps_translate', 'px_close', 'px_create_fp', 'px_date2string', 'px_delete', 'px_delete_record', 'px_get_field', 'px_get_info', 'px_get_parameter', 'px_get_record', 'px_get_schema', 'px_get_value', 'px_insert_record', 'px_new', 'px_numfields', 'px_numrecords', 'px_open_fp', 'px_put_record', 'px_retrieve_record', 'px_set_blob_file', 'px_set_parameter', 'px_set_tablename', 'px_set_targetencoding', 'px_set_value', 'px_timestamp2string', 'px_update_record', 'radius_acct_open', 'radius_add_server', 'radius_auth_open', 'radius_close', 'radius_config', 'radius_create_request', 'radius_demangle', 'radius_demangle_mppe_key', 'radius_get_attr', 'radius_put_addr', 'radius_put_attr', 'radius_put_int', 'radius_put_string', 'radius_put_vendor_addr', 'radius_put_vendor_attr', 'radius_put_vendor_int', 'radius_put_vendor_string', 'radius_request_authenticator', 'radius_salt_encrypt_attr', 'radius_send_request', 'radius_server_secret', 'radius_strerror', 'readdir', 'readfile', 'recode_file', 'rename', 'rewind', 'rewinddir', 'rmdir', 'rpm_close', 'rpm_get_tag', 'rpm_open', 'sapi_windows_vt100_support', 'scandir', 'sem_acquire', 'sem_get', 'sem_release', 'sem_remove', 'set_file_buffer', 'shm_attach', 'shm_detach', 'shm_get_var', 'shm_has_var', 'shm_put_var', 'shm_remove', 'shm_remove_var', 'shmop_close', 'shmop_delete', 'shmop_open', 'shmop_read', 'shmop_size', 'shmop_write', 'socket_accept', 'socket_addrinfo_bind', 'socket_addrinfo_connect', 'socket_addrinfo_explain', 'socket_bind', 'socket_clear_error', 'socket_close', 'socket_connect', 'socket_export_stream', 'socket_get_option', 'socket_get_status', 'socket_getopt', 'socket_getpeername', 'socket_getsockname', 'socket_import_stream', 'socket_last_error', 'socket_listen', 'socket_read', 'socket_recv', 'socket_recvfrom', 'socket_recvmsg', 'socket_send', 'socket_sendmsg', 'socket_sendto', 'socket_set_block', 'socket_set_blocking', 'socket_set_nonblock', 'socket_set_option', 'socket_set_timeout', 'socket_shutdown', 'socket_write', 'sqlite_close', 'sqlite_fetch_string', 'sqlite_has_more', 'sqlite_open', 'sqlite_popen', 'sqlsrv_begin_transaction', 'sqlsrv_cancel', 'sqlsrv_client_info', 'sqlsrv_close', 'sqlsrv_commit', 'sqlsrv_connect', 'sqlsrv_execute', 'sqlsrv_fetch', 'sqlsrv_fetch_array', 'sqlsrv_fetch_object', 'sqlsrv_field_metadata', 'sqlsrv_free_stmt', 'sqlsrv_get_field', 'sqlsrv_has_rows', 'sqlsrv_next_result', 'sqlsrv_num_fields', 'sqlsrv_num_rows', 'sqlsrv_prepare', 'sqlsrv_query', 'sqlsrv_rollback', 'sqlsrv_rows_affected', 'sqlsrv_send_stream_data', 'sqlsrv_server_info', 'ssh2_auth_agent', 'ssh2_auth_hostbased_file', 'ssh2_auth_none', 'ssh2_auth_password', 'ssh2_auth_pubkey_file', 'ssh2_disconnect', 'ssh2_exec', 'ssh2_fetch_stream', 'ssh2_fingerprint', 'ssh2_methods_negotiated', 'ssh2_publickey_add', 'ssh2_publickey_init', 'ssh2_publickey_list', 'ssh2_publickey_remove', 'ssh2_scp_recv', 'ssh2_scp_send', 'ssh2_sftp', 'ssh2_sftp_chmod', 'ssh2_sftp_lstat', 'ssh2_sftp_mkdir', 'ssh2_sftp_readlink', 'ssh2_sftp_realpath', 'ssh2_sftp_rename', 'ssh2_sftp_rmdir', 'ssh2_sftp_stat', 'ssh2_sftp_symlink', 'ssh2_sftp_unlink', 'ssh2_shell', 'ssh2_tunnel', 'stomp_connect', 'streamWrapper::stream_cast', 'stream_bucket_append', 'stream_bucket_make_writeable', 'stream_bucket_new', 'stream_bucket_prepend', 'stream_context_create', 'stream_context_get_default', 'stream_context_get_options', 'stream_context_get_params', 'stream_context_set_default', 'stream_context_set_params', 'stream_copy_to_stream', 'stream_encoding', 'stream_filter_append', 'stream_filter_prepend', 'stream_filter_remove', 'stream_get_contents', 'stream_get_line', 'stream_get_meta_data', 'stream_isatty', 'stream_set_blocking', 'stream_set_chunk_size', 'stream_set_read_buffer', 'stream_set_timeout', 'stream_set_write_buffer', 'stream_socket_accept', 'stream_socket_client', 'stream_socket_enable_crypto', 'stream_socket_get_name', 'stream_socket_recvfrom', 'stream_socket_sendto', 'stream_socket_server', 'stream_socket_shutdown', 'stream_supports_lock', 'svn_fs_abort_txn', 'svn_fs_apply_text', 'svn_fs_begin_txn2', 'svn_fs_change_node_prop', 'svn_fs_check_path', 'svn_fs_contents_changed', 'svn_fs_copy', 'svn_fs_delete', 'svn_fs_dir_entries', 'svn_fs_file_contents', 'svn_fs_file_length', 'svn_fs_is_dir', 'svn_fs_is_file', 'svn_fs_make_dir', 'svn_fs_make_file', 'svn_fs_node_created_rev', 'svn_fs_node_prop', 'svn_fs_props_changed', 'svn_fs_revision_prop', 'svn_fs_revision_root', 'svn_fs_txn_root', 'svn_fs_youngest_rev', 'svn_repos_create', 'svn_repos_fs', 'svn_repos_fs_begin_txn_for_commit', 'svn_repos_fs_commit_txn', 'svn_repos_open', 'sybase_affected_rows', 'sybase_close', 'sybase_connect', 'sybase_data_seek', 'sybase_fetch_array', 'sybase_fetch_assoc', 'sybase_fetch_field', 'sybase_fetch_object', 'sybase_fetch_row', 'sybase_field_seek', 'sybase_free_result', 'sybase_num_fields', 'sybase_num_rows', 'sybase_pconnect', 'sybase_query', 'sybase_result', 'sybase_select_db', 'sybase_set_message_handler', 'sybase_unbuffered_query', 'tmpfile', 'udm_add_search_limit', 'udm_alloc_agent', 'udm_alloc_agent_array', 'udm_cat_list', 'udm_cat_path', 'udm_check_charset', 'udm_clear_search_limits', 'udm_crc32', 'udm_errno', 'udm_error', 'udm_find', 'udm_free_agent', 'udm_free_res', 'udm_get_doc_count', 'udm_get_res_field', 'udm_get_res_param', 'udm_hash32', 'udm_load_ispell_data', 'udm_set_agent_param', 'unlink', 'vfprintf', 'w32api_init_dtype', 'wddx_add_vars', 'wddx_packet_end', 'wddx_packet_start', 'xml_get_current_byte_index', 'xml_get_current_column_number', 'xml_get_current_line_number', 'xml_get_error_code', 'xml_parse', 'xml_parse_into_struct', 'xml_parser_create', 'xml_parser_create_ns', 'xml_parser_free', 'xml_parser_get_option', 'xml_parser_set_option', 'xml_set_character_data_handler', 'xml_set_default_handler', 'xml_set_element_handler', 'xml_set_end_namespace_decl_handler', 'xml_set_external_entity_ref_handler', 'xml_set_notation_decl_handler', 'xml_set_object', 'xml_set_processing_instruction_handler', 'xml_set_start_namespace_decl_handler', 'xml_set_unparsed_entity_decl_handler', 'xmlrpc_server_add_introspection_data', 'xmlrpc_server_call_method', 'xmlrpc_server_create', 'xmlrpc_server_destroy', 'xmlrpc_server_register_introspection_callback', 'xmlrpc_server_register_method', 'xmlwriter_end_attribute', 'xmlwriter_end_cdata', 'xmlwriter_end_comment', 'xmlwriter_end_document', 'xmlwriter_end_dtd', 'xmlwriter_end_dtd_attlist', 'xmlwriter_end_dtd_element', 'xmlwriter_end_dtd_entity', 'xmlwriter_end_element', 'xmlwriter_end_pi', 'xmlwriter_flush', 'xmlwriter_full_end_element', 'xmlwriter_open_memory', 'xmlwriter_open_uri', 'xmlwriter_output_memory', 'xmlwriter_set_indent', 'xmlwriter_set_indent_string', 'xmlwriter_start_attribute', 'xmlwriter_start_attribute_ns', 'xmlwriter_start_cdata', 'xmlwriter_start_comment', 'xmlwriter_start_document', 'xmlwriter_start_dtd', 'xmlwriter_start_dtd_attlist', 'xmlwriter_start_dtd_element', 'xmlwriter_start_dtd_entity', 'xmlwriter_start_element', 'xmlwriter_start_element_ns', 'xmlwriter_start_pi', 'xmlwriter_text', 'xmlwriter_write_attribute', 'xmlwriter_write_attribute_ns', 'xmlwriter_write_cdata', 'xmlwriter_write_comment', 'xmlwriter_write_dtd', 'xmlwriter_write_dtd_attlist', 'xmlwriter_write_dtd_element', 'xmlwriter_write_dtd_entity', 'xmlwriter_write_element', 'xmlwriter_write_element_ns', 'xmlwriter_write_pi', 'xmlwriter_write_raw', 'xslt_create', 'yaz_addinfo', 'yaz_ccl_conf', 'yaz_ccl_parse', 'yaz_close', 'yaz_database', 'yaz_element', 'yaz_errno', 'yaz_error', 'yaz_es', 'yaz_es_result', 'yaz_get_option', 'yaz_hits', 'yaz_itemorder', 'yaz_present', 'yaz_range', 'yaz_record', 'yaz_scan', 'yaz_scan_result', 'yaz_schema', 'yaz_search', 'yaz_sort', 'yaz_syntax', 'zip_close', 'zip_entry_close', 'zip_entry_compressedsize', 'zip_entry_compressionmethod', 'zip_entry_filesize', 'zip_entry_name', 'zip_entry_open', 'zip_entry_read', 'zip_open', 'zip_read']; + return new Builder\Use_($name, Use_::TYPE_CONSTANT); } -} -Resource Operations - -Copyright (c) 2015-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\PharIo\Version; - -class VersionConstraintParser -{ /** - * @param string $value + * Creates a class constant builder. * - * @throws UnsupportedVersionConstraintException + * @param string|Identifier $name Name + * @param Node\Expr|bool|null|int|float|string|array $value Value + * + * @return Builder\ClassConst The created use const builder */ - public function parse($value) : \PHPUnit\PharIo\Version\VersionConstraint + public function classConst($name, $value) : Builder\ClassConst { - if (\strpos($value, '||') !== \false) { - return $this->handleOrGroup($value); - } - if (!\preg_match('/^[\\^~*]?v?[\\d.*]+(?:-.*)?$/i', $value)) { - throw new \PHPUnit\PharIo\Version\UnsupportedVersionConstraintException(\sprintf('Version constraint %s is not supported.', $value)); - } - switch ($value[0]) { - case '~': - return $this->handleTildeOperator($value); - case '^': - return $this->handleCaretOperator($value); - } - $version = new \PHPUnit\PharIo\Version\VersionConstraintValue($value); - if ($version->getMajor()->isAny()) { - return new \PHPUnit\PharIo\Version\AnyVersionConstraint(); - } - if ($version->getMinor()->isAny()) { - return new \PHPUnit\PharIo\Version\SpecificMajorVersionConstraint($version->getVersionString(), $version->getMajor()->getValue()); - } - if ($version->getPatch()->isAny()) { - return new \PHPUnit\PharIo\Version\SpecificMajorAndMinorVersionConstraint($version->getVersionString(), $version->getMajor()->getValue(), $version->getMinor()->getValue()); - } - return new \PHPUnit\PharIo\Version\ExactVersionConstraint($version->getVersionString()); + return new Builder\ClassConst($name, $value); } /** - * @param $value + * Creates node a for a literal value. + * + * @param Expr|bool|null|int|float|string|array $value $value + * + * @return Expr */ - private function handleOrGroup($value) : \PHPUnit\PharIo\Version\OrVersionConstraintGroup + public function val($value) : Expr { - $constraints = []; - foreach (\explode('||', $value) as $groupSegment) { - $constraints[] = $this->parse(\trim($groupSegment)); - } - return new \PHPUnit\PharIo\Version\OrVersionConstraintGroup($value, $constraints); + return BuilderHelpers::normalizeValue($value); } /** - * @param string $value + * Creates variable node. + * + * @param string|Expr $name Name + * + * @return Expr\Variable */ - private function handleTildeOperator($value) : \PHPUnit\PharIo\Version\AndVersionConstraintGroup + public function var($name) : Expr\Variable { - $version = new \PHPUnit\PharIo\Version\Version(\substr($value, 1)); - $constraints = [new \PHPUnit\PharIo\Version\GreaterThanOrEqualToVersionConstraint($value, $version)]; - if ($version->getPatch()->isAny()) { - $constraints[] = new \PHPUnit\PharIo\Version\SpecificMajorVersionConstraint($value, $version->getMajor()->getValue()); - } else { - $constraints[] = new \PHPUnit\PharIo\Version\SpecificMajorAndMinorVersionConstraint($value, $version->getMajor()->getValue(), $version->getMinor()->getValue()); + if (!\is_string($name) && !$name instanceof Expr) { + throw new \LogicException('Variable name must be string or Expr'); } - return new \PHPUnit\PharIo\Version\AndVersionConstraintGroup($value, $constraints); + return new Expr\Variable($name); } /** - * @param string $value + * Normalizes an argument list. + * + * Creates Arg nodes for all arguments and converts literal values to expressions. + * + * @param array $args List of arguments to normalize + * + * @return Arg[] */ - private function handleCaretOperator($value) : \PHPUnit\PharIo\Version\AndVersionConstraintGroup + public function args(array $args) : array { - $version = new \PHPUnit\PharIo\Version\Version(\substr($value, 1)); - return new \PHPUnit\PharIo\Version\AndVersionConstraintGroup($value, [new \PHPUnit\PharIo\Version\GreaterThanOrEqualToVersionConstraint($value, $version), new \PHPUnit\PharIo\Version\SpecificMajorVersionConstraint($value, $version->getMajor()->getValue())]); + $normalizedArgs = []; + foreach ($args as $key => $arg) { + if (!$arg instanceof Arg) { + $arg = new Arg(BuilderHelpers::normalizeValue($arg)); + } + if (\is_string($key)) { + $arg->name = BuilderHelpers::normalizeIdentifier($key); + } + $normalizedArgs[] = $arg; + } + return $normalizedArgs; } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\PharIo\Version; - -class Version -{ - /** @var VersionNumber */ - private $major; - /** @var VersionNumber */ - private $minor; - /** @var VersionNumber */ - private $patch; - /** @var PreReleaseSuffix */ - private $preReleaseSuffix; /** - * @param string $versionString + * Creates a function call node. + * + * @param string|Name|Expr $name Function name + * @param array $args Function arguments + * + * @return Expr\FuncCall */ - public function __construct($versionString) - { - $this->ensureVersionStringIsValid($versionString); - } - public function getPreReleaseSuffix() : \PHPUnit\PharIo\Version\PreReleaseSuffix - { - return $this->preReleaseSuffix; - } - public function getVersionString() : string + public function funcCall($name, array $args = []) : Expr\FuncCall { - $str = \sprintf('%d.%d.%d', $this->getMajor()->getValue(), $this->getMinor()->getValue(), $this->getPatch()->getValue()); - if (!$this->hasPreReleaseSuffix()) { - return $str; - } - return $str . '-' . $this->getPreReleaseSuffix()->asString(); + return new Expr\FuncCall(BuilderHelpers::normalizeNameOrExpr($name), $this->args($args)); } - public function hasPreReleaseSuffix() : bool + /** + * Creates a method call node. + * + * @param Expr $var Variable the method is called on + * @param string|Identifier|Expr $name Method name + * @param array $args Method arguments + * + * @return Expr\MethodCall + */ + public function methodCall(Expr $var, $name, array $args = []) : Expr\MethodCall { - return $this->preReleaseSuffix !== null; + return new Expr\MethodCall($var, BuilderHelpers::normalizeIdentifierOrExpr($name), $this->args($args)); } - public function equals(\PHPUnit\PharIo\Version\Version $other) : bool + /** + * Creates a static method call node. + * + * @param string|Name|Expr $class Class name + * @param string|Identifier|Expr $name Method name + * @param array $args Method arguments + * + * @return Expr\StaticCall + */ + public function staticCall($class, $name, array $args = []) : Expr\StaticCall { - return $this->getVersionString() === $other->getVersionString(); + return new Expr\StaticCall(BuilderHelpers::normalizeNameOrExpr($class), BuilderHelpers::normalizeIdentifierOrExpr($name), $this->args($args)); } - public function isGreaterThan(\PHPUnit\PharIo\Version\Version $version) : bool + /** + * Creates an object creation node. + * + * @param string|Name|Expr $class Class name + * @param array $args Constructor arguments + * + * @return Expr\New_ + */ + public function new($class, array $args = []) : Expr\New_ { - if ($version->getMajor()->getValue() > $this->getMajor()->getValue()) { - return \false; - } - if ($version->getMajor()->getValue() < $this->getMajor()->getValue()) { - return \true; - } - if ($version->getMinor()->getValue() > $this->getMinor()->getValue()) { - return \false; - } - if ($version->getMinor()->getValue() < $this->getMinor()->getValue()) { - return \true; - } - if ($version->getPatch()->getValue() > $this->getPatch()->getValue()) { - return \false; - } - if ($version->getPatch()->getValue() < $this->getPatch()->getValue()) { - return \true; - } - if (!$version->hasPreReleaseSuffix() && !$this->hasPreReleaseSuffix()) { - return \false; - } - if ($version->hasPreReleaseSuffix() && !$this->hasPreReleaseSuffix()) { - return \true; - } - if (!$version->hasPreReleaseSuffix() && $this->hasPreReleaseSuffix()) { - return \false; - } - return $this->getPreReleaseSuffix()->isGreaterThan($version->getPreReleaseSuffix()); + return new Expr\New_(BuilderHelpers::normalizeNameOrExpr($class), $this->args($args)); } - public function getMajor() : \PHPUnit\PharIo\Version\VersionNumber + /** + * Creates a constant fetch node. + * + * @param string|Name $name Constant name + * + * @return Expr\ConstFetch + */ + public function constFetch($name) : Expr\ConstFetch { - return $this->major; + return new Expr\ConstFetch(BuilderHelpers::normalizeName($name)); } - public function getMinor() : \PHPUnit\PharIo\Version\VersionNumber + /** + * Creates a property fetch node. + * + * @param Expr $var Variable holding object + * @param string|Identifier|Expr $name Property name + * + * @return Expr\PropertyFetch + */ + public function propertyFetch(Expr $var, $name) : Expr\PropertyFetch { - return $this->minor; + return new Expr\PropertyFetch($var, BuilderHelpers::normalizeIdentifierOrExpr($name)); } - public function getPatch() : \PHPUnit\PharIo\Version\VersionNumber + /** + * Creates a class constant fetch node. + * + * @param string|Name|Expr $class Class name + * @param string|Identifier $name Constant name + * + * @return Expr\ClassConstFetch + */ + public function classConstFetch($class, $name) : Expr\ClassConstFetch { - return $this->patch; + return new Expr\ClassConstFetch(BuilderHelpers::normalizeNameOrExpr($class), BuilderHelpers::normalizeIdentifier($name)); } - private function parseVersion(array $matches) : void + /** + * Creates nested Concat nodes from a list of expressions. + * + * @param Expr|string ...$exprs Expressions or literal strings + * + * @return Concat + */ + public function concat(...$exprs) : Concat { - $this->major = new \PHPUnit\PharIo\Version\VersionNumber((int) $matches['Major']); - $this->minor = new \PHPUnit\PharIo\Version\VersionNumber((int) $matches['Minor']); - $this->patch = isset($matches['Patch']) ? new \PHPUnit\PharIo\Version\VersionNumber((int) $matches['Patch']) : new \PHPUnit\PharIo\Version\VersionNumber(0); - if (isset($matches['PreReleaseSuffix'])) { - $this->preReleaseSuffix = new \PHPUnit\PharIo\Version\PreReleaseSuffix($matches['PreReleaseSuffix']); + $numExprs = \count($exprs); + if ($numExprs < 2) { + throw new \LogicException('Expected at least two expressions'); + } + $lastConcat = $this->normalizeStringExpr($exprs[0]); + for ($i = 1; $i < $numExprs; $i++) { + $lastConcat = new Concat($lastConcat, $this->normalizeStringExpr($exprs[$i])); } + return $lastConcat; } /** - * @param string $version - * - * @throws InvalidVersionException + * @param string|Expr $expr + * @return Expr */ - private function ensureVersionStringIsValid($version) : void + private function normalizeStringExpr($expr) : Expr { - $regex = '/^v? - (?(0|(?:[1-9]\\d*))) - \\. - (?(0|(?:[1-9]\\d*))) - (\\. - (?(0|(?:[1-9]\\d*))) - )? - (?: - - - (?(?:(dev|beta|b|rc|alpha|a|patch|p)\\.?\\d*)) - )? - $/xi'; - if (\preg_match($regex, $version, $matches) !== 1) { - throw new \PHPUnit\PharIo\Version\InvalidVersionException(\sprintf("Version string '%s' does not follow SemVer semantics", $version)); + if ($expr instanceof Expr) { + return $expr; } - $this->parseVersion($matches); + if (\is_string($expr)) { + return new String_($expr); + } + throw new \LogicException('Expected string or Expr'); } } , Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\PharIo\Version; - -use Throwable; -interface Exception extends \Throwable -{ -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\PharIo\Version; +namespace PHPUnit\PhpParser; -final class UnsupportedVersionConstraintException extends \RuntimeException implements \PHPUnit\PharIo\Version\Exception +interface NodeTraverserInterface { + /** + * Adds a visitor. + * + * @param NodeVisitor $visitor Visitor to add + */ + public function addVisitor(NodeVisitor $visitor); + /** + * Removes an added visitor. + * + * @param NodeVisitor $visitor + */ + public function removeVisitor(NodeVisitor $visitor); + /** + * Traverses an array of nodes using the registered visitors. + * + * @param Node[] $nodes Array of nodes + * + * @return Node[] Traversed array of nodes + */ + public function traverse(array $nodes) : array; } 0, 'a' => 1, 'alpha' => 1, 'b' => 2, 'beta' => 2, 'rc' => 3, 'p' => 4, 'patch' => 4]; - /** @var string */ - private $value; - /** @var int */ - private $valueScore; - /** @var int */ - private $number = 0; - /** @var string */ - private $full; - /** - * @throws InvalidPreReleaseSuffixException - */ - public function __construct(string $value) - { - $this->parseValue($value); - } - public function asString() : string - { - return $this->full; - } - public function getValue() : string - { - return $this->value; - } - public function getNumber() : ?int + /** @var Error[] Collected errors */ + private $errors = []; + public function handleError(Error $error) { - return $this->number; + $this->errors[] = $error; } - public function isGreaterThan(\PHPUnit\PharIo\Version\PreReleaseSuffix $suffix) : bool + /** + * Get collected errors. + * + * @return Error[] + */ + public function getErrors() : array { - if ($this->valueScore > $suffix->valueScore) { - return \true; - } - if ($this->valueScore < $suffix->valueScore) { - return \false; - } - return $this->getNumber() > $suffix->getNumber(); + return $this->errors; } /** - * @param $value + * Check whether there are any errors. + * + * @return bool */ - private function mapValueToScore($value) : int + public function hasErrors() : bool { - if (\array_key_exists($value, self::valueScoreMap)) { - return self::valueScoreMap[$value]; - } - return 0; + return !empty($this->errors); } - private function parseValue($value) : void + /** + * Reset/clear collected errors. + */ + public function clearErrors() { - $regex = '/-?((dev|beta|b|rc|alpha|a|patch|p)\\.?(\\d*)).*$/i'; - if (\preg_match($regex, $value, $matches) !== 1) { - throw new \PHPUnit\PharIo\Version\InvalidPreReleaseSuffixException(\sprintf('Invalid label %s', $value)); - } - $this->full = $matches[1]; - $this->value = $matches[2]; - if ($matches[3] !== '') { - $this->number = (int) $matches[3]; - } - $this->valueScore = $this->mapValueToScore($matches[2]); + $this->errors = []; } } versionString = $versionString; - $this->parseVersion($versionString); - } - public function getLabel() : string - { - return $this->label; - } - public function getBuildMetaData() : string - { - return $this->buildMetaData; - } - public function getVersionString() : string - { - return $this->versionString; - } - public function getMajor() : \PHPUnit\PharIo\Version\VersionNumber - { - return $this->major; - } - public function getMinor() : \PHPUnit\PharIo\Version\VersionNumber - { - return $this->minor; - } - public function getPatch() : \PHPUnit\PharIo\Version\VersionNumber - { - return $this->patch; + if (!\is_array($nodes)) { + $nodes = [$nodes]; + } + $visitor = new FindingVisitor($filter); + $traverser = new NodeTraverser(); + $traverser->addVisitor($visitor); + $traverser->traverse($nodes); + return $visitor->getFoundNodes(); } /** - * @param $versionString + * Find all nodes that are instances of a certain class. + * + * @param Node|Node[] $nodes Single node or array of nodes to search in + * @param string $class Class name + * + * @return Node[] Found nodes (all instances of $class) */ - private function parseVersion($versionString) : void + public function findInstanceOf($nodes, string $class) : array { - $this->extractBuildMetaData($versionString); - $this->extractLabel($versionString); - $this->stripPotentialVPrefix($versionString); - $versionSegments = \explode('.', $versionString); - $this->major = new \PHPUnit\PharIo\Version\VersionNumber(\is_numeric($versionSegments[0]) ? (int) $versionSegments[0] : null); - $minorValue = isset($versionSegments[1]) && \is_numeric($versionSegments[1]) ? (int) $versionSegments[1] : null; - $patchValue = isset($versionSegments[2]) && \is_numeric($versionSegments[2]) ? (int) $versionSegments[2] : null; - $this->minor = new \PHPUnit\PharIo\Version\VersionNumber($minorValue); - $this->patch = new \PHPUnit\PharIo\Version\VersionNumber($patchValue); + return $this->find($nodes, function ($node) use($class) { + return $node instanceof $class; + }); } /** - * @param string $versionString + * Find first node satisfying a filter callback. + * + * @param Node|Node[] $nodes Single node or array of nodes to search in + * @param callable $filter Filter callback: function(Node $node) : bool + * + * @return null|Node Found node (or null if none found) */ - private function extractBuildMetaData(&$versionString) : void + public function findFirst($nodes, callable $filter) { - if (\preg_match('/\\+(.*)/', $versionString, $matches) === 1) { - $this->buildMetaData = $matches[1]; - $versionString = \str_replace($matches[0], '', $versionString); + if (!\is_array($nodes)) { + $nodes = [$nodes]; } + $visitor = new FirstFindingVisitor($filter); + $traverser = new NodeTraverser(); + $traverser->addVisitor($visitor); + $traverser->traverse($nodes); + return $visitor->getFoundNode(); } /** - * @param string $versionString + * Find first node that is an instance of a certain class. + * + * @param Node|Node[] $nodes Single node or array of nodes to search in + * @param string $class Class name + * + * @return null|Node Found node, which is an instance of $class (or null if none found) */ - private function extractLabel(&$versionString) : void - { - if (\preg_match('/-(.*)/', $versionString, $matches) === 1) { - $this->label = $matches[1]; - $versionString = \str_replace($matches[0], '', $versionString); - } - } - private function stripPotentialVPrefix(&$versionString) : void + public function findFirstInstanceOf($nodes, string $class) { - if ($versionString[0] !== 'v') { - return; - } - $versionString = \substr($versionString, 1); + return $this->findFirst($nodes, function ($node) use($class) { + return $node instanceof $class; + }); } } , Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\PharIo\Version; +namespace PHPUnit\PhpParser; -interface VersionConstraint +interface Node { - public function complies(\PHPUnit\PharIo\Version\Version $version) : bool; - public function asString() : string; + /** + * Gets the type of the node. + * + * @return string Type of the node + */ + public function getType() : string; + /** + * Gets the names of the sub nodes. + * + * @return array Names of sub nodes + */ + public function getSubNodeNames() : array; + /** + * Gets line the node started in (alias of getStartLine). + * + * @return int Start line (or -1 if not available) + */ + public function getLine() : int; + /** + * Gets line the node started in. + * + * Requires the 'startLine' attribute to be enabled in the lexer (enabled by default). + * + * @return int Start line (or -1 if not available) + */ + public function getStartLine() : int; + /** + * Gets the line the node ended in. + * + * Requires the 'endLine' attribute to be enabled in the lexer (enabled by default). + * + * @return int End line (or -1 if not available) + */ + public function getEndLine() : int; + /** + * Gets the token offset of the first token that is part of this node. + * + * The offset is an index into the array returned by Lexer::getTokens(). + * + * Requires the 'startTokenPos' attribute to be enabled in the lexer (DISABLED by default). + * + * @return int Token start position (or -1 if not available) + */ + public function getStartTokenPos() : int; + /** + * Gets the token offset of the last token that is part of this node. + * + * The offset is an index into the array returned by Lexer::getTokens(). + * + * Requires the 'endTokenPos' attribute to be enabled in the lexer (DISABLED by default). + * + * @return int Token end position (or -1 if not available) + */ + public function getEndTokenPos() : int; + /** + * Gets the file offset of the first character that is part of this node. + * + * Requires the 'startFilePos' attribute to be enabled in the lexer (DISABLED by default). + * + * @return int File start position (or -1 if not available) + */ + public function getStartFilePos() : int; + /** + * Gets the file offset of the last character that is part of this node. + * + * Requires the 'endFilePos' attribute to be enabled in the lexer (DISABLED by default). + * + * @return int File end position (or -1 if not available) + */ + public function getEndFilePos() : int; + /** + * Gets all comments directly preceding this node. + * + * The comments are also available through the "comments" attribute. + * + * @return Comment[] + */ + public function getComments() : array; + /** + * Gets the doc comment of the node. + * + * @return null|Comment\Doc Doc comment object or null + */ + public function getDocComment(); + /** + * Sets the doc comment of the node. + * + * This will either replace an existing doc comment or add it to the comments array. + * + * @param Comment\Doc $docComment Doc comment to set + */ + public function setDocComment(Comment\Doc $docComment); + /** + * Sets an attribute on a node. + * + * @param string $key + * @param mixed $value + */ + public function setAttribute(string $key, $value); + /** + * Returns whether an attribute exists. + * + * @param string $key + * + * @return bool + */ + public function hasAttribute(string $key) : bool; + /** + * Returns the value of an attribute. + * + * @param string $key + * @param mixed $default + * + * @return mixed + */ + public function getAttribute(string $key, $default = null); + /** + * Returns all the attributes of this node. + * + * @return array + */ + public function getAttributes() : array; + /** + * Replaces all the attributes of this node. + * + * @param array $attributes + */ + public function setAttributes(array $attributes); } , Sebastian Heuer , Sebastian Bergmann +namespace PHPUnit\PhpParser; + +use PHPUnit\PhpParser\Node\ComplexType; +use PHPUnit\PhpParser\Node\Expr; +use PHPUnit\PhpParser\Node\Identifier; +use PHPUnit\PhpParser\Node\Name; +use PHPUnit\PhpParser\Node\NullableType; +use PHPUnit\PhpParser\Node\Scalar; +use PHPUnit\PhpParser\Node\Stmt; +/** + * This class defines helpers used in the implementation of builders. Don't use it directly. * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * @internal */ -namespace PHPUnit\PharIo\Version; - -abstract class AbstractVersionConstraint implements \PHPUnit\PharIo\Version\VersionConstraint +final class BuilderHelpers { - /** @var string */ - private $originalValue; - public function __construct(string $originalValue) + /** + * Normalizes a node: Converts builder objects to nodes. + * + * @param Node|Builder $node The node to normalize + * + * @return Node The normalized node + */ + public static function normalizeNode($node) : Node { - $this->originalValue = $originalValue; + if ($node instanceof Builder) { + return $node->getNode(); + } + if ($node instanceof Node) { + return $node; + } + throw new \LogicException('Expected node or builder object'); } - public function asString() : string + /** + * Normalizes a node to a statement. + * + * Expressions are wrapped in a Stmt\Expression node. + * + * @param Node|Builder $node The node to normalize + * + * @return Stmt The normalized statement node + */ + public static function normalizeStmt($node) : Stmt { - return $this->originalValue; + $node = self::normalizeNode($node); + if ($node instanceof Stmt) { + return $node; + } + if ($node instanceof Expr) { + return new Stmt\Expression($node); + } + throw new \LogicException('Expected statement or expression node'); } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\PharIo\Version; - -class SpecificMajorVersionConstraint extends \PHPUnit\PharIo\Version\AbstractVersionConstraint -{ - /** @var int */ - private $major = 0; /** - * @param string $originalValue - * @param int $major + * Normalizes strings to Identifier. + * + * @param string|Identifier $name The identifier to normalize + * + * @return Identifier The normalized identifier */ - public function __construct($originalValue, $major) + public static function normalizeIdentifier($name) : Identifier { - parent::__construct($originalValue); - $this->major = $major; + if ($name instanceof Identifier) { + return $name; + } + if (\is_string($name)) { + return new Identifier($name); + } + throw new \LogicException('PHPUnit\\Expected string or instance of Node\\Identifier'); } - public function complies(\PHPUnit\PharIo\Version\Version $version) : bool + /** + * Normalizes strings to Identifier, also allowing expressions. + * + * @param string|Identifier|Expr $name The identifier to normalize + * + * @return Identifier|Expr The normalized identifier or expression + */ + public static function normalizeIdentifierOrExpr($name) { - return $version->getMajor()->getValue() === $this->major; + if ($name instanceof Identifier || $name instanceof Expr) { + return $name; + } + if (\is_string($name)) { + return new Identifier($name); + } + throw new \LogicException('PHPUnit\\Expected string or instance of Node\\Identifier or Node\\Expr'); } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\PharIo\Version; - -class ExactVersionConstraint extends \PHPUnit\PharIo\Version\AbstractVersionConstraint -{ - public function complies(\PHPUnit\PharIo\Version\Version $version) : bool + /** + * Normalizes a name: Converts string names to Name nodes. + * + * @param Name|string $name The name to normalize + * + * @return Name The normalized name + */ + public static function normalizeName($name) : Name { - return $this->asString() === $version->getVersionString(); + if ($name instanceof Name) { + return $name; + } + if (\is_string($name)) { + if (!$name) { + throw new \LogicException('Name cannot be empty'); + } + if ($name[0] === '\\') { + return new Name\FullyQualified(\substr($name, 1)); + } + if (0 === \strpos($name, 'namespace\\')) { + return new Name\Relative(\substr($name, \strlen('namespace\\'))); + } + return new Name($name); + } + throw new \LogicException('PHPUnit\\Name must be a string or an instance of Node\\Name'); } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\PharIo\Version; - -class GreaterThanOrEqualToVersionConstraint extends \PHPUnit\PharIo\Version\AbstractVersionConstraint -{ - /** @var Version */ - private $minimalVersion; /** - * @param string $originalValue + * Normalizes a name: Converts string names to Name nodes, while also allowing expressions. + * + * @param Expr|Name|string $name The name to normalize + * + * @return Name|Expr The normalized name or expression */ - public function __construct($originalValue, \PHPUnit\PharIo\Version\Version $minimalVersion) + public static function normalizeNameOrExpr($name) { - parent::__construct($originalValue); - $this->minimalVersion = $minimalVersion; + if ($name instanceof Expr) { + return $name; + } + if (!\is_string($name) && !$name instanceof Name) { + throw new \LogicException('PHPUnit\\Name must be a string or an instance of Node\\Name or Node\\Expr'); + } + return self::normalizeName($name); } - public function complies(\PHPUnit\PharIo\Version\Version $version) : bool + /** + * Normalizes a type: Converts plain-text type names into proper AST representation. + * + * In particular, builtin types become Identifiers, custom types become Names and nullables + * are wrapped in NullableType nodes. + * + * @param string|Name|Identifier|ComplexType $type The type to normalize + * + * @return Name|Identifier|ComplexType The normalized type + */ + public static function normalizeType($type) { - return $version->getVersionString() === $this->minimalVersion->getVersionString() || $version->isGreaterThan($this->minimalVersion); + if (!\is_string($type)) { + if (!$type instanceof Name && !$type instanceof Identifier && !$type instanceof ComplexType) { + throw new \LogicException('Type must be a string, or an instance of Name, Identifier or ComplexType'); + } + return $type; + } + $nullable = \false; + if (\strlen($type) > 0 && $type[0] === '?') { + $nullable = \true; + $type = \substr($type, 1); + } + $builtinTypes = ['array', 'callable', 'string', 'int', 'float', 'bool', 'iterable', 'void', 'object', 'mixed', 'never']; + $lowerType = \strtolower($type); + if (\in_array($lowerType, $builtinTypes)) { + $type = new Identifier($lowerType); + } else { + $type = self::normalizeName($type); + } + $notNullableTypes = ['void', 'mixed', 'never']; + if ($nullable && \in_array((string) $type, $notNullableTypes)) { + throw new \LogicException(\sprintf('%s type cannot be nullable', $type)); + } + return $nullable ? new NullableType($type) : $type; } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\PharIo\Version; - -class AnyVersionConstraint implements \PHPUnit\PharIo\Version\VersionConstraint -{ - public function complies(\PHPUnit\PharIo\Version\Version $version) : bool + /** + * Normalizes a value: Converts nulls, booleans, integers, + * floats, strings and arrays into their respective nodes + * + * @param Node\Expr|bool|null|int|float|string|array $value The value to normalize + * + * @return Expr The normalized value + */ + public static function normalizeValue($value) : Expr { - return \true; + if ($value instanceof Node\Expr) { + return $value; + } + if (\is_null($value)) { + return new Expr\ConstFetch(new Name('null')); + } + if (\is_bool($value)) { + return new Expr\ConstFetch(new Name($value ? 'true' : 'false')); + } + if (\is_int($value)) { + return new Scalar\LNumber($value); + } + if (\is_float($value)) { + return new Scalar\DNumber($value); + } + if (\is_string($value)) { + return new Scalar\String_($value); + } + if (\is_array($value)) { + $items = []; + $lastKey = -1; + foreach ($value as $itemKey => $itemValue) { + // for consecutive, numeric keys don't generate keys + if (null !== $lastKey && ++$lastKey === $itemKey) { + $items[] = new Expr\ArrayItem(self::normalizeValue($itemValue)); + } else { + $lastKey = null; + $items[] = new Expr\ArrayItem(self::normalizeValue($itemValue), self::normalizeValue($itemKey)); + } + } + return new Expr\Array_($items); + } + throw new \LogicException('Invalid value'); } - public function asString() : string + /** + * Normalizes a doc comment: Converts plain strings to PhpParser\Comment\Doc. + * + * @param Comment\Doc|string $docComment The doc comment to normalize + * + * @return Comment\Doc The normalized doc comment + */ + public static function normalizeDocComment($docComment) : Comment\Doc { - return '*'; + if ($docComment instanceof Comment\Doc) { + return $docComment; + } + if (\is_string($docComment)) { + return new Comment\Doc($docComment); + } + throw new \LogicException('PHPUnit\\Doc comment must be a string or an instance of PhpParser\\Comment\\Doc'); } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\PharIo\Version; - -class OrVersionConstraintGroup extends \PHPUnit\PharIo\Version\AbstractVersionConstraint -{ - /** @var VersionConstraint[] */ - private $constraints = []; /** - * @param string $originalValue - * @param VersionConstraint[] $constraints + * Normalizes a attribute: Converts attribute to the Attribute Group if needed. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return Node\AttributeGroup The Attribute Group */ - public function __construct($originalValue, array $constraints) + public static function normalizeAttribute($attribute) : Node\AttributeGroup { - parent::__construct($originalValue); - $this->constraints = $constraints; + if ($attribute instanceof Node\AttributeGroup) { + return $attribute; + } + if (!$attribute instanceof Node\Attribute) { + throw new \LogicException('PHPUnit\\Attribute must be an instance of PhpParser\\Node\\Attribute or PhpParser\\Node\\AttributeGroup'); + } + return new Node\AttributeGroup([$attribute]); } - public function complies(\PHPUnit\PharIo\Version\Version $version) : bool + /** + * Adds a modifier and returns new modifier bitmask. + * + * @param int $modifiers Existing modifiers + * @param int $modifier Modifier to set + * + * @return int New modifiers + */ + public static function addModifier(int $modifiers, int $modifier) : int { - foreach ($this->constraints as $constraint) { - if ($constraint->complies($version)) { - return \true; - } - } - return \false; + Stmt\Class_::verifyModifier($modifiers, $modifier); + return $modifiers | $modifier; } } , Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\PharIo\Version; +namespace PHPUnit\PhpParser\PrettyPrinter; -class AndVersionConstraintGroup extends \PHPUnit\PharIo\Version\AbstractVersionConstraint +use PHPUnit\PhpParser\Node; +use PHPUnit\PhpParser\Node\Expr; +use PHPUnit\PhpParser\Node\Expr\AssignOp; +use PHPUnit\PhpParser\Node\Expr\BinaryOp; +use PHPUnit\PhpParser\Node\Expr\Cast; +use PHPUnit\PhpParser\Node\Name; +use PHPUnit\PhpParser\Node\Scalar; +use PHPUnit\PhpParser\Node\Scalar\MagicConst; +use PHPUnit\PhpParser\Node\Stmt; +use PHPUnit\PhpParser\PrettyPrinterAbstract; +class Standard extends PrettyPrinterAbstract { - /** @var VersionConstraint[] */ - private $constraints = []; - /** - * @param VersionConstraint[] $constraints - */ - public function __construct(string $originalValue, array $constraints) + // Special nodes + protected function pParam(Node\Param $node) { - parent::__construct($originalValue); - $this->constraints = $constraints; + return $this->pAttrGroups($node->attrGroups, \true) . $this->pModifiers($node->flags) . ($node->type ? $this->p($node->type) . ' ' : '') . ($node->byRef ? '&' : '') . ($node->variadic ? '...' : '') . $this->p($node->var) . ($node->default ? ' = ' . $this->p($node->default) : ''); } - public function complies(\PHPUnit\PharIo\Version\Version $version) : bool + protected function pArg(Node\Arg $node) { - foreach ($this->constraints as $constraint) { - if (!$constraint->complies($version)) { - return \false; - } - } - return \true; + return ($node->name ? $node->name->toString() . ': ' : '') . ($node->byRef ? '&' : '') . ($node->unpack ? '...' : '') . $this->p($node->value); } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\PharIo\Version; - -class SpecificMajorAndMinorVersionConstraint extends \PHPUnit\PharIo\Version\AbstractVersionConstraint -{ - /** @var int */ - private $major = 0; - /** @var int */ - private $minor = 0; - /** - * @param string $originalValue - * @param int $major - * @param int $minor - */ - public function __construct($originalValue, $major, $minor) + protected function pVariadicPlaceholder(Node\VariadicPlaceholder $node) { - parent::__construct($originalValue); - $this->major = $major; - $this->minor = $minor; + return '...'; } - public function complies(\PHPUnit\PharIo\Version\Version $version) : bool + protected function pConst(Node\Const_ $node) { - if ($version->getMajor()->getValue() !== $this->major) { - return \false; - } - return $version->getMinor()->getValue() === $this->minor; + return $node->name . ' = ' . $this->p($node->value); } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\PharIo\Version; - -class VersionNumber -{ - /** @var ?int */ - private $value; - public function __construct(?int $value) + protected function pNullableType(Node\NullableType $node) { - $this->value = $value; + return '?' . $this->p($node->type); } - public function isAny() : bool + protected function pUnionType(Node\UnionType $node) { - return $this->value === null; + return $this->pImplode($node->types, '|'); } - public function getValue() : ?int + protected function pIntersectionType(Node\IntersectionType $node) { - return $this->value; + return $this->pImplode($node->types, '&'); } -} -phar-io/version - -Copyright (c) 2016-2017 Arne Blankerts , Sebastian Heuer and contributors -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of Arne Blankerts nor the names of contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Invoker; - -use RuntimeException; -final class ProcessControlExtensionNotLoadedException extends \RuntimeException implements \PHPUnit\SebastianBergmann\Invoker\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Invoker; - -use RuntimeException; -final class TimeoutException extends \RuntimeException implements \PHPUnit\SebastianBergmann\Invoker\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Invoker; - -use Throwable; -interface Exception extends \Throwable -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Invoker; - -use const SIGALRM; -use function call_user_func_array; -use function function_exists; -use function pcntl_alarm; -use function pcntl_async_signals; -use function pcntl_signal; -use function sprintf; -use Throwable; -final class Invoker -{ - /** - * @var int - */ - private $timeout; - /** - * @throws Throwable - */ - public function invoke(callable $callable, array $arguments, int $timeout) + protected function pIdentifier(Node\Identifier $node) { - if (!$this->canInvokeWithTimeout()) { - throw new \PHPUnit\SebastianBergmann\Invoker\ProcessControlExtensionNotLoadedException('The pcntl (process control) extension for PHP is required'); - } - \pcntl_signal(\SIGALRM, function () : void { - throw new \PHPUnit\SebastianBergmann\Invoker\TimeoutException(\sprintf('Execution aborted after %d second%s', $this->timeout, $this->timeout === 1 ? '' : 's')); - }, \true); - $this->timeout = $timeout; - \pcntl_async_signals(\true); - \pcntl_alarm($timeout); - try { - return \call_user_func_array($callable, $arguments); - } finally { - \pcntl_alarm(0); - } + return $node->name; } - public function canInvokeWithTimeout() : bool + protected function pVarLikeIdentifier(Node\VarLikeIdentifier $node) { - return \function_exists('pcntl_signal') && \function_exists('pcntl_async_signals') && \function_exists('pcntl_alarm'); + return '$' . $node->name; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\ObjectEnumerator; - -use Throwable; -interface Exception extends \Throwable -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\ObjectEnumerator; - -class InvalidArgumentException extends \InvalidArgumentException implements \PHPUnit\SebastianBergmann\ObjectEnumerator\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\ObjectEnumerator; - -use function array_merge; -use function func_get_args; -use function is_array; -use function is_object; -use PHPUnit\SebastianBergmann\ObjectReflector\ObjectReflector; -use PHPUnit\SebastianBergmann\RecursionContext\Context; -/** - * Traverses array structures and object graphs - * to enumerate all referenced objects. - */ -class Enumerator -{ - /** - * Returns an array of all objects referenced either - * directly or indirectly by a variable. - * - * @param array|object $variable - * - * @return object[] - */ - public function enumerate($variable) + protected function pAttribute(Node\Attribute $node) { - if (!\is_array($variable) && !\is_object($variable)) { - throw new \PHPUnit\SebastianBergmann\ObjectEnumerator\InvalidArgumentException(); + return $this->p($node->name) . ($node->args ? '(' . $this->pCommaSeparated($node->args) . ')' : ''); + } + protected function pAttributeGroup(Node\AttributeGroup $node) + { + return '#[' . $this->pCommaSeparated($node->attrs) . ']'; + } + // Names + protected function pName(Name $node) + { + return \implode('\\', $node->parts); + } + protected function pName_FullyQualified(Name\FullyQualified $node) + { + return '\\' . \implode('\\', $node->parts); + } + protected function pName_Relative(Name\Relative $node) + { + return 'namespace\\' . \implode('\\', $node->parts); + } + // Magic Constants + protected function pScalar_MagicConst_Class(MagicConst\Class_ $node) + { + return '__CLASS__'; + } + protected function pScalar_MagicConst_Dir(MagicConst\Dir $node) + { + return '__DIR__'; + } + protected function pScalar_MagicConst_File(MagicConst\File $node) + { + return '__FILE__'; + } + protected function pScalar_MagicConst_Function(MagicConst\Function_ $node) + { + return '__FUNCTION__'; + } + protected function pScalar_MagicConst_Line(MagicConst\Line $node) + { + return '__LINE__'; + } + protected function pScalar_MagicConst_Method(MagicConst\Method $node) + { + return '__METHOD__'; + } + protected function pScalar_MagicConst_Namespace(MagicConst\Namespace_ $node) + { + return '__NAMESPACE__'; + } + protected function pScalar_MagicConst_Trait(MagicConst\Trait_ $node) + { + return '__TRAIT__'; + } + // Scalars + protected function pScalar_String(Scalar\String_ $node) + { + $kind = $node->getAttribute('kind', Scalar\String_::KIND_SINGLE_QUOTED); + switch ($kind) { + case Scalar\String_::KIND_NOWDOC: + $label = $node->getAttribute('docLabel'); + if ($label && !$this->containsEndLabel($node->value, $label)) { + if ($node->value === '') { + return "<<<'{$label}'\n{$label}" . $this->docStringEndToken; + } + return "<<<'{$label}'\n{$node->value}\n{$label}" . $this->docStringEndToken; + } + /* break missing intentionally */ + case Scalar\String_::KIND_SINGLE_QUOTED: + return $this->pSingleQuotedString($node->value); + case Scalar\String_::KIND_HEREDOC: + $label = $node->getAttribute('docLabel'); + if ($label && !$this->containsEndLabel($node->value, $label)) { + if ($node->value === '') { + return "<<<{$label}\n{$label}" . $this->docStringEndToken; + } + $escaped = $this->escapeString($node->value, null); + return "<<<{$label}\n" . $escaped . "\n{$label}" . $this->docStringEndToken; + } + /* break missing intentionally */ + case Scalar\String_::KIND_DOUBLE_QUOTED: + return '"' . $this->escapeString($node->value, '"') . '"'; } - if (isset(\func_get_args()[1])) { - if (!\func_get_args()[1] instanceof \PHPUnit\SebastianBergmann\RecursionContext\Context) { - throw new \PHPUnit\SebastianBergmann\ObjectEnumerator\InvalidArgumentException(); + throw new \Exception('Invalid string kind'); + } + protected function pScalar_Encapsed(Scalar\Encapsed $node) + { + if ($node->getAttribute('kind') === Scalar\String_::KIND_HEREDOC) { + $label = $node->getAttribute('docLabel'); + if ($label && !$this->encapsedContainsEndLabel($node->parts, $label)) { + if (\count($node->parts) === 1 && $node->parts[0] instanceof Scalar\EncapsedStringPart && $node->parts[0]->value === '') { + return "<<<{$label}\n{$label}" . $this->docStringEndToken; + } + return "<<<{$label}\n" . $this->pEncapsList($node->parts, null) . "\n{$label}" . $this->docStringEndToken; } - $processed = \func_get_args()[1]; - } else { - $processed = new \PHPUnit\SebastianBergmann\RecursionContext\Context(); } - $objects = []; - if ($processed->contains($variable)) { - return $objects; + return '"' . $this->pEncapsList($node->parts, '"') . '"'; + } + protected function pScalar_LNumber(Scalar\LNumber $node) + { + if ($node->value === -\PHP_INT_MAX - 1) { + // PHP_INT_MIN cannot be represented as a literal, + // because the sign is not part of the literal + return '(-' . \PHP_INT_MAX . '-1)'; } - $array = $variable; - $processed->add($variable); - if (\is_array($variable)) { - foreach ($array as $element) { - if (!\is_array($element) && !\is_object($element)) { - continue; - } - $objects = \array_merge($objects, $this->enumerate($element, $processed)); - } + $kind = $node->getAttribute('kind', Scalar\LNumber::KIND_DEC); + if (Scalar\LNumber::KIND_DEC === $kind) { + return (string) $node->value; + } + if ($node->value < 0) { + $sign = '-'; + $str = (string) -$node->value; } else { - $objects[] = $variable; - $reflector = new \PHPUnit\SebastianBergmann\ObjectReflector\ObjectReflector(); - foreach ($reflector->getAttributes($variable) as $value) { - if (!\is_array($value) && !\is_object($value)) { - continue; - } - $objects = \array_merge($objects, $this->enumerate($value, $processed)); + $sign = ''; + $str = (string) $node->value; + } + switch ($kind) { + case Scalar\LNumber::KIND_BIN: + return $sign . '0b' . \base_convert($str, 10, 2); + case Scalar\LNumber::KIND_OCT: + return $sign . '0' . \base_convert($str, 10, 8); + case Scalar\LNumber::KIND_HEX: + return $sign . '0x' . \base_convert($str, 10, 16); + } + throw new \Exception('Invalid number kind'); + } + protected function pScalar_DNumber(Scalar\DNumber $node) + { + if (!\is_finite($node->value)) { + if ($node->value === \INF) { + return '\\INF'; + } elseif ($node->value === -\INF) { + return '-\\INF'; + } else { + return '\\NAN'; } } - return $objects; + // Try to find a short full-precision representation + $stringValue = \sprintf('%.16G', $node->value); + if ($node->value !== (double) $stringValue) { + $stringValue = \sprintf('%.17G', $node->value); + } + // %G is locale dependent and there exists no locale-independent alternative. We don't want + // mess with switching locales here, so let's assume that a comma is the only non-standard + // decimal separator we may encounter... + $stringValue = \str_replace(',', '.', $stringValue); + // ensure that number is really printed as float + return \preg_match('/^-?[0-9]+$/', $stringValue) ? $stringValue . '.0' : $stringValue; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeUnit; - -use function range; -use function sprintf; -use ReflectionClass; -use ReflectionFunction; -use ReflectionMethod; -/** - * @psalm-immutable - */ -abstract class CodeUnit -{ - /** - * @var string - */ - private $name; - /** - * @var string - */ - private $sourceFileName; - /** - * @var array - * @psalm-var list - */ - private $sourceLines; - /** - * @psalm-param class-string $className - * - * @throws InvalidCodeUnitException - * @throws ReflectionException - */ - public static function forClass(string $className) : \PHPUnit\SebastianBergmann\CodeUnit\ClassUnit + protected function pScalar_EncapsedStringPart(Scalar\EncapsedStringPart $node) { - self::ensureUserDefinedClass($className); - $reflector = self::reflectorForClass($className); - return new \PHPUnit\SebastianBergmann\CodeUnit\ClassUnit($className, $reflector->getFileName(), \range($reflector->getStartLine(), $reflector->getEndLine())); + throw new \LogicException('Cannot directly print EncapsedStringPart'); } - /** - * @psalm-param class-string $className - * - * @throws InvalidCodeUnitException - * @throws ReflectionException - */ - public static function forClassMethod(string $className, string $methodName) : \PHPUnit\SebastianBergmann\CodeUnit\ClassMethodUnit + // Assignments + protected function pExpr_Assign(Expr\Assign $node) { - self::ensureUserDefinedClass($className); - $reflector = self::reflectorForClassMethod($className, $methodName); - return new \PHPUnit\SebastianBergmann\CodeUnit\ClassMethodUnit($className . '::' . $methodName, $reflector->getFileName(), \range($reflector->getStartLine(), $reflector->getEndLine())); + return $this->pInfixOp(Expr\Assign::class, $node->var, ' = ', $node->expr); } - /** - * @psalm-param class-string $interfaceName - * - * @throws InvalidCodeUnitException - * @throws ReflectionException - */ - public static function forInterface(string $interfaceName) : \PHPUnit\SebastianBergmann\CodeUnit\InterfaceUnit + protected function pExpr_AssignRef(Expr\AssignRef $node) { - self::ensureUserDefinedInterface($interfaceName); - $reflector = self::reflectorForClass($interfaceName); - return new \PHPUnit\SebastianBergmann\CodeUnit\InterfaceUnit($interfaceName, $reflector->getFileName(), \range($reflector->getStartLine(), $reflector->getEndLine())); + return $this->pInfixOp(Expr\AssignRef::class, $node->var, ' =& ', $node->expr); } - /** - * @psalm-param class-string $interfaceName - * - * @throws InvalidCodeUnitException - * @throws ReflectionException - */ - public static function forInterfaceMethod(string $interfaceName, string $methodName) : \PHPUnit\SebastianBergmann\CodeUnit\InterfaceMethodUnit + protected function pExpr_AssignOp_Plus(AssignOp\Plus $node) { - self::ensureUserDefinedInterface($interfaceName); - $reflector = self::reflectorForClassMethod($interfaceName, $methodName); - return new \PHPUnit\SebastianBergmann\CodeUnit\InterfaceMethodUnit($interfaceName . '::' . $methodName, $reflector->getFileName(), \range($reflector->getStartLine(), $reflector->getEndLine())); + return $this->pInfixOp(AssignOp\Plus::class, $node->var, ' += ', $node->expr); } - /** - * @psalm-param class-string $traitName - * - * @throws InvalidCodeUnitException - * @throws ReflectionException - */ - public static function forTrait(string $traitName) : \PHPUnit\SebastianBergmann\CodeUnit\TraitUnit + protected function pExpr_AssignOp_Minus(AssignOp\Minus $node) { - self::ensureUserDefinedTrait($traitName); - $reflector = self::reflectorForClass($traitName); - return new \PHPUnit\SebastianBergmann\CodeUnit\TraitUnit($traitName, $reflector->getFileName(), \range($reflector->getStartLine(), $reflector->getEndLine())); + return $this->pInfixOp(AssignOp\Minus::class, $node->var, ' -= ', $node->expr); } - /** - * @psalm-param class-string $traitName - * - * @throws InvalidCodeUnitException - * @throws ReflectionException - */ - public static function forTraitMethod(string $traitName, string $methodName) : \PHPUnit\SebastianBergmann\CodeUnit\TraitMethodUnit + protected function pExpr_AssignOp_Mul(AssignOp\Mul $node) { - self::ensureUserDefinedTrait($traitName); - $reflector = self::reflectorForClassMethod($traitName, $methodName); - return new \PHPUnit\SebastianBergmann\CodeUnit\TraitMethodUnit($traitName . '::' . $methodName, $reflector->getFileName(), \range($reflector->getStartLine(), $reflector->getEndLine())); + return $this->pInfixOp(AssignOp\Mul::class, $node->var, ' *= ', $node->expr); } - /** - * @psalm-param callable-string $functionName - * - * @throws InvalidCodeUnitException - * @throws ReflectionException - */ - public static function forFunction(string $functionName) : \PHPUnit\SebastianBergmann\CodeUnit\FunctionUnit + protected function pExpr_AssignOp_Div(AssignOp\Div $node) { - $reflector = self::reflectorForFunction($functionName); - if (!$reflector->isUserDefined()) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\InvalidCodeUnitException(\sprintf('"%s" is not a user-defined function', $functionName)); - } - return new \PHPUnit\SebastianBergmann\CodeUnit\FunctionUnit($functionName, $reflector->getFileName(), \range($reflector->getStartLine(), $reflector->getEndLine())); + return $this->pInfixOp(AssignOp\Div::class, $node->var, ' /= ', $node->expr); } - /** - * @psalm-param list $sourceLines - */ - private function __construct(string $name, string $sourceFileName, array $sourceLines) + protected function pExpr_AssignOp_Concat(AssignOp\Concat $node) { - $this->name = $name; - $this->sourceFileName = $sourceFileName; - $this->sourceLines = $sourceLines; + return $this->pInfixOp(AssignOp\Concat::class, $node->var, ' .= ', $node->expr); } - public function name() : string + protected function pExpr_AssignOp_Mod(AssignOp\Mod $node) { - return $this->name; + return $this->pInfixOp(AssignOp\Mod::class, $node->var, ' %= ', $node->expr); } - public function sourceFileName() : string + protected function pExpr_AssignOp_BitwiseAnd(AssignOp\BitwiseAnd $node) { - return $this->sourceFileName; + return $this->pInfixOp(AssignOp\BitwiseAnd::class, $node->var, ' &= ', $node->expr); } - /** - * @psalm-return list - */ - public function sourceLines() : array + protected function pExpr_AssignOp_BitwiseOr(AssignOp\BitwiseOr $node) { - return $this->sourceLines; + return $this->pInfixOp(AssignOp\BitwiseOr::class, $node->var, ' |= ', $node->expr); } - public function isClass() : bool + protected function pExpr_AssignOp_BitwiseXor(AssignOp\BitwiseXor $node) { - return \false; + return $this->pInfixOp(AssignOp\BitwiseXor::class, $node->var, ' ^= ', $node->expr); } - public function isClassMethod() : bool + protected function pExpr_AssignOp_ShiftLeft(AssignOp\ShiftLeft $node) { - return \false; + return $this->pInfixOp(AssignOp\ShiftLeft::class, $node->var, ' <<= ', $node->expr); } - public function isInterface() : bool + protected function pExpr_AssignOp_ShiftRight(AssignOp\ShiftRight $node) { - return \false; + return $this->pInfixOp(AssignOp\ShiftRight::class, $node->var, ' >>= ', $node->expr); } - public function isInterfaceMethod() : bool + protected function pExpr_AssignOp_Pow(AssignOp\Pow $node) { - return \false; + return $this->pInfixOp(AssignOp\Pow::class, $node->var, ' **= ', $node->expr); } - public function isTrait() : bool + protected function pExpr_AssignOp_Coalesce(AssignOp\Coalesce $node) { - return \false; + return $this->pInfixOp(AssignOp\Coalesce::class, $node->var, ' ??= ', $node->expr); } - public function isTraitMethod() : bool + // Binary expressions + protected function pExpr_BinaryOp_Plus(BinaryOp\Plus $node) { - return \false; + return $this->pInfixOp(BinaryOp\Plus::class, $node->left, ' + ', $node->right); } - public function isFunction() : bool + protected function pExpr_BinaryOp_Minus(BinaryOp\Minus $node) { - return \false; + return $this->pInfixOp(BinaryOp\Minus::class, $node->left, ' - ', $node->right); } - /** - * @psalm-param class-string $className - * - * @throws InvalidCodeUnitException - */ - private static function ensureUserDefinedClass(string $className) : void + protected function pExpr_BinaryOp_Mul(BinaryOp\Mul $node) { - try { - $reflector = new \ReflectionClass($className); - if ($reflector->isInterface()) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\InvalidCodeUnitException(\sprintf('"%s" is an interface and not a class', $className)); - } - if ($reflector->isTrait()) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\InvalidCodeUnitException(\sprintf('"%s" is a trait and not a class', $className)); - } - if (!$reflector->isUserDefined()) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\InvalidCodeUnitException(\sprintf('"%s" is not a user-defined class', $className)); - } - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + return $this->pInfixOp(BinaryOp\Mul::class, $node->left, ' * ', $node->right); } - /** - * @psalm-param class-string $interfaceName - * - * @throws InvalidCodeUnitException - */ - private static function ensureUserDefinedInterface(string $interfaceName) : void + protected function pExpr_BinaryOp_Div(BinaryOp\Div $node) { - try { - $reflector = new \ReflectionClass($interfaceName); - if (!$reflector->isInterface()) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\InvalidCodeUnitException(\sprintf('"%s" is not an interface', $interfaceName)); - } - if (!$reflector->isUserDefined()) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\InvalidCodeUnitException(\sprintf('"%s" is not a user-defined interface', $interfaceName)); - } - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + return $this->pInfixOp(BinaryOp\Div::class, $node->left, ' / ', $node->right); } - /** - * @psalm-param class-string $traitName - * - * @throws InvalidCodeUnitException - */ - private static function ensureUserDefinedTrait(string $traitName) : void + protected function pExpr_BinaryOp_Concat(BinaryOp\Concat $node) { - try { - $reflector = new \ReflectionClass($traitName); - if (!$reflector->isTrait()) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\InvalidCodeUnitException(\sprintf('"%s" is not a trait', $traitName)); - } - // @codeCoverageIgnoreStart - if (!$reflector->isUserDefined()) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\InvalidCodeUnitException(\sprintf('"%s" is not a user-defined trait', $traitName)); - } - } catch (\ReflectionException $e) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + return $this->pInfixOp(BinaryOp\Concat::class, $node->left, ' . ', $node->right); } - /** - * @psalm-param class-string $className - * - * @throws ReflectionException - */ - private static function reflectorForClass(string $className) : \ReflectionClass + protected function pExpr_BinaryOp_Mod(BinaryOp\Mod $node) { - try { - return new \ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + return $this->pInfixOp(BinaryOp\Mod::class, $node->left, ' % ', $node->right); } - /** - * @psalm-param class-string $className - * - * @throws ReflectionException - */ - private static function reflectorForClassMethod(string $className, string $methodName) : \ReflectionMethod + protected function pExpr_BinaryOp_BooleanAnd(BinaryOp\BooleanAnd $node) { - try { - return new \ReflectionMethod($className, $methodName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + return $this->pInfixOp(BinaryOp\BooleanAnd::class, $node->left, ' && ', $node->right); } - /** - * @psalm-param callable-string $functionName - * - * @throws ReflectionException - */ - private static function reflectorForFunction(string $functionName) : \ReflectionFunction + protected function pExpr_BinaryOp_BooleanOr(BinaryOp\BooleanOr $node) { - try { - return new \ReflectionFunction($functionName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + return $this->pInfixOp(BinaryOp\BooleanOr::class, $node->left, ' || ', $node->right); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeUnit; - -/** - * @psalm-immutable - */ -final class InterfaceMethodUnit extends \PHPUnit\SebastianBergmann\CodeUnit\CodeUnit -{ - /** - * @psalm-assert-if-true InterfaceMethod $this - */ - public function isInterfaceMethod() : bool + protected function pExpr_BinaryOp_BitwiseAnd(BinaryOp\BitwiseAnd $node) { - return \true; + return $this->pInfixOp(BinaryOp\BitwiseAnd::class, $node->left, ' & ', $node->right); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeUnit; - -use function array_merge; -use function count; -use Countable; -use IteratorAggregate; -final class CodeUnitCollection implements \Countable, \IteratorAggregate -{ - /** - * @psalm-var list - */ - private $codeUnits = []; - /** - * @psalm-param list $items - */ - public static function fromArray(array $items) : self + protected function pExpr_BinaryOp_BitwiseOr(BinaryOp\BitwiseOr $node) { - $collection = new self(); - foreach ($items as $item) { - $collection->add($item); - } - return $collection; + return $this->pInfixOp(BinaryOp\BitwiseOr::class, $node->left, ' | ', $node->right); } - public static function fromList(\PHPUnit\SebastianBergmann\CodeUnit\CodeUnit ...$items) : self + protected function pExpr_BinaryOp_BitwiseXor(BinaryOp\BitwiseXor $node) { - return self::fromArray($items); + return $this->pInfixOp(BinaryOp\BitwiseXor::class, $node->left, ' ^ ', $node->right); } - private function __construct() + protected function pExpr_BinaryOp_ShiftLeft(BinaryOp\ShiftLeft $node) { + return $this->pInfixOp(BinaryOp\ShiftLeft::class, $node->left, ' << ', $node->right); } - /** - * @psalm-return list - */ - public function asArray() : array + protected function pExpr_BinaryOp_ShiftRight(BinaryOp\ShiftRight $node) { - return $this->codeUnits; + return $this->pInfixOp(BinaryOp\ShiftRight::class, $node->left, ' >> ', $node->right); } - public function getIterator() : \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollectionIterator + protected function pExpr_BinaryOp_Pow(BinaryOp\Pow $node) { - return new \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollectionIterator($this); + return $this->pInfixOp(BinaryOp\Pow::class, $node->left, ' ** ', $node->right); } - public function count() : int + protected function pExpr_BinaryOp_LogicalAnd(BinaryOp\LogicalAnd $node) { - return \count($this->codeUnits); + return $this->pInfixOp(BinaryOp\LogicalAnd::class, $node->left, ' and ', $node->right); } - public function isEmpty() : bool + protected function pExpr_BinaryOp_LogicalOr(BinaryOp\LogicalOr $node) { - return empty($this->codeUnits); + return $this->pInfixOp(BinaryOp\LogicalOr::class, $node->left, ' or ', $node->right); } - public function mergeWith(self $other) : self + protected function pExpr_BinaryOp_LogicalXor(BinaryOp\LogicalXor $node) { - return self::fromArray(\array_merge($this->asArray(), $other->asArray())); + return $this->pInfixOp(BinaryOp\LogicalXor::class, $node->left, ' xor ', $node->right); } - private function add(\PHPUnit\SebastianBergmann\CodeUnit\CodeUnit $item) : void + protected function pExpr_BinaryOp_Equal(BinaryOp\Equal $node) { - $this->codeUnits[] = $item; + return $this->pInfixOp(BinaryOp\Equal::class, $node->left, ' == ', $node->right); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeUnit; - -/** - * @psalm-immutable - */ -final class ClassUnit extends \PHPUnit\SebastianBergmann\CodeUnit\CodeUnit -{ - /** - * @psalm-assert-if-true ClassUnit $this - */ - public function isClass() : bool + protected function pExpr_BinaryOp_NotEqual(BinaryOp\NotEqual $node) { - return \true; + return $this->pInfixOp(BinaryOp\NotEqual::class, $node->left, ' != ', $node->right); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeUnit; - -use Throwable; -interface Exception extends \Throwable -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeUnit; - -use RuntimeException; -final class InvalidCodeUnitException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeUnit\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeUnit; - -use RuntimeException; -final class NoTraitException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeUnit\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeUnit; - -use RuntimeException; -final class ReflectionException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeUnit\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeUnit; - -/** - * @psalm-immutable - */ -final class FunctionUnit extends \PHPUnit\SebastianBergmann\CodeUnit\CodeUnit -{ - /** - * @psalm-assert-if-true FunctionUnit $this - */ - public function isFunction() : bool + protected function pExpr_BinaryOp_Identical(BinaryOp\Identical $node) { - return \true; + return $this->pInfixOp(BinaryOp\Identical::class, $node->left, ' === ', $node->right); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeUnit; - -use Iterator; -final class CodeUnitCollectionIterator implements \Iterator -{ - /** - * @psalm-var list - */ - private $codeUnits; - /** - * @var int - */ - private $position = 0; - public function __construct(\PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection $collection) + protected function pExpr_BinaryOp_NotIdentical(BinaryOp\NotIdentical $node) { - $this->codeUnits = $collection->asArray(); + return $this->pInfixOp(BinaryOp\NotIdentical::class, $node->left, ' !== ', $node->right); } - public function rewind() : void + protected function pExpr_BinaryOp_Spaceship(BinaryOp\Spaceship $node) { - $this->position = 0; + return $this->pInfixOp(BinaryOp\Spaceship::class, $node->left, ' <=> ', $node->right); } - public function valid() : bool + protected function pExpr_BinaryOp_Greater(BinaryOp\Greater $node) { - return isset($this->codeUnits[$this->position]); + return $this->pInfixOp(BinaryOp\Greater::class, $node->left, ' > ', $node->right); } - public function key() : int + protected function pExpr_BinaryOp_GreaterOrEqual(BinaryOp\GreaterOrEqual $node) { - return $this->position; + return $this->pInfixOp(BinaryOp\GreaterOrEqual::class, $node->left, ' >= ', $node->right); } - public function current() : \PHPUnit\SebastianBergmann\CodeUnit\CodeUnit + protected function pExpr_BinaryOp_Smaller(BinaryOp\Smaller $node) { - return $this->codeUnits[$this->position]; + return $this->pInfixOp(BinaryOp\Smaller::class, $node->left, ' < ', $node->right); } - public function next() : void + protected function pExpr_BinaryOp_SmallerOrEqual(BinaryOp\SmallerOrEqual $node) { - $this->position++; + return $this->pInfixOp(BinaryOp\SmallerOrEqual::class, $node->left, ' <= ', $node->right); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeUnit; - -use function array_keys; -use function array_merge; -use function array_unique; -use function array_values; -use function class_exists; -use function explode; -use function function_exists; -use function interface_exists; -use function ksort; -use function method_exists; -use function sort; -use function sprintf; -use function str_replace; -use function strpos; -use function trait_exists; -use ReflectionClass; -use ReflectionFunction; -use ReflectionMethod; -final class Mapper -{ - /** - * @psalm-return array> - */ - public function codeUnitsToSourceLines(\PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection $codeUnits) : array + protected function pExpr_BinaryOp_Coalesce(BinaryOp\Coalesce $node) { - $result = []; - foreach ($codeUnits as $codeUnit) { - $sourceFileName = $codeUnit->sourceFileName(); - if (!isset($result[$sourceFileName])) { - $result[$sourceFileName] = []; - } - $result[$sourceFileName] = \array_merge($result[$sourceFileName], $codeUnit->sourceLines()); - } - foreach (\array_keys($result) as $sourceFileName) { - $result[$sourceFileName] = \array_values(\array_unique($result[$sourceFileName])); - \sort($result[$sourceFileName]); + return $this->pInfixOp(BinaryOp\Coalesce::class, $node->left, ' ?? ', $node->right); + } + protected function pExpr_Instanceof(Expr\Instanceof_ $node) + { + list($precedence, $associativity) = $this->precedenceMap[Expr\Instanceof_::class]; + return $this->pPrec($node->expr, $precedence, $associativity, -1) . ' instanceof ' . $this->pNewVariable($node->class); + } + // Unary expressions + protected function pExpr_BooleanNot(Expr\BooleanNot $node) + { + return $this->pPrefixOp(Expr\BooleanNot::class, '!', $node->expr); + } + protected function pExpr_BitwiseNot(Expr\BitwiseNot $node) + { + return $this->pPrefixOp(Expr\BitwiseNot::class, '~', $node->expr); + } + protected function pExpr_UnaryMinus(Expr\UnaryMinus $node) + { + if ($node->expr instanceof Expr\UnaryMinus || $node->expr instanceof Expr\PreDec) { + // Enforce -(-$expr) instead of --$expr + return '-(' . $this->p($node->expr) . ')'; } - \ksort($result); - return $result; + return $this->pPrefixOp(Expr\UnaryMinus::class, '-', $node->expr); } - /** - * @throws InvalidCodeUnitException - * @throws ReflectionException - */ - public function stringToCodeUnits(string $unit) : \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection + protected function pExpr_UnaryPlus(Expr\UnaryPlus $node) { - if (\strpos($unit, '::') !== \false) { - [$firstPart, $secondPart] = \explode('::', $unit); - if (empty($firstPart) && $this->isUserDefinedFunction($secondPart)) { - return \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection::fromList(\PHPUnit\SebastianBergmann\CodeUnit\CodeUnit::forFunction($secondPart)); - } - if ($this->isUserDefinedClass($firstPart)) { - if ($secondPart === '') { - return $this->publicMethodsOfClass($firstPart); - } - if ($secondPart === '') { - return $this->protectedAndPrivateMethodsOfClass($firstPart); - } - if ($secondPart === '') { - return $this->protectedMethodsOfClass($firstPart); - } - if ($secondPart === '') { - return $this->publicAndPrivateMethodsOfClass($firstPart); - } - if ($secondPart === '') { - return $this->privateMethodsOfClass($firstPart); - } - if ($secondPart === '') { - return $this->publicAndProtectedMethodsOfClass($firstPart); - } - if ($this->isUserDefinedMethod($firstPart, $secondPart)) { - return \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection::fromList(\PHPUnit\SebastianBergmann\CodeUnit\CodeUnit::forClassMethod($firstPart, $secondPart)); - } - } - if ($this->isUserDefinedInterface($firstPart)) { - return \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection::fromList(\PHPUnit\SebastianBergmann\CodeUnit\CodeUnit::forInterfaceMethod($firstPart, $secondPart)); - } - if ($this->isUserDefinedTrait($firstPart)) { - return \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection::fromList(\PHPUnit\SebastianBergmann\CodeUnit\CodeUnit::forTraitMethod($firstPart, $secondPart)); - } - } else { - if ($this->isUserDefinedClass($unit)) { - $units = [\PHPUnit\SebastianBergmann\CodeUnit\CodeUnit::forClass($unit)]; - foreach ($this->reflectorForClass($unit)->getTraits() as $trait) { - if (!$trait->isUserDefined()) { - // @codeCoverageIgnoreStart - continue; - // @codeCoverageIgnoreEnd - } - $units[] = \PHPUnit\SebastianBergmann\CodeUnit\CodeUnit::forTrait($trait->getName()); - } - return \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection::fromArray($units); - } - if ($this->isUserDefinedInterface($unit)) { - return \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection::fromList(\PHPUnit\SebastianBergmann\CodeUnit\CodeUnit::forInterface($unit)); - } - if ($this->isUserDefinedTrait($unit)) { - return \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection::fromList(\PHPUnit\SebastianBergmann\CodeUnit\CodeUnit::forTrait($unit)); - } - if ($this->isUserDefinedFunction($unit)) { - return \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection::fromList(\PHPUnit\SebastianBergmann\CodeUnit\CodeUnit::forFunction($unit)); - } - $unit = \str_replace('', '', $unit); - if ($this->isUserDefinedClass($unit)) { - return $this->classAndParentClassesAndTraits($unit); - } + if ($node->expr instanceof Expr\UnaryPlus || $node->expr instanceof Expr\PreInc) { + // Enforce +(+$expr) instead of ++$expr + return '+(' . $this->p($node->expr) . ')'; } - throw new \PHPUnit\SebastianBergmann\CodeUnit\InvalidCodeUnitException(\sprintf('"%s" is not a valid code unit', $unit)); + return $this->pPrefixOp(Expr\UnaryPlus::class, '+', $node->expr); } - /** - * @psalm-param class-string $className - * - * @throws ReflectionException - */ - private function publicMethodsOfClass(string $className) : \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection + protected function pExpr_PreInc(Expr\PreInc $node) { - return $this->methodsOfClass($className, \ReflectionMethod::IS_PUBLIC); + return $this->pPrefixOp(Expr\PreInc::class, '++', $node->var); } - /** - * @psalm-param class-string $className - * - * @throws ReflectionException - */ - private function publicAndProtectedMethodsOfClass(string $className) : \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection + protected function pExpr_PreDec(Expr\PreDec $node) { - return $this->methodsOfClass($className, \ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED); + return $this->pPrefixOp(Expr\PreDec::class, '--', $node->var); } - /** - * @psalm-param class-string $className - * - * @throws ReflectionException - */ - private function publicAndPrivateMethodsOfClass(string $className) : \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection + protected function pExpr_PostInc(Expr\PostInc $node) { - return $this->methodsOfClass($className, \ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PRIVATE); + return $this->pPostfixOp(Expr\PostInc::class, $node->var, '++'); } - /** - * @psalm-param class-string $className - * - * @throws ReflectionException - */ - private function protectedMethodsOfClass(string $className) : \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection + protected function pExpr_PostDec(Expr\PostDec $node) { - return $this->methodsOfClass($className, \ReflectionMethod::IS_PROTECTED); + return $this->pPostfixOp(Expr\PostDec::class, $node->var, '--'); } - /** - * @psalm-param class-string $className - * - * @throws ReflectionException - */ - private function protectedAndPrivateMethodsOfClass(string $className) : \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection + protected function pExpr_ErrorSuppress(Expr\ErrorSuppress $node) { - return $this->methodsOfClass($className, \ReflectionMethod::IS_PROTECTED | \ReflectionMethod::IS_PRIVATE); + return $this->pPrefixOp(Expr\ErrorSuppress::class, '@', $node->expr); } - /** - * @psalm-param class-string $className - * - * @throws ReflectionException - */ - private function privateMethodsOfClass(string $className) : \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection + protected function pExpr_YieldFrom(Expr\YieldFrom $node) { - return $this->methodsOfClass($className, \ReflectionMethod::IS_PRIVATE); + return $this->pPrefixOp(Expr\YieldFrom::class, 'yield from ', $node->expr); } - /** - * @psalm-param class-string $className - * - * @throws ReflectionException - */ - private function methodsOfClass(string $className, int $filter) : \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection + protected function pExpr_Print(Expr\Print_ $node) { - $units = []; - foreach ($this->reflectorForClass($className)->getMethods($filter) as $method) { - if (!$method->isUserDefined()) { - continue; - } - $units[] = \PHPUnit\SebastianBergmann\CodeUnit\CodeUnit::forClassMethod($className, $method->getName()); - } - return \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection::fromArray($units); + return $this->pPrefixOp(Expr\Print_::class, 'print ', $node->expr); } - /** - * @psalm-param class-string $className - * - * @throws ReflectionException - */ - private function classAndParentClassesAndTraits(string $className) : \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection + // Casts + protected function pExpr_Cast_Int(Cast\Int_ $node) { - $units = [\PHPUnit\SebastianBergmann\CodeUnit\CodeUnit::forClass($className)]; - $reflector = $this->reflectorForClass($className); - foreach ($this->reflectorForClass($className)->getTraits() as $trait) { - if (!$trait->isUserDefined()) { - // @codeCoverageIgnoreStart - continue; - // @codeCoverageIgnoreEnd - } - $units[] = \PHPUnit\SebastianBergmann\CodeUnit\CodeUnit::forTrait($trait->getName()); - } - while ($reflector = $reflector->getParentClass()) { - if (!$reflector->isUserDefined()) { - break; - } - $units[] = \PHPUnit\SebastianBergmann\CodeUnit\CodeUnit::forClass($reflector->getName()); - foreach ($reflector->getTraits() as $trait) { - if (!$trait->isUserDefined()) { - // @codeCoverageIgnoreStart - continue; - // @codeCoverageIgnoreEnd - } - $units[] = \PHPUnit\SebastianBergmann\CodeUnit\CodeUnit::forTrait($trait->getName()); - } - } - return \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection::fromArray($units); + return $this->pPrefixOp(Cast\Int_::class, '(int) ', $node->expr); } - /** - * @psalm-param class-string $className - * - * @throws ReflectionException - */ - private function reflectorForClass(string $className) : \ReflectionClass + protected function pExpr_Cast_Double(Cast\Double $node) { - try { - return new \ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + $kind = $node->getAttribute('kind', Cast\Double::KIND_DOUBLE); + if ($kind === Cast\Double::KIND_DOUBLE) { + $cast = '(double)'; + } elseif ($kind === Cast\Double::KIND_FLOAT) { + $cast = '(float)'; + } elseif ($kind === Cast\Double::KIND_REAL) { + $cast = '(real)'; } - // @codeCoverageIgnoreEnd + return $this->pPrefixOp(Cast\Double::class, $cast . ' ', $node->expr); } - /** - * @throws ReflectionException - */ - private function isUserDefinedFunction(string $functionName) : bool + protected function pExpr_Cast_String(Cast\String_ $node) { - if (!\function_exists($functionName)) { - return \false; - } - try { - return (new \ReflectionFunction($functionName))->isUserDefined(); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + return $this->pPrefixOp(Cast\String_::class, '(string) ', $node->expr); } - /** - * @throws ReflectionException - */ - private function isUserDefinedClass(string $className) : bool + protected function pExpr_Cast_Array(Cast\Array_ $node) { - if (!\class_exists($className)) { - return \false; - } - try { - return (new \ReflectionClass($className))->isUserDefined(); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + return $this->pPrefixOp(Cast\Array_::class, '(array) ', $node->expr); } - /** - * @throws ReflectionException - */ - private function isUserDefinedInterface(string $interfaceName) : bool + protected function pExpr_Cast_Object(Cast\Object_ $node) { - if (!\interface_exists($interfaceName)) { - return \false; - } - try { - return (new \ReflectionClass($interfaceName))->isUserDefined(); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + return $this->pPrefixOp(Cast\Object_::class, '(object) ', $node->expr); } - /** - * @throws ReflectionException - */ - private function isUserDefinedTrait(string $traitName) : bool + protected function pExpr_Cast_Bool(Cast\Bool_ $node) { - if (!\trait_exists($traitName)) { - return \false; + return $this->pPrefixOp(Cast\Bool_::class, '(bool) ', $node->expr); + } + protected function pExpr_Cast_Unset(Cast\Unset_ $node) + { + return $this->pPrefixOp(Cast\Unset_::class, '(unset) ', $node->expr); + } + // Function calls and similar constructs + protected function pExpr_FuncCall(Expr\FuncCall $node) + { + return $this->pCallLhs($node->name) . '(' . $this->pMaybeMultiline($node->args) . ')'; + } + protected function pExpr_MethodCall(Expr\MethodCall $node) + { + return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name) . '(' . $this->pMaybeMultiline($node->args) . ')'; + } + protected function pExpr_NullsafeMethodCall(Expr\NullsafeMethodCall $node) + { + return $this->pDereferenceLhs($node->var) . '?->' . $this->pObjectProperty($node->name) . '(' . $this->pMaybeMultiline($node->args) . ')'; + } + protected function pExpr_StaticCall(Expr\StaticCall $node) + { + return $this->pDereferenceLhs($node->class) . '::' . ($node->name instanceof Expr ? $node->name instanceof Expr\Variable ? $this->p($node->name) : '{' . $this->p($node->name) . '}' : $node->name) . '(' . $this->pMaybeMultiline($node->args) . ')'; + } + protected function pExpr_Empty(Expr\Empty_ $node) + { + return 'empty(' . $this->p($node->expr) . ')'; + } + protected function pExpr_Isset(Expr\Isset_ $node) + { + return 'isset(' . $this->pCommaSeparated($node->vars) . ')'; + } + protected function pExpr_Eval(Expr\Eval_ $node) + { + return 'eval(' . $this->p($node->expr) . ')'; + } + protected function pExpr_Include(Expr\Include_ $node) + { + static $map = [Expr\Include_::TYPE_INCLUDE => 'include', Expr\Include_::TYPE_INCLUDE_ONCE => 'include_once', Expr\Include_::TYPE_REQUIRE => 'require', Expr\Include_::TYPE_REQUIRE_ONCE => 'require_once']; + return $map[$node->type] . ' ' . $this->p($node->expr); + } + protected function pExpr_List(Expr\List_ $node) + { + return 'list(' . $this->pCommaSeparated($node->items) . ')'; + } + // Other + protected function pExpr_Error(Expr\Error $node) + { + throw new \LogicException('Cannot pretty-print AST with Error nodes'); + } + protected function pExpr_Variable(Expr\Variable $node) + { + if ($node->name instanceof Expr) { + return '${' . $this->p($node->name) . '}'; + } else { + return '$' . $node->name; } - try { - return (new \ReflectionClass($traitName))->isUserDefined(); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + protected function pExpr_Array(Expr\Array_ $node) + { + $syntax = $node->getAttribute('kind', $this->options['shortArraySyntax'] ? Expr\Array_::KIND_SHORT : Expr\Array_::KIND_LONG); + if ($syntax === Expr\Array_::KIND_SHORT) { + return '[' . $this->pMaybeMultiline($node->items, \true) . ']'; + } else { + return 'array(' . $this->pMaybeMultiline($node->items, \true) . ')'; } - // @codeCoverageIgnoreEnd + } + protected function pExpr_ArrayItem(Expr\ArrayItem $node) + { + return (null !== $node->key ? $this->p($node->key) . ' => ' : '') . ($node->byRef ? '&' : '') . ($node->unpack ? '...' : '') . $this->p($node->value); + } + protected function pExpr_ArrayDimFetch(Expr\ArrayDimFetch $node) + { + return $this->pDereferenceLhs($node->var) . '[' . (null !== $node->dim ? $this->p($node->dim) : '') . ']'; + } + protected function pExpr_ConstFetch(Expr\ConstFetch $node) + { + return $this->p($node->name); + } + protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node) + { + return $this->pDereferenceLhs($node->class) . '::' . $this->p($node->name); + } + protected function pExpr_PropertyFetch(Expr\PropertyFetch $node) + { + return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name); + } + protected function pExpr_NullsafePropertyFetch(Expr\NullsafePropertyFetch $node) + { + return $this->pDereferenceLhs($node->var) . '?->' . $this->pObjectProperty($node->name); + } + protected function pExpr_StaticPropertyFetch(Expr\StaticPropertyFetch $node) + { + return $this->pDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name); + } + protected function pExpr_ShellExec(Expr\ShellExec $node) + { + return '`' . $this->pEncapsList($node->parts, '`') . '`'; + } + protected function pExpr_Closure(Expr\Closure $node) + { + return $this->pAttrGroups($node->attrGroups, \true) . ($node->static ? 'static ' : '') . 'function ' . ($node->byRef ? '&' : '') . '(' . $this->pCommaSeparated($node->params) . ')' . (!empty($node->uses) ? ' use(' . $this->pCommaSeparated($node->uses) . ')' : '') . (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '') . ' {' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + protected function pExpr_Match(Expr\Match_ $node) + { + return 'match (' . $this->p($node->cond) . ') {' . $this->pCommaSeparatedMultiline($node->arms, \true) . $this->nl . '}'; + } + protected function pMatchArm(Node\MatchArm $node) + { + return ($node->conds ? $this->pCommaSeparated($node->conds) : 'default') . ' => ' . $this->p($node->body); + } + protected function pExpr_ArrowFunction(Expr\ArrowFunction $node) + { + return $this->pAttrGroups($node->attrGroups, \true) . ($node->static ? 'static ' : '') . 'fn' . ($node->byRef ? '&' : '') . '(' . $this->pCommaSeparated($node->params) . ')' . (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '') . ' => ' . $this->p($node->expr); + } + protected function pExpr_ClosureUse(Expr\ClosureUse $node) + { + return ($node->byRef ? '&' : '') . $this->p($node->var); + } + protected function pExpr_New(Expr\New_ $node) + { + if ($node->class instanceof Stmt\Class_) { + $args = $node->args ? '(' . $this->pMaybeMultiline($node->args) . ')' : ''; + return 'new ' . $this->pClassCommon($node->class, $args); + } + return 'new ' . $this->pNewVariable($node->class) . '(' . $this->pMaybeMultiline($node->args) . ')'; + } + protected function pExpr_Clone(Expr\Clone_ $node) + { + return 'clone ' . $this->p($node->expr); + } + protected function pExpr_Ternary(Expr\Ternary $node) + { + // a bit of cheating: we treat the ternary as a binary op where the ?...: part is the operator. + // this is okay because the part between ? and : never needs parentheses. + return $this->pInfixOp(Expr\Ternary::class, $node->cond, ' ?' . (null !== $node->if ? ' ' . $this->p($node->if) . ' ' : '') . ': ', $node->else); + } + protected function pExpr_Exit(Expr\Exit_ $node) + { + $kind = $node->getAttribute('kind', Expr\Exit_::KIND_DIE); + return ($kind === Expr\Exit_::KIND_EXIT ? 'exit' : 'die') . (null !== $node->expr ? '(' . $this->p($node->expr) . ')' : ''); + } + protected function pExpr_Throw(Expr\Throw_ $node) + { + return 'throw ' . $this->p($node->expr); + } + protected function pExpr_Yield(Expr\Yield_ $node) + { + if ($node->value === null) { + return 'yield'; + } else { + // this is a bit ugly, but currently there is no way to detect whether the parentheses are necessary + return '(yield ' . ($node->key !== null ? $this->p($node->key) . ' => ' : '') . $this->p($node->value) . ')'; + } + } + // Declarations + protected function pStmt_Namespace(Stmt\Namespace_ $node) + { + if ($this->canUseSemicolonNamespaces) { + return 'namespace ' . $this->p($node->name) . ';' . $this->nl . $this->pStmts($node->stmts, \false); + } else { + return 'namespace' . (null !== $node->name ? ' ' . $this->p($node->name) : '') . ' {' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + } + protected function pStmt_Use(Stmt\Use_ $node) + { + return 'use ' . $this->pUseType($node->type) . $this->pCommaSeparated($node->uses) . ';'; + } + protected function pStmt_GroupUse(Stmt\GroupUse $node) + { + return 'use ' . $this->pUseType($node->type) . $this->pName($node->prefix) . '\\{' . $this->pCommaSeparated($node->uses) . '};'; + } + protected function pStmt_UseUse(Stmt\UseUse $node) + { + return $this->pUseType($node->type) . $this->p($node->name) . (null !== $node->alias ? ' as ' . $node->alias : ''); + } + protected function pUseType($type) + { + return $type === Stmt\Use_::TYPE_FUNCTION ? 'function ' : ($type === Stmt\Use_::TYPE_CONSTANT ? 'const ' : ''); + } + protected function pStmt_Interface(Stmt\Interface_ $node) + { + return $this->pAttrGroups($node->attrGroups) . 'interface ' . $node->name . (!empty($node->extends) ? ' extends ' . $this->pCommaSeparated($node->extends) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + protected function pStmt_Enum(Stmt\Enum_ $node) + { + return $this->pAttrGroups($node->attrGroups) . 'enum ' . $node->name . ($node->scalarType ? " : {$node->scalarType}" : '') . (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + protected function pStmt_Class(Stmt\Class_ $node) + { + return $this->pClassCommon($node, ' ' . $node->name); + } + protected function pStmt_Trait(Stmt\Trait_ $node) + { + return $this->pAttrGroups($node->attrGroups) . 'trait ' . $node->name . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + protected function pStmt_EnumCase(Stmt\EnumCase $node) + { + return $this->pAttrGroups($node->attrGroups) . 'case ' . $node->name . ($node->expr ? ' = ' . $this->p($node->expr) : '') . ';'; + } + protected function pStmt_TraitUse(Stmt\TraitUse $node) + { + return 'use ' . $this->pCommaSeparated($node->traits) . (empty($node->adaptations) ? ';' : ' {' . $this->pStmts($node->adaptations) . $this->nl . '}'); + } + protected function pStmt_TraitUseAdaptation_Precedence(Stmt\TraitUseAdaptation\Precedence $node) + { + return $this->p($node->trait) . '::' . $node->method . ' insteadof ' . $this->pCommaSeparated($node->insteadof) . ';'; + } + protected function pStmt_TraitUseAdaptation_Alias(Stmt\TraitUseAdaptation\Alias $node) + { + return (null !== $node->trait ? $this->p($node->trait) . '::' : '') . $node->method . ' as' . (null !== $node->newModifier ? ' ' . \rtrim($this->pModifiers($node->newModifier), ' ') : '') . (null !== $node->newName ? ' ' . $node->newName : '') . ';'; + } + protected function pStmt_Property(Stmt\Property $node) + { + return $this->pAttrGroups($node->attrGroups) . (0 === $node->flags ? 'var ' : $this->pModifiers($node->flags)) . ($node->type ? $this->p($node->type) . ' ' : '') . $this->pCommaSeparated($node->props) . ';'; + } + protected function pStmt_PropertyProperty(Stmt\PropertyProperty $node) + { + return '$' . $node->name . (null !== $node->default ? ' = ' . $this->p($node->default) : ''); + } + protected function pStmt_ClassMethod(Stmt\ClassMethod $node) + { + return $this->pAttrGroups($node->attrGroups) . $this->pModifiers($node->flags) . 'function ' . ($node->byRef ? '&' : '') . $node->name . '(' . $this->pMaybeMultiline($node->params) . ')' . (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '') . (null !== $node->stmts ? $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}' : ';'); + } + protected function pStmt_ClassConst(Stmt\ClassConst $node) + { + return $this->pAttrGroups($node->attrGroups) . $this->pModifiers($node->flags) . 'const ' . $this->pCommaSeparated($node->consts) . ';'; + } + protected function pStmt_Function(Stmt\Function_ $node) + { + return $this->pAttrGroups($node->attrGroups) . 'function ' . ($node->byRef ? '&' : '') . $node->name . '(' . $this->pCommaSeparated($node->params) . ')' . (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + protected function pStmt_Const(Stmt\Const_ $node) + { + return 'const ' . $this->pCommaSeparated($node->consts) . ';'; + } + protected function pStmt_Declare(Stmt\Declare_ $node) + { + return 'declare (' . $this->pCommaSeparated($node->declares) . ')' . (null !== $node->stmts ? ' {' . $this->pStmts($node->stmts) . $this->nl . '}' : ';'); + } + protected function pStmt_DeclareDeclare(Stmt\DeclareDeclare $node) + { + return $node->key . '=' . $this->p($node->value); + } + // Control flow + protected function pStmt_If(Stmt\If_ $node) + { + return 'if (' . $this->p($node->cond) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}' . ($node->elseifs ? ' ' . $this->pImplode($node->elseifs, ' ') : '') . (null !== $node->else ? ' ' . $this->p($node->else) : ''); + } + protected function pStmt_ElseIf(Stmt\ElseIf_ $node) + { + return 'elseif (' . $this->p($node->cond) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + protected function pStmt_Else(Stmt\Else_ $node) + { + return 'else {' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + protected function pStmt_For(Stmt\For_ $node) + { + return 'for (' . $this->pCommaSeparated($node->init) . ';' . (!empty($node->cond) ? ' ' : '') . $this->pCommaSeparated($node->cond) . ';' . (!empty($node->loop) ? ' ' : '') . $this->pCommaSeparated($node->loop) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + protected function pStmt_Foreach(Stmt\Foreach_ $node) + { + return 'foreach (' . $this->p($node->expr) . ' as ' . (null !== $node->keyVar ? $this->p($node->keyVar) . ' => ' : '') . ($node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + protected function pStmt_While(Stmt\While_ $node) + { + return 'while (' . $this->p($node->cond) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + protected function pStmt_Do(Stmt\Do_ $node) + { + return 'do {' . $this->pStmts($node->stmts) . $this->nl . '} while (' . $this->p($node->cond) . ');'; + } + protected function pStmt_Switch(Stmt\Switch_ $node) + { + return 'switch (' . $this->p($node->cond) . ') {' . $this->pStmts($node->cases) . $this->nl . '}'; + } + protected function pStmt_Case(Stmt\Case_ $node) + { + return (null !== $node->cond ? 'case ' . $this->p($node->cond) : 'default') . ':' . $this->pStmts($node->stmts); + } + protected function pStmt_TryCatch(Stmt\TryCatch $node) + { + return 'try {' . $this->pStmts($node->stmts) . $this->nl . '}' . ($node->catches ? ' ' . $this->pImplode($node->catches, ' ') : '') . ($node->finally !== null ? ' ' . $this->p($node->finally) : ''); + } + protected function pStmt_Catch(Stmt\Catch_ $node) + { + return 'catch (' . $this->pImplode($node->types, '|') . ($node->var !== null ? ' ' . $this->p($node->var) : '') . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + protected function pStmt_Finally(Stmt\Finally_ $node) + { + return 'finally {' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + protected function pStmt_Break(Stmt\Break_ $node) + { + return 'break' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';'; + } + protected function pStmt_Continue(Stmt\Continue_ $node) + { + return 'continue' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';'; + } + protected function pStmt_Return(Stmt\Return_ $node) + { + return 'return' . (null !== $node->expr ? ' ' . $this->p($node->expr) : '') . ';'; + } + protected function pStmt_Throw(Stmt\Throw_ $node) + { + return 'throw ' . $this->p($node->expr) . ';'; + } + protected function pStmt_Label(Stmt\Label $node) + { + return $node->name . ':'; + } + protected function pStmt_Goto(Stmt\Goto_ $node) + { + return 'goto ' . $node->name . ';'; + } + // Other + protected function pStmt_Expression(Stmt\Expression $node) + { + return $this->p($node->expr) . ';'; + } + protected function pStmt_Echo(Stmt\Echo_ $node) + { + return 'echo ' . $this->pCommaSeparated($node->exprs) . ';'; + } + protected function pStmt_Static(Stmt\Static_ $node) + { + return 'static ' . $this->pCommaSeparated($node->vars) . ';'; + } + protected function pStmt_Global(Stmt\Global_ $node) + { + return 'global ' . $this->pCommaSeparated($node->vars) . ';'; + } + protected function pStmt_StaticVar(Stmt\StaticVar $node) + { + return $this->p($node->var) . (null !== $node->default ? ' = ' . $this->p($node->default) : ''); + } + protected function pStmt_Unset(Stmt\Unset_ $node) + { + return 'unset(' . $this->pCommaSeparated($node->vars) . ');'; + } + protected function pStmt_InlineHTML(Stmt\InlineHTML $node) + { + $newline = $node->getAttribute('hasLeadingNewline', \true) ? "\n" : ''; + return '?>' . $newline . $node->value . 'remaining; + } + protected function pStmt_Nop(Stmt\Nop $node) + { + return ''; + } + // Helpers + protected function pClassCommon(Stmt\Class_ $node, $afterClassToken) + { + return $this->pAttrGroups($node->attrGroups, $node->name === null) . $this->pModifiers($node->flags) . 'class' . $afterClassToken . (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '') . (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + protected function pObjectProperty($node) + { + if ($node instanceof Expr) { + return '{' . $this->p($node) . '}'; + } else { + return $node; + } + } + protected function pEncapsList(array $encapsList, $quote) + { + $return = ''; + foreach ($encapsList as $element) { + if ($element instanceof Scalar\EncapsedStringPart) { + $return .= $this->escapeString($element->value, $quote); + } else { + $return .= '{' . $this->p($element) . '}'; + } + } + return $return; + } + protected function pSingleQuotedString(string $string) + { + return '\'' . \addcslashes($string, '\'\\') . '\''; + } + protected function escapeString($string, $quote) + { + if (null === $quote) { + // For doc strings, don't escape newlines + $escaped = \addcslashes($string, "\t\f\v\$\\"); + } else { + $escaped = \addcslashes($string, "\n\r\t\f\v\$" . $quote . "\\"); + } + // Escape control characters and non-UTF-8 characters. + // Regex based on https://stackoverflow.com/a/11709412/385378. + $regex = '/( + [\\x00-\\x08\\x0E-\\x1F] # Control characters + | [\\xC0-\\xC1] # Invalid UTF-8 Bytes + | [\\xF5-\\xFF] # Invalid UTF-8 Bytes + | \\xE0(?=[\\x80-\\x9F]) # Overlong encoding of prior code point + | \\xF0(?=[\\x80-\\x8F]) # Overlong encoding of prior code point + | [\\xC2-\\xDF](?![\\x80-\\xBF]) # Invalid UTF-8 Sequence Start + | [\\xE0-\\xEF](?![\\x80-\\xBF]{2}) # Invalid UTF-8 Sequence Start + | [\\xF0-\\xF4](?![\\x80-\\xBF]{3}) # Invalid UTF-8 Sequence Start + | (?<=[\\x00-\\x7F\\xF5-\\xFF])[\\x80-\\xBF] # Invalid UTF-8 Sequence Middle + | (? $part) { + $atStart = $i === 0; + $atEnd = $i === \count($parts) - 1; + if ($part instanceof Scalar\EncapsedStringPart && $this->containsEndLabel($part->value, $label, $atStart, $atEnd)) { + return \true; + } + } + return \false; + } + protected function pDereferenceLhs(Node $node) + { + if (!$this->dereferenceLhsRequiresParens($node)) { + return $this->p($node); + } else { + return '(' . $this->p($node) . ')'; + } + } + protected function pCallLhs(Node $node) + { + if (!$this->callLhsRequiresParens($node)) { + return $this->p($node); + } else { + return '(' . $this->p($node) . ')'; + } + } + protected function pNewVariable(Node $node) + { + // TODO: This is not fully accurate. + return $this->pDereferenceLhs($node); } /** - * @throws ReflectionException + * @param Node[] $nodes + * @return bool */ - private function isUserDefinedMethod(string $className, string $methodName) : bool + protected function hasNodeWithComments(array $nodes) { - if (!\class_exists($className)) { - // @codeCoverageIgnoreStart - return \false; - // @codeCoverageIgnoreEnd + foreach ($nodes as $node) { + if ($node && $node->getComments()) { + return \true; + } } - if (!\method_exists($className, $methodName)) { - // @codeCoverageIgnoreStart - return \false; - // @codeCoverageIgnoreEnd + return \false; + } + protected function pMaybeMultiline(array $nodes, bool $trailingComma = \false) + { + if (!$this->hasNodeWithComments($nodes)) { + return $this->pCommaSeparated($nodes); + } else { + return $this->pCommaSeparatedMultiline($nodes, $trailingComma) . $this->nl; } - try { - return (new \ReflectionMethod($className, $methodName))->isUserDefined(); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\SebastianBergmann\CodeUnit\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + protected function pAttrGroups(array $nodes, bool $inline = \false) : string + { + $result = ''; + $sep = $inline ? ' ' : $this->nl; + foreach ($nodes as $node) { + $result .= $this->p($node) . $sep; } - // @codeCoverageIgnoreEnd + return $result; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnit\PhpParser; -/** - * @psalm-immutable - */ -final class ClassMethodUnit extends \PHPUnit\SebastianBergmann\CodeUnit\CodeUnit +interface ErrorHandler { /** - * @psalm-assert-if-true ClassMethodUnit $this + * Handle an error generated during lexing, parsing or some other operation. + * + * @param Error $error The error that needs to be handled */ - public function isClassMethod() : bool - { - return \true; - } + public function handleError(Error $error); } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnit\PhpParser\Node; -/** - * @psalm-immutable - */ -final class TraitUnit extends \PHPUnit\SebastianBergmann\CodeUnit\CodeUnit +use PHPUnit\PhpParser\Node; +use PHPUnit\PhpParser\NodeAbstract; +class Attribute extends NodeAbstract { + /** @var Name Attribute name */ + public $name; + /** @var Arg[] Attribute arguments */ + public $args; /** - * @psalm-assert-if-true TraitUnit $this + * @param Node\Name $name Attribute name + * @param Arg[] $args Attribute arguments + * @param array $attributes Additional node attributes */ - public function isTrait() : bool + public function __construct(Name $name, array $args = [], array $attributes = []) { - return \true; + $this->attributes = $attributes; + $this->name = $name; + $this->args = $args; + } + public function getSubNodeNames() : array + { + return ['name', 'args']; + } + public function getType() : string + { + return 'Attribute'; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnit\PhpParser\Node; /** - * @psalm-immutable + * Represents a name that is written in source code with a leading dollar, + * but is not a proper variable. The leading dollar is not stored as part of the name. + * + * Examples: Names in property declarations are formatted as variables. Names in static property + * lookups are also formatted as variables. */ -final class InterfaceUnit extends \PHPUnit\SebastianBergmann\CodeUnit\CodeUnit +class VarLikeIdentifier extends Identifier { - /** - * @psalm-assert-if-true InterfaceUnit $this - */ - public function isInterface() : bool + public function getType() : string { - return \true; + return 'VarLikeIdentifier'; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeUnit; +namespace PHPUnit\PhpParser\Node; -/** - * @psalm-immutable - */ -final class TraitMethodUnit extends \PHPUnit\SebastianBergmann\CodeUnit\CodeUnit +use PHPUnit\PhpParser\Node\VariadicPlaceholder; +use PHPUnit\PhpParser\NodeAbstract; +class Arg extends NodeAbstract { + /** @var Identifier|null Parameter name (for named parameters) */ + public $name; + /** @var Expr Value to pass */ + public $value; + /** @var bool Whether to pass by ref */ + public $byRef; + /** @var bool Whether to unpack the argument */ + public $unpack; /** - * @psalm-assert-if-true TraitMethodUnit $this + * Constructs a function call argument node. + * + * @param Expr $value Value to pass + * @param bool $byRef Whether to pass by ref + * @param bool $unpack Whether to unpack the argument + * @param array $attributes Additional attributes + * @param Identifier|null $name Parameter name (for named parameters) */ - public function isTraitMethod() : bool + public function __construct(Expr $value, bool $byRef = \false, bool $unpack = \false, array $attributes = [], Identifier $name = null) { - return \true; + $this->attributes = $attributes; + $this->name = $name; + $this->value = $value; + $this->byRef = $byRef; + $this->unpack = $unpack; + } + public function getSubNodeNames() : array + { + return ['name', 'value', 'byRef', 'unpack']; + } + public function getType() : string + { + return 'Arg'; } } -sebastian/code-unit - -Copyright (c) 2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. +attributes = $attributes; + $this->types = $types; + } + public function getSubNodeNames() : array + { + return ['types']; + } + public function getType() : string + { + return 'UnionType'; + } +} +namespace PHPUnit\PhpParser\Node; + +use PHPUnit\PhpParser\NodeAbstract; +/** + * This is a base class for complex types, including nullable types and union types. * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * It does not provide any shared behavior and exists only for type-checking purposes. */ -namespace PHPUnit\SebastianBergmann\Environment; +abstract class ComplexType extends NodeAbstract +{ +} +isWindows()) { - // @codeCoverageIgnoreStart - return \defined('STDOUT') && \function_exists('sapi_windows_vt100_support') && @\sapi_windows_vt100_support(\STDOUT) || \false !== \getenv('ANSICON') || 'ON' === \getenv('ConEmuANSI') || 'xterm' === \getenv('TERM'); - // @codeCoverageIgnoreEnd - } - if (!\defined('STDOUT')) { - // @codeCoverageIgnoreStart - return \false; - // @codeCoverageIgnoreEnd - } - return $this->isInteractive(\STDOUT); - } + public function getStmts(); /** - * Returns the number of columns of the terminal. + * Get PHP attribute groups. * - * @codeCoverageIgnore + * @return AttributeGroup[] */ - public function getNumberOfColumns() : int - { - if (!$this->isInteractive(\defined('STDIN') ? \STDIN : self::STDIN)) { - return 80; - } - if ($this->isWindows()) { - return $this->getNumberOfColumnsWindows(); - } - return $this->getNumberOfColumnsInteractive(); - } + public function getAttrGroups() : array; +} +attributes = $attributes; + $this->remaining = $remaining; } - private function isWindows() : bool + public function getSubNodeNames() : array { - return \DIRECTORY_SEPARATOR === '\\'; + return ['remaining']; } - /** - * @codeCoverageIgnore - */ - private function getNumberOfColumnsInteractive() : int + public function getType() : string { - if (\function_exists('shell_exec') && \preg_match('#\\d+ (\\d+)#', \shell_exec('stty size') ?: '', $match) === 1) { - if ((int) $match[1] > 0) { - return (int) $match[1]; - } - } - if (\function_exists('shell_exec') && \preg_match('#columns = (\\d+);#', \shell_exec('stty') ?: '', $match) === 1) { - if ((int) $match[1] > 0) { - return (int) $match[1]; - } - } - return 80; + return 'Stmt_HaltCompiler'; } +} + ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes, null, null, ['suppress_errors' => \true]); - if (\is_resource($process)) { - $info = \stream_get_contents($pipes[1]); - \fclose($pipes[1]); - \fclose($pipes[2]); - \proc_close($process); - if (\preg_match('/--------+\\r?\\n.+?(\\d+)\\r?\\n.+?(\\d+)\\r?\\n/', $info, $matches)) { - $columns = (int) $matches[2]; - } - } - } - return $columns - 1; + $this->attributes = $attributes; + $this->num = $num; + } + public function getSubNodeNames() : array + { + return ['num']; + } + public function getType() : string + { + return 'Stmt_Break'; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Environment; +namespace PHPUnit\PhpParser\Node\Stmt; -use const DIRECTORY_SEPARATOR; -use const PHP_OS; -use const PHP_OS_FAMILY; -use function defined; -final class OperatingSystem +use PHPUnit\PhpParser\Node; +class If_ extends Node\Stmt { + /** @var Node\Expr Condition expression */ + public $cond; + /** @var Node\Stmt[] Statements */ + public $stmts; + /** @var ElseIf_[] Elseif clauses */ + public $elseifs; + /** @var null|Else_ Else clause */ + public $else; /** - * Returns PHP_OS_FAMILY (if defined (which it is on PHP >= 7.2)). - * Returns a string (compatible with PHP_OS_FAMILY) derived from PHP_OS otherwise. + * Constructs an if node. + * + * @param Node\Expr $cond Condition + * @param array $subNodes Array of the following optional subnodes: + * 'stmts' => array(): Statements + * 'elseifs' => array(): Elseif clauses + * 'else' => null : Else clause + * @param array $attributes Additional attributes */ - public function getFamily() : string + public function __construct(Node\Expr $cond, array $subNodes = [], array $attributes = []) { - if (\defined('PHP_OS_FAMILY')) { - return \PHP_OS_FAMILY; - } - if (\DIRECTORY_SEPARATOR === '\\') { - return 'Windows'; - } - switch (\PHP_OS) { - case 'Darwin': - return 'Darwin'; - case 'DragonFly': - case 'FreeBSD': - case 'NetBSD': - case 'OpenBSD': - return 'BSD'; - case 'Linux': - return 'Linux'; - case 'SunOS': - return 'Solaris'; - default: - return 'Unknown'; - } + $this->attributes = $attributes; + $this->cond = $cond; + $this->stmts = $subNodes['stmts'] ?? []; + $this->elseifs = $subNodes['elseifs'] ?? []; + $this->else = $subNodes['else'] ?? null; + } + public function getSubNodeNames() : array + { + return ['cond', 'stmts', 'elseifs', 'else']; + } + public function getType() : string + { + return 'Stmt_If'; } } -sebastian/environment - -Copyright (c) 2014-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Environment; +namespace PHPUnit\PhpParser\Node\Stmt; -use const PHP_BINARY; -use const PHP_BINDIR; -use const PHP_MAJOR_VERSION; -use const PHP_SAPI; -use const PHP_VERSION; -use function array_map; -use function array_merge; -use function defined; -use function escapeshellarg; -use function explode; -use function extension_loaded; -use function getenv; -use function ini_get; -use function is_readable; -use function parse_ini_file; -use function php_ini_loaded_file; -use function php_ini_scanned_files; -use function phpversion; -use function sprintf; -use function strpos; -/** - * Utility class for HHVM/PHP environment handling. - */ -final class Runtime +use PHPUnit\PhpParser\Node; +class While_ extends Node\Stmt { + /** @var Node\Expr Condition */ + public $cond; + /** @var Node\Stmt[] Statements */ + public $stmts; /** - * @var string - */ - private static $binary; - /** - * Returns true when Xdebug or PCOV is available or - * the runtime used is PHPDBG. + * Constructs a while node. + * + * @param Node\Expr $cond Condition + * @param Node\Stmt[] $stmts Statements + * @param array $attributes Additional attributes */ - public function canCollectCodeCoverage() : bool + public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) { - return $this->hasXdebug() || $this->hasPCOV() || $this->hasPHPDBGCodeCoverage(); + $this->attributes = $attributes; + $this->cond = $cond; + $this->stmts = $stmts; } - /** - * Returns true when Zend OPcache is loaded, enabled, - * and is configured to discard comments. - */ - public function discardsComments() : bool + public function getSubNodeNames() : array { - if (!$this->isOpcacheActive()) { - return \false; - } - if (\ini_get('opcache.save_comments') !== '0') { - return \false; - } - return \true; + return ['cond', 'stmts']; } - /** - * Returns true when Zend OPcache is loaded, enabled, - * and is configured to perform just-in-time compilation. - */ - public function performsJustInTimeCompilation() : bool + public function getType() : string { - if (\PHP_MAJOR_VERSION < 8) { - return \false; - } - if (!$this->isOpcacheActive()) { - return \false; - } - if (\strpos(\ini_get('opcache.jit'), '0') === 0) { - return \false; - } - return \true; + return 'Stmt_While'; } +} + false : Whether to return by reference + * 'params' => array(): Parameters + * 'returnType' => null : Return type + * 'stmts' => array(): Statements + * 'attrGroups' => array(): PHP attribute groups + * @param array $attributes Additional attributes */ - public function getBinary() : string + public function __construct($name, array $subNodes = [], array $attributes = []) { - // HHVM - if (self::$binary === null && $this->isHHVM()) { - // @codeCoverageIgnoreStart - if ((self::$binary = \getenv('PHP_BINARY')) === \false) { - self::$binary = \PHP_BINARY; - } - self::$binary = \escapeshellarg(self::$binary) . ' --php' . ' -d hhvm.php7.all=1'; - // @codeCoverageIgnoreEnd - } - if (self::$binary === null && \PHP_BINARY !== '') { - self::$binary = \escapeshellarg(\PHP_BINARY); - } - if (self::$binary === null) { - // @codeCoverageIgnoreStart - $possibleBinaryLocations = [\PHP_BINDIR . '/php', \PHP_BINDIR . '/php-cli.exe', \PHP_BINDIR . '/php.exe']; - foreach ($possibleBinaryLocations as $binary) { - if (\is_readable($binary)) { - self::$binary = \escapeshellarg($binary); - break; - } - } - // @codeCoverageIgnoreEnd - } - if (self::$binary === null) { - // @codeCoverageIgnoreStart - self::$binary = 'php'; - // @codeCoverageIgnoreEnd - } - return self::$binary; + $this->attributes = $attributes; + $this->byRef = $subNodes['byRef'] ?? \false; + $this->name = \is_string($name) ? new Node\Identifier($name) : $name; + $this->params = $subNodes['params'] ?? []; + $returnType = $subNodes['returnType'] ?? null; + $this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType; + $this->stmts = $subNodes['stmts'] ?? []; + $this->attrGroups = $subNodes['attrGroups'] ?? []; } - public function getNameWithVersion() : string + public function getSubNodeNames() : array { - return $this->getName() . ' ' . $this->getVersion(); + return ['attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts']; } - public function getNameWithVersionAndCodeCoverageDriver() : string + public function returnsByRef() : bool { - if (!$this->canCollectCodeCoverage() || $this->hasPHPDBGCodeCoverage()) { - return $this->getNameWithVersion(); - } - if ($this->hasPCOV()) { - return \sprintf('%s with PCOV %s', $this->getNameWithVersion(), \phpversion('pcov')); - } - if ($this->hasXdebug()) { - return \sprintf('%s with Xdebug %s', $this->getNameWithVersion(), \phpversion('xdebug')); - } + return $this->byRef; } - public function getName() : string + public function getParams() : array { - if ($this->isHHVM()) { - // @codeCoverageIgnoreStart - return 'HHVM'; - // @codeCoverageIgnoreEnd - } - if ($this->isPHPDBG()) { - // @codeCoverageIgnoreStart - return 'PHPDBG'; - // @codeCoverageIgnoreEnd - } - return 'PHP'; + return $this->params; } - public function getVendorUrl() : string + public function getReturnType() { - if ($this->isHHVM()) { - // @codeCoverageIgnoreStart - return 'http://hhvm.com/'; - // @codeCoverageIgnoreEnd - } - return 'https://secure.php.net/'; + return $this->returnType; } - public function getVersion() : string + public function getAttrGroups() : array { - if ($this->isHHVM()) { - // @codeCoverageIgnoreStart - return HHVM_VERSION; - // @codeCoverageIgnoreEnd - } - return \PHP_VERSION; + return $this->attrGroups; } - /** - * Returns true when the runtime used is PHP and Xdebug is loaded. - */ - public function hasXdebug() : bool + /** @return Node\Stmt[] */ + public function getStmts() : array { - return ($this->isPHP() || $this->isHHVM()) && \extension_loaded('xdebug'); + return $this->stmts; } + public function getType() : string + { + return 'Stmt_Function'; + } +} +attributes = $attributes; + $this->cond = $cond; + $this->stmts = $stmts; + } + public function getSubNodeNames() : array + { + return ['cond', 'stmts']; + } + public function getType() : string { - return \defined('HHVM_VERSION'); + return 'Stmt_ElseIf'; } +} + array(): Name of extended interfaces + * 'stmts' => array(): Statements + * 'attrGroups' => array(): PHP attribute groups + * @param array $attributes Additional attributes */ - public function isPHP() : bool + public function __construct($name, array $subNodes = [], array $attributes = []) { - return !$this->isHHVM() && !$this->isPHPDBG(); + $this->attributes = $attributes; + $this->name = \is_string($name) ? new Node\Identifier($name) : $name; + $this->extends = $subNodes['extends'] ?? []; + $this->stmts = $subNodes['stmts'] ?? []; + $this->attrGroups = $subNodes['attrGroups'] ?? []; + } + public function getSubNodeNames() : array + { + return ['attrGroups', 'name', 'extends', 'stmts']; } + public function getType() : string + { + return 'Stmt_Interface'; + } +} + 0 : Flags + * 'extends' => null : Name of extended class + * 'implements' => array(): Names of implemented interfaces + * 'stmts' => array(): Statements + * 'attrGroups' => array(): PHP attribute groups + * @param array $attributes Additional attributes */ - public function isPHPDBG() : bool + public function __construct($name, array $subNodes = [], array $attributes = []) { - return \PHP_SAPI === 'phpdbg' && !$this->isHHVM(); + $this->attributes = $attributes; + $this->flags = $subNodes['flags'] ?? $subNodes['type'] ?? 0; + $this->name = \is_string($name) ? new Node\Identifier($name) : $name; + $this->extends = $subNodes['extends'] ?? null; + $this->implements = $subNodes['implements'] ?? []; + $this->stmts = $subNodes['stmts'] ?? []; + $this->attrGroups = $subNodes['attrGroups'] ?? []; + } + public function getSubNodeNames() : array + { + return ['attrGroups', 'flags', 'name', 'extends', 'implements', 'stmts']; } /** - * Returns true when the runtime used is PHP with the PHPDBG SAPI - * and the phpdbg_*_oplog() functions are available (PHP >= 7.0). + * Whether the class is explicitly abstract. + * + * @return bool */ - public function hasPHPDBGCodeCoverage() : bool + public function isAbstract() : bool { - return $this->isPHPDBG(); + return (bool) ($this->flags & self::MODIFIER_ABSTRACT); } /** - * Returns true when the runtime used is PHP with PCOV loaded and enabled. + * Whether the class is final. + * + * @return bool */ - public function hasPCOV() : bool + public function isFinal() : bool { - return $this->isPHP() && \extension_loaded('pcov') && \ini_get('pcov.enabled'); + return (bool) ($this->flags & self::MODIFIER_FINAL); } /** - * Parses the loaded php.ini file (if any) as well as all - * additional php.ini files from the additional ini dir for - * a list of all configuration settings loaded from files - * at startup. Then checks for each php.ini setting passed - * via the `$values` parameter whether this setting has - * been changed at runtime. Returns an array of strings - * where each string has the format `key=value` denoting - * the name of a changed php.ini setting with its new value. + * Whether the class is anonymous. * - * @return string[] + * @return bool */ - public function getCurrentSettings(array $values) : array + public function isAnonymous() : bool { - $diff = []; - $files = []; - if ($file = \php_ini_loaded_file()) { - $files[] = $file; + return null === $this->name; + } + /** + * @internal + */ + public static function verifyModifier($a, $b) + { + if ($a & self::VISIBILITY_MODIFIER_MASK && $b & self::VISIBILITY_MODIFIER_MASK) { + throw new Error('Multiple access type modifiers are not allowed'); } - if ($scanned = \php_ini_scanned_files()) { - $files = \array_merge($files, \array_map('trim', \explode(",\n", $scanned))); + if ($a & self::MODIFIER_ABSTRACT && $b & self::MODIFIER_ABSTRACT) { + throw new Error('Multiple abstract modifiers are not allowed'); } - foreach ($files as $ini) { - $config = \parse_ini_file($ini, \true); - foreach ($values as $value) { - $set = \ini_get($value); - if (isset($config[$value]) && $set != $config[$value]) { - $diff[] = \sprintf('%s=%s', $value, $set); - } - } + if ($a & self::MODIFIER_STATIC && $b & self::MODIFIER_STATIC) { + throw new Error('Multiple static modifiers are not allowed'); } - return $diff; - } - private function isOpcacheActive() : bool - { - if (!\extension_loaded('Zend OPcache')) { - return \false; + if ($a & self::MODIFIER_FINAL && $b & self::MODIFIER_FINAL) { + throw new Error('Multiple final modifiers are not allowed'); } - if ((\PHP_SAPI === 'cli' || \PHP_SAPI === 'phpdbg') && \ini_get('opcache.enable_cli') === '1') { - return \true; + if ($a & self::MODIFIER_READONLY && $b & self::MODIFIER_READONLY) { + throw new Error('Multiple readonly modifiers are not allowed'); } - if (\PHP_SAPI !== 'cli' && \PHP_SAPI !== 'phpdbg' && \ini_get('opcache.enable') === '1') { - return \true; + if ($a & 48 && $b & 48) { + throw new Error('Cannot use the final modifier on an abstract class member'); } - return \false; + } + public function getType() : string + { + return 'Stmt_Class'; } } -phpunit/phpunit: 9.4.4 -doctrine/instantiator: 1.4.0 -myclabs/deep-copy: 1.10.2 -nikic/php-parser: v4.10.2 -phar-io/manifest: 2.0.1 -phar-io/version: 3.0.3 -phpdocumentor/reflection-common: 2.2.0 -phpdocumentor/reflection-docblock: 5.2.2 -phpdocumentor/type-resolver: 1.4.0 -phpspec/prophecy: 1.12.1 -phpunit/php-code-coverage: 9.2.5 -phpunit/php-file-iterator: 3.0.5 -phpunit/php-invoker: 3.1.1 -phpunit/php-text-template: 2.0.4 -phpunit/php-timer: 5.0.3 -sebastian/cli-parser: 1.0.1 -sebastian/code-unit: 1.0.8 -sebastian/code-unit-reverse-lookup: 2.0.3 -sebastian/comparator: 4.0.6 -sebastian/complexity: 2.0.2 -sebastian/diff: 4.0.4 -sebastian/environment: 5.1.3 -sebastian/exporter: 4.0.3 -sebastian/global-state: 5.0.2 -sebastian/lines-of-code: 1.0.3 -sebastian/object-enumerator: 4.0.4 -sebastian/object-reflector: 2.0.4 -sebastian/recursion-context: 4.0.4 -sebastian/resource-operations: 3.0.3 -sebastian/type: 2.3.1 -sebastian/version: 3.0.2 -symfony/polyfill-ctype: v1.20.0 -theseer/tokenizer: 1.2.0 -webmozart/assert: 1.9.1 - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CliParser; +namespace PHPUnit\PhpParser\Node\Stmt; -use Throwable; -interface Exception extends \Throwable +use PHPUnit\PhpParser\Node\Name; +use PHPUnit\PhpParser\Node\Stmt; +class GroupUse extends Stmt { + /** @var int Type of group use */ + public $type; + /** @var Name Prefix for uses */ + public $prefix; + /** @var UseUse[] Uses */ + public $uses; + /** + * Constructs a group use node. + * + * @param Name $prefix Prefix for uses + * @param UseUse[] $uses Uses + * @param int $type Type of group use + * @param array $attributes Additional attributes + */ + public function __construct(Name $prefix, array $uses, int $type = Use_::TYPE_NORMAL, array $attributes = []) + { + $this->attributes = $attributes; + $this->type = $type; + $this->prefix = $prefix; + $this->uses = $uses; + } + public function getSubNodeNames() : array + { + return ['type', 'prefix', 'uses']; + } + public function getType() : string + { + return 'Stmt_GroupUse'; + } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CliParser; +namespace PHPUnit\PhpParser\Node\Stmt; -use function sprintf; -use RuntimeException; -final class OptionDoesNotAllowArgumentException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CliParser\Exception +use PHPUnit\PhpParser\Node; +class Throw_ extends Node\Stmt { - public function __construct(string $option) + /** @var Node\Expr Expression */ + public $expr; + /** + * Constructs a legacy throw statement node. + * + * @param Node\Expr $expr Expression + * @param array $attributes Additional attributes + */ + public function __construct(Node\Expr $expr, array $attributes = []) + { + $this->attributes = $attributes; + $this->expr = $expr; + } + public function getSubNodeNames() : array + { + return ['expr']; + } + public function getType() : string { - parent::__construct(\sprintf('Option "%s" does not allow an argument', $option)); + return 'Stmt_Throw'; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CliParser; +namespace PHPUnit\PhpParser\Node\Stmt; -use function sprintf; -use RuntimeException; -final class UnknownOptionException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CliParser\Exception +use PHPUnit\PhpParser\Node; +class Continue_ extends Node\Stmt { - public function __construct(string $option) + /** @var null|Node\Expr Number of loops to continue */ + public $num; + /** + * Constructs a continue node. + * + * @param null|Node\Expr $num Number of loops to continue + * @param array $attributes Additional attributes + */ + public function __construct(Node\Expr $num = null, array $attributes = []) + { + $this->attributes = $attributes; + $this->num = $num; + } + public function getSubNodeNames() : array + { + return ['num']; + } + public function getType() : string { - parent::__construct(\sprintf('Unknown option "%s"', $option)); + return 'Stmt_Continue'; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CliParser; +namespace PHPUnit\PhpParser\Node\Stmt; -use function sprintf; -use RuntimeException; -final class RequiredOptionArgumentMissingException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CliParser\Exception +use PHPUnit\PhpParser\Node; +class Else_ extends Node\Stmt { - public function __construct(string $option) + /** @var Node\Stmt[] Statements */ + public $stmts; + /** + * Constructs an else node. + * + * @param Node\Stmt[] $stmts Statements + * @param array $attributes Additional attributes + */ + public function __construct(array $stmts = [], array $attributes = []) + { + $this->attributes = $attributes; + $this->stmts = $stmts; + } + public function getSubNodeNames() : array + { + return ['stmts']; + } + public function getType() : string { - parent::__construct(\sprintf('Required argument for option "%s" is missing', $option)); + return 'Stmt_Else'; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CliParser; +namespace PHPUnit\PhpParser\Node\Stmt; -use function sprintf; -use RuntimeException; -final class AmbiguousOptionException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CliParser\Exception +use PHPUnit\PhpParser\Node; +class Trait_ extends ClassLike { - public function __construct(string $option) + /** + * Constructs a trait node. + * + * @param string|Node\Identifier $name Name + * @param array $subNodes Array of the following optional subnodes: + * 'stmts' => array(): Statements + * 'attrGroups' => array(): PHP attribute groups + * @param array $attributes Additional attributes + */ + public function __construct($name, array $subNodes = [], array $attributes = []) + { + $this->attributes = $attributes; + $this->name = \is_string($name) ? new Node\Identifier($name) : $name; + $this->stmts = $subNodes['stmts'] ?? []; + $this->attrGroups = $subNodes['attrGroups'] ?? []; + } + public function getSubNodeNames() : array + { + return ['attrGroups', 'name', 'stmts']; + } + public function getType() : string { - parent::__construct(\sprintf('Option "%s" is ambiguous', $option)); + return 'Stmt_Trait'; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CliParser; +namespace PHPUnit\PhpParser\Node\Stmt; -use function array_map; -use function array_merge; -use function array_shift; -use function array_slice; -use function assert; -use function count; -use function current; -use function explode; -use function is_array; -use function is_int; -use function is_string; -use function key; -use function next; -use function preg_replace; -use function reset; -use function sort; -use function strlen; -use function strpos; -use function strstr; -use function substr; -final class Parser +use PHPUnit\PhpParser\Node; +class Unset_ extends Node\Stmt { + /** @var Node\Expr[] Variables to unset */ + public $vars; /** - * @psalm-param list $argv - * @psalm-param list $longOptions + * Constructs an unset node. * - * @throws AmbiguousOptionException - * @throws RequiredOptionArgumentMissingException - * @throws OptionDoesNotAllowArgumentException - * @throws UnknownOptionException + * @param Node\Expr[] $vars Variables to unset + * @param array $attributes Additional attributes */ - public function parse(array $argv, string $shortOptions, array $longOptions = null) : array + public function __construct(array $vars, array $attributes = []) { - if (empty($argv)) { - return [[], []]; - } - $options = []; - $nonOptions = []; - if ($longOptions) { - \sort($longOptions); - } - if (isset($argv[0][0]) && $argv[0][0] !== '-') { - \array_shift($argv); - } - \reset($argv); - $argv = \array_map('trim', $argv); - while (\false !== ($arg = \current($argv))) { - $i = \key($argv); - \assert(\is_int($i)); - \next($argv); - if ($arg === '') { - continue; - } - if ($arg === '--') { - $nonOptions = \array_merge($nonOptions, \array_slice($argv, $i + 1)); - break; - } - if ($arg[0] !== '-' || \strlen($arg) > 1 && $arg[1] === '-' && !$longOptions) { - $nonOptions[] = $arg; - continue; - } - if (\strlen($arg) > 1 && $arg[1] === '-' && \is_array($longOptions)) { - $this->parseLongOption(\substr($arg, 2), $longOptions, $options, $argv); - } else { - $this->parseShortOption(\substr($arg, 1), $shortOptions, $options, $argv); - } - } - return [$options, $nonOptions]; + $this->attributes = $attributes; + $this->vars = $vars; } - /** - * @throws RequiredOptionArgumentMissingException - */ - private function parseShortOption(string $arg, string $shortOptions, array &$opts, array &$args) : void + public function getSubNodeNames() : array { - $argLength = \strlen($arg); - for ($i = 0; $i < $argLength; $i++) { - $option = $arg[$i]; - $optionArgument = null; - if ($arg[$i] === ':' || ($spec = \strstr($shortOptions, $option)) === \false) { - throw new \PHPUnit\SebastianBergmann\CliParser\UnknownOptionException('-' . $option); - } - \assert(\is_string($spec)); - if (\strlen($spec) > 1 && $spec[1] === ':') { - if ($i + 1 < $argLength) { - $opts[] = [$option, \substr($arg, $i + 1)]; - break; - } - if (!(\strlen($spec) > 2 && $spec[2] === ':')) { - $optionArgument = \current($args); - if (!$optionArgument) { - throw new \PHPUnit\SebastianBergmann\CliParser\RequiredOptionArgumentMissingException('-' . $option); - } - \assert(\is_string($optionArgument)); - \next($args); - } - } - $opts[] = [$option, $optionArgument]; - } + return ['vars']; + } + public function getType() : string + { + return 'Stmt_Unset'; } +} + $longOptions + * Constructs an echo node. * - * @throws AmbiguousOptionException - * @throws RequiredOptionArgumentMissingException - * @throws OptionDoesNotAllowArgumentException - * @throws UnknownOptionException + * @param Node\Expr[] $exprs Expressions + * @param array $attributes Additional attributes */ - private function parseLongOption(string $arg, array $longOptions, array &$opts, array &$args) : void + public function __construct(array $exprs, array $attributes = []) { - $count = \count($longOptions); - $list = \explode('=', $arg); - $option = $list[0]; - $optionArgument = null; - if (\count($list) > 1) { - $optionArgument = $list[1]; - } - $optionLength = \strlen($option); - foreach ($longOptions as $i => $longOption) { - $opt_start = \substr($longOption, 0, $optionLength); - if ($opt_start !== $option) { - continue; - } - $opt_rest = \substr($longOption, $optionLength); - if ($opt_rest !== '' && $i + 1 < $count && $option[0] !== '=' && \strpos($longOptions[$i + 1], $option) === 0) { - throw new \PHPUnit\SebastianBergmann\CliParser\AmbiguousOptionException('--' . $option); - } - if (\substr($longOption, -1) === '=') { - /* @noinspection StrlenInEmptyStringCheckContextInspection */ - if (\substr($longOption, -2) !== '==' && !\strlen((string) $optionArgument)) { - if (\false === ($optionArgument = \current($args))) { - throw new \PHPUnit\SebastianBergmann\CliParser\RequiredOptionArgumentMissingException('--' . $option); - } - \next($args); - } - } elseif ($optionArgument) { - throw new \PHPUnit\SebastianBergmann\CliParser\OptionDoesNotAllowArgumentException('--' . $option); - } - $fullOption = '--' . \preg_replace('/={1,2}$/', '', $longOption); - $opts[] = [$fullOption, $optionArgument]; - return; - } - throw new \PHPUnit\SebastianBergmann\CliParser\UnknownOptionException('--' . $option); + $this->attributes = $attributes; + $this->exprs = $exprs; + } + public function getSubNodeNames() : array + { + return ['exprs']; + } + public function getType() : string + { + return 'Stmt_Echo'; } } -sebastian/cli-parser - -Copyright (c) 2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -Object Reflector - -Copyright (c) 2017-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - List of recognized keywords and unto which Value Object they map - * @psalm-var array> - */ - private $keywords = ['string' => \PHPUnit\phpDocumentor\Reflection\Types\String_::class, 'class-string' => \PHPUnit\phpDocumentor\Reflection\Types\ClassString::class, 'int' => \PHPUnit\phpDocumentor\Reflection\Types\Integer::class, 'integer' => \PHPUnit\phpDocumentor\Reflection\Types\Integer::class, 'bool' => \PHPUnit\phpDocumentor\Reflection\Types\Boolean::class, 'boolean' => \PHPUnit\phpDocumentor\Reflection\Types\Boolean::class, 'real' => \PHPUnit\phpDocumentor\Reflection\Types\Float_::class, 'float' => \PHPUnit\phpDocumentor\Reflection\Types\Float_::class, 'double' => \PHPUnit\phpDocumentor\Reflection\Types\Float_::class, 'object' => \PHPUnit\phpDocumentor\Reflection\Types\Object_::class, 'mixed' => \PHPUnit\phpDocumentor\Reflection\Types\Mixed_::class, 'array' => \PHPUnit\phpDocumentor\Reflection\Types\Array_::class, 'resource' => \PHPUnit\phpDocumentor\Reflection\Types\Resource_::class, 'void' => \PHPUnit\phpDocumentor\Reflection\Types\Void_::class, 'null' => \PHPUnit\phpDocumentor\Reflection\Types\Null_::class, 'scalar' => \PHPUnit\phpDocumentor\Reflection\Types\Scalar::class, 'callback' => \PHPUnit\phpDocumentor\Reflection\Types\Callable_::class, 'callable' => \PHPUnit\phpDocumentor\Reflection\Types\Callable_::class, 'false' => \PHPUnit\phpDocumentor\Reflection\PseudoTypes\False_::class, 'true' => \PHPUnit\phpDocumentor\Reflection\PseudoTypes\True_::class, 'self' => \PHPUnit\phpDocumentor\Reflection\Types\Self_::class, '$this' => \PHPUnit\phpDocumentor\Reflection\Types\This::class, 'static' => \PHPUnit\phpDocumentor\Reflection\Types\Static_::class, 'parent' => \PHPUnit\phpDocumentor\Reflection\Types\Parent_::class, 'iterable' => \PHPUnit\phpDocumentor\Reflection\Types\Iterable_::class]; - /** - * @var FqsenResolver - * @psalm-readonly - */ - private $fqsenResolver; - /** - * Initializes this TypeResolver with the means to create and resolve Fqsen objects. - */ - public function __construct(?\PHPUnit\phpDocumentor\Reflection\FqsenResolver $fqsenResolver = null) - { - $this->fqsenResolver = $fqsenResolver ?: new \PHPUnit\phpDocumentor\Reflection\FqsenResolver(); - } + /** @var Identifier Name */ + public $name; /** - * Analyzes the given type and returns the FQCN variant. - * - * When a type is provided this method checks whether it is not a keyword or - * Fully Qualified Class Name. If so it will use the given namespace and - * aliases to expand the type to a FQCN representation. - * - * This method only works as expected if the namespace and aliases are set; - * no dynamic reflection is being performed here. - * - * @uses Context::getNamespaceAliases() to check whether the first part of the relative type name should not be - * replaced with another namespace. - * @uses Context::getNamespace() to determine with what to prefix the type name. + * Constructs a label node. * - * @param string $type The relative or absolute type. + * @param string|Identifier $name Name + * @param array $attributes Additional attributes */ - public function resolve(string $type, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : \PHPUnit\phpDocumentor\Reflection\Type + public function __construct($name, array $attributes = []) { - $type = \trim($type); - if (!$type) { - throw new \InvalidArgumentException('Attempted to resolve "' . $type . '" but it appears to be empty'); - } - if ($context === null) { - $context = new \PHPUnit\phpDocumentor\Reflection\Types\Context(''); - } - // split the type string into tokens `|`, `?`, `<`, `>`, `,`, `(`, `)`, `[]`, '<', '>' and type names - $tokens = \preg_split('/(\\||\\?|<|>|&|, ?|\\(|\\)|\\[\\]+)/', $type, -1, \PREG_SPLIT_NO_EMPTY | \PREG_SPLIT_DELIM_CAPTURE); - if ($tokens === \false) { - throw new \InvalidArgumentException('Unable to split the type string "' . $type . '" into tokens'); - } - /** @var ArrayIterator $tokenIterator */ - $tokenIterator = new \ArrayIterator($tokens); - return $this->parseTypes($tokenIterator, $context, self::PARSER_IN_COMPOUND); + $this->attributes = $attributes; + $this->name = \is_string($name) ? new Identifier($name) : $name; } - /** - * Analyse each tokens and creates types - * - * @param ArrayIterator $tokens the iterator on tokens - * @param int $parserContext on of self::PARSER_* constants, indicating - * the context where we are in the parsing - */ - private function parseTypes(\ArrayIterator $tokens, \PHPUnit\phpDocumentor\Reflection\Types\Context $context, int $parserContext) : \PHPUnit\phpDocumentor\Reflection\Type + public function getSubNodeNames() : array { - $types = []; - $token = ''; - $compoundToken = '|'; - while ($tokens->valid()) { - $token = $tokens->current(); - if ($token === null) { - throw new \RuntimeException('Unexpected nullable character'); - } - if ($token === '|' || $token === '&') { - if (\count($types) === 0) { - throw new \RuntimeException('A type is missing before a type separator'); - } - if (!\in_array($parserContext, [self::PARSER_IN_COMPOUND, self::PARSER_IN_ARRAY_EXPRESSION, self::PARSER_IN_COLLECTION_EXPRESSION], \true)) { - throw new \RuntimeException('Unexpected type separator'); - } - $compoundToken = $token; - $tokens->next(); - } elseif ($token === '?') { - if (!\in_array($parserContext, [self::PARSER_IN_COMPOUND, self::PARSER_IN_ARRAY_EXPRESSION, self::PARSER_IN_COLLECTION_EXPRESSION], \true)) { - throw new \RuntimeException('Unexpected nullable character'); - } - $tokens->next(); - $type = $this->parseTypes($tokens, $context, self::PARSER_IN_NULLABLE); - $types[] = new \PHPUnit\phpDocumentor\Reflection\Types\Nullable($type); - } elseif ($token === '(') { - $tokens->next(); - $type = $this->parseTypes($tokens, $context, self::PARSER_IN_ARRAY_EXPRESSION); - $token = $tokens->current(); - if ($token === null) { - // Someone did not properly close their array expression .. - break; - } - $tokens->next(); - $resolvedType = new \PHPUnit\phpDocumentor\Reflection\Types\Expression($type); - $types[] = $resolvedType; - } elseif ($parserContext === self::PARSER_IN_ARRAY_EXPRESSION && $token[0] === ')') { - break; - } elseif ($token === '<') { - if (\count($types) === 0) { - throw new \RuntimeException('Unexpected collection operator "<", class name is missing'); - } - $classType = \array_pop($types); - if ($classType !== null) { - if ((string) $classType === 'class-string') { - $types[] = $this->resolveClassString($tokens, $context); - } else { - $types[] = $this->resolveCollection($tokens, $classType, $context); - } - } - $tokens->next(); - } elseif ($parserContext === self::PARSER_IN_COLLECTION_EXPRESSION && ($token === '>' || \trim($token) === ',')) { - break; - } elseif ($token === self::OPERATOR_ARRAY) { - \end($types); - $last = \key($types); - $lastItem = $types[$last]; - if ($lastItem instanceof \PHPUnit\phpDocumentor\Reflection\Types\Expression) { - $lastItem = $lastItem->getValueType(); - } - $types[$last] = new \PHPUnit\phpDocumentor\Reflection\Types\Array_($lastItem); - $tokens->next(); - } else { - $type = $this->resolveSingleType($token, $context); - $tokens->next(); - if ($parserContext === self::PARSER_IN_NULLABLE) { - return $type; - } - $types[] = $type; - } - } - if ($token === '|' || $token === '&') { - throw new \RuntimeException('A type is missing after a type separator'); - } - if (\count($types) === 0) { - if ($parserContext === self::PARSER_IN_NULLABLE) { - throw new \RuntimeException('A type is missing after a nullable character'); - } - if ($parserContext === self::PARSER_IN_ARRAY_EXPRESSION) { - throw new \RuntimeException('A type is missing in an array expression'); - } - if ($parserContext === self::PARSER_IN_COLLECTION_EXPRESSION) { - throw new \RuntimeException('A type is missing in a collection expression'); - } - } elseif (\count($types) === 1) { - return $types[0]; - } - if ($compoundToken === '|') { - return new \PHPUnit\phpDocumentor\Reflection\Types\Compound(\array_values($types)); - } - return new \PHPUnit\phpDocumentor\Reflection\Types\Intersection(\array_values($types)); + return ['name']; } - /** - * resolve the given type into a type object - * - * @param string $type the type string, representing a single type - * - * @return Type|Array_|Object_ - * - * @psalm-mutation-free - */ - private function resolveSingleType(string $type, \PHPUnit\phpDocumentor\Reflection\Types\Context $context) : object + public function getType() : string { - switch (\true) { - case $this->isKeyword($type): - return $this->resolveKeyword($type); - case $this->isFqsen($type): - return $this->resolveTypedObject($type); - case $this->isPartialStructuralElementName($type): - return $this->resolveTypedObject($type, $context); - // @codeCoverageIgnoreStart - default: - // I haven't got the foggiest how the logic would come here but added this as a defense. - throw new \RuntimeException('Unable to resolve type "' . $type . '", there is no known method to resolve it'); - } - // @codeCoverageIgnoreEnd + return 'Stmt_Label'; } +} + $typeClassName + * @param Node\Expr $cond Condition + * @param Node\Stmt[] $stmts Statements + * @param array $attributes Additional attributes */ - public function addKeyword(string $keyword, string $typeClassName) : void + public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) { - if (!\class_exists($typeClassName)) { - throw new \InvalidArgumentException('The Value Object that needs to be created with a keyword "' . $keyword . '" must be an existing class' . ' but we could not find the class ' . $typeClassName); - } - if (!\in_array(\PHPUnit\phpDocumentor\Reflection\Type::class, \class_implements($typeClassName), \true)) { - throw new \InvalidArgumentException('The class "' . $typeClassName . '" must implement the interface "phpDocumentor\\Reflection\\Type"'); - } - $this->keywords[$keyword] = $typeClassName; + $this->attributes = $attributes; + $this->cond = $cond; + $this->stmts = $stmts; } - /** - * Detects whether the given type represents a PHPDoc keyword. - * - * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. - * - * @psalm-mutation-free - */ - private function isKeyword(string $type) : bool + public function getSubNodeNames() : array { - return \array_key_exists(\strtolower($type), $this->keywords); + return ['stmts', 'cond']; } - /** - * Detects whether the given type represents a relative structural element name. - * - * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. - * - * @psalm-mutation-free - */ - private function isPartialStructuralElementName(string $type) : bool + public function getType() : string { - return $type[0] !== self::OPERATOR_NAMESPACE && !$this->isKeyword($type); + return 'Stmt_Do'; } +} +value pair node. * - * @psalm-mutation-free + * @param string|Node\Identifier $key Key + * @param Node\Expr $value Value + * @param array $attributes Additional attributes */ - private function isFqsen(string $type) : bool + public function __construct($key, Node\Expr $value, array $attributes = []) { - return \strpos($type, self::OPERATOR_NAMESPACE) === 0; + $this->attributes = $attributes; + $this->key = \is_string($key) ? new Node\Identifier($key) : $key; + $this->value = $value; } - /** - * Resolves the given keyword (such as `string`) into a Type object representing that keyword. - * - * @psalm-mutation-free - */ - private function resolveKeyword(string $type) : \PHPUnit\phpDocumentor\Reflection\Type + public function getSubNodeNames() : array { - $className = $this->keywords[\strtolower($type)]; - return new $className(); + return ['key', 'value']; } - /** - * Resolves the given FQSEN string into an FQSEN object. - * - * @psalm-mutation-free - */ - private function resolveTypedObject(string $type, ?\PHPUnit\phpDocumentor\Reflection\Types\Context $context = null) : \PHPUnit\phpDocumentor\Reflection\Types\Object_ + public function getType() : string { - return new \PHPUnit\phpDocumentor\Reflection\Types\Object_($this->fqsenResolver->resolve($type, $context)); + return 'Stmt_DeclareDeclare'; } +} + $tokens + * @param Node\Stmt[] $stmts Statements + * @param array $attributes Additional attributes */ - private function resolveClassString(\ArrayIterator $tokens, \PHPUnit\phpDocumentor\Reflection\Types\Context $context) : \PHPUnit\phpDocumentor\Reflection\Type + public function __construct(array $stmts = [], array $attributes = []) { - $tokens->next(); - $classType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION); - if (!$classType instanceof \PHPUnit\phpDocumentor\Reflection\Types\Object_ || $classType->getFqsen() === null) { - throw new \RuntimeException($classType . ' is not a class string'); - } - $token = $tokens->current(); - if ($token !== '>') { - if (empty($token)) { - throw new \RuntimeException('class-string: ">" is missing'); - } - throw new \RuntimeException('Unexpected character "' . $token . '", ">" is missing'); - } - return new \PHPUnit\phpDocumentor\Reflection\Types\ClassString($classType->getFqsen()); + $this->attributes = $attributes; + $this->stmts = $stmts; } - /** - * Resolves the collection values and keys - * - * @param ArrayIterator $tokens - * - * @return Array_|Iterable_|Collection - */ - private function resolveCollection(\ArrayIterator $tokens, \PHPUnit\phpDocumentor\Reflection\Type $classType, \PHPUnit\phpDocumentor\Reflection\Types\Context $context) : \PHPUnit\phpDocumentor\Reflection\Type + public function getSubNodeNames() : array { - $isArray = (string) $classType === 'array'; - $isIterable = (string) $classType === 'iterable'; - // allow only "array", "iterable" or class name before "<" - if (!$isArray && !$isIterable && (!$classType instanceof \PHPUnit\phpDocumentor\Reflection\Types\Object_ || $classType->getFqsen() === null)) { - throw new \RuntimeException($classType . ' is not a collection'); - } - $tokens->next(); - $valueType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION); - $keyType = null; - $token = $tokens->current(); - if ($token !== null && \trim($token) === ',') { - // if we have a comma, then we just parsed the key type, not the value type - $keyType = $valueType; - if ($isArray) { - // check the key type for an "array" collection. We allow only - // strings or integers. - if (!$keyType instanceof \PHPUnit\phpDocumentor\Reflection\Types\String_ && !$keyType instanceof \PHPUnit\phpDocumentor\Reflection\Types\Integer && !$keyType instanceof \PHPUnit\phpDocumentor\Reflection\Types\Compound) { - throw new \RuntimeException('An array can have only integers or strings as keys'); - } - if ($keyType instanceof \PHPUnit\phpDocumentor\Reflection\Types\Compound) { - foreach ($keyType->getIterator() as $item) { - if (!$item instanceof \PHPUnit\phpDocumentor\Reflection\Types\String_ && !$item instanceof \PHPUnit\phpDocumentor\Reflection\Types\Integer) { - throw new \RuntimeException('An array can have only integers or strings as keys'); - } - } - } - } - $tokens->next(); - // now let's parse the value type - $valueType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION); - } - $token = $tokens->current(); - if ($token !== '>') { - if (empty($token)) { - throw new \RuntimeException('Collection: ">" is missing'); - } - throw new \RuntimeException('Unexpected character "' . $token . '", ">" is missing'); - } - if ($isArray) { - return new \PHPUnit\phpDocumentor\Reflection\Types\Array_($valueType, $keyType); - } - if ($isIterable) { - return new \PHPUnit\phpDocumentor\Reflection\Types\Iterable_($valueType, $keyType); - } - if ($classType instanceof \PHPUnit\phpDocumentor\Reflection\Types\Object_) { - return $this->makeCollectionFromObject($classType, $valueType, $keyType); - } - throw new \RuntimeException('Invalid $classType provided'); + return ['stmts']; } - /** - * @psalm-pure - */ - private function makeCollectionFromObject(\PHPUnit\phpDocumentor\Reflection\Types\Object_ $object, \PHPUnit\phpDocumentor\Reflection\Type $valueType, ?\PHPUnit\phpDocumentor\Reflection\Type $keyType = null) : \PHPUnit\phpDocumentor\Reflection\Types\Collection + public function getType() : string { - return new \PHPUnit\phpDocumentor\Reflection\Types\Collection($object->getFqsen(), $valueType, $keyType); + return 'Stmt_Finally'; } } attributes = $attributes; + $this->trait = $trait; + $this->method = \is_string($method) ? new Node\Identifier($method) : $method; + $this->newModifier = $newModifier; + $this->newName = \is_string($newName) ? new Node\Identifier($newName) : $newName; + } + public function getSubNodeNames() : array + { + return ['trait', 'method', 'newModifier', 'newName']; + } + public function getType() : string + { + return 'Stmt_TraitUseAdaptation_Alias'; + } } attributes = $attributes; + $this->trait = $trait; + $this->method = \is_string($method) ? new Node\Identifier($method) : $method; + $this->insteadof = $insteadof; } - public function __toString() : string + public function getSubNodeNames() : array { - return 'false'; + return ['trait', 'method', 'insteadof']; + } + public function getType() : string + { + return 'Stmt_TraitUseAdaptation_Precedence'; } } -\class_alias('PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\False_', 'PHPUnit\\phpDocumentor\\Reflection\\Types\\False_', \false); attributes = $attributes; + $this->cond = $cond; + $this->cases = $cases; } - public function __toString() : string + public function getSubNodeNames() : array { - return 'true'; + return ['cond', 'cases']; + } + public function getType() : string + { + return 'Stmt_Switch'; } } -\class_alias('PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\True_', 'PHPUnit\\phpDocumentor\\Reflection\\Types\\True_', \false); attributes = $attributes; + $this->traits = $traits; + $this->adaptations = $adaptations; + } + public function getSubNodeNames() : array + { + return ['traits', 'adaptations']; + } + public function getType() : string + { + return 'Stmt_TraitUse'; + } } attributes = $attributes; + $this->value = $value; + } + public function getSubNodeNames() : array + { + return ['value']; + } + public function getType() : string + { + return 'Stmt_InlineHTML'; + } +} +stmts as $stmt) { + if ($stmt instanceof TraitUse) { + $traitUses[] = $stmt; + } } - if ($this->isFqsen($fqsen)) { - return new \PHPUnit\phpDocumentor\Reflection\Fqsen($fqsen); + return $traitUses; + } + /** + * @return ClassConst[] + */ + public function getConstants() : array + { + $constants = []; + foreach ($this->stmts as $stmt) { + if ($stmt instanceof ClassConst) { + $constants[] = $stmt; + } } - return $this->resolvePartialStructuralElementName($fqsen, $context); + return $constants; } /** - * Tests whether the given type is a Fully Qualified Structural Element Name. + * @return Property[] */ - private function isFqsen(string $type) : bool + public function getProperties() : array { - return \strpos($type, self::OPERATOR_NAMESPACE) === 0; + $properties = []; + foreach ($this->stmts as $stmt) { + if ($stmt instanceof Property) { + $properties[] = $stmt; + } + } + return $properties; } /** - * Resolves a partial Structural Element Name (i.e. `Reflection\DocBlock`) to its FQSEN representation - * (i.e. `\phpDocumentor\Reflection\DocBlock`) based on the Namespace and aliases mentioned in the Context. + * Gets property with the given name defined directly in this class/interface/trait. * - * @throws InvalidArgumentException When type is not a valid FQSEN. + * @param string $name Name of the property + * + * @return Property|null Property node or null if the property does not exist */ - private function resolvePartialStructuralElementName(string $type, \PHPUnit\phpDocumentor\Reflection\Types\Context $context) : \PHPUnit\phpDocumentor\Reflection\Fqsen + public function getProperty(string $name) { - $typeParts = \explode(self::OPERATOR_NAMESPACE, $type, 2); - $namespaceAliases = $context->getNamespaceAliases(); - // if the first segment is not an alias; prepend namespace name and return - if (!isset($namespaceAliases[$typeParts[0]])) { - $namespace = $context->getNamespace(); - if ($namespace !== '') { - $namespace .= self::OPERATOR_NAMESPACE; + foreach ($this->stmts as $stmt) { + if ($stmt instanceof Property) { + foreach ($stmt->props as $prop) { + if ($prop instanceof PropertyProperty && $name === $prop->name->toString()) { + return $stmt; + } + } } - return new \PHPUnit\phpDocumentor\Reflection\Fqsen(self::OPERATOR_NAMESPACE . $namespace . $type); } - $typeParts[0] = $namespaceAliases[$typeParts[0]]; - return new \PHPUnit\phpDocumentor\Reflection\Fqsen(self::OPERATOR_NAMESPACE . \implode(self::OPERATOR_NAMESPACE, $typeParts)); + return null; + } + /** + * Gets all methods defined directly in this class/interface/trait + * + * @return ClassMethod[] + */ + public function getMethods() : array + { + $methods = []; + foreach ($this->stmts as $stmt) { + if ($stmt instanceof ClassMethod) { + $methods[] = $stmt; + } + } + return $methods; + } + /** + * Gets method with the given name defined directly in this class/interface/trait. + * + * @param string $name Name of the method (compared case-insensitively) + * + * @return ClassMethod|null Method node or null if the method does not exist + */ + public function getMethod(string $name) + { + $lowerName = \strtolower($name); + foreach ($this->stmts as $stmt) { + if ($stmt instanceof ClassMethod && $lowerName === $stmt->name->toLowerString()) { + return $stmt; + } + } + return null; } } -The MIT License (MIT) - -Copyright (c) 2010 Mike van Riel - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. attributes = $attributes; + $this->name = \is_string($name) ? new Identifier($name) : $name; + } + public function getSubNodeNames() : array + { + return ['name']; + } + public function getType() : string + { + return 'Stmt_Goto'; } } attributes = $attributes; + $this->expr = $expr; + } + public function getSubNodeNames() : array + { + return ['expr']; + } + public function getType() : string + { + return 'Stmt_Return'; } } valueType = $valueType; + $this->attributes = $attributes; + $this->declares = $declares; + $this->stmts = $stmts; } - /** - * Returns the value for the keys of this array. - */ - public function getValueType() : \PHPUnit\phpDocumentor\Reflection\Type + public function getSubNodeNames() : array { - return $this->valueType; + return ['declares', 'stmts']; } - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string + public function getType() : string { - return '(' . $this->valueType . ')'; + return 'Stmt_Declare'; } } attributes = $attributes; + $this->var = $var; + $this->default = $default; + } + public function getSubNodeNames() : array + { + return ['var', 'default']; + } + public function getType() : string + { + return 'Stmt_StaticVar'; } } attributes = $attributes; + $this->cond = $cond; + $this->stmts = $stmts; + } + public function getSubNodeNames() : array + { + return ['cond', 'stmts']; + } + public function getType() : string + { + return 'Stmt_Case'; } } attributes = $attributes; + $this->consts = $consts; + } + public function getSubNodeNames() : array + { + return ['consts']; + } + public function getType() : string + { + return 'Stmt_Const'; } } attributes = $attributes; + $this->vars = $vars; + } + public function getSubNodeNames() : array + { + return ['vars']; + } + public function getType() : string + { + return 'Stmt_Static'; } } attributes = $attributes; + $this->stmts = $stmts; + $this->catches = $catches; + $this->finally = $finally; + } + public function getSubNodeNames() : array + { + return ['stmts', 'catches', 'finally']; + } + public function getType() : string + { + return 'Stmt_TryCatch'; + } } name = \is_string($name) ? new Node\Identifier($name) : $name; + $this->expr = $expr; + $this->attrGroups = $attrGroups; + } + public function getSubNodeNames() : array + { + return ['attrGroups', 'name', 'expr']; + } + public function getType() : string + { + return 'Stmt_EnumCase'; } } $types + * @param Node\Expr $expr Expression to iterate + * @param Node\Expr $valueVar Variable to assign value to + * @param array $subNodes Array of the following optional subnodes: + * 'keyVar' => null : Variable to assign key to + * 'byRef' => false : Whether to assign value by reference + * 'stmts' => array(): Statements + * @param array $attributes Additional attributes */ - public function __construct(array $types) + public function __construct(Node\Expr $expr, Node\Expr $valueVar, array $subNodes = [], array $attributes = []) { - parent::__construct($types, '&'); + $this->attributes = $attributes; + $this->expr = $expr; + $this->keyVar = $subNodes['keyVar'] ?? null; + $this->byRef = $subNodes['byRef'] ?? \false; + $this->valueVar = $valueVar; + $this->stmts = $subNodes['stmts'] ?? []; + } + public function getSubNodeNames() : array + { + return ['expr', 'keyVar', 'byRef', 'valueVar', 'stmts']; + } + public function getType() : string + { + return 'Stmt_Foreach'; } } fqsen = $fqsen; + $this->attributes = $attributes; + $this->type = $type; + $this->name = $name; + $this->alias = \is_string($alias) ? new Identifier($alias) : $alias; + } + public function getSubNodeNames() : array + { + return ['type', 'name', 'alias']; } /** - * Returns the FQSEN associated with this object. + * Get alias. If not explicitly given this is the last component of the used name. + * + * @return Identifier */ - public function getFqsen() : ?\PHPUnit\phpDocumentor\Reflection\Fqsen + public function getAlias() : Identifier { - return $this->fqsen; + if (null !== $this->alias) { + return $this->alias; + } + return new Identifier($this->name->getLast()); } - public function __toString() : string + public function getType() : string { - if ($this->fqsen) { - return (string) $this->fqsen; - } - return 'object'; + return 'Stmt_UseUse'; } } null : Scalar type + * 'implements' => array() : Names of implemented interfaces + * 'stmts' => array() : Statements + * 'attrGroups' => array() : PHP attribute groups + * @param array $attributes Additional attributes */ - public function __toString() : string + public function __construct($name, array $subNodes = [], array $attributes = []) { - return 'float'; + $this->name = \is_string($name) ? new Node\Identifier($name) : $name; + $this->scalarType = $subNodes['scalarType'] ?? null; + $this->implements = $subNodes['implements'] ?? []; + $this->stmts = $subNodes['stmts'] ?? []; + $this->attrGroups = $subNodes['attrGroups'] ?? []; + parent::__construct($attributes); + } + public function getSubNodeNames() : array + { + return ['attrGroups', 'name', 'scalarType', 'implements', 'stmts']; + } + public function getType() : string + { + return 'Stmt_Enum'; } } attributes = $attributes; + $this->expr = $expr; + } + public function getSubNodeNames() : array + { + return ['expr']; + } + public function getType() : string + { + return 'Stmt_Expression'; } } \true, '__destruct' => \true, '__call' => \true, '__callstatic' => \true, '__get' => \true, '__set' => \true, '__isset' => \true, '__unset' => \true, '__sleep' => \true, '__wakeup' => \true, '__tostring' => \true, '__set_state' => \true, '__clone' => \true, '__invoke' => \true, '__debuginfo' => \true]; /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * Constructs a class method node. + * + * @param string|Node\Identifier $name Name + * @param array $subNodes Array of the following optional subnodes: + * 'flags => MODIFIER_PUBLIC: Flags + * 'byRef' => false : Whether to return by reference + * 'params' => array() : Parameters + * 'returnType' => null : Return type + * 'stmts' => array() : Statements + * 'attrGroups' => array() : PHP attribute groups + * @param array $attributes Additional attributes */ - public function __toString() : string + public function __construct($name, array $subNodes = [], array $attributes = []) { - return 'static'; + $this->attributes = $attributes; + $this->flags = $subNodes['flags'] ?? $subNodes['type'] ?? 0; + $this->byRef = $subNodes['byRef'] ?? \false; + $this->name = \is_string($name) ? new Node\Identifier($name) : $name; + $this->params = $subNodes['params'] ?? []; + $returnType = $subNodes['returnType'] ?? null; + $this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType; + $this->stmts = \array_key_exists('stmts', $subNodes) ? $subNodes['stmts'] : []; + $this->attrGroups = $subNodes['attrGroups'] ?? []; + } + public function getSubNodeNames() : array + { + return ['attrGroups', 'flags', 'byRef', 'name', 'params', 'returnType', 'stmts']; + } + public function returnsByRef() : bool + { + return $this->byRef; + } + public function getParams() : array + { + return $this->params; + } + public function getReturnType() + { + return $this->returnType; + } + public function getStmts() + { + return $this->stmts; + } + public function getAttrGroups() : array + { + return $this->attrGroups; } -} -valueType = $valueType; - $this->defaultKeyType = new \PHPUnit\phpDocumentor\Reflection\Types\Compound([new \PHPUnit\phpDocumentor\Reflection\Types\String_(), new \PHPUnit\phpDocumentor\Reflection\Types\Integer()]); - $this->keyType = $keyType; + return ($this->flags & Class_::MODIFIER_PUBLIC) !== 0 || ($this->flags & Class_::VISIBILITY_MODIFIER_MASK) === 0; } /** - * Returns the type for the keys of this array. + * Whether the method is protected. + * + * @return bool */ - public function getKeyType() : \PHPUnit\phpDocumentor\Reflection\Type + public function isProtected() : bool { - return $this->keyType ?? $this->defaultKeyType; + return (bool) ($this->flags & Class_::MODIFIER_PROTECTED); } /** - * Returns the value for the keys of this array. + * Whether the method is private. + * + * @return bool */ - public function getValueType() : \PHPUnit\phpDocumentor\Reflection\Type + public function isPrivate() : bool { - return $this->valueType; + return (bool) ($this->flags & Class_::MODIFIER_PRIVATE); } /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * Whether the method is abstract. + * + * @return bool */ - public function __toString() : string + public function isAbstract() : bool { - if ($this->keyType) { - return 'array<' . $this->keyType . ',' . $this->valueType . '>'; - } - if ($this->valueType instanceof \PHPUnit\phpDocumentor\Reflection\Types\Mixed_) { - return 'array'; - } - if ($this->valueType instanceof \PHPUnit\phpDocumentor\Reflection\Types\Compound) { - return '(' . $this->valueType . ')[]'; - } - return $this->valueType . '[]'; + return (bool) ($this->flags & Class_::MODIFIER_ABSTRACT); } -} -realType = $realType; + return (bool) ($this->flags & Class_::MODIFIER_FINAL); } /** - * Provide access to the actual type directly, if needed. + * Whether the method is static. + * + * @return bool */ - public function getActualType() : \PHPUnit\phpDocumentor\Reflection\Type + public function isStatic() : bool { - return $this->realType; + return (bool) ($this->flags & Class_::MODIFIER_STATIC); } /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * Whether the method is magic. + * + * @return bool */ - public function __toString() : string + public function isMagic() : bool { - return '?' . $this->realType->__toString(); + return isset(self::$magicNames[$this->name->toLowerString()]); + } + public function getType() : string + { + return 'Stmt_ClassMethod'; } } attributes = $attributes; + $this->types = $types; + $this->var = $var; + $this->stmts = $stmts; + } + public function getSubNodeNames() : array + { + return ['types', 'var', 'stmts']; + } + public function getType() : string + { + return 'Stmt_Catch'; } } keyType) { - return 'iterable<' . $this->keyType . ',' . $this->valueType . '>'; - } - if ($this->valueType instanceof \PHPUnit\phpDocumentor\Reflection\Types\Mixed_) { - return 'iterable'; - } - return 'iterable<' . $this->valueType . '>'; + $this->attributes = $attributes; + $this->vars = $vars; + } + public function getSubNodeNames() : array + { + return ['vars']; + } + public function getType() : string + { + return 'Stmt_Global'; } } - */ -abstract class AggregatedType implements \PHPUnit\phpDocumentor\Reflection\Type, \IteratorAggregate +use PHPUnit\PhpParser\Node; +class PropertyProperty extends Node\Stmt { + /** @var Node\VarLikeIdentifier Name */ + public $name; + /** @var null|Node\Expr Default */ + public $default; /** - * @psalm-allow-private-mutation - * @var array - */ - private $types = []; - /** @var string */ - private $token; - /** - * @param array $types - */ - public function __construct(array $types, string $token) - { - foreach ($types as $type) { - $this->add($type); - } - $this->token = $token; - } - /** - * Returns the type at the given index. + * Constructs a class property node. + * + * @param string|Node\VarLikeIdentifier $name Name + * @param null|Node\Expr $default Default value + * @param array $attributes Additional attributes */ - public function get(int $index) : ?\PHPUnit\phpDocumentor\Reflection\Type + public function __construct($name, Node\Expr $default = null, array $attributes = []) { - if (!$this->has($index)) { - return null; - } - return $this->types[$index]; + $this->attributes = $attributes; + $this->name = \is_string($name) ? new Node\VarLikeIdentifier($name) : $name; + $this->default = $default; } - /** - * Tests if this compound type has a type with the given index. - */ - public function has(int $index) : bool + public function getSubNodeNames() : array { - return \array_key_exists($index, $this->types); + return ['name', 'default']; } - /** - * Tests if this compound type contains the given type. - */ - public function contains(\PHPUnit\phpDocumentor\Reflection\Type $type) : bool + public function getType() : string { - foreach ($this->types as $typePart) { - // if the type is duplicate; do not add it - if ((string) $typePart === (string) $type) { - return \true; - } - } - return \false; + return 'Stmt_PropertyProperty'; } +} + array(): Init expressions + * 'cond' => array(): Loop conditions + * 'loop' => array(): Loop expressions + * 'stmts' => array(): Statements + * @param array $attributes Additional attributes */ - public function __toString() : string + public function __construct(array $subNodes = [], array $attributes = []) { - return \implode($this->token, $this->types); + $this->attributes = $attributes; + $this->init = $subNodes['init'] ?? []; + $this->cond = $subNodes['cond'] ?? []; + $this->loop = $subNodes['loop'] ?? []; + $this->stmts = $subNodes['stmts'] ?? []; } - /** - * @return ArrayIterator - */ - public function getIterator() : \ArrayIterator + public function getSubNodeNames() : array { - return new \ArrayIterator($this->types); + return ['init', 'cond', 'loop', 'stmts']; } - /** - * @psalm-suppress ImpureMethodCall - */ - private function add(\PHPUnit\phpDocumentor\Reflection\Type $type) : void + public function getType() : string { - if ($type instanceof self) { - foreach ($type->getIterator() as $subType) { - $this->add($subType); - } - return; - } - // if the type is duplicate; do not add it - if ($this->contains($type)) { - return; - } - $this->types[] = $type; + return 'Stmt_For'; } } attributes = $attributes; + $this->type = $type; + $this->uses = $uses; + } + public function getSubNodeNames() : array + { + return ['type', 'uses']; + } + public function getType() : string + { + return 'Stmt_Use'; } } $reflector */ - return $this->createFromReflectionClass($reflector); - } - if ($reflector instanceof \ReflectionParameter) { - return $this->createFromReflectionParameter($reflector); - } - if ($reflector instanceof \ReflectionMethod) { - return $this->createFromReflectionMethod($reflector); - } - if ($reflector instanceof \ReflectionProperty) { - return $this->createFromReflectionProperty($reflector); - } - if ($reflector instanceof \ReflectionClassConstant) { - return $this->createFromReflectionClassConstant($reflector); - } - throw new \UnexpectedValueException('Unhandled \\Reflector instance given: ' . \get_class($reflector)); + $this->attributes = $attributes; + $this->name = $name; + $this->stmts = $stmts; } - private function createFromReflectionParameter(\ReflectionParameter $parameter) : \PHPUnit\phpDocumentor\Reflection\Types\Context + public function getSubNodeNames() : array { - $class = $parameter->getDeclaringClass(); - if (!$class) { - throw new \InvalidArgumentException('Unable to get class of ' . $parameter->getName()); - } - //phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable - /** @var ReflectionClass $class */ - return $this->createFromReflectionClass($class); + return ['name', 'stmts']; } - private function createFromReflectionMethod(\ReflectionMethod $method) : \PHPUnit\phpDocumentor\Reflection\Types\Context + public function getType() : string { - //phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable - /** @var ReflectionClass $class */ - $class = $method->getDeclaringClass(); - return $this->createFromReflectionClass($class); + return 'Stmt_Namespace'; } - private function createFromReflectionProperty(\ReflectionProperty $property) : \PHPUnit\phpDocumentor\Reflection\Types\Context +} + $class */ - $class = $property->getDeclaringClass(); - return $this->createFromReflectionClass($class); + $this->attributes = $attributes; + $this->flags = $flags; + $this->consts = $consts; + $this->attrGroups = $attrGroups; } - private function createFromReflectionClassConstant(\ReflectionClassConstant $constant) : \PHPUnit\phpDocumentor\Reflection\Types\Context + public function getSubNodeNames() : array { - //phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable - /** @var ReflectionClass $class */ - $class = $constant->getDeclaringClass(); - return $this->createFromReflectionClass($class); + return ['attrGroups', 'flags', 'consts']; } /** - * @param ReflectionClass $class + * Whether constant is explicitly or implicitly public. + * + * @return bool */ - private function createFromReflectionClass(\ReflectionClass $class) : \PHPUnit\phpDocumentor\Reflection\Types\Context + public function isPublic() : bool { - $fileName = $class->getFileName(); - $namespace = $class->getNamespaceName(); - if (\is_string($fileName) && \file_exists($fileName)) { - $contents = \file_get_contents($fileName); - if ($contents === \false) { - throw new \RuntimeException('Unable to read file "' . $fileName . '"'); - } - return $this->createForNamespace($namespace, $contents); - } - return new \PHPUnit\phpDocumentor\Reflection\Types\Context($namespace, []); + return ($this->flags & Class_::MODIFIER_PUBLIC) !== 0 || ($this->flags & Class_::VISIBILITY_MODIFIER_MASK) === 0; } /** - * Build a Context for a namespace in the provided file contents. - * - * @see Context for more information on Contexts. + * Whether constant is protected. * - * @param string $namespace It does not matter if a `\` precedes the namespace name, - * this method first normalizes. - * @param string $fileContents The file's contents to retrieve the aliases from with the given namespace. + * @return bool */ - public function createForNamespace(string $namespace, string $fileContents) : \PHPUnit\phpDocumentor\Reflection\Types\Context + public function isProtected() : bool { - $namespace = \trim($namespace, '\\'); - $useStatements = []; - $currentNamespace = ''; - $tokens = new \ArrayIterator(\token_get_all($fileContents)); - while ($tokens->valid()) { - $currentToken = $tokens->current(); - switch ($currentToken[0]) { - case \T_NAMESPACE: - $currentNamespace = $this->parseNamespace($tokens); - break; - case \T_CLASS: - // Fast-forward the iterator through the class so that any - // T_USE tokens found within are skipped - these are not - // valid namespace use statements so should be ignored. - $braceLevel = 0; - $firstBraceFound = \false; - while ($tokens->valid() && ($braceLevel > 0 || !$firstBraceFound)) { - $currentToken = $tokens->current(); - if ($currentToken === '{' || \in_array($currentToken[0], [\T_CURLY_OPEN, \T_DOLLAR_OPEN_CURLY_BRACES], \true)) { - if (!$firstBraceFound) { - $firstBraceFound = \true; - } - ++$braceLevel; - } - if ($currentToken === '}') { - --$braceLevel; - } - $tokens->next(); - } - break; - case \T_USE: - if ($currentNamespace === $namespace) { - $useStatements += $this->parseUseStatement($tokens); - } - break; - } - $tokens->next(); - } - return new \PHPUnit\phpDocumentor\Reflection\Types\Context($namespace, $useStatements); + return (bool) ($this->flags & Class_::MODIFIER_PROTECTED); } /** - * Deduce the name from tokens when we are at the T_NAMESPACE token. + * Whether constant is private. * - * @param ArrayIterator $tokens + * @return bool */ - private function parseNamespace(\ArrayIterator $tokens) : string + public function isPrivate() : bool { - // skip to the first string or namespace separator - $this->skipToNextStringOrNamespaceSeparator($tokens); - $name = ''; - $acceptedTokens = [\T_STRING, \T_NS_SEPARATOR, T_NAME_QUALIFIED]; - while ($tokens->valid() && \in_array($tokens->current()[0], $acceptedTokens, \true)) { - $name .= $tokens->current()[1]; - $tokens->next(); - } - return $name; + return (bool) ($this->flags & Class_::MODIFIER_PRIVATE); } /** - * Deduce the names of all imports when we are at the T_USE token. - * - * @param ArrayIterator $tokens + * Whether constant is final. * - * @return string[] - * - * @psalm-return array + * @return bool */ - private function parseUseStatement(\ArrayIterator $tokens) : array + public function isFinal() : bool { - $uses = []; - while ($tokens->valid()) { - $this->skipToNextStringOrNamespaceSeparator($tokens); - $uses += $this->extractUseStatements($tokens); - $currentToken = $tokens->current(); - if ($currentToken[0] === self::T_LITERAL_END_OF_USE) { - return $uses; - } - } - return $uses; + return (bool) ($this->flags & Class_::MODIFIER_FINAL); } - /** - * Fast-forwards the iterator as longs as we don't encounter a T_STRING or T_NS_SEPARATOR token. - * - * @param ArrayIterator $tokens - */ - private function skipToNextStringOrNamespaceSeparator(\ArrayIterator $tokens) : void + public function getType() : string { - while ($tokens->valid()) { - $currentToken = $tokens->current(); - if (\in_array($currentToken[0], [\T_STRING, \T_NS_SEPARATOR], \true)) { - break; - } - if ($currentToken[0] === T_NAME_QUALIFIED) { - break; - } - if (\defined('T_NAME_FULLY_QUALIFIED') && $currentToken[0] === T_NAME_FULLY_QUALIFIED) { - break; - } - $tokens->next(); - } + return 'Stmt_ClassConst'; } +} + namespace. - * - * @param ArrayIterator $tokens - * - * @return string[] - * - * @psalm-suppress TypeDoesNotContainType + * Constructs a class property list node. * - * @psalm-return array + * @param int $flags Modifiers + * @param PropertyProperty[] $props Properties + * @param array $attributes Additional attributes + * @param null|string|Identifier|Name|ComplexType $type Type declaration + * @param Node\AttributeGroup[] $attrGroups PHP attribute groups */ - private function extractUseStatements(\ArrayIterator $tokens) : array + public function __construct(int $flags, array $props, array $attributes = [], $type = null, array $attrGroups = []) { - $extractedUseStatements = []; - $groupedNs = ''; - $currentNs = ''; - $currentAlias = ''; - $state = 'start'; - while ($tokens->valid()) { - $currentToken = $tokens->current(); - $tokenId = \is_string($currentToken) ? $currentToken : $currentToken[0]; - $tokenValue = \is_string($currentToken) ? null : $currentToken[1]; - switch ($state) { - case 'start': - switch ($tokenId) { - case \T_STRING: - case \T_NS_SEPARATOR: - $currentNs .= (string) $tokenValue; - $currentAlias = $tokenValue; - break; - case T_NAME_QUALIFIED: - case T_NAME_FULLY_QUALIFIED: - $currentNs .= (string) $tokenValue; - $currentAlias = \substr((string) $tokenValue, (int) \strrpos((string) $tokenValue, '\\') + 1); - break; - case \T_CURLY_OPEN: - case '{': - $state = 'grouped'; - $groupedNs = $currentNs; - break; - case \T_AS: - $state = 'start-alias'; - break; - case self::T_LITERAL_USE_SEPARATOR: - case self::T_LITERAL_END_OF_USE: - $state = 'end'; - break; - default: - break; - } - break; - case 'start-alias': - switch ($tokenId) { - case \T_STRING: - $currentAlias = $tokenValue; - break; - case self::T_LITERAL_USE_SEPARATOR: - case self::T_LITERAL_END_OF_USE: - $state = 'end'; - break; - default: - break; - } - break; - case 'grouped': - switch ($tokenId) { - case \T_STRING: - case \T_NS_SEPARATOR: - $currentNs .= (string) $tokenValue; - $currentAlias = $tokenValue; - break; - case \T_AS: - $state = 'grouped-alias'; - break; - case self::T_LITERAL_USE_SEPARATOR: - $state = 'grouped'; - $extractedUseStatements[(string) $currentAlias] = $currentNs; - $currentNs = $groupedNs; - $currentAlias = ''; - break; - case self::T_LITERAL_END_OF_USE: - $state = 'end'; - break; - default: - break; - } - break; - case 'grouped-alias': - switch ($tokenId) { - case \T_STRING: - $currentAlias = $tokenValue; - break; - case self::T_LITERAL_USE_SEPARATOR: - $state = 'grouped'; - $extractedUseStatements[(string) $currentAlias] = $currentNs; - $currentNs = $groupedNs; - $currentAlias = ''; - break; - case self::T_LITERAL_END_OF_USE: - $state = 'end'; - break; - default: - break; - } - } - if ($state === 'end') { - break; - } - $tokens->next(); - } - if ($groupedNs !== $currentNs) { - $extractedUseStatements[(string) $currentAlias] = $currentNs; - } - return $extractedUseStatements; + $this->attributes = $attributes; + $this->flags = $flags; + $this->props = $props; + $this->type = \is_string($type) ? new Identifier($type) : $type; + $this->attrGroups = $attrGroups; + } + public function getSubNodeNames() : array + { + return ['attrGroups', 'flags', 'type', 'props']; + } + /** + * Whether the property is explicitly or implicitly public. + * + * @return bool + */ + public function isPublic() : bool + { + return ($this->flags & Class_::MODIFIER_PUBLIC) !== 0 || ($this->flags & Class_::VISIBILITY_MODIFIER_MASK) === 0; + } + /** + * Whether the property is protected. + * + * @return bool + */ + public function isProtected() : bool + { + return (bool) ($this->flags & Class_::MODIFIER_PROTECTED); + } + /** + * Whether the property is private. + * + * @return bool + */ + public function isPrivate() : bool + { + return (bool) ($this->flags & Class_::MODIFIER_PRIVATE); + } + /** + * Whether the property is static. + * + * @return bool + */ + public function isStatic() : bool + { + return (bool) ($this->flags & Class_::MODIFIER_STATIC); + } + /** + * Whether the property is readonly. + * + * @return bool + */ + public function isReadonly() : bool + { + return (bool) ($this->flags & Class_::MODIFIER_READONLY); + } + public function getType() : string + { + return 'Stmt_Property'; } } attributes = $attributes; + $this->var = $var; + $this->name = \is_string($name) ? new Identifier($name) : $name; + } + public function getSubNodeNames() : array + { + return ['var', 'name']; + } + public function getType() : string + { + return 'Expr_NullsafePropertyFetch'; } } Fully Qualified Namespace. - * @psalm-var array - */ - private $namespaceAliases; + /** @var null|Expr Key */ + public $key; + /** @var Expr Value */ + public $value; + /** @var bool Whether to assign by reference */ + public $byRef; + /** @var bool Whether to unpack the argument */ + public $unpack; /** - * Initializes the new context and normalizes all passed namespaces to be in Qualified Namespace Name (QNN) - * format (without a preceding `\`). - * - * @param string $namespace The namespace where this DocBlock resides in. - * @param string[] $namespaceAliases List of namespace aliases => Fully Qualified Namespace. + * Constructs an array item node. * - * @psalm-param array $namespaceAliases + * @param Expr $value Value + * @param null|Expr $key Key + * @param bool $byRef Whether to assign by reference + * @param array $attributes Additional attributes */ - public function __construct(string $namespace, array $namespaceAliases = []) + public function __construct(Expr $value, Expr $key = null, bool $byRef = \false, array $attributes = [], bool $unpack = \false) { - $this->namespace = $namespace !== 'global' && $namespace !== 'default' ? \trim($namespace, '\\') : ''; - foreach ($namespaceAliases as $alias => $fqnn) { - if ($fqnn[0] === '\\') { - $fqnn = \substr($fqnn, 1); - } - if ($fqnn[\strlen($fqnn) - 1] === '\\') { - $fqnn = \substr($fqnn, 0, -1); - } - $namespaceAliases[$alias] = $fqnn; - } - $this->namespaceAliases = $namespaceAliases; + $this->attributes = $attributes; + $this->key = $key; + $this->value = $value; + $this->byRef = $byRef; + $this->unpack = $unpack; } - /** - * Returns the Qualified Namespace Name (thus without `\` in front) where the associated element is in. - */ - public function getNamespace() : string + public function getSubNodeNames() : array { - return $this->namespace; + return ['key', 'value', 'byRef', 'unpack']; + } + public function getType() : string + { + return 'Expr_ArrayItem'; } +} + + * @param Expr $var Variable + * @param array $attributes Additional attributes */ - public function getNamespaceAliases() : array + public function __construct(Expr $var, array $attributes = []) { - return $this->namespaceAliases; + $this->attributes = $attributes; + $this->var = $var; + } + public function getSubNodeNames() : array + { + return ['var']; + } + public function getType() : string + { + return 'Expr_PreDec'; } } fqsen = $fqsen; + $this->attributes = $attributes; + $this->expr = $expr; } - /** - * Returns the FQSEN associated with this object. - */ - public function getFqsen() : ?\PHPUnit\phpDocumentor\Reflection\Fqsen + public function getSubNodeNames() : array { - return $this->fqsen; + return ['expr']; } +} +fqsen === null) { - return 'class-string'; - } - return 'class-string<' . (string) $this->fqsen . '>'; + $this->attributes = $attributes; + $this->expr = $expr; + } + public function getSubNodeNames() : array + { + return ['expr']; + } + public function getType() : string + { + return 'Expr_Clone'; } } ` - * 2. `ACollectionObject` - * - * - ACollectionObject can be 'array' or an object that can act as an array - * - aValueType and aKeyType can be any type expression - * - * @psalm-immutable - */ -final class Collection extends \PHPUnit\phpDocumentor\Reflection\Types\AbstractList +use PHPUnit\PhpParser\Node\Expr; +class UnaryPlus extends Expr { - /** @var Fqsen|null */ - private $fqsen; + /** @var Expr Expression */ + public $expr; /** - * Initializes this representation of an array with the given Type or Fqsen. + * Constructs a unary plus node. + * + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ - public function __construct(?\PHPUnit\phpDocumentor\Reflection\Fqsen $fqsen, \PHPUnit\phpDocumentor\Reflection\Type $valueType, ?\PHPUnit\phpDocumentor\Reflection\Type $keyType = null) + public function __construct(Expr $expr, array $attributes = []) { - parent::__construct($valueType, $keyType); - $this->fqsen = $fqsen; + $this->attributes = $attributes; + $this->expr = $expr; } - /** - * Returns the FQSEN associated with this object. - */ - public function getFqsen() : ?\PHPUnit\phpDocumentor\Reflection\Fqsen + public function getSubNodeNames() : array { - return $this->fqsen; + return ['expr']; } - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString() : string + public function getType() : string { - $objectType = (string) ($this->fqsen ?? 'object'); - if ($this->keyType === null) { - return $objectType . '<' . $this->valueType . '>'; - } - return $objectType . '<' . $this->keyType . ',' . $this->valueType . '>'; + return 'Expr_UnaryPlus'; } } $types + * @param Expr $var Variable + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ - public function __construct(array $types) + public function __construct(Expr $var, Expr $expr, array $attributes = []) { - parent::__construct($types, '|'); + $this->attributes = $attributes; + $this->var = $var; + $this->expr = $expr; + } + public function getSubNodeNames() : array + { + return ['var', 'expr']; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann; +declare (strict_types=1); +namespace PHPUnit\PhpParser\Node\Expr; -final class Version +use PHPUnit\PhpParser\Node\Arg; +use PHPUnit\PhpParser\Node\Expr; +use PHPUnit\PhpParser\Node\VariadicPlaceholder; +abstract class CallLike extends Expr { /** - * @var string + * Return raw arguments, which may be actual Args, or VariadicPlaceholders for first-class + * callables. + * + * @return array */ - private $path; + public abstract function getRawArgs() : array; /** - * @var string + * Returns whether this call expression is actually a first class callable. */ - private $release; + public function isFirstClassCallable() : bool + { + foreach ($this->getRawArgs() as $arg) { + if ($arg instanceof VariadicPlaceholder) { + return \true; + } + } + return \false; + } /** - * @var string + * Assert that this is not a first-class callable and return only ordinary Args. + * + * @return Arg[] */ - private $version; - public function __construct(string $release, string $path) + public function getArgs() : array { - $this->release = $release; - $this->path = $path; - } - public function getVersion() : string - { - if ($this->version === null) { - if (\substr_count($this->release, '.') + 1 === 3) { - $this->version = $this->release; - } else { - $this->version = $this->release . '-dev'; - } - $git = $this->getGitInformation($this->path); - if ($git) { - if (\substr_count($this->release, '.') + 1 === 3) { - $this->version = $git; - } else { - $git = \explode('-', $git); - $this->version = $this->release . '-' . \end($git); - } - } - } - return $this->version; + \assert(!$this->isFirstClassCallable()); + return $this->getRawArgs(); } +} + ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes, $path); - if (!\is_resource($process)) { - return \false; - } - $result = \trim(\stream_get_contents($pipes[1])); - \fclose($pipes[1]); - \fclose($pipes[2]); - $returnCode = \proc_close($process); - if ($returnCode !== 0) { - return \false; - } - return $result; + $this->attributes = $attributes; + $this->expr = $expr; + } + public function getSubNodeNames() : array + { + return ['expr']; + } + public function getType() : string + { + return 'Expr_Throw'; } } -Version - -Copyright (c) 2013-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Webmozart\Assert; +declare (strict_types=1); +namespace PHPUnit\PhpParser\Node\Expr; -use ArrayAccess; -use BadMethodCallException; -use Closure; -use Countable; -use DateTime; -use DateTimeImmutable; -use Exception; -use InvalidArgumentException; -use ResourceBundle; -use SimpleXMLElement; -use Throwable; -use Traversable; -/** - * Efficient assertions to validate the input/output of your methods. - * - * @mixin Mixin - * - * @since 1.0 - * - * @author Bernhard Schussek - */ -class Assert +use PHPUnit\PhpParser\Node\Expr; +class Yield_ extends Expr { + /** @var null|Expr Key expression */ + public $key; + /** @var null|Expr Value expression */ + public $value; /** - * @psalm-pure - * @psalm-assert string $value - * - * @param mixed $value - * @param string $message + * Constructs a yield expression node. * - * @throws InvalidArgumentException + * @param null|Expr $value Value expression + * @param null|Expr $key Key expression + * @param array $attributes Additional attributes */ - public static function string($value, $message = '') + public function __construct(Expr $value = null, Expr $key = null, array $attributes = []) { - if (!\is_string($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a string. Got: %s', static::typeToString($value))); - } + $this->attributes = $attributes; + $this->key = $key; + $this->value = $value; } - /** - * @psalm-pure - * @psalm-assert non-empty-string $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function stringNotEmpty($value, $message = '') + public function getSubNodeNames() : array { - static::string($value, $message); - static::notEq($value, '', $message); + return ['key', 'value']; } - /** - * @psalm-pure - * @psalm-assert int $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function integer($value, $message = '') + public function getType() : string { - if (!\is_int($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an integer. Got: %s', static::typeToString($value))); - } + return 'Expr_Yield'; } +} +attributes = $attributes; + $this->cond = $cond; + $this->arms = $arms; } - /** - * @psalm-pure - * @psalm-assert float $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function float($value, $message = '') + public function getSubNodeNames() : array { - if (!\is_float($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a float. Got: %s', static::typeToString($value))); - } + return ['cond', 'arms']; } - /** - * @psalm-pure - * @psalm-assert numeric $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function numeric($value, $message = '') + public function getType() : string { - if (!\is_numeric($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a numeric. Got: %s', static::typeToString($value))); - } + return 'Expr_Match'; } +} +attributes = $attributes; + $this->expr = $expr; } - /** - * @psalm-pure - * @psalm-assert bool $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function boolean($value, $message = '') + public function getSubNodeNames() : array { - if (!\is_bool($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a boolean. Got: %s', static::typeToString($value))); - } + return ['expr']; } - /** - * @psalm-pure - * @psalm-assert scalar $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function scalar($value, $message = '') + public function getType() : string { - if (!\is_scalar($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a scalar. Got: %s', static::typeToString($value))); - } + return 'Expr_Print'; } +} +attributes = $attributes; + $this->name = $name; } - /** - * @psalm-pure - * @psalm-assert resource $value - * - * @param mixed $value - * @param string|null $type type of resource this should be. @see https://www.php.net/manual/en/function.get-resource-type.php - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function resource($value, $type = null, $message = '') + public function getSubNodeNames() : array { - if (!\is_resource($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a resource. Got: %s', static::typeToString($value))); - } - if ($type && $type !== \get_resource_type($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a resource of type %2$s. Got: %s', static::typeToString($value), $type)); - } + return ['name']; } - /** - * @psalm-pure - * @psalm-assert callable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function isCallable($value, $message = '') + public function getType() : string { - if (!\is_callable($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a callable. Got: %s', static::typeToString($value))); - } + return 'Expr_Variable'; } +} +attributes = $attributes; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @deprecated use "isIterable" or "isInstanceOf" instead - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function isTraversable($value, $message = '') + public function getSubNodeNames() : array { - @\trigger_error(\sprintf('The "%s" assertion is deprecated. You should stop using it, as it will soon be removed in 2.0 version. Use "isIterable" or "isInstanceOf" instead.', __METHOD__), \E_USER_DEPRECATED); - if (!\is_array($value) && !$value instanceof \Traversable) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a traversable. Got: %s', static::typeToString($value))); - } + return []; } - /** - * @psalm-pure - * @psalm-assert array|ArrayAccess $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function isArrayAccessible($value, $message = '') + public function getType() : string { - if (!\is_array($value) && !$value instanceof \ArrayAccess) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an array accessible. Got: %s', static::typeToString($value))); - } + return 'Expr_Error'; } +} +attributes = $attributes; + $this->var = $var; } - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function isIterable($value, $message = '') + public function getSubNodeNames() : array { - if (!\is_array($value) && !$value instanceof \Traversable) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an iterable. Got: %s', static::typeToString($value))); - } + return ['var']; + } + public function getType() : string + { + return 'Expr_PostDec'; } +} + $class - * @psalm-assert ExpectedType $value - * - * @param mixed $value - * @param string|object $class - * @param string $message + * Constructs an eval() node. * - * @throws InvalidArgumentException + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ - public static function isInstanceOf($value, $class, $message = '') + public function __construct(Expr $expr, array $attributes = []) { - if (!$value instanceof $class) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an instance of %2$s. Got: %s', static::typeToString($value), $class)); - } + $this->attributes = $attributes; + $this->expr = $expr; + } + public function getSubNodeNames() : array + { + return ['expr']; + } + public function getType() : string + { + return 'Expr_Eval'; } +} + Arguments */ + public $args; /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert !ExpectedType $value - * - * @param mixed $value - * @param string|object $class - * @param string $message + * Constructs a function call node. * - * @throws InvalidArgumentException + * @param Node\Name|Expr|Node\Stmt\Class_ $class Class name (or class node for anonymous classes) + * @param array $args Arguments + * @param array $attributes Additional attributes */ - public static function notInstanceOf($value, $class, $message = '') + public function __construct($class, array $args = [], array $attributes = []) { - if ($value instanceof $class) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an instance other than %2$s. Got: %s', static::typeToString($value), $class)); - } + $this->attributes = $attributes; + $this->class = $class; + $this->args = $args; } - /** - * @psalm-pure - * @psalm-param array $classes - * - * @param mixed $value - * @param array $classes - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function isInstanceOfAny($value, array $classes, $message = '') + public function getSubNodeNames() : array { - foreach ($classes as $class) { - if ($value instanceof $class) { - return; - } - } - static::reportInvalidArgument(\sprintf($message ?: 'Expected an instance of any of %2$s. Got: %s', static::typeToString($value), \implode(', ', \array_map(array('static', 'valueToString'), $classes)))); + return ['class', 'args']; } - /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert ExpectedType|class-string $value - * - * @param object|string $value - * @param string $class - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function isAOf($value, $class, $message = '') + public function getType() : string { - static::string($class, 'Expected class as a string. Got: %s'); - if (!\is_a($value, $class, \is_string($value))) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an instance of this class or to this class among his parents %2$s. Got: %s', static::typeToString($value), $class)); - } + return 'Expr_New'; } - /** - * @psalm-pure - * @psalm-template UnexpectedType of object - * @psalm-param class-string $class - * @psalm-assert !UnexpectedType $value - * @psalm-assert !class-string $value - * - * @param object|string $value - * @param string $class - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function isNotA($value, $class, $message = '') + public function getRawArgs() : array { - static::string($class, 'Expected class as a string. Got: %s'); - if (\is_a($value, $class, \is_string($value))) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an instance of this class or to this class among his parents other than %2$s. Got: %s', static::typeToString($value), $class)); - } + return $this->args; } +} + $classes - * - * @param object|string $value - * @param string[] $classes - * @param string $message - * - * @throws InvalidArgumentException + * @param array $subNodes Array of the following optional subnodes: + * 'static' => false : Whether the closure is static + * 'byRef' => false : Whether to return by reference + * 'params' => array() : Parameters + * 'returnType' => null : Return type + * 'expr' => Expr : Expression body + * 'attrGroups' => array() : PHP attribute groups + * @param array $attributes Additional attributes */ - public static function isAnyOf($value, array $classes, $message = '') + public function __construct(array $subNodes = [], array $attributes = []) { - foreach ($classes as $class) { - static::string($class, 'Expected class as a string. Got: %s'); - if (\is_a($value, $class, \is_string($value))) { - return; - } - } - static::reportInvalidArgument(\sprintf($message ?: 'Expected an any of instance of this class or to this class among his parents other than %2$s. Got: %s', static::typeToString($value), \implode(', ', \array_map(array('static', 'valueToString'), $classes)))); + $this->attributes = $attributes; + $this->static = $subNodes['static'] ?? \false; + $this->byRef = $subNodes['byRef'] ?? \false; + $this->params = $subNodes['params'] ?? []; + $returnType = $subNodes['returnType'] ?? null; + $this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType; + $this->expr = $subNodes['expr'] ?? null; + $this->attrGroups = $subNodes['attrGroups'] ?? []; } - /** - * @psalm-pure - * @psalm-assert empty $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function isEmpty($value, $message = '') + public function getSubNodeNames() : array { - if (!empty($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an empty value. Got: %s', static::valueToString($value))); - } + return ['attrGroups', 'static', 'byRef', 'params', 'returnType', 'expr']; } - /** - * @psalm-pure - * @psalm-assert !empty $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function notEmpty($value, $message = '') + public function returnsByRef() : bool { - if (empty($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a non-empty value. Got: %s', static::valueToString($value))); - } + return $this->byRef; } - /** - * @psalm-pure - * @psalm-assert null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function null($value, $message = '') + public function getParams() : array { - if (null !== $value) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected null. Got: %s', static::valueToString($value))); - } + return $this->params; } - /** - * @psalm-pure - * @psalm-assert !null $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function notNull($value, $message = '') + public function getReturnType() { - if (null === $value) { - static::reportInvalidArgument($message ?: 'Expected a value other than null.'); - } + return $this->returnType; } - /** - * @psalm-pure - * @psalm-assert true $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function true($value, $message = '') + public function getAttrGroups() : array { - if (\true !== $value) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to be true. Got: %s', static::valueToString($value))); - } + return $this->attrGroups; } /** - * @psalm-pure - * @psalm-assert false $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @return Node\Stmt\Return_[] */ - public static function false($value, $message = '') + public function getStmts() : array { - if (\false !== $value) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to be false. Got: %s', static::valueToString($value))); - } + return [new Node\Stmt\Return_($this->expr)]; } - /** - * @psalm-pure - * @psalm-assert !false $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function notFalse($value, $message = '') + public function getType() : string { - if (\false === $value) { - static::reportInvalidArgument($message ?: 'Expected a value other than false.'); - } + return 'Expr_ArrowFunction'; } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function ip($value, $message = '') +} += $limit) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value less than %2$s. Got: %s', static::valueToString($value), static::valueToString($limit))); - } + return 'Expr_BinaryOp_Identical'; } - /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function lessThanEq($value, $limit, $message = '') +} + $limit) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value less than or equal to %2$s. Got: %s', static::valueToString($value), static::valueToString($limit))); - } + return '|'; } - /** - * Inclusive range, so Assert::(3, 3, 5) passes. - * - * @psalm-pure - * - * @param mixed $value - * @param mixed $min - * @param mixed $max - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function range($value, $min, $max, $message = '') + public function getType() : string { - if ($value < $min || $value > $max) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value between %2$s and %3$s. Got: %s', static::valueToString($value), static::valueToString($min), static::valueToString($max))); - } + return 'Expr_BinaryOp_BitwiseOr'; } - /** - * A more human-readable alias of Assert::inArray(). - * - * @psalm-pure - * - * @param mixed $value - * @param array $values - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function oneOf($value, array $values, $message = '') +} +'; } - /** - * @psalm-pure - * - * @param string $value - * @param string $subString - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function notContains($value, $subString, $message = '') + public function getType() : string { - if (\false !== \strpos($value, $subString)) { - static::reportInvalidArgument(\sprintf($message ?: '%2$s was not expected to be contained in a value. Got: %s', static::valueToString($value), static::valueToString($subString))); - } + return 'Expr_BinaryOp_Spaceship'; } - /** - * @psalm-pure - * - * @param string $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function notWhitespaceOnly($value, $message = '') +} +'; } - /** - * Inclusive min. - * - * @psalm-pure - * - * @param string $value - * @param int|float $min - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function minLength($value, $min, $message = '') + public function getType() : string { - if (static::strlen($value) < $min) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain at least %2$s characters. Got: %s', static::valueToString($value), $min)); - } + return 'Expr_BinaryOp_Greater'; } - /** - * Inclusive max. - * - * @psalm-pure - * - * @param string $value - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function maxLength($value, $max, $message = '') +} + $max) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain at most %2$s characters. Got: %s', static::valueToString($value), $max)); - } + return '/'; } - /** - * Inclusive , so Assert::lengthBetween('asd', 3, 5); passes the assertion. - * - * @psalm-pure - * - * @param string $value - * @param int|float $min - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function lengthBetween($value, $min, $max, $message = '') + public function getType() : string { - $length = static::strlen($value); - if ($length < $min || $length > $max) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s', static::valueToString($value), $min, $max)); - } + return 'Expr_BinaryOp_Div'; } - /** - * Will also pass if $value is a directory, use Assert::file() instead if you need to be sure it is a file. - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function fileExists($value, $message = '') +} +>'; } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function file($value, $message = '') + public function getType() : string { - static::fileExists($value, $message); - if (!\is_file($value)) { - static::reportInvalidArgument(\sprintf($message ?: 'The path %s is not a file.', static::valueToString($value))); - } + return 'Expr_BinaryOp_ShiftRight'; } - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function directory($value, $message = '') +} + $class - * @psalm-assert class-string|ExpectedType $value - * - * @param mixed $value - * @param string|object $class - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function subclassOf($value, $class, $message = '') +} + $interface - * @psalm-assert class-string $value - * - * @param mixed $value - * @param mixed $interface - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function implementsInterface($value, $interface, $message = '') +} +='; } - /** - * Checks if a value is a valid array key (int or string). - * - * @psalm-pure - * @psalm-assert array-key $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function validArrayKey($value, $message = '') + public function getType() : string { - if (!(\is_int($value) || \is_string($value))) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected string or integer. Got: %s', static::typeToString($value))); - } + return 'Expr_BinaryOp_GreaterOrEqual'; } - /** - * Does not check if $array is countable, this can generate a warning on php versions after 7.2. - * - * @param Countable|array $array - * @param int $number - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function count($array, $number, $message = '') +} + $max) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an array to contain at most %2$d elements. Got: %d', \count($array), $max)); - } + $this->attributes = $attributes; + $this->var = $var; } - /** - * Does not check if $array is countable, this can generate a warning on php versions after 7.2. - * - * @param Countable|array $array - * @param int|float $min - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function countBetween($array, $min, $max, $message = '') + public function getSubNodeNames() : array { - $count = \count($array); - if ($count < $min || $count > $max) { - static::reportInvalidArgument(\sprintf($message ?: 'Expected an array to contain between %2$d and %3$d elements. Got: %d', $count, $min, $max)); - } + return ['var']; + } + public function getType() : string + { + return 'Expr_PostInc'; } +} +attributes = $attributes; + $this->cond = $cond; + $this->if = $if; + $this->else = $else; + } + public function getSubNodeNames() : array + { + return ['cond', 'if', 'else']; + } + public function getType() : string + { + return 'Expr_Ternary'; } +} +attributes = $attributes; + $this->var = $var; + $this->name = \is_string($name) ? new Identifier($name) : $name; + $this->args = $args; + } + public function getSubNodeNames() : array + { + return ['var', 'name', 'args']; + } + public function getType() : string + { + return 'Expr_NullsafeMethodCall'; } +} + $array - * @psalm-assert array $array - * - * @param mixed $array - * @param string $message + * Constructs a function call node. * - * @throws InvalidArgumentException + * @param Expr $var Variable holding object + * @param string|Identifier|Expr $name Property name + * @param array $attributes Additional attributes */ - public static function isMap($array, $message = '') + public function __construct(Expr $var, $name, array $attributes = []) { - if (!\is_array($array) || \array_keys($array) !== \array_filter(\array_keys($array), '\\is_string')) { - static::reportInvalidArgument($message ?: 'Expected map - associative array with string keys.'); - } + $this->attributes = $attributes; + $this->var = $var; + $this->name = \is_string($name) ? new Identifier($name) : $name; + } + public function getSubNodeNames() : array + { + return ['var', 'name']; + } + public function getType() : string + { + return 'Expr_PropertyFetch'; } +} + $array - * @psalm-assert array $array - * @psalm-assert !empty $array - * - * @param mixed $array - * @param string $message + * Constructs an instanceof check node. * - * @throws InvalidArgumentException + * @param Expr $expr Expression + * @param Name|Expr $class Class name + * @param array $attributes Additional attributes */ - public static function isNonEmptyMap($array, $message = '') + public function __construct(Expr $expr, $class, array $attributes = []) { - static::isMap($array, $message); - static::notEmpty($array, $message); + $this->attributes = $attributes; + $this->expr = $expr; + $this->class = $class; + } + public function getSubNodeNames() : array + { + return ['expr', 'class']; + } + public function getType() : string + { + return 'Expr_Instanceof'; } +} +attributes = $attributes; + $this->expr = $expr; + } + public function getSubNodeNames() : array + { + return ['expr']; + } + public function getType() : string + { + return 'Expr_Empty'; } +} + $class - * - * @param Closure $expression - * @param string $class - * @param string $message + * Constructs an array index fetch node. * - * @throws InvalidArgumentException + * @param Expr $var Variable + * @param null|Expr $dim Array index / dim + * @param array $attributes Additional attributes */ - public static function throws(\Closure $expression, $class = 'Exception', $message = '') + public function __construct(Expr $var, Expr $dim = null, array $attributes = []) { - static::string($class); - $actual = 'none'; - try { - $expression(); - } catch (\Exception $e) { - $actual = \get_class($e); - if ($e instanceof $class) { - return; - } - } catch (\Throwable $e) { - $actual = \get_class($e); - if ($e instanceof $class) { - return; - } - } - static::reportInvalidArgument($message ?: \sprintf('Expected to throw "%s", got "%s"', $class, $actual)); + $this->attributes = $attributes; + $this->var = $var; + $this->dim = $dim; } - /** - * @throws BadMethodCallException - */ - public static function __callStatic($name, $arguments) + public function getSubNodeNames() : array { - if ('nullOr' === \substr($name, 0, 6)) { - if (null !== $arguments[0]) { - $method = \lcfirst(\substr($name, 6)); - \call_user_func_array(array('static', $method), $arguments); - } - return; - } - if ('all' === \substr($name, 0, 3)) { - static::isIterable($arguments[0]); - $method = \lcfirst(\substr($name, 3)); - $args = $arguments; - foreach ($arguments[0] as $entry) { - $args[0] = $entry; - \call_user_func_array(array('static', $method), $args); - } - return; - } - throw new \BadMethodCallException('No such method: ' . $name); + return ['var', 'dim']; } + public function getType() : string + { + return 'Expr_ArrayDimFetch'; + } +} +__toString()); - } - if ($value instanceof \DateTime || $value instanceof \DateTimeImmutable) { - return \get_class($value) . ': ' . self::valueToString($value->format('c')); - } - return \get_class($value); - } - if (\is_resource($value)) { - return 'resource'; - } - if (\is_string($value)) { - return '"' . $value . '"'; - } - return (string) $value; + $this->attributes = $attributes; + $this->class = $class; + $this->name = \is_string($name) ? new Identifier($name) : $name; + } + public function getSubNodeNames() : array + { + return ['class', 'name']; + } + public function getType() : string + { + return 'Expr_ClassConstFetch'; } +} +attributes = $attributes; + $this->name = $name; } - protected static function strlen($value) + public function getSubNodeNames() : array { - if (!\function_exists('mb_detect_encoding')) { - return \strlen($value); - } - if (\false === ($encoding = \mb_detect_encoding($value))) { - return \strlen($value); - } - return \mb_strlen($value, $encoding); + return ['name']; + } + public function getType() : string + { + return 'Expr_ConstFetch'; } +} + Arguments */ + public $args; /** - * @param string $message - * - * @throws InvalidArgumentException + * Constructs a static method call node. * - * @psalm-pure this method is not supposed to perform side-effects + * @param Node\Name|Expr $class Class name + * @param string|Identifier|Expr $name Method name + * @param array $args Arguments + * @param array $attributes Additional attributes */ - protected static function reportInvalidArgument($message) + public function __construct($class, $name, array $args = [], array $attributes = []) { - throw new \InvalidArgumentException($message); + $this->attributes = $attributes; + $this->class = $class; + $this->name = \is_string($name) ? new Identifier($name) : $name; + $this->args = $args; } - private function __construct() + public function getSubNodeNames() : array + { + return ['class', 'name', 'args']; + } + public function getType() : string + { + return 'Expr_StaticCall'; + } + public function getRawArgs() : array { + return $this->args; } } Arguments */ + public $args; /** - * @psalm-pure - * @psalm-assert null|string $value - * - * @param mixed $value - * @param string $message + * Constructs a function call node. * - * @throws InvalidArgumentException + * @param Node\Name|Expr $name Function name + * @param array $args Arguments + * @param array $attributes Additional attributes */ - public static function nullOrString($value, $message = ''); + public function __construct($name, array $args = [], array $attributes = []) + { + $this->attributes = $attributes; + $this->name = $name; + $this->args = $args; + } + public function getSubNodeNames() : array + { + return ['name', 'args']; + } + public function getType() : string + { + return 'Expr_FuncCall'; + } + public function getRawArgs() : array + { + return $this->args; + } +} + $value - * - * @param mixed $value - * @param string $message + * Constructs a list() destructuring node. * - * @throws InvalidArgumentException + * @param (ArrayItem|null)[] $items List of items to assign to + * @param array $attributes Additional attributes */ - public static function allString($value, $message = ''); + public function __construct(array $items, array $attributes = []) + { + $this->attributes = $attributes; + $this->items = $items; + } + public function getSubNodeNames() : array + { + return ['items']; + } + public function getType() : string + { + return 'Expr_List'; + } +} +attributes = $attributes; + $this->vars = $vars; + } + public function getSubNodeNames() : array + { + return ['vars']; + } + public function getType() : string + { + return 'Expr_Isset'; + } +} + $value - * - * @param mixed $value - * @param string $message + * Constructs an error suppress node. * - * @throws InvalidArgumentException + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ - public static function allStringNotEmpty($value, $message = ''); + public function __construct(Expr $expr, array $attributes = []) + { + $this->attributes = $attributes; + $this->expr = $expr; + } + public function getSubNodeNames() : array + { + return ['expr']; + } + public function getType() : string + { + return 'Expr_ErrorSuppress'; + } +} +attributes = $attributes; + $this->expr = $expr; + } + public function getSubNodeNames() : array + { + return ['expr']; + } + public function getType() : string + { + return 'Expr_BooleanNot'; + } +} + $value - * - * @param mixed $value - * @param string $message + * Constructs an array node. * - * @throws InvalidArgumentException + * @param (ArrayItem|null)[] $items Items of the array + * @param array $attributes Additional attributes */ - public static function allInteger($value, $message = ''); - /** - * @psalm-pure - * @psalm-assert null|numeric $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrIntegerish($value, $message = ''); + public function __construct(array $items = [], array $attributes = []) + { + $this->attributes = $attributes; + $this->items = $items; + } + public function getSubNodeNames() : array + { + return ['items']; + } + public function getType() : string + { + return 'Expr_Array'; + } +} + $value - * - * @param mixed $value - * @param string $message + * Constructs a unary minus node. * - * @throws InvalidArgumentException + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ - public static function allIntegerish($value, $message = ''); + public function __construct(Expr $expr, array $attributes = []) + { + $this->attributes = $attributes; + $this->expr = $expr; + } + public function getSubNodeNames() : array + { + return ['expr']; + } + public function getType() : string + { + return 'Expr_UnaryMinus'; + } +} + false : Whether the closure is static + * 'byRef' => false : Whether to return by reference + * 'params' => array(): Parameters + * 'uses' => array(): use()s + * 'returnType' => null : Return type + * 'stmts' => array(): Statements + * 'attrGroups' => array(): PHP attributes groups + * @param array $attributes Additional attributes */ - public static function nullOrFloat($value, $message = ''); + public function __construct(array $subNodes = [], array $attributes = []) + { + $this->attributes = $attributes; + $this->static = $subNodes['static'] ?? \false; + $this->byRef = $subNodes['byRef'] ?? \false; + $this->params = $subNodes['params'] ?? []; + $this->uses = $subNodes['uses'] ?? []; + $returnType = $subNodes['returnType'] ?? null; + $this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType; + $this->stmts = $subNodes['stmts'] ?? []; + $this->attrGroups = $subNodes['attrGroups'] ?? []; + } + public function getSubNodeNames() : array + { + return ['attrGroups', 'static', 'byRef', 'params', 'uses', 'returnType', 'stmts']; + } + public function returnsByRef() : bool + { + return $this->byRef; + } + public function getParams() : array + { + return $this->params; + } + public function getReturnType() + { + return $this->returnType; + } + /** @return Node\Stmt[] */ + public function getStmts() : array + { + return $this->stmts; + } + public function getAttrGroups() : array + { + return $this->attrGroups; + } + public function getType() : string + { + return 'Expr_Closure'; + } +} + $value - * - * @param mixed $value - * @param string $message + * Constructs a pre increment node. * - * @throws InvalidArgumentException + * @param Expr $var Variable + * @param array $attributes Additional attributes */ - public static function allFloat($value, $message = ''); + public function __construct(Expr $var, array $attributes = []) + { + $this->attributes = $attributes; + $this->var = $var; + } + public function getSubNodeNames() : array + { + return ['var']; + } + public function getType() : string + { + return 'Expr_PreInc'; + } +} +attributes = $attributes; + $this->var = $var; + $this->expr = $expr; + } + public function getSubNodeNames() : array + { + return ['var', 'expr']; + } + public function getType() : string + { + return 'Expr_Assign'; + } +} + $value - * - * @param mixed $value - * @param string $message + * Constructs a binary operator node. * - * @throws InvalidArgumentException + * @param Expr $left The left hand side expression + * @param Expr $right The right hand side expression + * @param array $attributes Additional attributes */ - public static function allNumeric($value, $message = ''); + public function __construct(Expr $left, Expr $right, array $attributes = []) + { + $this->attributes = $attributes; + $this->left = $left; + $this->right = $right; + } + public function getSubNodeNames() : array + { + return ['left', 'right']; + } /** - * @psalm-pure - * @psalm-assert null|int $value + * Get the operator sigil for this binary operation. * - * @param mixed $value - * @param string $message + * In the case there are multiple possible sigils for an operator, this method does not + * necessarily return the one used in the parsed code. * - * @throws InvalidArgumentException + * @return string */ - public static function nullOrNatural($value, $message = ''); + public abstract function getOperatorSigil() : string; +} + $value - * - * @param mixed $value - * @param string $message + * Constructs an "yield from" node. * - * @throws InvalidArgumentException + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ - public static function allNatural($value, $message = ''); + public function __construct(Expr $expr, array $attributes = []) + { + $this->attributes = $attributes; + $this->expr = $expr; + } + public function getSubNodeNames() : array + { + return ['expr']; + } + public function getType() : string + { + return 'Expr_YieldFrom'; + } +} +attributes = $attributes; + $this->class = $class; + $this->name = \is_string($name) ? new VarLikeIdentifier($name) : $name; + } + public function getSubNodeNames() : array + { + return ['class', 'name']; + } + public function getType() : string + { + return 'Expr_StaticPropertyFetch'; + } +} + $value - * - * @param mixed $value - * @param string $message + * Constructs an assignment node. * - * @throws InvalidArgumentException + * @param Expr $var Variable + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ - public static function allBoolean($value, $message = ''); + public function __construct(Expr $var, Expr $expr, array $attributes = []) + { + $this->attributes = $attributes; + $this->var = $var; + $this->expr = $expr; + } + public function getSubNodeNames() : array + { + return ['var', 'expr']; + } + public function getType() : string + { + return 'Expr_AssignRef'; + } +} + Arguments */ + public $args; /** - * @psalm-pure - * @psalm-assert null|scalar $value - * - * @param mixed $value - * @param string $message + * Constructs a function call node. * - * @throws InvalidArgumentException + * @param Expr $var Variable holding object + * @param string|Identifier|Expr $name Method name + * @param array $args Arguments + * @param array $attributes Additional attributes */ - public static function nullOrScalar($value, $message = ''); + public function __construct(Expr $var, $name, array $args = [], array $attributes = []) + { + $this->attributes = $attributes; + $this->var = $var; + $this->name = \is_string($name) ? new Identifier($name) : $name; + $this->args = $args; + } + public function getSubNodeNames() : array + { + return ['var', 'name', 'args']; + } + public function getType() : string + { + return 'Expr_MethodCall'; + } + public function getRawArgs() : array + { + return $this->args; + } +} + $value - * - * @param mixed $value - * @param string $message + * Constructs a bitwise not node. * - * @throws InvalidArgumentException + * @param Expr $expr Expression + * @param array $attributes Additional attributes */ - public static function allScalar($value, $message = ''); + public function __construct(Expr $expr, array $attributes = []) + { + $this->attributes = $attributes; + $this->expr = $expr; + } + public function getSubNodeNames() : array + { + return ['expr']; + } + public function getType() : string + { + return 'Expr_BitwiseNot'; + } +} + $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allObject($value, $message = ''); - /** - * @psalm-pure - * @psalm-assert null|resource $value - * - * @param mixed $value - * @param string|null $type type of resource this should be. @see https://www.php.net/manual/en/function.get-resource-type.php - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrResource($value, $type = null, $message = ''); - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string|null $type type of resource this should be. @see https://www.php.net/manual/en/function.get-resource-type.php - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allResource($value, $type = null, $message = ''); - /** - * @psalm-pure - * @psalm-assert null|callable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrIsCallable($value, $message = ''); - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allIsCallable($value, $message = ''); - /** - * @psalm-pure - * @psalm-assert null|array $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrIsArray($value, $message = ''); - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allIsArray($value, $message = ''); - /** - * @psalm-pure - * @psalm-assert null|iterable $value - * - * @deprecated use "isIterable" or "isInstanceOf" instead - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrIsTraversable($value, $message = ''); - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @deprecated use "isIterable" or "isInstanceOf" instead - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allIsTraversable($value, $message = ''); - /** - * @psalm-pure - * @psalm-assert null|array|ArrayAccess $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrIsArrayAccessible($value, $message = ''); - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @param Expr\Variable $var Variable to use + * @param bool $byRef Whether to use by reference + * @param array $attributes Additional attributes */ - public static function allIsArrayAccessible($value, $message = ''); + public function __construct(Expr\Variable $var, bool $byRef = \false, array $attributes = []) + { + $this->attributes = $attributes; + $this->var = $var; + $this->byRef = $byRef; + } + public function getSubNodeNames() : array + { + return ['var', 'byRef']; + } + public function getType() : string + { + return 'Expr_ClosureUse'; + } +} +attributes = $attributes; + $this->parts = $parts; + } + public function getSubNodeNames() : array + { + return ['parts']; + } + public function getType() : string + { + return 'Expr_ShellExec'; + } +} + $value - * - * @param mixed $value - * @param string $message + * Constructs an exit() node. * - * @throws InvalidArgumentException + * @param null|Expr $expr Expression + * @param array $attributes Additional attributes */ - public static function allIsCountable($value, $message = ''); + public function __construct(Expr $expr = null, array $attributes = []) + { + $this->attributes = $attributes; + $this->expr = $expr; + } + public function getSubNodeNames() : array + { + return ['expr']; + } + public function getType() : string + { + return 'Expr_Exit'; + } +} +attributes = $attributes; + $this->expr = $expr; + $this->type = $type; + } + public function getSubNodeNames() : array + { + return ['expr', 'type']; + } + public function getType() : string + { + return 'Expr_Include'; + } +} + $value - * - * @param mixed $value - * @param string $message + * Constructs a nullable type (wrapping another type). * - * @throws InvalidArgumentException + * @param string|Identifier|Name $type Type + * @param array $attributes Additional attributes */ - public static function allIsIterable($value, $message = ''); + public function __construct($type, array $attributes = []) + { + $this->attributes = $attributes; + $this->type = \is_string($type) ? new Identifier($type) : $type; + } + public function getSubNodeNames() : array + { + return ['type']; + } + public function getType() : string + { + return 'NullableType'; + } +} + $class - * @psalm-assert null|ExpectedType $value - * - * @param mixed $value - * @param string|object $class - * @param string $message + * Constructs a magic constant node. * - * @throws InvalidArgumentException + * @param array $attributes Additional attributes */ - public static function nullOrIsInstanceOf($value, $class, $message = ''); + public function __construct(array $attributes = []) + { + $this->attributes = $attributes; + } + public function getSubNodeNames() : array + { + return []; + } /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string|object $class - * @param string $message + * Get name of magic constant. * - * @throws InvalidArgumentException + * @return string Name of magic constant */ - public static function allIsInstanceOf($value, $class, $message = ''); + public abstract function getName() : string; +} + $class - * - * @param mixed $value - * @param string|object $class - * @param string $message + * Constructs a node representing a string part of an encapsed string. * - * @throws InvalidArgumentException + * @param string $value String value + * @param array $attributes Additional attributes */ - public static function nullOrNotInstanceOf($value, $class, $message = ''); + public function __construct(string $value, array $attributes = []) + { + $this->attributes = $attributes; + $this->value = $value; + } + public function getSubNodeNames() : array + { + return ['value']; + } + public function getType() : string + { + return 'Scalar_EncapsedStringPart'; + } +} + $class - * - * @param mixed $value - * @param string|object $class - * @param string $message + * Constructs an integer number scalar node. * - * @throws InvalidArgumentException + * @param int $value Value of the number + * @param array $attributes Additional attributes */ - public static function allNotInstanceOf($value, $class, $message = ''); + public function __construct(int $value, array $attributes = []) + { + $this->attributes = $attributes; + $this->value = $value; + } + public function getSubNodeNames() : array + { + return ['value']; + } /** - * @psalm-pure - * @psalm-param array $classes + * Constructs an LNumber node from a string number literal. * - * @param mixed $value - * @param array $classes - * @param string $message + * @param string $str String number literal (decimal, octal, hex or binary) + * @param array $attributes Additional attributes + * @param bool $allowInvalidOctal Whether to allow invalid octal numbers (PHP 5) * - * @throws InvalidArgumentException + * @return LNumber The constructed LNumber, including kind attribute */ - public static function nullOrIsInstanceOfAny($value, $classes, $message = ''); + public static function fromString(string $str, array $attributes = [], bool $allowInvalidOctal = \false) : LNumber + { + $str = \str_replace('_', '', $str); + if ('0' !== $str[0] || '0' === $str) { + $attributes['kind'] = LNumber::KIND_DEC; + return new LNumber((int) $str, $attributes); + } + if ('x' === $str[1] || 'X' === $str[1]) { + $attributes['kind'] = LNumber::KIND_HEX; + return new LNumber(\hexdec($str), $attributes); + } + if ('b' === $str[1] || 'B' === $str[1]) { + $attributes['kind'] = LNumber::KIND_BIN; + return new LNumber(\bindec($str), $attributes); + } + if (!$allowInvalidOctal && \strpbrk($str, '89')) { + throw new Error('Invalid numeric literal', $attributes); + } + // Strip optional explicit octal prefix. + if ('o' === $str[1] || 'O' === $str[1]) { + $str = \substr($str, 2); + } + // use intval instead of octdec to get proper cutting behavior with malformed numbers + $attributes['kind'] = LNumber::KIND_OCT; + return new LNumber(\intval($str, 8), $attributes); + } + public function getType() : string + { + return 'Scalar_LNumber'; + } +} + '\\', '$' => '$', 'n' => "\n", 'r' => "\r", 't' => "\t", 'f' => "\f", 'v' => "\v", 'e' => "\33"]; /** - * @psalm-pure - * @psalm-param array $classes - * - * @param mixed $value - * @param array $classes - * @param string $message + * Constructs a string scalar node. * - * @throws InvalidArgumentException + * @param string $value Value of the string + * @param array $attributes Additional attributes */ - public static function allIsInstanceOfAny($value, $classes, $message = ''); + public function __construct(string $value, array $attributes = []) + { + $this->attributes = $attributes; + $this->value = $value; + } + public function getSubNodeNames() : array + { + return ['value']; + } /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert null|ExpectedType|class-string $value - * - * @param null|object|string $value - * @param string $class - * @param string $message + * @internal * - * @throws InvalidArgumentException - */ - public static function nullOrIsAOf($value, $class, $message = ''); - /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert iterable> $value + * Parses a string token. * - * @param iterable $value - * @param string $class - * @param string $message + * @param string $str String token content + * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes * - * @throws InvalidArgumentException + * @return string The parsed string */ - public static function allIsAOf($value, $class, $message = ''); + public static function parse(string $str, bool $parseUnicodeEscape = \true) : string + { + $bLength = 0; + if ('b' === $str[0] || 'B' === $str[0]) { + $bLength = 1; + } + if ('\'' === $str[$bLength]) { + return \str_replace(['\\\\', '\\\''], ['\\', '\''], \substr($str, $bLength + 1, -1)); + } else { + return self::parseEscapeSequences(\substr($str, $bLength + 1, -1), '"', $parseUnicodeEscape); + } + } /** - * @psalm-pure - * @psalm-template UnexpectedType of object - * @psalm-param class-string $class - * - * @param null|object|string $value - * @param string $class - * @param string $message + * @internal * - * @throws InvalidArgumentException - */ - public static function nullOrIsNotA($value, $class, $message = ''); - /** - * @psalm-pure - * @psalm-template UnexpectedType of object - * @psalm-param class-string $class + * Parses escape sequences in strings (all string types apart from single quoted). * - * @param iterable $value - * @param string $class - * @param string $message + * @param string $str String without quotes + * @param null|string $quote Quote type + * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes * - * @throws InvalidArgumentException + * @return string String with escape sequences parsed */ - public static function allIsNotA($value, $class, $message = ''); + public static function parseEscapeSequences(string $str, $quote, bool $parseUnicodeEscape = \true) : string + { + if (null !== $quote) { + $str = \str_replace('\\' . $quote, $quote, $str); + } + $extra = ''; + if ($parseUnicodeEscape) { + $extra = '|u\\{([0-9a-fA-F]+)\\}'; + } + return \preg_replace_callback('~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}' . $extra . ')~', function ($matches) { + $str = $matches[1]; + if (isset(self::$replacements[$str])) { + return self::$replacements[$str]; + } elseif ('x' === $str[0] || 'X' === $str[0]) { + return \chr(\hexdec(\substr($str, 1))); + } elseif ('u' === $str[0]) { + return self::codePointToUtf8(\hexdec($matches[2])); + } else { + return \chr(\octdec($str)); + } + }, $str); + } /** - * @psalm-pure - * @psalm-param array $classes + * Converts a Unicode code point to its UTF-8 encoded representation. * - * @param null|object|string $value - * @param string[] $classes - * @param string $message + * @param int $num Code point * - * @throws InvalidArgumentException + * @return string UTF-8 representation of code point */ - public static function nullOrIsAnyOf($value, $classes, $message = ''); - /** - * @psalm-pure - * @psalm-param array $classes - * - * @param iterable $value - * @param string[] $classes - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allIsAnyOf($value, $classes, $message = ''); + private static function codePointToUtf8(int $num) : string + { + if ($num <= 0x7f) { + return \chr($num); + } + if ($num <= 0x7ff) { + return \chr(($num >> 6) + 0xc0) . \chr(($num & 0x3f) + 0x80); + } + if ($num <= 0xffff) { + return \chr(($num >> 12) + 0xe0) . \chr(($num >> 6 & 0x3f) + 0x80) . \chr(($num & 0x3f) + 0x80); + } + if ($num <= 0x1fffff) { + return \chr(($num >> 18) + 0xf0) . \chr(($num >> 12 & 0x3f) + 0x80) . \chr(($num >> 6 & 0x3f) + 0x80) . \chr(($num & 0x3f) + 0x80); + } + throw new Error('Invalid UTF-8 codepoint escape sequence: Codepoint too large'); + } + public function getType() : string + { + return 'Scalar_String'; + } +} +attributes = $attributes; + $this->parts = $parts; + } + public function getSubNodeNames() : array + { + return ['parts']; + } + public function getType() : string + { + return 'Scalar_Encapsed'; + } +} + $value - * - * @param mixed $value - * @param string $message + * Constructs a float number scalar node. * - * @throws InvalidArgumentException + * @param float $value Value of the number + * @param array $attributes Additional attributes */ - public static function allIsEmpty($value, $message = ''); + public function __construct(float $value, array $attributes = []) + { + $this->attributes = $attributes; + $this->value = $value; + } + public function getSubNodeNames() : array + { + return ['value']; + } /** - * @psalm-pure - * - * @param mixed $value - * @param string $message + * @internal * - * @throws InvalidArgumentException - */ - public static function nullOrNotEmpty($value, $message = ''); - /** - * @psalm-pure + * Parses a DNUMBER token like PHP would. * - * @param mixed $value - * @param string $message + * @param string $str A string number * - * @throws InvalidArgumentException + * @return float The parsed number */ - public static function allNotEmpty($value, $message = ''); + public static function parse(string $str) : float + { + $str = \str_replace('_', '', $str); + // if string contains any of .eE just cast it to float + if (\false !== \strpbrk($str, '.eE')) { + return (float) $str; + } + // otherwise it's an integer notation that overflowed into a float + // if it starts with 0 it's one of the special integer notations + if ('0' === $str[0]) { + // hex + if ('x' === $str[1] || 'X' === $str[1]) { + return \hexdec($str); + } + // bin + if ('b' === $str[1] || 'B' === $str[1]) { + return \bindec($str); + } + // oct + // substr($str, 0, strcspn($str, '89')) cuts the string at the first invalid digit (8 or 9) + // so that only the digits before that are used + return \octdec(\substr($str, 0, \strcspn($str, '89'))); + } + // dec + return (float) $str; + } + public function getType() : string + { + return 'Scalar_DNumber'; + } +} + $value + * Create a variadic argument placeholder (first-class callable syntax). * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException + * @param array $attributes Additional attributes */ - public static function allNull($value, $message = ''); + public function __construct(array $attributes = []) + { + $this->attributes = $attributes; + } + public function getType() : string + { + return 'VariadicPlaceholder'; + } + public function getSubNodeNames() : array + { + return []; + } +} + \true, 'parent' => \true, 'static' => \true]; /** - * @psalm-pure - * - * @param mixed $value - * @param string $message + * Constructs a name node. * - * @throws InvalidArgumentException + * @param string|string[]|self $name Name as string, part array or Name instance (copy ctor) + * @param array $attributes Additional attributes */ - public static function allNotNull($value, $message = ''); + public function __construct($name, array $attributes = []) + { + $this->attributes = $attributes; + $this->parts = self::prepareName($name); + } + public function getSubNodeNames() : array + { + return ['parts']; + } /** - * @psalm-pure - * @psalm-assert null|true $value - * - * @param mixed $value - * @param string $message + * Gets the first part of the name, i.e. everything before the first namespace separator. * - * @throws InvalidArgumentException + * @return string First part of the name */ - public static function nullOrTrue($value, $message = ''); + public function getFirst() : string + { + return $this->parts[0]; + } /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message + * Gets the last part of the name, i.e. everything after the last namespace separator. * - * @throws InvalidArgumentException + * @return string Last part of the name */ - public static function allTrue($value, $message = ''); + public function getLast() : string + { + return $this->parts[\count($this->parts) - 1]; + } /** - * @psalm-pure - * @psalm-assert null|false $value - * - * @param mixed $value - * @param string $message + * Checks whether the name is unqualified. (E.g. Name) * - * @throws InvalidArgumentException + * @return bool Whether the name is unqualified */ - public static function nullOrFalse($value, $message = ''); + public function isUnqualified() : bool + { + return 1 === \count($this->parts); + } /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message + * Checks whether the name is qualified. (E.g. Name\Name) * - * @throws InvalidArgumentException + * @return bool Whether the name is qualified */ - public static function allFalse($value, $message = ''); + public function isQualified() : bool + { + return 1 < \count($this->parts); + } /** - * @psalm-pure - * - * @param mixed $value - * @param string $message + * Checks whether the name is fully qualified. (E.g. \Name) * - * @throws InvalidArgumentException + * @return bool Whether the name is fully qualified */ - public static function nullOrNotFalse($value, $message = ''); + public function isFullyQualified() : bool + { + return \false; + } /** - * @psalm-pure - * - * @param mixed $value - * @param string $message + * Checks whether the name is explicitly relative to the current namespace. (E.g. namespace\Name) * - * @throws InvalidArgumentException + * @return bool Whether the name is relative */ - public static function allNotFalse($value, $message = ''); + public function isRelative() : bool + { + return \false; + } /** - * @param mixed $value - * @param string $message + * Returns a string representation of the name itself, without taking the name type into + * account (e.g., not including a leading backslash for fully qualified names). * - * @throws InvalidArgumentException + * @return string String representation */ - public static function nullOrIp($value, $message = ''); + public function toString() : string + { + return \implode('\\', $this->parts); + } /** - * @param mixed $value - * @param string $message + * Returns a string representation of the name as it would occur in code (e.g., including + * leading backslash for fully qualified names. * - * @throws InvalidArgumentException + * @return string String representation */ - public static function allIp($value, $message = ''); + public function toCodeString() : string + { + return $this->toString(); + } /** - * @param mixed $value - * @param string $message + * Returns lowercased string representation of the name, without taking the name type into + * account (e.g., no leading backslash for fully qualified names). * - * @throws InvalidArgumentException + * @return string Lowercased string representation */ - public static function nullOrIpv4($value, $message = ''); + public function toLowerString() : string + { + return \strtolower(\implode('\\', $this->parts)); + } /** - * @param mixed $value - * @param string $message + * Checks whether the identifier is a special class name (self, parent or static). * - * @throws InvalidArgumentException + * @return bool Whether identifier is a special class name */ - public static function allIpv4($value, $message = ''); + public function isSpecialClassName() : bool + { + return \count($this->parts) === 1 && isset(self::$specialClassNames[\strtolower($this->parts[0])]); + } /** - * @param mixed $value - * @param string $message + * Returns a string representation of the name by imploding the namespace parts with the + * namespace separator. * - * @throws InvalidArgumentException + * @return string String representation */ - public static function nullOrIpv6($value, $message = ''); + public function __toString() : string + { + return \implode('\\', $this->parts); + } /** - * @param mixed $value - * @param string $message + * Gets a slice of a name (similar to array_slice). * - * @throws InvalidArgumentException - */ - public static function allIpv6($value, $message = ''); - /** - * @param mixed $value - * @param string $message + * This method returns a new instance of the same type as the original and with the same + * attributes. * - * @throws InvalidArgumentException - */ - public static function nullOrEmail($value, $message = ''); - /** - * @param mixed $value - * @param string $message + * If the slice is empty, null is returned. The null value will be correctly handled in + * concatenations using concat(). * - * @throws InvalidArgumentException - */ - public static function allEmail($value, $message = ''); - /** - * @param null|array $values - * @param string $message + * Offset and length have the same meaning as in array_slice(). * - * @throws InvalidArgumentException - */ - public static function nullOrUniqueValues($values, $message = ''); - /** - * @param iterable $values - * @param string $message + * @param int $offset Offset to start the slice at (may be negative) + * @param int|null $length Length of the slice (may be negative) * - * @throws InvalidArgumentException + * @return static|null Sliced name */ - public static function allUniqueValues($values, $message = ''); + public function slice(int $offset, int $length = null) + { + $numParts = \count($this->parts); + $realOffset = $offset < 0 ? $offset + $numParts : $offset; + if ($realOffset < 0 || $realOffset > $numParts) { + throw new \OutOfBoundsException(\sprintf('Offset %d is out of bounds', $offset)); + } + if (null === $length) { + $realLength = $numParts - $realOffset; + } else { + $realLength = $length < 0 ? $length + $numParts - $realOffset : $length; + if ($realLength < 0 || $realLength > $numParts) { + throw new \OutOfBoundsException(\sprintf('Length %d is out of bounds', $length)); + } + } + if ($realLength === 0) { + // Empty slice is represented as null + return null; + } + return new static(\array_slice($this->parts, $realOffset, $realLength), $this->attributes); + } /** - * @param mixed $value - * @param mixed $expect - * @param string $message + * Concatenate two names, yielding a new Name instance. * - * @throws InvalidArgumentException - */ - public static function nullOrEq($value, $expect, $message = ''); - /** - * @param mixed $value - * @param mixed $expect - * @param string $message + * The type of the generated instance depends on which class this method is called on, for + * example Name\FullyQualified::concat() will yield a Name\FullyQualified instance. * - * @throws InvalidArgumentException - */ - public static function allEq($value, $expect, $message = ''); - /** - * @param mixed $value - * @param mixed $expect - * @param string $message + * If one of the arguments is null, a new instance of the other name will be returned. If both + * arguments are null, null will be returned. As such, writing + * Name::concat($namespace, $shortName) + * where $namespace is a Name node or null will work as expected. * - * @throws InvalidArgumentException - */ - public static function nullOrNotEq($value, $expect, $message = ''); - /** - * @param mixed $value - * @param mixed $expect - * @param string $message + * @param string|string[]|self|null $name1 The first name + * @param string|string[]|self|null $name2 The second name + * @param array $attributes Attributes to assign to concatenated name * - * @throws InvalidArgumentException + * @return static|null Concatenated name */ - public static function allNotEq($value, $expect, $message = ''); + public static function concat($name1, $name2, array $attributes = []) + { + if (null === $name1 && null === $name2) { + return null; + } elseif (null === $name1) { + return new static(self::prepareName($name2), $attributes); + } elseif (null === $name2) { + return new static(self::prepareName($name1), $attributes); + } else { + return new static(\array_merge(self::prepareName($name1), self::prepareName($name2)), $attributes); + } + } /** - * @psalm-pure + * Prepares a (string, array or Name node) name for use in name changing methods by converting + * it to an array. * - * @param mixed $value - * @param mixed $expect - * @param string $message + * @param string|string[]|self $name Name to prepare * - * @throws InvalidArgumentException + * @return string[] Prepared name */ - public static function nullOrSame($value, $expect, $message = ''); + private static function prepareName($name) : array + { + if (\is_string($name)) { + if ('' === $name) { + throw new \InvalidArgumentException('Name cannot be empty'); + } + return \explode('\\', $name); + } elseif (\is_array($name)) { + if (empty($name)) { + throw new \InvalidArgumentException('Name cannot be empty'); + } + return $name; + } elseif ($name instanceof self) { + return $name->parts; + } + throw new \InvalidArgumentException('Expected string, array of parts or Name instance'); + } + public function getType() : string + { + return 'Name'; + } +} +attributes = $attributes; + $this->attrs = $attrs; + } + public function getSubNodeNames() : array + { + return ['attrs']; + } + public function getType() : string + { + return 'AttributeGroup'; + } +} +attributes = $attributes; + $this->name = \is_string($name) ? new Identifier($name) : $name; + $this->value = $value; + } + public function getSubNodeNames() : array + { + return ['name', 'value']; + } + public function getType() : string + { + return 'Const'; + } +} +toString(); + } + public function getType() : string + { + return 'Name_Relative'; + } +} +toString(); + } + public function getType() : string + { + return 'Name_FullyQualified'; + } +} + \true, 'parent' => \true, 'static' => \true]; /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $limit - * @param string $message + * Constructs an identifier node. * - * @throws InvalidArgumentException + * @param string $name Identifier as string + * @param array $attributes Additional attributes */ - public static function allLessThanEq($value, $limit, $message = ''); + public function __construct(string $name, array $attributes = []) + { + $this->attributes = $attributes; + $this->name = $name; + } + public function getSubNodeNames() : array + { + return ['name']; + } /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $min - * @param mixed $max - * @param string $message + * Get identifier as string. * - * @throws InvalidArgumentException + * @return string Identifier as string. */ - public static function nullOrRange($value, $min, $max, $message = ''); + public function toString() : string + { + return $this->name; + } /** - * @psalm-pure - * - * @param mixed $value - * @param mixed $min - * @param mixed $max - * @param string $message + * Get lowercased identifier as string. * - * @throws InvalidArgumentException + * @return string Lowercased identifier as string */ - public static function allRange($value, $min, $max, $message = ''); + public function toLowerString() : string + { + return \strtolower($this->name); + } /** - * @psalm-pure - * - * @param mixed $value - * @param array $values - * @param string $message + * Checks whether the identifier is a special class name (self, parent or static). * - * @throws InvalidArgumentException + * @return bool Whether identifier is a special class name */ - public static function nullOrOneOf($value, $values, $message = ''); + public function isSpecialClassName() : bool + { + return isset(self::$specialClassNames[\strtolower($this->name)]); + } /** - * @psalm-pure - * - * @param mixed $value - * @param array $values - * @param string $message + * Get identifier as string. * - * @throws InvalidArgumentException + * @return string Identifier as string */ - public static function allOneOf($value, $values, $message = ''); + public function __toString() : string + { + return $this->name; + } + public function getType() : string + { + return 'Identifier'; + } +} +attributes = $attributes; + $this->type = \is_string($type) ? new Identifier($type) : $type; + $this->byRef = $byRef; + $this->variadic = $variadic; + $this->var = $var; + $this->default = $default; + $this->flags = $flags; + $this->attrGroups = $attrGroups; + } + public function getSubNodeNames() : array + { + return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default']; + } + public function getType() : string + { + return 'Param'; + } +} +conds = $conds; + $this->body = $body; + $this->attributes = $attributes; + } + public function getSubNodeNames() : array + { + return ['conds', 'body']; + } + public function getType() : string + { + return 'MatchArm'; + } +} +attributes = $attributes; + $this->types = $types; + } + public function getSubNodeNames() : array + { + return ['types']; + } + public function getType() : string + { + return 'IntersectionType'; + } +} + $value - * @param string $subString - * @param string $message + * If NodeVisitor::enterNode() returns DONT_TRAVERSE_CHILDREN, child nodes + * of the current node will not be traversed for any visitors. * - * @throws InvalidArgumentException + * For subsequent visitors enterNode() will still be called on the current + * node and leaveNode() will also be invoked for the current node. */ - public static function allContains($value, $subString, $message = ''); + const DONT_TRAVERSE_CHILDREN = 1; /** - * @psalm-pure - * - * @param null|string $value - * @param string $subString - * @param string $message + * If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns + * STOP_TRAVERSAL, traversal is aborted. * - * @throws InvalidArgumentException + * The afterTraverse() method will still be invoked. */ - public static function nullOrNotContains($value, $subString, $message = ''); + const STOP_TRAVERSAL = 2; /** - * @psalm-pure - * - * @param iterable $value - * @param string $subString - * @param string $message + * If NodeVisitor::leaveNode() returns REMOVE_NODE for a node that occurs + * in an array, it will be removed from the array. * - * @throws InvalidArgumentException + * For subsequent visitors leaveNode() will still be invoked for the + * removed node. */ - public static function allNotContains($value, $subString, $message = ''); + const REMOVE_NODE = 3; /** - * @psalm-pure - * - * @param null|string $value - * @param string $message + * If NodeVisitor::enterNode() returns DONT_TRAVERSE_CURRENT_AND_CHILDREN, child nodes + * of the current node will not be traversed for any visitors. * - * @throws InvalidArgumentException + * For subsequent visitors enterNode() will not be called as well. + * leaveNode() will be invoked for visitors that has enterNode() method invoked. */ - public static function nullOrNotWhitespaceOnly($value, $message = ''); - /** - * @psalm-pure - * - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allNotWhitespaceOnly($value, $message = ''); + const DONT_TRAVERSE_CURRENT_AND_CHILDREN = 4; + /** @var NodeVisitor[] Visitors */ + protected $visitors = []; + /** @var bool Whether traversal should be stopped */ + protected $stopTraversal; + public function __construct() + { + // for BC + } /** - * @psalm-pure - * - * @param null|string $value - * @param string $prefix - * @param string $message + * Adds a visitor. * - * @throws InvalidArgumentException + * @param NodeVisitor $visitor Visitor to add */ - public static function nullOrStartsWith($value, $prefix, $message = ''); + public function addVisitor(NodeVisitor $visitor) + { + $this->visitors[] = $visitor; + } /** - * @psalm-pure - * - * @param iterable $value - * @param string $prefix - * @param string $message + * Removes an added visitor. * - * @throws InvalidArgumentException + * @param NodeVisitor $visitor */ - public static function allStartsWith($value, $prefix, $message = ''); + public function removeVisitor(NodeVisitor $visitor) + { + foreach ($this->visitors as $index => $storedVisitor) { + if ($storedVisitor === $visitor) { + unset($this->visitors[$index]); + break; + } + } + } /** - * @psalm-pure + * Traverses an array of nodes using the registered visitors. * - * @param null|string $value - * @param string $prefix - * @param string $message + * @param Node[] $nodes Array of nodes * - * @throws InvalidArgumentException + * @return Node[] Traversed array of nodes */ - public static function nullOrNotStartsWith($value, $prefix, $message = ''); + public function traverse(array $nodes) : array + { + $this->stopTraversal = \false; + foreach ($this->visitors as $visitor) { + if (null !== ($return = $visitor->beforeTraverse($nodes))) { + $nodes = $return; + } + } + $nodes = $this->traverseArray($nodes); + foreach ($this->visitors as $visitor) { + if (null !== ($return = $visitor->afterTraverse($nodes))) { + $nodes = $return; + } + } + return $nodes; + } /** - * @psalm-pure + * Recursively traverse a node. * - * @param iterable $value - * @param string $prefix - * @param string $message + * @param Node $node Node to traverse. * - * @throws InvalidArgumentException + * @return Node Result of traversal (may be original node or new one) */ - public static function allNotStartsWith($value, $prefix, $message = ''); + protected function traverseNode(Node $node) : Node + { + foreach ($node->getSubNodeNames() as $name) { + $subNode =& $node->{$name}; + if (\is_array($subNode)) { + $subNode = $this->traverseArray($subNode); + if ($this->stopTraversal) { + break; + } + } elseif ($subNode instanceof Node) { + $traverseChildren = \true; + $breakVisitorIndex = null; + foreach ($this->visitors as $visitorIndex => $visitor) { + $return = $visitor->enterNode($subNode); + if (null !== $return) { + if ($return instanceof Node) { + $this->ensureReplacementReasonable($subNode, $return); + $subNode = $return; + } elseif (self::DONT_TRAVERSE_CHILDREN === $return) { + $traverseChildren = \false; + } elseif (self::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) { + $traverseChildren = \false; + $breakVisitorIndex = $visitorIndex; + break; + } elseif (self::STOP_TRAVERSAL === $return) { + $this->stopTraversal = \true; + break 2; + } else { + throw new \LogicException('enterNode() returned invalid value of type ' . \gettype($return)); + } + } + } + if ($traverseChildren) { + $subNode = $this->traverseNode($subNode); + if ($this->stopTraversal) { + break; + } + } + foreach ($this->visitors as $visitorIndex => $visitor) { + $return = $visitor->leaveNode($subNode); + if (null !== $return) { + if ($return instanceof Node) { + $this->ensureReplacementReasonable($subNode, $return); + $subNode = $return; + } elseif (self::STOP_TRAVERSAL === $return) { + $this->stopTraversal = \true; + break 2; + } elseif (\is_array($return)) { + throw new \LogicException('leaveNode() may only return an array ' . 'if the parent structure is an array'); + } else { + throw new \LogicException('leaveNode() returned invalid value of type ' . \gettype($return)); + } + } + if ($breakVisitorIndex === $visitorIndex) { + break; + } + } + } + } + return $node; + } /** - * @psalm-pure + * Recursively traverse array (usually of nodes). * - * @param mixed $value - * @param string $message + * @param array $nodes Array to traverse * - * @throws InvalidArgumentException + * @return array Result of traversal (may be original array or changed one) */ - public static function nullOrStartsWithLetter($value, $message = ''); + protected function traverseArray(array $nodes) : array + { + $doNodes = []; + foreach ($nodes as $i => &$node) { + if ($node instanceof Node) { + $traverseChildren = \true; + $breakVisitorIndex = null; + foreach ($this->visitors as $visitorIndex => $visitor) { + $return = $visitor->enterNode($node); + if (null !== $return) { + if ($return instanceof Node) { + $this->ensureReplacementReasonable($node, $return); + $node = $return; + } elseif (self::DONT_TRAVERSE_CHILDREN === $return) { + $traverseChildren = \false; + } elseif (self::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) { + $traverseChildren = \false; + $breakVisitorIndex = $visitorIndex; + break; + } elseif (self::STOP_TRAVERSAL === $return) { + $this->stopTraversal = \true; + break 2; + } else { + throw new \LogicException('enterNode() returned invalid value of type ' . \gettype($return)); + } + } + } + if ($traverseChildren) { + $node = $this->traverseNode($node); + if ($this->stopTraversal) { + break; + } + } + foreach ($this->visitors as $visitorIndex => $visitor) { + $return = $visitor->leaveNode($node); + if (null !== $return) { + if ($return instanceof Node) { + $this->ensureReplacementReasonable($node, $return); + $node = $return; + } elseif (\is_array($return)) { + $doNodes[] = [$i, $return]; + break; + } elseif (self::REMOVE_NODE === $return) { + $doNodes[] = [$i, []]; + break; + } elseif (self::STOP_TRAVERSAL === $return) { + $this->stopTraversal = \true; + break 2; + } elseif (\false === $return) { + throw new \LogicException('bool(false) return from leaveNode() no longer supported. ' . 'Return NodeTraverser::REMOVE_NODE instead'); + } else { + throw new \LogicException('leaveNode() returned invalid value of type ' . \gettype($return)); + } + } + if ($breakVisitorIndex === $visitorIndex) { + break; + } + } + } elseif (\is_array($node)) { + throw new \LogicException('Invalid node structure: Contains nested arrays'); + } + } + if (!empty($doNodes)) { + while (list($i, $replace) = \array_pop($doNodes)) { + \array_splice($nodes, $i, 1, $replace); + } + } + return $nodes; + } + private function ensureReplacementReasonable($old, $new) + { + if ($old instanceof Node\Stmt && $new instanceof Node\Expr) { + throw new \LogicException("Trying to replace statement ({$old->getType()}) " . "with expression ({$new->getType()}). Are you missing a " . "Stmt_Expression wrapper?"); + } + if ($old instanceof Node\Expr && $new instanceof Node\Stmt) { + throw new \LogicException("Trying to replace expression ({$old->getType()}) " . "with statement ({$new->getType()})"); + } + } +} + $value - * @param string $suffix - * @param string $message + * Return value semantics: + * * null + * => $node stays as-is + * * NodeTraverser::DONT_TRAVERSE_CHILDREN + * => Children of $node are not traversed. $node stays as-is + * * NodeTraverser::STOP_TRAVERSAL + * => Traversal is aborted. $node stays as-is + * * otherwise + * => $node is set to the return value * - * @throws InvalidArgumentException + * @param Node $node Node + * + * @return null|int|Node Replacement node (or special return value) */ - public static function allEndsWith($value, $suffix, $message = ''); + public function enterNode(Node $node); /** - * @psalm-pure + * Called when leaving a node. * - * @param null|string $value - * @param string $suffix - * @param string $message + * Return value semantics: + * * null + * => $node stays as-is + * * NodeTraverser::REMOVE_NODE + * => $node is removed from the parent array + * * NodeTraverser::STOP_TRAVERSAL + * => Traversal is aborted. $node stays as-is + * * array (of Nodes) + * => The return value is merged into the parent array (at the position of the $node) + * * otherwise + * => $node is set to the return value * - * @throws InvalidArgumentException + * @param Node $node Node + * + * @return null|int|Node|Node[] Replacement node (or special return value) */ - public static function nullOrNotEndsWith($value, $suffix, $message = ''); + public function leaveNode(Node $node); /** - * @psalm-pure + * Called once after traversal. * - * @param iterable $value - * @param string $suffix - * @param string $message + * Return value semantics: + * * null: $nodes stays as-is + * * otherwise: $nodes is set to the return value * - * @throws InvalidArgumentException + * @param Node[] $nodes Array of nodes + * + * @return null|Node[] Array of nodes */ - public static function allNotEndsWith($value, $suffix, $message = ''); + public function afterTraverse(array $nodes); +} +decodeRecursive($value); + } + private function decodeRecursive($value) + { + if (\is_array($value)) { + if (isset($value['nodeType'])) { + if ($value['nodeType'] === 'Comment' || $value['nodeType'] === 'Comment_Doc') { + return $this->decodeComment($value); + } + return $this->decodeNode($value); + } + return $this->decodeArray($value); + } + return $value; + } + private function decodeArray(array $array) : array + { + $decodedArray = []; + foreach ($array as $key => $value) { + $decodedArray[$key] = $this->decodeRecursive($value); + } + return $decodedArray; + } + private function decodeNode(array $value) : Node + { + $nodeType = $value['nodeType']; + if (!\is_string($nodeType)) { + throw new \RuntimeException('Node type must be a string'); + } + $reflectionClass = $this->reflectionClassFromNodeType($nodeType); + /** @var Node $node */ + $node = $reflectionClass->newInstanceWithoutConstructor(); + if (isset($value['attributes'])) { + if (!\is_array($value['attributes'])) { + throw new \RuntimeException('Attributes must be an array'); + } + $node->setAttributes($this->decodeArray($value['attributes'])); + } + foreach ($value as $name => $subNode) { + if ($name === 'nodeType' || $name === 'attributes') { + continue; + } + $node->{$name} = $this->decodeRecursive($subNode); + } + return $node; + } + private function decodeComment(array $value) : Comment + { + $className = $value['nodeType'] === 'Comment' ? Comment::class : Comment\Doc::class; + if (!isset($value['text'])) { + throw new \RuntimeException('Comment must have text'); + } + return new $className($value['text'], $value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1, $value['endLine'] ?? -1, $value['endFilePos'] ?? -1, $value['endTokenPos'] ?? -1); + } + private function reflectionClassFromNodeType(string $nodeType) : \ReflectionClass + { + if (!isset($this->reflectionClassCache[$nodeType])) { + $className = $this->classNameFromNodeType($nodeType); + $this->reflectionClassCache[$nodeType] = new \ReflectionClass($className); + } + return $this->reflectionClassCache[$nodeType]; + } + private function classNameFromNodeType(string $nodeType) : string + { + $className = 'PhpParser\\Node\\' . \strtr($nodeType, '_', '\\'); + if (\class_exists($className)) { + return $className; + } + $className .= '_'; + if (\class_exists($className)) { + return $className; + } + throw new \RuntimeException("Unknown node type \"{$nodeType}\""); + } +} +attributes = $attributes; + } /** - * @psalm-pure - * - * @param iterable $value - * @param string $pattern - * @param string $message + * Gets line the node started in (alias of getStartLine). * - * @throws InvalidArgumentException + * @return int Start line (or -1 if not available) */ - public static function allRegex($value, $pattern, $message = ''); + public function getLine() : int + { + return $this->attributes['startLine'] ?? -1; + } /** - * @psalm-pure + * Gets line the node started in. * - * @param null|string $value - * @param string $pattern - * @param string $message + * Requires the 'startLine' attribute to be enabled in the lexer (enabled by default). * - * @throws InvalidArgumentException + * @return int Start line (or -1 if not available) */ - public static function nullOrNotRegex($value, $pattern, $message = ''); + public function getStartLine() : int + { + return $this->attributes['startLine'] ?? -1; + } /** - * @psalm-pure + * Gets the line the node ended in. * - * @param iterable $value - * @param string $pattern - * @param string $message + * Requires the 'endLine' attribute to be enabled in the lexer (enabled by default). * - * @throws InvalidArgumentException + * @return int End line (or -1 if not available) */ - public static function allNotRegex($value, $pattern, $message = ''); + public function getEndLine() : int + { + return $this->attributes['endLine'] ?? -1; + } /** - * @psalm-pure + * Gets the token offset of the first token that is part of this node. * - * @param mixed $value - * @param string $message + * The offset is an index into the array returned by Lexer::getTokens(). * - * @throws InvalidArgumentException + * Requires the 'startTokenPos' attribute to be enabled in the lexer (DISABLED by default). + * + * @return int Token start position (or -1 if not available) */ - public static function nullOrUnicodeLetters($value, $message = ''); + public function getStartTokenPos() : int + { + return $this->attributes['startTokenPos'] ?? -1; + } /** - * @psalm-pure + * Gets the token offset of the last token that is part of this node. * - * @param mixed $value - * @param string $message + * The offset is an index into the array returned by Lexer::getTokens(). * - * @throws InvalidArgumentException + * Requires the 'endTokenPos' attribute to be enabled in the lexer (DISABLED by default). + * + * @return int Token end position (or -1 if not available) */ - public static function allUnicodeLetters($value, $message = ''); + public function getEndTokenPos() : int + { + return $this->attributes['endTokenPos'] ?? -1; + } /** - * @psalm-pure + * Gets the file offset of the first character that is part of this node. * - * @param mixed $value - * @param string $message + * Requires the 'startFilePos' attribute to be enabled in the lexer (DISABLED by default). * - * @throws InvalidArgumentException + * @return int File start position (or -1 if not available) */ - public static function nullOrAlpha($value, $message = ''); + public function getStartFilePos() : int + { + return $this->attributes['startFilePos'] ?? -1; + } /** - * @psalm-pure + * Gets the file offset of the last character that is part of this node. * - * @param mixed $value - * @param string $message + * Requires the 'endFilePos' attribute to be enabled in the lexer (DISABLED by default). * - * @throws InvalidArgumentException + * @return int File end position (or -1 if not available) */ - public static function allAlpha($value, $message = ''); + public function getEndFilePos() : int + { + return $this->attributes['endFilePos'] ?? -1; + } /** - * @psalm-pure + * Gets all comments directly preceding this node. * - * @param null|string $value - * @param string $message + * The comments are also available through the "comments" attribute. * - * @throws InvalidArgumentException + * @return Comment[] */ - public static function nullOrDigits($value, $message = ''); + public function getComments() : array + { + return $this->attributes['comments'] ?? []; + } /** - * @psalm-pure - * - * @param iterable $value - * @param string $message + * Gets the doc comment of the node. * - * @throws InvalidArgumentException + * @return null|Comment\Doc Doc comment object or null */ - public static function allDigits($value, $message = ''); + public function getDocComment() + { + $comments = $this->getComments(); + for ($i = \count($comments) - 1; $i >= 0; $i--) { + $comment = $comments[$i]; + if ($comment instanceof Comment\Doc) { + return $comment; + } + } + return null; + } /** - * @psalm-pure + * Sets the doc comment of the node. * - * @param null|string $value - * @param string $message + * This will either replace an existing doc comment or add it to the comments array. * - * @throws InvalidArgumentException - */ - public static function nullOrAlnum($value, $message = ''); - /** - * @psalm-pure - * - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allAlnum($value, $message = ''); - /** - * @psalm-pure - * @psalm-assert null|lowercase-string $value - * - * @param null|string $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrLower($value, $message = ''); - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allLower($value, $message = ''); - /** - * @psalm-pure - * - * @param null|string $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrUpper($value, $message = ''); - /** - * @psalm-pure - * - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allUpper($value, $message = ''); - /** - * @psalm-pure - * - * @param null|string $value - * @param int $length - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrLength($value, $length, $message = ''); - /** - * @psalm-pure - * - * @param iterable $value - * @param int $length - * @param string $message - * - * @throws InvalidArgumentException + * @param Comment\Doc $docComment Doc comment to set */ - public static function allLength($value, $length, $message = ''); + public function setDocComment(Comment\Doc $docComment) + { + $comments = $this->getComments(); + for ($i = \count($comments) - 1; $i >= 0; $i--) { + if ($comments[$i] instanceof Comment\Doc) { + // Replace existing doc comment. + $comments[$i] = $docComment; + $this->setAttribute('comments', $comments); + return; + } + } + // Append new doc comment. + $comments[] = $docComment; + $this->setAttribute('comments', $comments); + } + public function setAttribute(string $key, $value) + { + $this->attributes[$key] = $value; + } + public function hasAttribute(string $key) : bool + { + return \array_key_exists($key, $this->attributes); + } + public function getAttribute(string $key, $default = null) + { + if (\array_key_exists($key, $this->attributes)) { + return $this->attributes[$key]; + } + return $default; + } + public function getAttributes() : array + { + return $this->attributes; + } + public function setAttributes(array $attributes) + { + $this->attributes = $attributes; + } /** - * @psalm-pure - * - * @param null|string $value - * @param int|float $min - * @param string $message - * - * @throws InvalidArgumentException + * @return array */ - public static function nullOrMinLength($value, $min, $message = ''); + public function jsonSerialize() : array + { + return ['nodeType' => $this->getType()] + \get_object_vars($this); + } +} + $value - * @param int|float $min - * @param string $message + * Creates a Lexer. * - * @throws InvalidArgumentException + * @param array $options Options array. Currently only the 'usedAttributes' option is supported, + * which is an array of attributes to add to the AST nodes. Possible + * attributes are: 'comments', 'startLine', 'endLine', 'startTokenPos', + * 'endTokenPos', 'startFilePos', 'endFilePos'. The option defaults to the + * first three. For more info see getNextToken() docs. */ - public static function allMinLength($value, $min, $message = ''); + public function __construct(array $options = []) + { + // Create Map from internal tokens to PhpParser tokens. + $this->defineCompatibilityTokens(); + $this->tokenMap = $this->createTokenMap(); + $this->identifierTokens = $this->createIdentifierTokenMap(); + // map of tokens to drop while lexing (the map is only used for isset lookup, + // that's why the value is simply set to 1; the value is never actually used.) + $this->dropTokens = \array_fill_keys([\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT, \T_BAD_CHARACTER], 1); + $defaultAttributes = ['comments', 'startLine', 'endLine']; + $usedAttributes = \array_fill_keys($options['usedAttributes'] ?? $defaultAttributes, \true); + // Create individual boolean properties to make these checks faster. + $this->attributeStartLineUsed = isset($usedAttributes['startLine']); + $this->attributeEndLineUsed = isset($usedAttributes['endLine']); + $this->attributeStartTokenPosUsed = isset($usedAttributes['startTokenPos']); + $this->attributeEndTokenPosUsed = isset($usedAttributes['endTokenPos']); + $this->attributeStartFilePosUsed = isset($usedAttributes['startFilePos']); + $this->attributeEndFilePosUsed = isset($usedAttributes['endFilePos']); + $this->attributeCommentsUsed = isset($usedAttributes['comments']); + } /** - * @psalm-pure + * Initializes the lexer for lexing the provided source code. * - * @param null|string $value - * @param int|float $max - * @param string $message + * This function does not throw if lexing errors occur. Instead, errors may be retrieved using + * the getErrors() method. * - * @throws InvalidArgumentException + * @param string $code The source code to lex + * @param ErrorHandler|null $errorHandler Error handler to use for lexing errors. Defaults to + * ErrorHandler\Throwing */ - public static function nullOrMaxLength($value, $max, $message = ''); + public function startLexing(string $code, ErrorHandler $errorHandler = null) + { + if (null === $errorHandler) { + $errorHandler = new ErrorHandler\Throwing(); + } + $this->code = $code; + // keep the code around for __halt_compiler() handling + $this->pos = -1; + $this->line = 1; + $this->filePos = 0; + // If inline HTML occurs without preceding code, treat it as if it had a leading newline. + // This ensures proper composability, because having a newline is the "safe" assumption. + $this->prevCloseTagHasNewline = \true; + $scream = \ini_set('xdebug.scream', '0'); + $this->tokens = @\token_get_all($code); + $this->postprocessTokens($errorHandler); + if (\false !== $scream) { + \ini_set('xdebug.scream', $scream); + } + } + private function handleInvalidCharacterRange($start, $end, $line, ErrorHandler $errorHandler) + { + $tokens = []; + for ($i = $start; $i < $end; $i++) { + $chr = $this->code[$i]; + if ($chr === "\0") { + // PHP cuts error message after null byte, so need special case + $errorMsg = 'Unexpected null byte'; + } else { + $errorMsg = \sprintf('Unexpected character "%s" (ASCII %d)', $chr, \ord($chr)); + } + $tokens[] = [\T_BAD_CHARACTER, $chr, $line]; + $errorHandler->handleError(new Error($errorMsg, ['startLine' => $line, 'endLine' => $line, 'startFilePos' => $i, 'endFilePos' => $i])); + } + return $tokens; + } /** - * @psalm-pure - * - * @param iterable $value - * @param int|float $max - * @param string $message + * Check whether comment token is unterminated. * - * @throws InvalidArgumentException + * @return bool */ - public static function allMaxLength($value, $max, $message = ''); + private function isUnterminatedComment($token) : bool + { + return ($token[0] === \T_COMMENT || $token[0] === \T_DOC_COMMENT) && \substr($token[1], 0, 2) === '/*' && \substr($token[1], -2) !== '*/'; + } + protected function postprocessTokens(ErrorHandler $errorHandler) + { + // PHP's error handling for token_get_all() is rather bad, so if we want detailed + // error information we need to compute it ourselves. Invalid character errors are + // detected by finding "gaps" in the token array. Unterminated comments are detected + // by checking if a trailing comment has a "*/" at the end. + // + // Additionally, we perform a number of canonicalizations here: + // * Use the PHP 8.0 comment format, which does not include trailing whitespace anymore. + // * Use PHP 8.0 T_NAME_* tokens. + // * Use PHP 8.1 T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG and + // T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG tokens used to disambiguate intersection types. + $filePos = 0; + $line = 1; + $numTokens = \count($this->tokens); + for ($i = 0; $i < $numTokens; $i++) { + $token = $this->tokens[$i]; + // Since PHP 7.4 invalid characters are represented by a T_BAD_CHARACTER token. + // In this case we only need to emit an error. + if ($token[0] === \T_BAD_CHARACTER) { + $this->handleInvalidCharacterRange($filePos, $filePos + 1, $line, $errorHandler); + } + if ($token[0] === \T_COMMENT && \substr($token[1], 0, 2) !== '/*' && \preg_match('/(\\r\\n|\\n|\\r)$/D', $token[1], $matches)) { + $trailingNewline = $matches[0]; + $token[1] = \substr($token[1], 0, -\strlen($trailingNewline)); + $this->tokens[$i] = $token; + if (isset($this->tokens[$i + 1]) && $this->tokens[$i + 1][0] === \T_WHITESPACE) { + // Move trailing newline into following T_WHITESPACE token, if it already exists. + $this->tokens[$i + 1][1] = $trailingNewline . $this->tokens[$i + 1][1]; + $this->tokens[$i + 1][2]--; + } else { + // Otherwise, we need to create a new T_WHITESPACE token. + \array_splice($this->tokens, $i + 1, 0, [[\T_WHITESPACE, $trailingNewline, $line]]); + $numTokens++; + } + } + // Emulate PHP 8 T_NAME_* tokens, by combining sequences of T_NS_SEPARATOR and T_STRING + // into a single token. + if (\is_array($token) && ($token[0] === \T_NS_SEPARATOR || isset($this->identifierTokens[$token[0]]))) { + $lastWasSeparator = $token[0] === \T_NS_SEPARATOR; + $text = $token[1]; + for ($j = $i + 1; isset($this->tokens[$j]); $j++) { + if ($lastWasSeparator) { + if (!isset($this->identifierTokens[$this->tokens[$j][0]])) { + break; + } + $lastWasSeparator = \false; + } else { + if ($this->tokens[$j][0] !== \T_NS_SEPARATOR) { + break; + } + $lastWasSeparator = \true; + } + $text .= $this->tokens[$j][1]; + } + if ($lastWasSeparator) { + // Trailing separator is not part of the name. + $j--; + $text = \substr($text, 0, -1); + } + if ($j > $i + 1) { + if ($token[0] === \T_NS_SEPARATOR) { + $type = \T_NAME_FULLY_QUALIFIED; + } else { + if ($token[0] === \T_NAMESPACE) { + $type = \T_NAME_RELATIVE; + } else { + $type = \T_NAME_QUALIFIED; + } + } + $token = [$type, $text, $line]; + \array_splice($this->tokens, $i, $j - $i, [$token]); + $numTokens -= $j - $i - 1; + } + } + if ($token === '&') { + $next = $i + 1; + while (isset($this->tokens[$next]) && $this->tokens[$next][0] === \T_WHITESPACE) { + $next++; + } + $followedByVarOrVarArg = isset($this->tokens[$next]) && ($this->tokens[$next][0] === \T_VARIABLE || $this->tokens[$next][0] === \T_ELLIPSIS); + $this->tokens[$i] = $token = [$followedByVarOrVarArg ? \T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG : \T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG, '&', $line]; + } + $tokenValue = \is_string($token) ? $token : $token[1]; + $tokenLen = \strlen($tokenValue); + if (\substr($this->code, $filePos, $tokenLen) !== $tokenValue) { + // Something is missing, must be an invalid character + $nextFilePos = \strpos($this->code, $tokenValue, $filePos); + $badCharTokens = $this->handleInvalidCharacterRange($filePos, $nextFilePos, $line, $errorHandler); + $filePos = (int) $nextFilePos; + \array_splice($this->tokens, $i, 0, $badCharTokens); + $numTokens += \count($badCharTokens); + $i += \count($badCharTokens); + } + $filePos += $tokenLen; + $line += \substr_count($tokenValue, "\n"); + } + if ($filePos !== \strlen($this->code)) { + if (\substr($this->code, $filePos, 2) === '/*') { + // Unlike PHP, HHVM will drop unterminated comments entirely + $comment = \substr($this->code, $filePos); + $errorHandler->handleError(new Error('Unterminated comment', ['startLine' => $line, 'endLine' => $line + \substr_count($comment, "\n"), 'startFilePos' => $filePos, 'endFilePos' => $filePos + \strlen($comment)])); + // Emulate the PHP behavior + $isDocComment = isset($comment[3]) && $comment[3] === '*'; + $this->tokens[] = [$isDocComment ? \T_DOC_COMMENT : \T_COMMENT, $comment, $line]; + } else { + // Invalid characters at the end of the input + $badCharTokens = $this->handleInvalidCharacterRange($filePos, \strlen($this->code), $line, $errorHandler); + $this->tokens = \array_merge($this->tokens, $badCharTokens); + } + return; + } + if (\count($this->tokens) > 0) { + // Check for unterminated comment + $lastToken = $this->tokens[\count($this->tokens) - 1]; + if ($this->isUnterminatedComment($lastToken)) { + $errorHandler->handleError(new Error('Unterminated comment', ['startLine' => $line - \substr_count($lastToken[1], "\n"), 'endLine' => $line, 'startFilePos' => $filePos - \strlen($lastToken[1]), 'endFilePos' => $filePos])); + } + } + } /** - * @psalm-pure + * Fetches the next token. * - * @param null|string $value - * @param int|float $min - * @param int|float $max - * @param string $message + * The available attributes are determined by the 'usedAttributes' option, which can + * be specified in the constructor. The following attributes are supported: * - * @throws InvalidArgumentException - */ - public static function nullOrLengthBetween($value, $min, $max, $message = ''); - /** - * @psalm-pure + * * 'comments' => Array of PhpParser\Comment or PhpParser\Comment\Doc instances, + * representing all comments that occurred between the previous + * non-discarded token and the current one. + * * 'startLine' => Line in which the node starts. + * * 'endLine' => Line in which the node ends. + * * 'startTokenPos' => Offset into the token array of the first token in the node. + * * 'endTokenPos' => Offset into the token array of the last token in the node. + * * 'startFilePos' => Offset into the code string of the first character that is part of the node. + * * 'endFilePos' => Offset into the code string of the last character that is part of the node. * - * @param iterable $value - * @param int|float $min - * @param int|float $max - * @param string $message + * @param mixed $value Variable to store token content in + * @param mixed $startAttributes Variable to store start attributes in + * @param mixed $endAttributes Variable to store end attributes in * - * @throws InvalidArgumentException + * @return int Token id */ - public static function allLengthBetween($value, $min, $max, $message = ''); + public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) : int + { + $startAttributes = []; + $endAttributes = []; + while (1) { + if (isset($this->tokens[++$this->pos])) { + $token = $this->tokens[$this->pos]; + } else { + // EOF token with ID 0 + $token = "\0"; + } + if ($this->attributeStartLineUsed) { + $startAttributes['startLine'] = $this->line; + } + if ($this->attributeStartTokenPosUsed) { + $startAttributes['startTokenPos'] = $this->pos; + } + if ($this->attributeStartFilePosUsed) { + $startAttributes['startFilePos'] = $this->filePos; + } + if (\is_string($token)) { + $value = $token; + if (isset($token[1])) { + // bug in token_get_all + $this->filePos += 2; + $id = \ord('"'); + } else { + $this->filePos += 1; + $id = \ord($token); + } + } elseif (!isset($this->dropTokens[$token[0]])) { + $value = $token[1]; + $id = $this->tokenMap[$token[0]]; + if (\T_CLOSE_TAG === $token[0]) { + $this->prevCloseTagHasNewline = \false !== \strpos($token[1], "\n") || \false !== \strpos($token[1], "\r"); + } elseif (\T_INLINE_HTML === $token[0]) { + $startAttributes['hasLeadingNewline'] = $this->prevCloseTagHasNewline; + } + $this->line += \substr_count($value, "\n"); + $this->filePos += \strlen($value); + } else { + $origLine = $this->line; + $origFilePos = $this->filePos; + $this->line += \substr_count($token[1], "\n"); + $this->filePos += \strlen($token[1]); + if (\T_COMMENT === $token[0] || \T_DOC_COMMENT === $token[0]) { + if ($this->attributeCommentsUsed) { + $comment = \T_DOC_COMMENT === $token[0] ? new Comment\Doc($token[1], $origLine, $origFilePos, $this->pos, $this->line, $this->filePos - 1, $this->pos) : new Comment($token[1], $origLine, $origFilePos, $this->pos, $this->line, $this->filePos - 1, $this->pos); + $startAttributes['comments'][] = $comment; + } + } + continue; + } + if ($this->attributeEndLineUsed) { + $endAttributes['endLine'] = $this->line; + } + if ($this->attributeEndTokenPosUsed) { + $endAttributes['endTokenPos'] = $this->pos; + } + if ($this->attributeEndFilePosUsed) { + $endAttributes['endFilePos'] = $this->filePos - 1; + } + return $id; + } + throw new \RuntimeException('Reached end of lexer loop'); + } /** - * @param mixed $value - * @param string $message + * Returns the token array for current code. * - * @throws InvalidArgumentException - */ - public static function nullOrFileExists($value, $message = ''); - /** - * @param mixed $value - * @param string $message + * The token array is in the same format as provided by the + * token_get_all() function and does not discard tokens (i.e. + * whitespace and comments are included). The token position + * attributes are against this token array. * - * @throws InvalidArgumentException + * @return array Array of tokens in token_get_all() format */ - public static function allFileExists($value, $message = ''); + public function getTokens() : array + { + return $this->tokens; + } /** - * @param mixed $value - * @param string $message + * Handles __halt_compiler() by returning the text after it. * - * @throws InvalidArgumentException + * @return string Remaining text */ - public static function nullOrFile($value, $message = ''); + public function handleHaltCompiler() : string + { + // text after T_HALT_COMPILER, still including (); + $textAfter = \substr($this->code, $this->filePos); + // ensure that it is followed by (); + // this simplifies the situation, by not allowing any comments + // in between of the tokens. + if (!\preg_match('~^\\s*\\(\\s*\\)\\s*(?:;|\\?>\\r?\\n?)~', $textAfter, $matches)) { + throw new Error('__HALT_COMPILER must be followed by "();"'); + } + // prevent the lexer from returning any further tokens + $this->pos = \count($this->tokens); + // return with (); removed + return \substr($textAfter, \strlen($matches[0])); + } + private function defineCompatibilityTokens() + { + static $compatTokensDefined = \false; + if ($compatTokensDefined) { + return; + } + $compatTokens = [ + // PHP 7.4 + 'T_BAD_CHARACTER', + 'T_FN', + 'T_COALESCE_EQUAL', + // PHP 8.0 + 'T_NAME_QUALIFIED', + 'T_NAME_FULLY_QUALIFIED', + 'T_NAME_RELATIVE', + 'T_MATCH', + 'T_NULLSAFE_OBJECT_OPERATOR', + 'T_ATTRIBUTE', + // PHP 8.1 + 'T_ENUM', + 'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', + 'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', + 'T_READONLY', + ]; + // PHP-Parser might be used together with another library that also emulates some or all + // of these tokens. Perform a sanity-check that all already defined tokens have been + // assigned a unique ID. + $usedTokenIds = []; + foreach ($compatTokens as $token) { + if (\defined($token)) { + $tokenId = \constant($token); + $clashingToken = $usedTokenIds[$tokenId] ?? null; + if ($clashingToken !== null) { + throw new \Error(\sprintf('Token %s has same ID as token %s, ' . 'you may be using a library with broken token emulation', $token, $clashingToken)); + } + $usedTokenIds[$tokenId] = $token; + } + } + // Now define any tokens that have not yet been emulated. Try to assign IDs from -1 + // downwards, but skip any IDs that may already be in use. + $newTokenId = -1; + foreach ($compatTokens as $token) { + if (!\defined($token)) { + while (isset($usedTokenIds[$newTokenId])) { + $newTokenId--; + } + \define($token, $newTokenId); + $newTokenId--; + } + } + $compatTokensDefined = \true; + } /** - * @param mixed $value - * @param string $message + * Creates the token map. * - * @throws InvalidArgumentException - */ - public static function allFile($value, $message = ''); - /** - * @param mixed $value - * @param string $message + * The token map maps the PHP internal token identifiers + * to the identifiers used by the Parser. Additionally it + * maps T_OPEN_TAG_WITH_ECHO to T_ECHO and T_CLOSE_TAG to ';'. * - * @throws InvalidArgumentException + * @return array The token map */ - public static function nullOrDirectory($value, $message = ''); - /** - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allDirectory($value, $message = ''); - /** - * @param null|string $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrReadable($value, $message = ''); - /** - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allReadable($value, $message = ''); - /** - * @param null|string $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrWritable($value, $message = ''); - /** - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allWritable($value, $message = ''); - /** - * @psalm-assert null|class-string $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrClassExists($value, $message = ''); - /** - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allClassExists($value, $message = ''); - /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert null|class-string|ExpectedType $value - * - * @param mixed $value - * @param string|object $class - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrSubclassOf($value, $class, $message = ''); - /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $class - * @psalm-assert iterable|ExpectedType> $value - * - * @param mixed $value - * @param string|object $class - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allSubclassOf($value, $class, $message = ''); - /** - * @psalm-assert null|class-string $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrInterfaceExists($value, $message = ''); - /** - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allInterfaceExists($value, $message = ''); - /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $interface - * @psalm-assert null|class-string $value - * - * @param mixed $value - * @param mixed $interface - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrImplementsInterface($value, $interface, $message = ''); - /** - * @psalm-pure - * @psalm-template ExpectedType of object - * @psalm-param class-string $interface - * @psalm-assert iterable> $value - * - * @param mixed $value - * @param mixed $interface - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allImplementsInterface($value, $interface, $message = ''); - /** - * @psalm-pure - * @psalm-param null|class-string|object $classOrObject - * - * @param null|string|object $classOrObject - * @param mixed $property - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrPropertyExists($classOrObject, $property, $message = ''); - /** - * @psalm-pure - * @psalm-param iterable $classOrObject - * - * @param iterable $classOrObject - * @param mixed $property - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allPropertyExists($classOrObject, $property, $message = ''); - /** - * @psalm-pure - * @psalm-param null|class-string|object $classOrObject - * - * @param null|string|object $classOrObject - * @param mixed $property - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrPropertyNotExists($classOrObject, $property, $message = ''); - /** - * @psalm-pure - * @psalm-param iterable $classOrObject - * - * @param iterable $classOrObject - * @param mixed $property - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allPropertyNotExists($classOrObject, $property, $message = ''); - /** - * @psalm-pure - * @psalm-param null|class-string|object $classOrObject - * - * @param null|string|object $classOrObject - * @param mixed $method - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrMethodExists($classOrObject, $method, $message = ''); - /** - * @psalm-pure - * @psalm-param iterable $classOrObject - * - * @param iterable $classOrObject - * @param mixed $method - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allMethodExists($classOrObject, $method, $message = ''); - /** - * @psalm-pure - * @psalm-param null|class-string|object $classOrObject - * - * @param null|string|object $classOrObject - * @param mixed $method - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrMethodNotExists($classOrObject, $method, $message = ''); - /** - * @psalm-pure - * @psalm-param iterable $classOrObject - * - * @param iterable $classOrObject - * @param mixed $method - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allMethodNotExists($classOrObject, $method, $message = ''); - /** - * @psalm-pure - * - * @param null|array $array - * @param string|int $key - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrKeyExists($array, $key, $message = ''); - /** - * @psalm-pure - * - * @param iterable $array - * @param string|int $key - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allKeyExists($array, $key, $message = ''); - /** - * @psalm-pure - * - * @param null|array $array - * @param string|int $key - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrKeyNotExists($array, $key, $message = ''); - /** - * @psalm-pure - * - * @param iterable $array - * @param string|int $key - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allKeyNotExists($array, $key, $message = ''); - /** - * @psalm-pure - * @psalm-assert null|array-key $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrValidArrayKey($value, $message = ''); - /** - * @psalm-pure - * @psalm-assert iterable $value - * - * @param mixed $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allValidArrayKey($value, $message = ''); - /** - * @param null|Countable|array $array - * @param int $number - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrCount($array, $number, $message = ''); - /** - * @param iterable $array - * @param int $number - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allCount($array, $number, $message = ''); - /** - * @param null|Countable|array $array - * @param int|float $min - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrMinCount($array, $min, $message = ''); - /** - * @param iterable $array - * @param int|float $min - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allMinCount($array, $min, $message = ''); - /** - * @param null|Countable|array $array - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrMaxCount($array, $max, $message = ''); - /** - * @param iterable $array - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allMaxCount($array, $max, $message = ''); - /** - * @param null|Countable|array $array - * @param int|float $min - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrCountBetween($array, $min, $max, $message = ''); - /** - * @param iterable $array - * @param int|float $min - * @param int|float $max - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allCountBetween($array, $min, $max, $message = ''); - /** - * @psalm-pure - * @psalm-assert null|list $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrIsList($array, $message = ''); - /** - * @psalm-pure - * @psalm-assert iterable $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allIsList($array, $message = ''); - /** - * @psalm-pure - * @psalm-assert null|non-empty-list $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrIsNonEmptyList($array, $message = ''); - /** - * @psalm-pure - * @psalm-assert iterable $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allIsNonEmptyList($array, $message = ''); - /** - * @psalm-pure - * @psalm-template T - * @psalm-param null|mixed|array $array - * @psalm-assert null|array $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrIsMap($array, $message = ''); - /** - * @psalm-pure - * @psalm-template T - * @psalm-param iterable> $array - * @psalm-assert iterable> $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allIsMap($array, $message = ''); - /** - * @psalm-pure - * @psalm-template T - * @psalm-param null|mixed|array $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrIsNonEmptyMap($array, $message = ''); - /** - * @psalm-pure - * @psalm-template T - * @psalm-param iterable> $array - * - * @param mixed $array - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allIsNonEmptyMap($array, $message = ''); - /** - * @psalm-pure - * - * @param null|string $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrUuid($value, $message = ''); - /** - * @psalm-pure - * - * @param iterable $value - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allUuid($value, $message = ''); - /** - * @psalm-param class-string $class - * - * @param null|Closure $expression - * @param string $class - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function nullOrThrows($expression, $class = 'Exception', $message = ''); - /** - * @psalm-param class-string $class - * - * @param iterable $expression - * @param string $class - * @param string $message - * - * @throws InvalidArgumentException - */ - public static function allThrows($expression, $class = 'Exception', $message = ''); -} -The MIT License (MIT) - -Copyright (c) 2014 Bernhard Schussek - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\FileIterator; - -use const GLOB_ONLYDIR; -use function array_filter; -use function array_map; -use function array_merge; -use function glob; -use function is_dir; -use function is_string; -use function realpath; -use AppendIterator; -use RecursiveDirectoryIterator; -use RecursiveIteratorIterator; -class Factory -{ - /** - * @param array|string $paths - * @param array|string $suffixes - * @param array|string $prefixes - */ - public function getFileIterator($paths, $suffixes = '', $prefixes = '', array $exclude = []) : \AppendIterator + protected function createTokenMap() : array { - if (\is_string($paths)) { - $paths = [$paths]; - } - $paths = $this->getPathsAfterResolvingWildcards($paths); - $exclude = $this->getPathsAfterResolvingWildcards($exclude); - if (\is_string($prefixes)) { - if ($prefixes !== '') { - $prefixes = [$prefixes]; - } else { - $prefixes = []; + $tokenMap = []; + // 256 is the minimum possible token number, as everything below + // it is an ASCII value + for ($i = 256; $i < 1000; ++$i) { + if (\T_DOUBLE_COLON === $i) { + // T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM + $tokenMap[$i] = Tokens::T_PAAMAYIM_NEKUDOTAYIM; + } elseif (\T_OPEN_TAG_WITH_ECHO === $i) { + // T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO + $tokenMap[$i] = Tokens::T_ECHO; + } elseif (\T_CLOSE_TAG === $i) { + // T_CLOSE_TAG is equivalent to ';' + $tokenMap[$i] = \ord(';'); + } elseif ('UNKNOWN' !== ($name = \token_name($i))) { + if ('T_HASHBANG' === $name) { + // HHVM uses a special token for #! hashbang lines + $tokenMap[$i] = Tokens::T_INLINE_HTML; + } elseif (\defined($name = Tokens::class . '::' . $name)) { + // Other tokens can be mapped directly + $tokenMap[$i] = \constant($name); + } } } - if (\is_string($suffixes)) { - if ($suffixes !== '') { - $suffixes = [$suffixes]; - } else { - $suffixes = []; - } + // HHVM uses a special token for numbers that overflow to double + if (\defined('T_ONUMBER')) { + $tokenMap[\T_ONUMBER] = Tokens::T_DNUMBER; } - $iterator = new \AppendIterator(); - foreach ($paths as $path) { - if (\is_dir($path)) { - $iterator->append(new \PHPUnit\SebastianBergmann\FileIterator\Iterator($path, new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS | \RecursiveDirectoryIterator::SKIP_DOTS)), $suffixes, $prefixes, $exclude)); - } + // HHVM also has a separate token for the __COMPILER_HALT_OFFSET__ constant + if (\defined('T_COMPILER_HALT_OFFSET')) { + $tokenMap[\T_COMPILER_HALT_OFFSET] = Tokens::T_STRING; } - return $iterator; + // Assign tokens for which we define compatibility constants, as token_name() does not know them. + $tokenMap[\T_FN] = Tokens::T_FN; + $tokenMap[\T_COALESCE_EQUAL] = Tokens::T_COALESCE_EQUAL; + $tokenMap[\T_NAME_QUALIFIED] = Tokens::T_NAME_QUALIFIED; + $tokenMap[\T_NAME_FULLY_QUALIFIED] = Tokens::T_NAME_FULLY_QUALIFIED; + $tokenMap[\T_NAME_RELATIVE] = Tokens::T_NAME_RELATIVE; + $tokenMap[\T_MATCH] = Tokens::T_MATCH; + $tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = Tokens::T_NULLSAFE_OBJECT_OPERATOR; + $tokenMap[\T_ATTRIBUTE] = Tokens::T_ATTRIBUTE; + $tokenMap[\T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG] = Tokens::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG; + $tokenMap[\T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG] = Tokens::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG; + $tokenMap[\T_ENUM] = Tokens::T_ENUM; + $tokenMap[\T_READONLY] = Tokens::T_READONLY; + return $tokenMap; } - protected function getPathsAfterResolvingWildcards(array $paths) : array + private function createIdentifierTokenMap() : array { - $_paths = []; - foreach ($paths as $path) { - if ($locals = \glob($path, \GLOB_ONLYDIR)) { - $_paths = \array_merge($_paths, \array_map('\\realpath', $locals)); - } else { - $_paths[] = \realpath($path); - } - } - return \array_filter($_paths); + // Based on semi_reserved production. + return \array_fill_keys([\T_STRING, \T_STATIC, \T_ABSTRACT, \T_FINAL, \T_PRIVATE, \T_PROTECTED, \T_PUBLIC, \T_READONLY, \T_INCLUDE, \T_INCLUDE_ONCE, \T_EVAL, \T_REQUIRE, \T_REQUIRE_ONCE, \T_LOGICAL_OR, \T_LOGICAL_XOR, \T_LOGICAL_AND, \T_INSTANCEOF, \T_NEW, \T_CLONE, \T_EXIT, \T_IF, \T_ELSEIF, \T_ELSE, \T_ENDIF, \T_ECHO, \T_DO, \T_WHILE, \T_ENDWHILE, \T_FOR, \T_ENDFOR, \T_FOREACH, \T_ENDFOREACH, \T_DECLARE, \T_ENDDECLARE, \T_AS, \T_TRY, \T_CATCH, \T_FINALLY, \T_THROW, \T_USE, \T_INSTEADOF, \T_GLOBAL, \T_VAR, \T_UNSET, \T_ISSET, \T_EMPTY, \T_CONTINUE, \T_GOTO, \T_FUNCTION, \T_CONST, \T_RETURN, \T_PRINT, \T_YIELD, \T_LIST, \T_SWITCH, \T_ENDSWITCH, \T_CASE, \T_DEFAULT, \T_BREAK, \T_ARRAY, \T_CALLABLE, \T_EXTENDS, \T_IMPLEMENTS, \T_NAMESPACE, \T_TRAIT, \T_INTERFACE, \T_CLASS, \T_CLASS_C, \T_TRAIT_C, \T_FUNC_C, \T_METHOD_C, \T_LINE, \T_FILE, \T_DIR, \T_NS_C, \T_HALT_COMPILER, \T_FN, \T_MATCH], \true); } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\FileIterator; +namespace PHPUnit\PhpParser\Parser; -use function array_filter; -use function array_map; -use function preg_match; -use function realpath; -use function str_replace; -use function strlen; -use function strpos; -use function substr; -use FilterIterator; -class Iterator extends \FilterIterator -{ - public const PREFIX = 0; - public const SUFFIX = 1; - /** - * @var string - */ - private $basePath; - /** - * @var array - */ - private $suffixes = []; - /** - * @var array - */ - private $prefixes = []; - /** - * @var array - */ - private $exclude = []; - public function __construct(string $basePath, \Iterator $iterator, array $suffixes = [], array $prefixes = [], array $exclude = []) - { - $this->basePath = \realpath($basePath); - $this->prefixes = $prefixes; - $this->suffixes = $suffixes; - $this->exclude = \array_filter(\array_map('realpath', $exclude)); - parent::__construct($iterator); - } - public function accept() : bool - { - $current = $this->getInnerIterator()->current(); - $filename = $current->getFilename(); - $realPath = $current->getRealPath(); - if ($realPath === \false) { - return \false; - } - return $this->acceptPath($realPath) && $this->acceptPrefix($filename) && $this->acceptSuffix($filename); - } - private function acceptPath(string $path) : bool - { - // Filter files in hidden directories by checking path that is relative to the base path. - if (\preg_match('=/\\.[^/]*/=', \str_replace($this->basePath, '', $path))) { - return \false; - } - foreach ($this->exclude as $exclude) { - if (\strpos($path, $exclude) === 0) { - return \false; - } - } - return \true; - } - private function acceptPrefix(string $filename) : bool - { - return $this->acceptSubString($filename, $this->prefixes, self::PREFIX); - } - private function acceptSuffix(string $filename) : bool - { - return $this->acceptSubString($filename, $this->suffixes, self::SUFFIX); - } - private function acceptSubString(string $filename, array $subStrings, int $type) : bool - { - if (empty($subStrings)) { - return \true; - } - $matched = \false; - foreach ($subStrings as $string) { - if ($type === self::PREFIX && \strpos($filename, $string) === 0 || $type === self::SUFFIX && \substr($filename, -1 * \strlen($string)) === $string) { - $matched = \true; - break; - } - } - return $matched; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. +use PHPUnit\PhpParser\Error; +use PHPUnit\PhpParser\Node; +use PHPUnit\PhpParser\Node\Expr; +use PHPUnit\PhpParser\Node\Name; +use PHPUnit\PhpParser\Node\Scalar; +use PHPUnit\PhpParser\Node\Stmt; +/* This is an automatically GENERATED file, which should not be manually edited. + * Instead edit one of the following: + * * the grammar files grammar/php5.y or grammar/php7.y + * * the skeleton file grammar/parser.template + * * the preprocessing script grammar/rebuildParsers.php */ -namespace PHPUnit\SebastianBergmann\FileIterator; - -use const DIRECTORY_SEPARATOR; -use function array_unique; -use function count; -use function dirname; -use function explode; -use function is_file; -use function is_string; -use function realpath; -use function sort; -class Facade +class Php7 extends \PHPUnit\PhpParser\ParserAbstract { - /** - * @param array|string $paths - * @param array|string $suffixes - * @param array|string $prefixes - */ - public function getFilesAsArray($paths, $suffixes = '', $prefixes = '', array $exclude = [], bool $commonPath = \false) : array + protected $tokenToSymbolMapSize = 396; + protected $actionTableSize = 1196; + protected $gotoTableSize = 585; + protected $invalidSymbol = 168; + protected $errorSymbol = 1; + protected $defaultAction = -32766; + protected $unexpectedTokenRule = 32767; + protected $YY2TBLSTATE = 419; + protected $numNonLeafStates = 710; + protected $symbolToName = array("EOF", "error", "T_THROW", "T_INCLUDE", "T_INCLUDE_ONCE", "T_EVAL", "T_REQUIRE", "T_REQUIRE_ONCE", "','", "T_LOGICAL_OR", "T_LOGICAL_XOR", "T_LOGICAL_AND", "T_PRINT", "T_YIELD", "T_DOUBLE_ARROW", "T_YIELD_FROM", "'='", "T_PLUS_EQUAL", "T_MINUS_EQUAL", "T_MUL_EQUAL", "T_DIV_EQUAL", "T_CONCAT_EQUAL", "T_MOD_EQUAL", "T_AND_EQUAL", "T_OR_EQUAL", "T_XOR_EQUAL", "T_SL_EQUAL", "T_SR_EQUAL", "T_POW_EQUAL", "T_COALESCE_EQUAL", "'?'", "':'", "T_COALESCE", "T_BOOLEAN_OR", "T_BOOLEAN_AND", "'|'", "'^'", "T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG", "T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG", "T_IS_EQUAL", "T_IS_NOT_EQUAL", "T_IS_IDENTICAL", "T_IS_NOT_IDENTICAL", "T_SPACESHIP", "'<'", "T_IS_SMALLER_OR_EQUAL", "'>'", "T_IS_GREATER_OR_EQUAL", "T_SL", "T_SR", "'+'", "'-'", "'.'", "'*'", "'/'", "'%'", "'!'", "T_INSTANCEOF", "'~'", "T_INC", "T_DEC", "T_INT_CAST", "T_DOUBLE_CAST", "T_STRING_CAST", "T_ARRAY_CAST", "T_OBJECT_CAST", "T_BOOL_CAST", "T_UNSET_CAST", "'@'", "T_POW", "'['", "T_NEW", "T_CLONE", "T_EXIT", "T_IF", "T_ELSEIF", "T_ELSE", "T_ENDIF", "T_LNUMBER", "T_DNUMBER", "T_STRING", "T_STRING_VARNAME", "T_VARIABLE", "T_NUM_STRING", "T_INLINE_HTML", "T_ENCAPSED_AND_WHITESPACE", "T_CONSTANT_ENCAPSED_STRING", "T_ECHO", "T_DO", "T_WHILE", "T_ENDWHILE", "T_FOR", "T_ENDFOR", "T_FOREACH", "T_ENDFOREACH", "T_DECLARE", "T_ENDDECLARE", "T_AS", "T_SWITCH", "T_MATCH", "T_ENDSWITCH", "T_CASE", "T_DEFAULT", "T_BREAK", "T_CONTINUE", "T_GOTO", "T_FUNCTION", "T_FN", "T_CONST", "T_RETURN", "T_TRY", "T_CATCH", "T_FINALLY", "T_USE", "T_INSTEADOF", "T_GLOBAL", "T_STATIC", "T_ABSTRACT", "T_FINAL", "T_PRIVATE", "T_PROTECTED", "T_PUBLIC", "T_READONLY", "T_VAR", "T_UNSET", "T_ISSET", "T_EMPTY", "T_HALT_COMPILER", "T_CLASS", "T_TRAIT", "T_INTERFACE", "T_ENUM", "T_EXTENDS", "T_IMPLEMENTS", "T_OBJECT_OPERATOR", "T_NULLSAFE_OBJECT_OPERATOR", "T_LIST", "T_ARRAY", "T_CALLABLE", "T_CLASS_C", "T_TRAIT_C", "T_METHOD_C", "T_FUNC_C", "T_LINE", "T_FILE", "T_START_HEREDOC", "T_END_HEREDOC", "T_DOLLAR_OPEN_CURLY_BRACES", "T_CURLY_OPEN", "T_PAAMAYIM_NEKUDOTAYIM", "T_NAMESPACE", "T_NS_C", "T_DIR", "T_NS_SEPARATOR", "T_ELLIPSIS", "T_NAME_FULLY_QUALIFIED", "T_NAME_QUALIFIED", "T_NAME_RELATIVE", "T_ATTRIBUTE", "';'", "']'", "'{'", "'}'", "'('", "')'", "'`'", "'\"'", "'\$'"); + protected $tokenToSymbol = array(0, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 56, 166, 168, 167, 55, 168, 168, 163, 164, 53, 50, 8, 51, 52, 54, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 31, 159, 44, 16, 46, 30, 68, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 70, 168, 160, 36, 168, 165, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 161, 35, 162, 58, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 37, 38, 39, 40, 41, 42, 43, 45, 47, 48, 49, 57, 59, 60, 61, 62, 63, 64, 65, 66, 67, 69, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158); + protected $action = array(131, 132, 133, 569, 134, 135, 0, 722, 723, 724, 136, 36, 834, 911, 835, 468, -32766, -32766, -32766, -32767, -32767, -32767, -32767, 100, 101, 102, 103, 104, 1068, 1069, 1070, 1067, 1066, 1065, 1071, 716, 715, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32767, -32767, -32767, -32767, -32767, 545, 546, -32766, -32766, 725, -32766, -32766, -32766, 998, 999, 806, 922, 446, 447, 448, 369, 370, 2, 266, 137, 395, 729, 730, 731, 732, 413, -32766, 419, -32766, -32766, -32766, -32766, -32766, 990, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 763, 570, 764, 765, 766, 767, 755, 756, 335, 336, 758, 759, 744, 745, 746, 748, 749, 750, 345, 790, 791, 792, 793, 794, 795, 751, 752, 571, 572, 784, 775, 773, 774, 787, 770, 771, 282, 419, 573, 574, 769, 575, 576, 577, 578, 579, 580, 598, -575, 469, 491, 798, 772, 581, 582, -575, 138, -32766, -32766, -32766, 131, 132, 133, 569, 134, 135, 1017, 722, 723, 724, 136, 36, 1060, -32766, -32766, -32766, 1303, 696, -32766, 1304, -32766, -32766, -32766, -32766, -32766, -32766, -32766, 1068, 1069, 1070, 1067, 1066, 1065, 1071, -32766, 716, 715, 371, 370, 1258, -32766, -32766, -32766, -572, 105, 106, 107, 413, 269, 891, -572, 239, 1193, 1192, 1194, 725, -32766, -32766, -32766, 1046, 108, -32766, -32766, -32766, -32766, 986, 985, 984, 987, 266, 137, 395, 729, 730, 731, 732, 12, -32766, 419, -32766, -32766, -32766, -32766, 998, 999, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 763, 570, 764, 765, 766, 767, 755, 756, 335, 336, 758, 759, 744, 745, 746, 748, 749, 750, 345, 790, 791, 792, 793, 794, 795, 751, 752, 571, 572, 784, 775, 773, 774, 787, 770, 771, 881, 320, 573, 574, 769, 575, 576, 577, 578, 579, 580, -32766, 81, 82, 83, -575, 772, 581, 582, -575, 147, 747, 717, 718, 719, 720, 721, 1278, 722, 723, 724, 760, 761, 35, 1277, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 996, 269, 149, -32766, -32766, -32766, 454, 455, 80, 33, -264, -572, 1016, 108, 319, -572, 893, 725, 682, 803, 127, 998, 999, 592, -32766, 1044, -32766, -32766, -32766, 809, 150, 726, 727, 728, 729, 730, 731, 732, -88, 1198, 796, 277, -526, 282, -32766, -32766, -32766, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 763, 786, 764, 765, 766, 767, 755, 756, 757, 785, 758, 759, 744, 745, 746, 748, 749, 750, 789, 790, 791, 792, 793, 794, 795, 751, 752, 753, 754, 784, 775, 773, 774, 787, 770, 771, 143, 804, 762, 768, 769, 776, 777, 779, 778, 780, 781, -314, -526, -526, -193, -192, 772, 783, 782, 48, 49, 50, 500, 51, 52, 238, 807, -526, -86, 53, 54, -111, 55, 996, 252, -32766, -111, 800, -111, -526, 541, -532, -352, 299, -352, 303, -111, -111, -111, -111, -111, -111, -111, -111, 998, 999, 998, 999, 152, -32766, -32766, -32766, 1191, 807, 125, 305, 1293, 56, 57, 102, 103, 104, -111, 58, 1218, 59, 245, 246, 60, 61, 62, 63, 64, 65, 66, 67, -525, 26, 267, 68, 435, 501, -328, 808, -86, 1224, 1225, 502, 1189, 807, 1198, 1230, 292, 1222, 40, 23, 503, 73, 504, 953, 505, 319, 506, 802, 153, 507, 508, 278, 684, 279, 42, 43, 436, 366, 365, 891, 44, 509, 34, 248, -16, -566, 357, 331, 317, -566, 1198, 1193, 1192, 1194, -527, 510, 511, 512, 332, -524, 1274, 47, 716, 715, -525, -525, 333, 513, 514, 807, 1212, 1213, 1214, 1215, 1209, 1210, 291, 359, 283, -525, 284, -314, 1216, 1211, -193, -192, 1193, 1192, 1194, 292, 891, -525, 363, -531, 69, 807, 315, 316, 319, 30, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, -153, -153, -153, 638, 24, -527, -527, 687, 378, 881, -524, -524, 295, 296, 891, -153, 431, -153, 807, -153, -527, -153, 716, 715, 432, -524, 798, 362, -111, 1105, 1107, 364, -527, 433, 891, 139, 434, -524, 954, 126, -524, 319, -111, -111, 688, 813, 380, -529, 11, 834, 154, 835, 867, -111, -111, -111, -111, 46, 292, -32766, 881, 654, 655, 73, 689, 1191, 1045, 319, 708, 148, 398, 156, -32766, -32766, -32766, 31, -32766, -79, -32766, 122, -32766, 716, 715, -32766, 893, 891, 682, -153, -32766, -32766, -32766, 716, 715, 891, -32766, -32766, 123, 881, 128, 73, -32766, 410, 129, 319, -524, -524, 142, 140, -75, -32766, 157, -529, -529, 319, 26, 691, 158, 881, 159, -524, 160, 293, 294, 698, 367, 368, 807, -73, -32766, -72, 1222, -524, 372, 373, 1191, 893, -71, 682, -529, 72, -70, -32766, -32766, -32766, -69, -32766, -68, -32766, 124, -32766, 630, 631, -32766, -67, -66, -47, -51, -32766, -32766, -32766, -18, 146, 270, -32766, -32766, 276, 697, 700, 881, -32766, 410, 890, 893, 145, 682, 281, 881, 907, -32766, 280, 513, 514, 285, 1212, 1213, 1214, 1215, 1209, 1210, 325, 130, 144, 939, 286, 682, 1216, 1211, 108, 269, -32766, 798, 807, -32766, 662, 639, 1191, 657, 71, 675, 1075, 316, 319, -32766, -32766, -32766, 1305, -32766, 300, -32766, 628, -32766, 430, 543, -32766, -32766, 923, 555, 924, -32766, -32766, -32766, 1229, 549, -32766, -32766, -32766, -4, 891, -490, 1191, -32766, 410, 644, 893, 298, 682, -32766, -32766, -32766, -32766, -32766, 893, -32766, 682, -32766, 13, 1231, -32766, 451, 479, 645, 909, -32766, -32766, -32766, -32766, 658, -480, -32766, -32766, 0, 1191, 0, 0, -32766, 410, 0, 297, -32766, -32766, -32766, 304, -32766, -32766, -32766, 0, -32766, 0, 806, -32766, 0, 0, 0, 474, -32766, -32766, -32766, -32766, 0, 7, -32766, -32766, 15, 1191, 561, 596, -32766, 410, 1219, 891, -32766, -32766, -32766, 361, -32766, -32766, -32766, 818, -32766, -267, 881, -32766, 38, 292, 0, 0, -32766, -32766, -32766, 39, 705, 706, -32766, -32766, 872, 963, 940, 947, -32766, 410, 937, 948, 364, 870, 426, 891, 935, -32766, 1049, 290, 1244, 1052, 1053, -111, -111, 1050, 1051, 1057, -560, 1262, 1296, 633, 0, 826, -111, -111, -111, -111, 32, 314, -32766, 360, 683, 686, 690, 692, 1191, 693, 694, 695, 699, 685, 319, -32766, -32766, -32766, 9, -32766, 702, -32766, 868, -32766, 881, 1300, -32766, 893, 1302, 682, -4, -32766, -32766, -32766, 829, 828, 837, -32766, -32766, 916, -242, -242, -242, -32766, 410, 955, 364, 26, 836, 1301, 915, 917, -32766, 914, 1177, 900, 910, -111, -111, 807, 881, 898, 945, 1222, 946, 1299, 1256, 867, -111, -111, -111, -111, 1245, 1263, 1269, 1272, -241, -241, -241, -558, -532, -531, 364, -530, 1, 27, 28, 37, 41, 45, 70, 0, 74, -111, -111, 75, 76, 77, 78, 893, 79, 682, -242, 867, -111, -111, -111, -111, 141, 151, 155, 244, 321, 346, 514, 347, 1212, 1213, 1214, 1215, 1209, 1210, 348, 349, 350, 351, 352, 353, 1216, 1211, 354, 355, 356, 358, 427, 893, -265, 682, -241, -264, 71, 0, 17, 316, 319, 18, 19, 20, 22, 397, 470, 471, 478, 481, 482, 483, 484, 488, 489, 490, 498, 669, 1202, 1145, 1220, 1019, 1018, 1181, -269, -103, 16, 21, 25, 289, 396, 589, 593, 620, 674, 1149, 1197, 1146, 1275, 0, -494, 1162, 0, 1223); + protected $actionCheck = array(2, 3, 4, 5, 6, 7, 0, 9, 10, 11, 12, 13, 106, 1, 108, 31, 9, 10, 11, 44, 45, 46, 47, 48, 49, 50, 51, 52, 116, 117, 118, 119, 120, 121, 122, 37, 38, 30, 116, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 117, 118, 9, 10, 57, 9, 10, 11, 137, 138, 155, 128, 129, 130, 131, 106, 107, 8, 71, 72, 73, 74, 75, 76, 77, 116, 30, 80, 32, 33, 34, 35, 36, 1, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 30, 80, 136, 137, 138, 139, 140, 141, 142, 143, 144, 51, 1, 161, 101, 80, 150, 151, 152, 8, 154, 9, 10, 11, 2, 3, 4, 5, 6, 7, 164, 9, 10, 11, 12, 13, 123, 9, 10, 11, 80, 161, 30, 83, 32, 33, 34, 35, 36, 37, 38, 116, 117, 118, 119, 120, 121, 122, 30, 37, 38, 106, 107, 1, 9, 10, 11, 1, 53, 54, 55, 116, 57, 1, 8, 14, 155, 156, 157, 57, 9, 10, 11, 162, 69, 30, 116, 32, 33, 119, 120, 121, 122, 71, 72, 73, 74, 75, 76, 77, 8, 30, 80, 32, 33, 34, 35, 137, 138, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 84, 70, 136, 137, 138, 139, 140, 141, 142, 143, 144, 9, 9, 10, 11, 160, 150, 151, 152, 164, 154, 2, 3, 4, 5, 6, 7, 1, 9, 10, 11, 12, 13, 30, 8, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 116, 57, 14, 9, 10, 11, 134, 135, 161, 8, 164, 160, 1, 69, 167, 164, 159, 57, 161, 80, 8, 137, 138, 1, 30, 1, 32, 33, 34, 1, 14, 71, 72, 73, 74, 75, 76, 77, 31, 1, 80, 30, 70, 30, 9, 10, 11, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 8, 156, 136, 137, 138, 139, 140, 141, 142, 143, 144, 8, 134, 135, 8, 8, 150, 151, 152, 2, 3, 4, 5, 6, 7, 97, 82, 149, 31, 12, 13, 101, 15, 116, 8, 116, 106, 80, 108, 161, 85, 163, 106, 113, 108, 8, 116, 117, 118, 119, 120, 121, 122, 123, 137, 138, 137, 138, 14, 9, 10, 11, 80, 82, 14, 8, 85, 50, 51, 50, 51, 52, 128, 56, 1, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 70, 70, 71, 72, 73, 74, 162, 159, 97, 78, 79, 80, 116, 82, 1, 146, 158, 86, 87, 88, 89, 163, 91, 31, 93, 167, 95, 156, 14, 98, 99, 35, 161, 37, 103, 104, 105, 106, 107, 1, 109, 110, 147, 148, 31, 160, 115, 116, 8, 164, 1, 155, 156, 157, 70, 124, 125, 126, 8, 70, 1, 70, 37, 38, 134, 135, 8, 136, 137, 82, 139, 140, 141, 142, 143, 144, 145, 8, 35, 149, 37, 164, 151, 152, 164, 164, 155, 156, 157, 158, 1, 161, 8, 163, 163, 82, 165, 166, 167, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 75, 76, 77, 75, 76, 134, 135, 31, 8, 84, 134, 135, 134, 135, 1, 90, 8, 92, 82, 94, 149, 96, 37, 38, 8, 149, 80, 149, 128, 59, 60, 106, 161, 8, 1, 161, 8, 161, 159, 161, 70, 167, 117, 118, 31, 8, 106, 70, 108, 106, 14, 108, 127, 128, 129, 130, 131, 70, 158, 74, 84, 75, 76, 163, 31, 80, 159, 167, 161, 101, 102, 14, 87, 88, 89, 14, 91, 31, 93, 16, 95, 37, 38, 98, 159, 1, 161, 162, 103, 104, 105, 37, 38, 1, 109, 110, 16, 84, 16, 163, 115, 116, 16, 167, 134, 135, 16, 161, 31, 124, 16, 134, 135, 167, 70, 31, 16, 84, 16, 149, 16, 134, 135, 31, 106, 107, 82, 31, 74, 31, 86, 161, 106, 107, 80, 159, 31, 161, 161, 154, 31, 87, 88, 89, 31, 91, 31, 93, 161, 95, 111, 112, 98, 31, 31, 31, 31, 103, 104, 105, 31, 31, 31, 109, 110, 31, 31, 31, 84, 115, 116, 31, 159, 31, 161, 37, 84, 38, 124, 35, 136, 137, 35, 139, 140, 141, 142, 143, 144, 35, 31, 70, 159, 37, 161, 151, 152, 69, 57, 74, 80, 82, 85, 77, 90, 80, 94, 163, 92, 82, 166, 167, 87, 88, 89, 83, 91, 114, 93, 113, 95, 128, 85, 98, 116, 128, 153, 128, 103, 104, 105, 146, 89, 74, 109, 110, 0, 1, 149, 80, 115, 116, 96, 159, 133, 161, 87, 88, 89, 124, 91, 159, 93, 161, 95, 97, 146, 98, 97, 97, 100, 154, 103, 104, 105, 74, 100, 149, 109, 110, -1, 80, -1, -1, 115, 116, -1, 132, 87, 88, 89, 132, 91, 124, 93, -1, 95, -1, 155, 98, -1, -1, -1, 102, 103, 104, 105, 74, -1, 149, 109, 110, 149, 80, 81, 153, 115, 116, 160, 1, 87, 88, 89, 149, 91, 124, 93, 160, 95, 164, 84, 98, 159, 158, -1, -1, 103, 104, 105, 159, 159, 159, 109, 110, 159, 159, 159, 159, 115, 116, 159, 159, 106, 159, 108, 1, 159, 124, 159, 113, 160, 159, 159, 117, 118, 159, 159, 159, 163, 160, 160, 160, -1, 127, 128, 129, 130, 131, 161, 161, 74, 161, 161, 161, 161, 161, 80, 161, 161, 161, 161, 161, 167, 87, 88, 89, 150, 91, 162, 93, 162, 95, 84, 162, 98, 159, 162, 161, 162, 103, 104, 105, 162, 162, 162, 109, 110, 162, 100, 101, 102, 115, 116, 162, 106, 70, 162, 162, 162, 162, 124, 162, 162, 162, 162, 117, 118, 82, 84, 162, 162, 86, 162, 162, 162, 127, 128, 129, 130, 131, 162, 162, 162, 162, 100, 101, 102, 163, 163, 163, 106, 163, 163, 163, 163, 163, 163, 163, 163, -1, 163, 117, 118, 163, 163, 163, 163, 159, 163, 161, 162, 127, 128, 129, 130, 131, 163, 163, 163, 163, 163, 163, 137, 163, 139, 140, 141, 142, 143, 144, 163, 163, 163, 163, 163, 163, 151, 152, 163, 163, 163, 163, 163, 159, 164, 161, 162, 164, 163, -1, 164, 166, 167, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, -1, 165, 165, -1, 166); + protected $actionBase = array(0, -2, 154, 565, 876, 948, 984, 514, 53, 398, 837, 307, 307, 67, 307, 307, 653, 724, 724, 732, 724, 616, 673, 204, 204, 204, 625, 625, 625, 625, 694, 694, 831, 831, 863, 799, 765, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 375, 519, 369, 701, 1017, 1023, 1019, 1024, 1015, 1014, 1018, 1020, 1025, 911, 912, 782, 918, 919, 920, 921, 1021, 841, 1016, 1022, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 290, 491, 44, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 160, 160, 160, 187, 684, 684, 341, 203, 610, 47, 985, 985, 985, 985, 985, 985, 985, 985, 985, 985, 144, 144, 7, 7, 7, 7, 7, 371, -25, -25, -25, -25, 540, 385, 102, 576, 358, 45, 377, 460, 460, 360, 231, 231, 231, 231, 231, 231, -78, -78, -78, -78, -78, -66, 319, 457, -94, 396, 423, 586, 586, 586, 586, 423, 423, 423, 423, 750, 1029, 423, 423, 423, 511, 516, 516, 518, 147, 147, 147, 516, 583, 777, 422, 583, 422, 194, 92, 748, -40, 87, 412, 748, 617, 627, 198, 143, 773, 658, 773, 1013, 757, 764, 717, 838, 860, 1026, 800, 908, 806, 910, 219, 686, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 855, 552, 1013, 286, 855, 855, 855, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 679, 286, 568, 626, 286, 794, 552, 375, 758, 375, 375, 375, 375, 958, 375, 375, 375, 375, 375, 375, 970, 769, -16, 375, 519, 12, 12, 547, 83, 12, 12, 12, 12, 375, 375, 375, 658, 781, 713, 666, 792, 448, 781, 781, 781, 438, 444, 193, 447, 570, 523, 580, 760, 760, 767, 929, 929, 760, 759, 760, 767, 934, 760, 929, 805, 359, 648, 577, 611, 656, 929, 478, 760, 760, 760, 760, 665, 760, 467, 433, 760, 760, 785, 774, 789, 60, 929, 929, 929, 789, 596, 751, 751, 751, 811, 812, 746, 771, 567, 498, 677, 348, 779, 771, 771, 760, 640, 746, 771, 746, 771, 747, 771, 771, 771, 746, 771, 760, 759, 585, 771, 734, 668, 224, 771, 6, 935, 937, 354, 940, 932, 941, 979, 942, 943, 851, 956, 933, 945, 931, 930, 780, 703, 720, 790, 729, 928, 768, 768, 768, 925, 768, 768, 768, 768, 768, 768, 768, 768, 703, 788, 804, 733, 783, 960, 722, 726, 725, 868, 1027, 1028, 737, 739, 958, 1006, 953, 803, 730, 992, 967, 866, 848, 968, 969, 993, 1007, 1008, 871, 761, 874, 880, 797, 971, 852, 768, 935, 943, 933, 945, 931, 930, 763, 762, 753, 755, 749, 745, 736, 738, 770, 1009, 924, 835, 830, 970, 926, 703, 839, 986, 847, 994, 995, 850, 801, 772, 840, 881, 972, 975, 976, 853, 1010, 810, 989, 795, 996, 802, 882, 997, 998, 999, 1000, 885, 854, 856, 857, 815, 754, 980, 786, 891, 335, 787, 796, 978, 363, 957, 858, 894, 895, 1001, 1002, 1003, 896, 954, 816, 990, 752, 991, 983, 817, 818, 485, 784, 778, 541, 676, 897, 899, 900, 955, 775, 766, 821, 822, 1011, 901, 697, 824, 740, 902, 1005, 742, 744, 756, 859, 793, 743, 798, 977, 776, 827, 907, 829, 832, 833, 1004, 836, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 458, 458, 458, 458, 458, 458, 307, 307, 307, 307, 0, 0, 307, 0, 0, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 423, 423, 291, 291, 0, 291, 423, 423, 423, 423, 423, 423, 423, 423, 423, 423, 291, 291, 291, 291, 291, 291, 291, 805, 147, 147, 147, 147, 423, 423, 423, 423, 423, -88, -88, 147, 147, 423, 423, 423, 423, 423, 423, 423, 423, 423, 423, 423, 423, 0, 0, 0, 286, 422, 0, 759, 759, 759, 759, 0, 0, 0, 0, 422, 422, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 286, 422, 0, 286, 0, 759, 759, 423, 805, 805, 314, 423, 0, 0, 0, 0, 286, 759, 286, 552, 422, 552, 552, 12, 375, 314, 608, 608, 608, 608, 0, 658, 805, 805, 805, 805, 805, 805, 805, 805, 805, 805, 805, 759, 0, 805, 0, 759, 759, 759, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 759, 0, 0, 929, 0, 0, 0, 0, 760, 0, 0, 0, 0, 0, 0, 760, 934, 0, 0, 0, 0, 0, 0, 759, 0, 0, 0, 0, 0, 0, 0, 0, 768, 801, 0, 801, 0, 768, 768, 768); + protected $actionDefault = array(3, 32767, 103, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 101, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 578, 578, 578, 578, 32767, 32767, 246, 103, 32767, 32767, 454, 372, 372, 372, 32767, 32767, 522, 522, 522, 522, 522, 522, 32767, 32767, 32767, 32767, 32767, 32767, 454, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 101, 32767, 32767, 32767, 37, 7, 8, 10, 11, 50, 17, 310, 32767, 32767, 32767, 32767, 103, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 571, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 458, 437, 438, 440, 441, 371, 523, 577, 313, 574, 370, 146, 325, 315, 234, 316, 250, 459, 251, 460, 463, 464, 211, 279, 367, 150, 401, 455, 403, 453, 457, 402, 377, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 375, 376, 456, 434, 433, 432, 399, 32767, 32767, 400, 404, 374, 407, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 103, 32767, 405, 406, 423, 424, 421, 422, 425, 32767, 426, 427, 428, 429, 32767, 32767, 302, 32767, 32767, 351, 349, 414, 415, 302, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 516, 431, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 103, 32767, 101, 518, 396, 398, 486, 409, 410, 408, 378, 32767, 493, 32767, 103, 495, 32767, 32767, 32767, 112, 32767, 32767, 32767, 517, 32767, 524, 524, 32767, 479, 101, 194, 32767, 194, 194, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 585, 479, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 32767, 194, 111, 32767, 32767, 32767, 101, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 189, 32767, 260, 262, 103, 539, 194, 32767, 498, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 491, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 479, 419, 139, 32767, 139, 524, 411, 412, 413, 481, 524, 524, 524, 298, 281, 32767, 32767, 32767, 32767, 496, 496, 101, 101, 101, 101, 491, 32767, 32767, 112, 100, 100, 100, 100, 100, 104, 102, 32767, 32767, 32767, 32767, 100, 32767, 102, 102, 32767, 32767, 217, 208, 215, 102, 32767, 543, 544, 215, 102, 219, 219, 219, 239, 239, 470, 304, 102, 100, 102, 102, 196, 304, 304, 32767, 102, 470, 304, 470, 304, 198, 304, 304, 304, 470, 304, 32767, 32767, 102, 304, 210, 100, 100, 304, 32767, 32767, 32767, 481, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 511, 32767, 528, 541, 417, 418, 420, 526, 442, 443, 444, 445, 446, 447, 448, 450, 573, 32767, 485, 32767, 32767, 32767, 32767, 324, 583, 32767, 583, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 584, 32767, 524, 32767, 32767, 32767, 32767, 416, 9, 76, 43, 44, 52, 58, 502, 503, 504, 505, 499, 500, 506, 501, 32767, 32767, 507, 549, 32767, 32767, 525, 576, 32767, 32767, 32767, 32767, 32767, 32767, 139, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 511, 32767, 137, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 524, 32767, 32767, 32767, 300, 301, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 524, 32767, 32767, 32767, 283, 284, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 278, 32767, 32767, 366, 32767, 32767, 32767, 32767, 345, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 152, 152, 3, 3, 327, 152, 152, 152, 327, 152, 327, 327, 327, 152, 152, 152, 152, 152, 152, 272, 184, 254, 257, 239, 239, 152, 337, 152); + protected $goto = array(193, 193, 670, 421, 643, 1022, 1290, 1290, 824, 415, 307, 308, 328, 563, 313, 420, 329, 422, 622, 801, 678, 341, 586, 1290, 825, 164, 164, 164, 164, 217, 194, 190, 190, 174, 176, 212, 190, 190, 190, 190, 190, 191, 191, 191, 191, 191, 191, 185, 186, 187, 188, 189, 214, 212, 215, 521, 522, 411, 523, 525, 526, 527, 528, 529, 530, 531, 532, 1091, 165, 166, 167, 192, 168, 169, 170, 163, 171, 172, 173, 175, 211, 213, 216, 234, 237, 240, 241, 243, 254, 255, 256, 257, 258, 259, 260, 262, 263, 264, 265, 273, 274, 310, 311, 312, 416, 417, 418, 568, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 177, 233, 178, 195, 196, 197, 235, 185, 186, 187, 188, 189, 214, 1091, 198, 179, 180, 181, 199, 195, 182, 236, 200, 198, 162, 201, 202, 183, 203, 204, 205, 184, 206, 207, 208, 209, 210, 322, 322, 322, 322, 827, 608, 608, 858, 547, 538, 1186, 1221, 1221, 1221, 1221, 1221, 1221, 1221, 1221, 1221, 1221, 1239, 1239, 462, 1264, 1265, 799, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 387, 538, 547, 556, 557, 394, 566, 588, 602, 603, 832, 938, 880, 875, 876, 889, 14, 833, 877, 830, 878, 879, 831, 453, 453, 884, 883, 885, 1187, 250, 250, 560, 453, 1237, 1237, 815, 1043, 1039, 1040, 1237, 1237, 1237, 1237, 1237, 1237, 1237, 1237, 1237, 1237, 820, 820, 1188, 1247, 1248, 247, 247, 247, 247, 249, 251, 342, 343, 339, 1190, 1190, 997, 1190, 997, 1279, 930, 401, 677, 997, 997, 997, 997, 997, 997, 997, 997, 997, 997, 997, 997, 1261, 1261, 414, 1261, 597, 1190, 287, 287, 287, 287, 1190, 1190, 1190, 1190, 959, 344, 1190, 1190, 1190, 1271, 1271, 1271, 1271, 606, 640, 344, 344, 1273, 1273, 1273, 1273, 1063, 1064, 637, 896, 651, 652, 653, 897, 344, 344, 383, 344, 486, 1306, 487, 535, 535, 5, 535, 6, 494, 559, 1257, 1140, 540, 524, 524, 344, 318, 302, 642, 524, 524, 524, 524, 524, 524, 524, 524, 524, 524, 444, 1266, 1267, 618, 619, 932, 932, 932, 932, 820, 428, 444, 926, 933, 330, 533, 533, 533, 533, 1030, 590, 817, 554, 1259, 1259, 1030, 704, 621, 623, 845, 641, 1250, 805, 393, 660, 664, 973, 668, 676, 969, 1183, 553, 842, 823, 1289, 1289, 564, 600, 601, 385, 389, 548, 587, 591, 663, 962, 936, 936, 934, 936, 703, 1289, 537, 971, 966, 438, 901, 1079, 981, 1028, 438, 438, 805, 605, 805, 707, 854, 1292, 978, 463, 539, 551, 1074, 467, 540, 539, 844, 551, 646, 957, 386, 1171, 912, 1032, 838, 1172, 1175, 913, 1176, 943, 567, 456, 457, 458, 0, 850, 0, 1182, 1297, 1298, 253, 253, 0, 399, 400, 0, 0, 0, 649, 0, 650, 423, 403, 404, 405, 840, 661, 0, 423, 0, 406, 0, 0, 848, 337, 1009, 1002, 1006, 1003, 1007, 852, 0, 0, 839, 1185, 495, 0, 0, 0, 0, 438, 438, 438, 438, 438, 438, 438, 438, 438, 438, 438, 0, 0, 438, 595, 609, 612, 613, 614, 615, 634, 635, 636, 680, 853, 841, 1027, 1031, 585, 1056, 0, 681, 667, 667, 941, 673, 1054, 0, 0, 0, 0, 0, 271, 0, 0, 0, 0, 536, 536, 919, 992, 1000, 1004, 1001, 1005, 0, 0, 931, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 976, 976, 0, 1072, 857); + protected $gotoCheck = array(42, 42, 72, 65, 65, 119, 173, 173, 26, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 7, 9, 93, 122, 173, 27, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 23, 23, 23, 23, 15, 104, 104, 45, 75, 75, 20, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 160, 160, 166, 166, 166, 6, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 15, 49, 15, 15, 15, 15, 75, 15, 15, 15, 15, 15, 15, 141, 141, 64, 15, 64, 20, 5, 5, 162, 141, 161, 161, 20, 15, 15, 15, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 22, 22, 20, 20, 20, 5, 5, 5, 5, 5, 5, 93, 93, 169, 72, 72, 72, 72, 72, 171, 89, 89, 89, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 122, 122, 13, 122, 13, 72, 24, 24, 24, 24, 72, 72, 72, 72, 99, 14, 72, 72, 72, 9, 9, 9, 9, 55, 55, 14, 14, 122, 122, 122, 122, 136, 136, 84, 72, 84, 84, 84, 72, 14, 14, 61, 14, 147, 14, 147, 19, 19, 46, 19, 46, 147, 100, 122, 143, 14, 163, 163, 14, 159, 159, 63, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 19, 168, 168, 83, 83, 19, 19, 19, 19, 22, 109, 19, 19, 19, 29, 103, 103, 103, 103, 122, 103, 18, 48, 122, 122, 122, 48, 48, 48, 39, 48, 14, 12, 28, 48, 48, 48, 48, 48, 48, 152, 9, 37, 25, 172, 172, 2, 2, 9, 58, 58, 58, 58, 58, 14, 25, 25, 25, 25, 25, 25, 172, 25, 25, 25, 23, 17, 17, 106, 121, 23, 23, 12, 17, 12, 95, 41, 172, 17, 149, 9, 9, 139, 82, 14, 9, 17, 9, 17, 17, 9, 78, 78, 124, 17, 78, 78, 78, 78, 92, 9, 9, 9, 9, -1, 9, -1, 17, 9, 9, 5, 5, -1, 80, 80, -1, -1, -1, 80, -1, 80, 113, 80, 80, 80, 35, 80, -1, 113, -1, 80, -1, -1, 9, 80, 113, 113, 113, 113, 113, 35, -1, -1, 35, 14, 9, -1, -1, -1, -1, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, -1, -1, 23, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 16, 16, 16, 16, 8, 8, -1, 8, 8, 8, 16, 8, 8, -1, -1, -1, -1, -1, 24, -1, -1, -1, -1, 24, 24, 87, 87, 87, 87, 87, 87, -1, -1, 16, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 103, 103, -1, 16, 16); + protected $gotoBase = array(0, 0, -285, 0, 0, 225, 173, 10, 524, 7, 0, 0, 95, -47, 5, -174, 87, -33, 71, 61, -212, 0, -76, 157, 284, 392, 4, 20, 56, 77, 0, 0, 0, 0, 0, 118, 0, 63, 0, 65, 0, -2, -1, 0, 0, 155, -378, 0, -308, 186, 0, 0, 0, 0, 0, 266, 0, 0, 359, 0, 0, 282, 0, 103, 204, -235, 0, 0, 0, 0, 0, 0, -6, 0, 0, -167, 0, 0, 45, 170, -11, 0, -27, -110, -376, 0, 0, 276, 0, -32, 0, 0, 19, -448, 0, 30, 0, 0, 0, 262, 292, 0, 0, 342, -73, 0, 62, 0, 0, 88, 0, 0, 0, 206, 0, 0, 0, 0, 0, 3, 0, 59, 15, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 1, 0, 188, 0, 66, 0, 0, 0, -157, 0, 2, 0, 0, 35, 0, 0, 0, 0, 0, 0, 25, -57, -8, 201, 99, 0, 0, -111, 0, -7, 231, 0, 236, 96, -295, 0, 0); + protected $gotoDefault = array(-32768, 499, 711, 4, 712, 905, 788, 797, 583, 515, 679, 338, 610, 412, 1255, 882, 1078, 565, 816, 1199, 1207, 445, 819, 323, 701, 864, 865, 866, 390, 375, 381, 388, 632, 611, 480, 851, 441, 843, 472, 846, 440, 855, 161, 409, 497, 859, 3, 861, 542, 892, 376, 869, 377, 656, 871, 550, 873, 874, 384, 391, 392, 1083, 558, 607, 886, 242, 552, 887, 374, 888, 895, 379, 382, 665, 452, 492, 485, 402, 1058, 594, 629, 449, 466, 617, 616, 604, 465, 424, 407, 928, 473, 450, 942, 340, 950, 709, 1090, 624, 475, 958, 625, 965, 968, 516, 517, 464, 980, 268, 983, 476, 1015, 647, 648, 995, 626, 627, 1013, 459, 584, 1021, 442, 1029, 1243, 443, 1033, 261, 1036, 275, 408, 425, 1041, 1042, 8, 1048, 671, 672, 10, 272, 496, 1073, 666, 439, 1089, 429, 1159, 1161, 544, 477, 1179, 1178, 659, 493, 1184, 1246, 437, 518, 460, 309, 519, 301, 326, 306, 534, 288, 327, 520, 461, 1252, 1260, 324, 29, 1280, 1291, 334, 562, 599); + protected $ruleToNonTerminal = array(0, 1, 3, 3, 2, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 9, 10, 11, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18, 21, 21, 22, 23, 23, 24, 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 29, 29, 30, 30, 32, 34, 34, 28, 36, 36, 33, 38, 38, 35, 35, 37, 37, 39, 39, 31, 40, 40, 41, 43, 44, 44, 45, 46, 46, 48, 47, 47, 47, 47, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 25, 25, 68, 68, 71, 71, 70, 69, 69, 62, 74, 74, 75, 75, 76, 76, 77, 77, 78, 78, 26, 26, 27, 27, 27, 27, 86, 86, 88, 88, 81, 81, 81, 82, 82, 85, 85, 83, 83, 89, 90, 90, 56, 56, 64, 64, 67, 67, 67, 66, 91, 91, 92, 57, 57, 57, 57, 93, 93, 94, 94, 95, 95, 96, 97, 97, 98, 98, 99, 99, 54, 54, 50, 50, 101, 52, 52, 102, 51, 51, 53, 53, 63, 63, 63, 63, 79, 79, 105, 105, 107, 107, 108, 108, 108, 108, 106, 106, 106, 110, 110, 110, 110, 87, 87, 113, 113, 113, 111, 111, 114, 114, 112, 112, 115, 115, 116, 116, 116, 116, 109, 109, 80, 80, 80, 20, 20, 20, 118, 117, 117, 119, 119, 119, 119, 59, 120, 120, 121, 60, 123, 123, 124, 124, 125, 125, 84, 126, 126, 126, 126, 126, 126, 131, 131, 132, 132, 133, 133, 133, 133, 133, 134, 135, 135, 130, 130, 127, 127, 129, 129, 137, 137, 136, 136, 136, 136, 136, 136, 136, 128, 138, 138, 140, 139, 139, 61, 100, 141, 141, 55, 55, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 148, 142, 142, 147, 147, 150, 151, 151, 152, 153, 153, 153, 19, 19, 72, 72, 72, 72, 143, 143, 143, 143, 155, 155, 144, 144, 146, 146, 146, 149, 149, 160, 160, 160, 160, 160, 160, 160, 160, 160, 161, 161, 104, 163, 163, 163, 163, 145, 145, 145, 145, 145, 145, 145, 145, 58, 58, 158, 158, 158, 158, 164, 164, 154, 154, 154, 165, 165, 165, 165, 165, 165, 73, 73, 65, 65, 65, 65, 122, 122, 122, 122, 168, 167, 157, 157, 157, 157, 157, 157, 157, 156, 156, 156, 166, 166, 166, 166, 103, 162, 170, 170, 169, 169, 171, 171, 171, 171, 171, 171, 171, 171, 159, 159, 159, 159, 173, 174, 172, 172, 172, 172, 172, 172, 172, 172, 175, 175, 175, 175); + protected $ruleToLength = array(1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 2, 1, 3, 4, 1, 2, 0, 1, 1, 1, 1, 1, 3, 5, 4, 3, 4, 2, 3, 1, 1, 7, 6, 2, 3, 1, 2, 3, 1, 2, 3, 1, 1, 3, 1, 3, 1, 2, 2, 3, 1, 3, 2, 3, 1, 3, 2, 0, 1, 1, 1, 1, 1, 3, 7, 10, 5, 7, 9, 5, 3, 3, 3, 3, 3, 3, 1, 2, 5, 7, 9, 6, 5, 6, 3, 2, 1, 1, 1, 0, 2, 1, 3, 8, 0, 4, 2, 1, 3, 0, 1, 0, 1, 0, 1, 3, 1, 8, 9, 8, 7, 6, 8, 0, 2, 0, 2, 1, 2, 2, 0, 2, 0, 2, 0, 2, 2, 1, 3, 1, 4, 1, 4, 1, 1, 4, 2, 1, 3, 3, 3, 4, 4, 5, 0, 2, 4, 3, 1, 1, 7, 0, 2, 1, 3, 3, 4, 1, 4, 0, 2, 5, 0, 2, 6, 0, 2, 0, 3, 1, 2, 1, 1, 2, 0, 1, 3, 0, 2, 1, 1, 1, 1, 6, 8, 6, 1, 2, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 1, 2, 1, 1, 0, 1, 0, 2, 2, 2, 4, 3, 1, 1, 3, 1, 2, 2, 3, 2, 3, 1, 1, 2, 3, 1, 1, 3, 2, 0, 1, 5, 5, 10, 3, 5, 1, 1, 3, 0, 2, 4, 5, 4, 4, 4, 3, 1, 1, 1, 1, 1, 1, 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 3, 1, 1, 3, 2, 2, 3, 1, 0, 1, 1, 3, 3, 3, 4, 1, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 3, 4, 4, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 2, 1, 2, 4, 2, 2, 8, 9, 8, 9, 9, 10, 9, 10, 8, 3, 2, 0, 4, 2, 1, 3, 2, 2, 2, 4, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 0, 3, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 4, 1, 1, 3, 1, 1, 1, 1, 1, 3, 2, 3, 0, 1, 1, 3, 1, 1, 1, 1, 1, 3, 1, 1, 4, 4, 1, 4, 4, 0, 1, 1, 1, 3, 3, 1, 4, 2, 2, 1, 3, 1, 4, 4, 3, 3, 3, 3, 1, 3, 1, 1, 3, 1, 1, 4, 1, 1, 1, 3, 1, 1, 2, 1, 3, 4, 3, 2, 0, 2, 2, 1, 2, 1, 1, 1, 4, 3, 3, 3, 3, 6, 3, 1, 1, 2, 1); + protected function initReduceCallbacks() { - if (\is_string($paths)) { - $paths = [$paths]; - } - $iterator = (new \PHPUnit\SebastianBergmann\FileIterator\Factory())->getFileIterator($paths, $suffixes, $prefixes, $exclude); - $files = []; - foreach ($iterator as $file) { - $file = $file->getRealPath(); - if ($file) { - $files[] = $file; + $this->reduceCallbacks = [0 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 1 => function ($stackPos) { + $this->semValue = $this->handleNamespaces($this->semStack[$stackPos - (1 - 1)]); + }, 2 => function ($stackPos) { + if (\is_array($this->semStack[$stackPos - (2 - 2)])) { + $this->semValue = \array_merge($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); + } else { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; } - } - foreach ($paths as $path) { - if (\is_file($path)) { - $files[] = \realpath($path); + }, 3 => function ($stackPos) { + $this->semValue = array(); + }, 4 => function ($stackPos) { + $startAttributes = $this->lookaheadStartAttributes; + if (isset($startAttributes['comments'])) { + $nop = new Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); + } else { + $nop = null; } - } - $files = \array_unique($files); - \sort($files); - if ($commonPath) { - return ['commonPath' => $this->getCommonPath($files), 'files' => $files]; - } - return $files; - } - protected function getCommonPath(array $files) : string - { - $count = \count($files); - if ($count === 0) { - return ''; - } - if ($count === 1) { - return \dirname($files[0]) . \DIRECTORY_SEPARATOR; - } - $_files = []; - foreach ($files as $file) { - $_files[] = $_fileParts = \explode(\DIRECTORY_SEPARATOR, $file); - if (empty($_fileParts[0])) { - $_fileParts[0] = \DIRECTORY_SEPARATOR; + if ($nop !== null) { + $this->semStack[$stackPos - (1 - 1)][] = $nop; } - } - $common = ''; - $done = \false; - $j = 0; - $count--; - while (!$done) { - for ($i = 0; $i < $count; $i++) { - if ($_files[$i][$j] != $_files[$i + 1][$j]) { - $done = \true; - break; - } + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 5 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 6 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 7 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 8 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 9 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 10 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 11 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 12 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 13 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 14 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 15 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 16 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 17 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 18 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 19 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 20 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 21 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 22 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 23 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 24 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 25 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 26 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 27 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 28 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 29 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 30 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 31 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 32 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 33 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 34 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 35 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 36 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 37 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 38 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 39 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 40 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 41 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 42 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 43 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 44 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 45 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 46 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 47 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 48 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 49 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 50 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 51 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 52 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 53 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 54 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 55 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 56 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 57 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 58 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 59 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 60 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 61 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 62 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 63 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 64 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 65 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 66 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 67 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 68 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 69 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 70 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 71 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 72 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 73 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 74 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 75 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 76 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 77 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 78 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 79 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 80 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 81 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 82 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 83 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 84 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 85 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 86 => function ($stackPos) { + $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 87 => function ($stackPos) { + $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 88 => function ($stackPos) { + $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 89 => function ($stackPos) { + $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 90 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 91 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 92 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 93 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 94 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 95 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 96 => function ($stackPos) { + $this->semValue = new Name(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 97 => function ($stackPos) { + $this->semValue = new Expr\Variable(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 98 => function ($stackPos) { + /* nothing */ + }, 99 => function ($stackPos) { + /* nothing */ + }, 100 => function ($stackPos) { + /* nothing */ + }, 101 => function ($stackPos) { + $this->emitError(new Error('A trailing comma is not allowed here', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes)); + }, 102 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 103 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 104 => function ($stackPos) { + $this->semValue = new Node\Attribute($this->semStack[$stackPos - (1 - 1)], [], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 105 => function ($stackPos) { + $this->semValue = new Node\Attribute($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 106 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 107 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 108 => function ($stackPos) { + $this->semValue = new Node\AttributeGroup($this->semStack[$stackPos - (4 - 2)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 109 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 110 => function ($stackPos) { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 111 => function ($stackPos) { + $this->semValue = []; + }, 112 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 113 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 114 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 115 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 116 => function ($stackPos) { + $this->semValue = new Stmt\HaltCompiler($this->lexer->handleHaltCompiler(), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 117 => function ($stackPos) { + $this->semValue = new Stmt\Namespace_($this->semStack[$stackPos - (3 - 2)], null, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON); + $this->checkNamespace($this->semValue); + }, 118 => function ($stackPos) { + $this->semValue = new Stmt\Namespace_($this->semStack[$stackPos - (5 - 2)], $this->semStack[$stackPos - (5 - 4)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); + $this->checkNamespace($this->semValue); + }, 119 => function ($stackPos) { + $this->semValue = new Stmt\Namespace_(null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); + $this->checkNamespace($this->semValue); + }, 120 => function ($stackPos) { + $this->semValue = new Stmt\Use_($this->semStack[$stackPos - (3 - 2)], Stmt\Use_::TYPE_NORMAL, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 121 => function ($stackPos) { + $this->semValue = new Stmt\Use_($this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 2)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 122 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 123 => function ($stackPos) { + $this->semValue = new Stmt\Const_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 124 => function ($stackPos) { + $this->semValue = Stmt\Use_::TYPE_FUNCTION; + }, 125 => function ($stackPos) { + $this->semValue = Stmt\Use_::TYPE_CONSTANT; + }, 126 => function ($stackPos) { + $this->semValue = new Stmt\GroupUse($this->semStack[$stackPos - (7 - 3)], $this->semStack[$stackPos - (7 - 6)], $this->semStack[$stackPos - (7 - 2)], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); + }, 127 => function ($stackPos) { + $this->semValue = new Stmt\GroupUse($this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 5)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); + }, 128 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 129 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 130 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 131 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 132 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 133 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 134 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 135 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 136 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 137 => function ($stackPos) { + $this->semValue = new Stmt\UseUse($this->semStack[$stackPos - (1 - 1)], null, Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + $this->checkUseUse($this->semValue, $stackPos - (1 - 1)); + }, 138 => function ($stackPos) { + $this->semValue = new Stmt\UseUse($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + $this->checkUseUse($this->semValue, $stackPos - (3 - 3)); + }, 139 => function ($stackPos) { + $this->semValue = new Stmt\UseUse($this->semStack[$stackPos - (1 - 1)], null, Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + $this->checkUseUse($this->semValue, $stackPos - (1 - 1)); + }, 140 => function ($stackPos) { + $this->semValue = new Stmt\UseUse($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + $this->checkUseUse($this->semValue, $stackPos - (3 - 3)); + }, 141 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + $this->semValue->type = Stmt\Use_::TYPE_NORMAL; + }, 142 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue->type = $this->semStack[$stackPos - (2 - 1)]; + }, 143 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 144 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 145 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 146 => function ($stackPos) { + $this->semValue = new Node\Const_($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 147 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 148 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 149 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 150 => function ($stackPos) { + $this->semValue = new Node\Const_($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 151 => function ($stackPos) { + if (\is_array($this->semStack[$stackPos - (2 - 2)])) { + $this->semValue = \array_merge($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); + } else { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; } - if (!$done) { - $common .= $_files[0][$j]; - if ($j > 0) { - $common .= \DIRECTORY_SEPARATOR; + }, 152 => function ($stackPos) { + $this->semValue = array(); + }, 153 => function ($stackPos) { + $startAttributes = $this->lookaheadStartAttributes; + if (isset($startAttributes['comments'])) { + $nop = new Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); + } else { + $nop = null; + } + if ($nop !== null) { + $this->semStack[$stackPos - (1 - 1)][] = $nop; + } + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 154 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 155 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 156 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 157 => function ($stackPos) { + throw new Error('__HALT_COMPILER() can only be used from the outermost scope', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 158 => function ($stackPos) { + if ($this->semStack[$stackPos - (3 - 2)]) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + $attrs = $this->startAttributeStack[$stackPos - (3 - 1)]; + $stmts = $this->semValue; + if (!empty($attrs['comments'])) { + $stmts[0]->setAttribute('comments', \array_merge($attrs['comments'], $stmts[0]->getAttribute('comments', []))); + } + } else { + $startAttributes = $this->startAttributeStack[$stackPos - (3 - 1)]; + if (isset($startAttributes['comments'])) { + $this->semValue = new Stmt\Nop($startAttributes + $this->endAttributes); + } else { + $this->semValue = null; + } + if (null === $this->semValue) { + $this->semValue = array(); } } - $j++; - } - return \DIRECTORY_SEPARATOR . $common; - } -} -php-file-iterator - -Copyright (c) 2009-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -tokens[] = $token; - } - public function current() : \PHPUnit\TheSeer\Tokenizer\Token - { - return \current($this->tokens); - } - public function key() : int - { - return \key($this->tokens); - } - public function next() : void - { - \next($this->tokens); - $this->pos++; - } - public function valid() : bool - { - return $this->count() > $this->pos; - } - public function rewind() : void - { - \reset($this->tokens); - $this->pos = 0; - } - public function count() : int - { - return \count($this->tokens); - } - public function offsetExists($offset) : bool - { - return isset($this->tokens[$offset]); - } - /** - * @throws TokenCollectionException - */ - public function offsetGet($offset) : \PHPUnit\TheSeer\Tokenizer\Token - { - if (!$this->offsetExists($offset)) { - throw new \PHPUnit\TheSeer\Tokenizer\TokenCollectionException(\sprintf('No Token at offest %s', $offset)); - } - return $this->tokens[$offset]; - } - /** - * @param Token $value - * - * @throws TokenCollectionException - */ - public function offsetSet($offset, $value) : void - { - if (!\is_int($offset)) { - $type = \gettype($offset); - throw new \PHPUnit\TheSeer\Tokenizer\TokenCollectionException(\sprintf('Offset must be of type integer, %s given', $type === 'object' ? \get_class($value) : $type)); - } - if (!$value instanceof \PHPUnit\TheSeer\Tokenizer\Token) { - $type = \gettype($value); - throw new \PHPUnit\TheSeer\Tokenizer\TokenCollectionException(\sprintf('Value must be of type %s, %s given', \PHPUnit\TheSeer\Tokenizer\Token::class, $type === 'object' ? \get_class($value) : $type)); - } - $this->tokens[$offset] = $value; - } - public function offsetUnset($offset) : void - { - unset($this->tokens[$offset]); - } -} -ensureValidUri($value); - $this->value = $value; - } - public function asString() : string - { - return $this->value; - } - private function ensureValidUri($value) : void - { - if (\strpos($value, ':') === \false) { - throw new \PHPUnit\TheSeer\Tokenizer\NamespaceUriException(\sprintf("Namespace URI '%s' must contain at least one colon", $value)); - } - } -} -xmlns = $xmlns; - } - public function toDom(\PHPUnit\TheSeer\Tokenizer\TokenCollection $tokens) : \DOMDocument - { - $dom = new \DOMDocument(); - $dom->preserveWhiteSpace = \false; - $dom->loadXML($this->toXML($tokens)); - return $dom; - } - public function toXML(\PHPUnit\TheSeer\Tokenizer\TokenCollection $tokens) : string - { - $this->writer = new \XMLWriter(); - $this->writer->openMemory(); - $this->writer->setIndent(\true); - $this->writer->startDocument(); - $this->writer->startElement('source'); - $this->writer->writeAttribute('xmlns', $this->xmlns->asString()); - if (\count($tokens) > 0) { - $this->writer->startElement('line'); - $this->writer->writeAttribute('no', '1'); - $this->previousToken = $tokens[0]; - foreach ($tokens as $token) { - $this->addToken($token); - } - } - $this->writer->endElement(); - $this->writer->endElement(); - $this->writer->endDocument(); - return $this->writer->outputMemory(); - } - private function addToken(\PHPUnit\TheSeer\Tokenizer\Token $token) : void - { - if ($this->previousToken->getLine() < $token->getLine()) { - $this->writer->endElement(); - $this->writer->startElement('line'); - $this->writer->writeAttribute('no', (string) $token->getLine()); - $this->previousToken = $token; - } - if ($token->getValue() !== '') { - $this->writer->startElement('token'); - $this->writer->writeAttribute('name', $token->getName()); - $this->writer->writeRaw(\htmlspecialchars($token->getValue(), \ENT_NOQUOTES | \ENT_DISALLOWED | \ENT_XML1)); - $this->writer->endElement(); - } - } -} - 'T_OPEN_BRACKET', ')' => 'T_CLOSE_BRACKET', '[' => 'T_OPEN_SQUARE', ']' => 'T_CLOSE_SQUARE', '{' => 'T_OPEN_CURLY', '}' => 'T_CLOSE_CURLY', ';' => 'T_SEMICOLON', '.' => 'T_DOT', ',' => 'T_COMMA', '=' => 'T_EQUAL', '<' => 'T_LT', '>' => 'T_GT', '+' => 'T_PLUS', '-' => 'T_MINUS', '*' => 'T_MULT', '/' => 'T_DIV', '?' => 'T_QUESTION_MARK', '!' => 'T_EXCLAMATION_MARK', ':' => 'T_COLON', '"' => 'T_DOUBLE_QUOTES', '@' => 'T_AT', '&' => 'T_AMPERSAND', '%' => 'T_PERCENT', '|' => 'T_PIPE', '$' => 'T_DOLLAR', '^' => 'T_CARET', '~' => 'T_TILDE', '`' => 'T_BACKTICK']; - public function parse(string $source) : \PHPUnit\TheSeer\Tokenizer\TokenCollection - { - $result = new \PHPUnit\TheSeer\Tokenizer\TokenCollection(); - if ($source === '') { - return $result; - } - $tokens = \token_get_all($source); - $lastToken = new \PHPUnit\TheSeer\Tokenizer\Token($tokens[0][2], 'Placeholder', ''); - foreach ($tokens as $pos => $tok) { - if (\is_string($tok)) { - $token = new \PHPUnit\TheSeer\Tokenizer\Token($lastToken->getLine(), $this->map[$tok], $tok); - $result->addToken($token); - $lastToken = $token; - continue; - } - $line = $tok[2]; - $values = \preg_split('/\\R+/Uu', $tok[1]); - foreach ($values as $v) { - $token = new \PHPUnit\TheSeer\Tokenizer\Token($line, \token_name($tok[0]), $v); - $lastToken = $token; - $line++; - if ($v === '') { - continue; - } - $result->addToken($token); + }, 159 => function ($stackPos) { + $this->semValue = new Stmt\If_($this->semStack[$stackPos - (7 - 3)], ['stmts' => \is_array($this->semStack[$stackPos - (7 - 5)]) ? $this->semStack[$stackPos - (7 - 5)] : array($this->semStack[$stackPos - (7 - 5)]), 'elseifs' => $this->semStack[$stackPos - (7 - 6)], 'else' => $this->semStack[$stackPos - (7 - 7)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); + }, 160 => function ($stackPos) { + $this->semValue = new Stmt\If_($this->semStack[$stackPos - (10 - 3)], ['stmts' => $this->semStack[$stackPos - (10 - 6)], 'elseifs' => $this->semStack[$stackPos - (10 - 7)], 'else' => $this->semStack[$stackPos - (10 - 8)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); + }, 161 => function ($stackPos) { + $this->semValue = new Stmt\While_($this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + }, 162 => function ($stackPos) { + $this->semValue = new Stmt\Do_($this->semStack[$stackPos - (7 - 5)], \is_array($this->semStack[$stackPos - (7 - 2)]) ? $this->semStack[$stackPos - (7 - 2)] : array($this->semStack[$stackPos - (7 - 2)]), $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); + }, 163 => function ($stackPos) { + $this->semValue = new Stmt\For_(['init' => $this->semStack[$stackPos - (9 - 3)], 'cond' => $this->semStack[$stackPos - (9 - 5)], 'loop' => $this->semStack[$stackPos - (9 - 7)], 'stmts' => $this->semStack[$stackPos - (9 - 9)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); + }, 164 => function ($stackPos) { + $this->semValue = new Stmt\Switch_($this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + }, 165 => function ($stackPos) { + $this->semValue = new Stmt\Break_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 166 => function ($stackPos) { + $this->semValue = new Stmt\Continue_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 167 => function ($stackPos) { + $this->semValue = new Stmt\Return_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 168 => function ($stackPos) { + $this->semValue = new Stmt\Global_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 169 => function ($stackPos) { + $this->semValue = new Stmt\Static_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 170 => function ($stackPos) { + $this->semValue = new Stmt\Echo_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 171 => function ($stackPos) { + $this->semValue = new Stmt\InlineHTML($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 172 => function ($stackPos) { + $e = $this->semStack[$stackPos - (2 - 1)]; + if ($e instanceof Expr\Throw_) { + // For backwards-compatibility reasons, convert throw in statement position into + // Stmt\Throw_ rather than Stmt\Expression(Expr\Throw_). + $this->semValue = new Stmt\Throw_($e->expr, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + } else { + $this->semValue = new Stmt\Expression($e, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); } - } - return $this->fillBlanks($result, $lastToken->getLine()); - } - private function fillBlanks(\PHPUnit\TheSeer\Tokenizer\TokenCollection $tokens, int $maxLine) : \PHPUnit\TheSeer\Tokenizer\TokenCollection - { - /** @var Token $prev */ - $prev = null; - $final = new \PHPUnit\TheSeer\Tokenizer\TokenCollection(); - foreach ($tokens as $token) { - if ($prev === null) { - $final->addToken($token); - $prev = $token; - continue; + }, 173 => function ($stackPos) { + $this->semValue = new Stmt\Unset_($this->semStack[$stackPos - (5 - 3)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + }, 174 => function ($stackPos) { + $this->semValue = new Stmt\Foreach_($this->semStack[$stackPos - (7 - 3)], $this->semStack[$stackPos - (7 - 5)][0], ['keyVar' => null, 'byRef' => $this->semStack[$stackPos - (7 - 5)][1], 'stmts' => $this->semStack[$stackPos - (7 - 7)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); + }, 175 => function ($stackPos) { + $this->semValue = new Stmt\Foreach_($this->semStack[$stackPos - (9 - 3)], $this->semStack[$stackPos - (9 - 7)][0], ['keyVar' => $this->semStack[$stackPos - (9 - 5)], 'byRef' => $this->semStack[$stackPos - (9 - 7)][1], 'stmts' => $this->semStack[$stackPos - (9 - 9)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); + }, 176 => function ($stackPos) { + $this->semValue = new Stmt\Foreach_($this->semStack[$stackPos - (6 - 3)], new Expr\Error($this->startAttributeStack[$stackPos - (6 - 4)] + $this->endAttributeStack[$stackPos - (6 - 4)]), ['stmts' => $this->semStack[$stackPos - (6 - 6)]], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); + }, 177 => function ($stackPos) { + $this->semValue = new Stmt\Declare_($this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + }, 178 => function ($stackPos) { + $this->semValue = new Stmt\TryCatch($this->semStack[$stackPos - (6 - 3)], $this->semStack[$stackPos - (6 - 5)], $this->semStack[$stackPos - (6 - 6)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); + $this->checkTryCatch($this->semValue); + }, 179 => function ($stackPos) { + $this->semValue = new Stmt\Goto_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 180 => function ($stackPos) { + $this->semValue = new Stmt\Label($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 181 => function ($stackPos) { + $this->semValue = array(); + /* means: no statement */ + }, 182 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 183 => function ($stackPos) { + $startAttributes = $this->startAttributeStack[$stackPos - (1 - 1)]; + if (isset($startAttributes['comments'])) { + $this->semValue = new Stmt\Nop($startAttributes + $this->endAttributes); + } else { + $this->semValue = null; } - $gap = $token->getLine() - $prev->getLine(); - while ($gap > 1) { - $linebreak = new \PHPUnit\TheSeer\Tokenizer\Token($prev->getLine() + 1, 'T_WHITESPACE', ''); - $final->addToken($linebreak); - $prev = $linebreak; - $gap--; + if ($this->semValue === null) { + $this->semValue = array(); } - $final->addToken($token); - $prev = $token; - } - $gap = $maxLine - $prev->getLine(); - while ($gap > 0) { - $linebreak = new \PHPUnit\TheSeer\Tokenizer\Token($prev->getLine() + 1, 'T_WHITESPACE', ''); - $final->addToken($linebreak); - $prev = $linebreak; - $gap--; - } - return $final; - } -} -line = $line; - $this->name = $name; - $this->value = $value; - } - public function getLine() : int - { - return $this->line; - } - public function getName() : string - { - return $this->name; - } - public function getValue() : string - { - return $this->value; - } -} -Tokenizer - -Copyright (c) 2017 Arne Blankerts and contributors -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of Arne Blankerts nor the names of contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -lineNumber = $lineNumber; - $this->columnNumber = $columnNumber; - } - /** - * Returns the line number that is covered by this location. - */ - public function getLineNumber() : int - { - return $this->lineNumber; - } - /** - * Returns the column number (character position on a line) for this location object. - */ - public function getColumnNumber() : int - { - return $this->columnNumber; - } -} -fqsen = $fqsen; - if (isset($matches[2])) { - $this->name = $matches[2]; - } else { - $matches = \explode('\\', $fqsen); - $name = \end($matches); - \assert(\is_string($name)); - $this->name = \trim($name, '()'); - } - } - /** - * converts this class to string. - */ - public function __toString() : string - { - return $this->fqsen; - } - /** - * Returns the name of the element without path. - */ - public function getName() : string - { - return $this->name; - } -} -The MIT License (MIT) - -Copyright (c) 2015 phpDocumentor - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Diff; - -use const PHP_INT_SIZE; -use const PREG_SPLIT_DELIM_CAPTURE; -use const PREG_SPLIT_NO_EMPTY; -use function array_shift; -use function array_unshift; -use function array_values; -use function count; -use function current; -use function end; -use function get_class; -use function gettype; -use function is_array; -use function is_object; -use function is_string; -use function key; -use function min; -use function preg_split; -use function prev; -use function reset; -use function sprintf; -use function substr; -use PHPUnit\SebastianBergmann\Diff\Output\DiffOutputBuilderInterface; -use PHPUnit\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; -final class Differ -{ - public const OLD = 0; - public const ADDED = 1; - public const REMOVED = 2; - public const DIFF_LINE_END_WARNING = 3; - public const NO_LINE_END_EOF_WARNING = 4; - /** - * @var DiffOutputBuilderInterface - */ - private $outputBuilder; - /** - * @param DiffOutputBuilderInterface $outputBuilder - * - * @throws InvalidArgumentException - */ - public function __construct($outputBuilder = null) - { - if ($outputBuilder instanceof \PHPUnit\SebastianBergmann\Diff\Output\DiffOutputBuilderInterface) { - $this->outputBuilder = $outputBuilder; - } elseif (null === $outputBuilder) { - $this->outputBuilder = new \PHPUnit\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder(); - } elseif (\is_string($outputBuilder)) { - // PHPUnit 6.1.4, 6.2.0, 6.2.1, 6.2.2, and 6.2.3 support - // @see https://github.com/sebastianbergmann/phpunit/issues/2734#issuecomment-314514056 - // @deprecated - $this->outputBuilder = new \PHPUnit\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder($outputBuilder); - } else { - throw new \PHPUnit\SebastianBergmann\Diff\InvalidArgumentException(\sprintf('Expected builder to be an instance of DiffOutputBuilderInterface, or a string, got %s.', \is_object($outputBuilder) ? 'instance of "' . \get_class($outputBuilder) . '"' : \gettype($outputBuilder) . ' "' . $outputBuilder . '"')); - } - } - /** - * Returns the diff between two arrays or strings as string. - * - * @param array|string $from - * @param array|string $to - */ - public function diff($from, $to, \PHPUnit\SebastianBergmann\Diff\LongestCommonSubsequenceCalculator $lcs = null) : string - { - $diff = $this->diffToArray($this->normalizeDiffInput($from), $this->normalizeDiffInput($to), $lcs); - return $this->outputBuilder->getDiff($diff); - } - /** - * Returns the diff between two arrays or strings as array. - * - * Each array element contains two elements: - * - [0] => mixed $token - * - [1] => 2|1|0 - * - * - 2: REMOVED: $token was removed from $from - * - 1: ADDED: $token was added to $from - * - 0: OLD: $token is not changed in $to - * - * @param array|string $from - * @param array|string $to - * @param LongestCommonSubsequenceCalculator $lcs - */ - public function diffToArray($from, $to, \PHPUnit\SebastianBergmann\Diff\LongestCommonSubsequenceCalculator $lcs = null) : array - { - if (\is_string($from)) { - $from = $this->splitStringByLines($from); - } elseif (!\is_array($from)) { - throw new \PHPUnit\SebastianBergmann\Diff\InvalidArgumentException('"from" must be an array or string.'); - } - if (\is_string($to)) { - $to = $this->splitStringByLines($to); - } elseif (!\is_array($to)) { - throw new \PHPUnit\SebastianBergmann\Diff\InvalidArgumentException('"to" must be an array or string.'); - } - [$from, $to, $start, $end] = self::getArrayDiffParted($from, $to); - if ($lcs === null) { - $lcs = $this->selectLcsImplementation($from, $to); - } - $common = $lcs->calculate(\array_values($from), \array_values($to)); - $diff = []; - foreach ($start as $token) { - $diff[] = [$token, self::OLD]; - } - \reset($from); - \reset($to); - foreach ($common as $token) { - while (($fromToken = \reset($from)) !== $token) { - $diff[] = [\array_shift($from), self::REMOVED]; + /* means: no statement */ + }, 184 => function ($stackPos) { + $this->semValue = array(); + }, 185 => function ($stackPos) { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 186 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 187 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 188 => function ($stackPos) { + $this->semValue = new Stmt\Catch_($this->semStack[$stackPos - (8 - 3)], $this->semStack[$stackPos - (8 - 4)], $this->semStack[$stackPos - (8 - 7)], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); + }, 189 => function ($stackPos) { + $this->semValue = null; + }, 190 => function ($stackPos) { + $this->semValue = new Stmt\Finally_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 191 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 192 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 193 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 194 => function ($stackPos) { + $this->semValue = \false; + }, 195 => function ($stackPos) { + $this->semValue = \true; + }, 196 => function ($stackPos) { + $this->semValue = \false; + }, 197 => function ($stackPos) { + $this->semValue = \true; + }, 198 => function ($stackPos) { + $this->semValue = \false; + }, 199 => function ($stackPos) { + $this->semValue = \true; + }, 200 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 201 => function ($stackPos) { + $this->semValue = []; + }, 202 => function ($stackPos) { + $this->semValue = new Stmt\Function_($this->semStack[$stackPos - (8 - 3)], ['byRef' => $this->semStack[$stackPos - (8 - 2)], 'params' => $this->semStack[$stackPos - (8 - 5)], 'returnType' => $this->semStack[$stackPos - (8 - 7)], 'stmts' => $this->semStack[$stackPos - (8 - 8)], 'attrGroups' => []], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); + }, 203 => function ($stackPos) { + $this->semValue = new Stmt\Function_($this->semStack[$stackPos - (9 - 4)], ['byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 6)], 'returnType' => $this->semStack[$stackPos - (9 - 8)], 'stmts' => $this->semStack[$stackPos - (9 - 9)], 'attrGroups' => $this->semStack[$stackPos - (9 - 1)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); + }, 204 => function ($stackPos) { + $this->semValue = new Stmt\Class_($this->semStack[$stackPos - (8 - 3)], ['type' => $this->semStack[$stackPos - (8 - 2)], 'extends' => $this->semStack[$stackPos - (8 - 4)], 'implements' => $this->semStack[$stackPos - (8 - 5)], 'stmts' => $this->semStack[$stackPos - (8 - 7)], 'attrGroups' => $this->semStack[$stackPos - (8 - 1)]], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); + $this->checkClass($this->semValue, $stackPos - (8 - 3)); + }, 205 => function ($stackPos) { + $this->semValue = new Stmt\Interface_($this->semStack[$stackPos - (7 - 3)], ['extends' => $this->semStack[$stackPos - (7 - 4)], 'stmts' => $this->semStack[$stackPos - (7 - 6)], 'attrGroups' => $this->semStack[$stackPos - (7 - 1)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); + $this->checkInterface($this->semValue, $stackPos - (7 - 3)); + }, 206 => function ($stackPos) { + $this->semValue = new Stmt\Trait_($this->semStack[$stackPos - (6 - 3)], ['stmts' => $this->semStack[$stackPos - (6 - 5)], 'attrGroups' => $this->semStack[$stackPos - (6 - 1)]], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); + }, 207 => function ($stackPos) { + $this->semValue = new Stmt\Enum_($this->semStack[$stackPos - (8 - 3)], ['scalarType' => $this->semStack[$stackPos - (8 - 4)], 'implements' => $this->semStack[$stackPos - (8 - 5)], 'stmts' => $this->semStack[$stackPos - (8 - 7)], 'attrGroups' => $this->semStack[$stackPos - (8 - 1)]], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); + $this->checkEnum($this->semValue, $stackPos - (8 - 3)); + }, 208 => function ($stackPos) { + $this->semValue = null; + }, 209 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 2)]; + }, 210 => function ($stackPos) { + $this->semValue = null; + }, 211 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 2)]; + }, 212 => function ($stackPos) { + $this->semValue = 0; + }, 213 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT; + }, 214 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_FINAL; + }, 215 => function ($stackPos) { + $this->semValue = null; + }, 216 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 2)]; + }, 217 => function ($stackPos) { + $this->semValue = array(); + }, 218 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 2)]; + }, 219 => function ($stackPos) { + $this->semValue = array(); + }, 220 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 2)]; + }, 221 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 222 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 223 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 224 => function ($stackPos) { + $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); + }, 225 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (4 - 2)]; + }, 226 => function ($stackPos) { + $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); + }, 227 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (4 - 2)]; + }, 228 => function ($stackPos) { + $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); + }, 229 => function ($stackPos) { + $this->semValue = null; + }, 230 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (4 - 2)]; + }, 231 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 232 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 233 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 234 => function ($stackPos) { + $this->semValue = new Stmt\DeclareDeclare($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 235 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 236 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (4 - 3)]; + }, 237 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (4 - 2)]; + }, 238 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (5 - 3)]; + }, 239 => function ($stackPos) { + $this->semValue = array(); + }, 240 => function ($stackPos) { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 241 => function ($stackPos) { + $this->semValue = new Stmt\Case_($this->semStack[$stackPos - (4 - 2)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 242 => function ($stackPos) { + $this->semValue = new Stmt\Case_(null, $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 243 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 244 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 245 => function ($stackPos) { + $this->semValue = new Expr\Match_($this->semStack[$stackPos - (7 - 3)], $this->semStack[$stackPos - (7 - 6)], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); + }, 246 => function ($stackPos) { + $this->semValue = []; + }, 247 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 248 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 249 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 250 => function ($stackPos) { + $this->semValue = new Node\MatchArm($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 251 => function ($stackPos) { + $this->semValue = new Node\MatchArm(null, $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 252 => function ($stackPos) { + $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); + }, 253 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (4 - 2)]; + }, 254 => function ($stackPos) { + $this->semValue = array(); + }, 255 => function ($stackPos) { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 256 => function ($stackPos) { + $this->semValue = new Stmt\ElseIf_($this->semStack[$stackPos - (5 - 3)], \is_array($this->semStack[$stackPos - (5 - 5)]) ? $this->semStack[$stackPos - (5 - 5)] : array($this->semStack[$stackPos - (5 - 5)]), $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + }, 257 => function ($stackPos) { + $this->semValue = array(); + }, 258 => function ($stackPos) { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 259 => function ($stackPos) { + $this->semValue = new Stmt\ElseIf_($this->semStack[$stackPos - (6 - 3)], $this->semStack[$stackPos - (6 - 6)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); + }, 260 => function ($stackPos) { + $this->semValue = null; + }, 261 => function ($stackPos) { + $this->semValue = new Stmt\Else_(\is_array($this->semStack[$stackPos - (2 - 2)]) ? $this->semStack[$stackPos - (2 - 2)] : array($this->semStack[$stackPos - (2 - 2)]), $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 262 => function ($stackPos) { + $this->semValue = null; + }, 263 => function ($stackPos) { + $this->semValue = new Stmt\Else_($this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 264 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)], \false); + }, 265 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (2 - 2)], \true); + }, 266 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)], \false); + }, 267 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)], \false); + }, 268 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 269 => function ($stackPos) { + $this->semValue = array(); + }, 270 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 271 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 272 => function ($stackPos) { + $this->semValue = 0; + }, 273 => function ($stackPos) { + $this->checkModifier($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $stackPos - (2 - 2)); + $this->semValue = $this->semStack[$stackPos - (2 - 1)] | $this->semStack[$stackPos - (2 - 2)]; + }, 274 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_PUBLIC; + }, 275 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_PROTECTED; + }, 276 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_PRIVATE; + }, 277 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_READONLY; + }, 278 => function ($stackPos) { + $this->semValue = new Node\Param($this->semStack[$stackPos - (6 - 6)], null, $this->semStack[$stackPos - (6 - 3)], $this->semStack[$stackPos - (6 - 4)], $this->semStack[$stackPos - (6 - 5)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 1)]); + $this->checkParam($this->semValue); + }, 279 => function ($stackPos) { + $this->semValue = new Node\Param($this->semStack[$stackPos - (8 - 6)], $this->semStack[$stackPos - (8 - 8)], $this->semStack[$stackPos - (8 - 3)], $this->semStack[$stackPos - (8 - 4)], $this->semStack[$stackPos - (8 - 5)], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (8 - 2)], $this->semStack[$stackPos - (8 - 1)]); + $this->checkParam($this->semValue); + }, 280 => function ($stackPos) { + $this->semValue = new Node\Param(new Expr\Error($this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes), null, $this->semStack[$stackPos - (6 - 3)], $this->semStack[$stackPos - (6 - 4)], $this->semStack[$stackPos - (6 - 5)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 1)]); + }, 281 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 282 => function ($stackPos) { + $this->semValue = new Node\NullableType($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 283 => function ($stackPos) { + $this->semValue = new Node\UnionType($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 284 => function ($stackPos) { + $this->semValue = new Node\IntersectionType($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 285 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 286 => function ($stackPos) { + $this->semValue = new Node\Name('static', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 287 => function ($stackPos) { + $this->semValue = $this->handleBuiltinTypes($this->semStack[$stackPos - (1 - 1)]); + }, 288 => function ($stackPos) { + $this->semValue = new Node\Identifier('array', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 289 => function ($stackPos) { + $this->semValue = new Node\Identifier('callable', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 290 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)]); + }, 291 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 292 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)]); + }, 293 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 294 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)]); + }, 295 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 296 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)]); + }, 297 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 298 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 299 => function ($stackPos) { + $this->semValue = new Node\NullableType($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 300 => function ($stackPos) { + $this->semValue = new Node\UnionType($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 301 => function ($stackPos) { + $this->semValue = new Node\IntersectionType($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 302 => function ($stackPos) { + $this->semValue = null; + }, 303 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 304 => function ($stackPos) { + $this->semValue = null; + }, 305 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 2)]; + }, 306 => function ($stackPos) { + $this->semValue = null; + }, 307 => function ($stackPos) { + $this->semValue = array(); + }, 308 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (4 - 2)]; + }, 309 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (3 - 2)]); + }, 310 => function ($stackPos) { + $this->semValue = new Node\VariadicPlaceholder($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 311 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 312 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 313 => function ($stackPos) { + $this->semValue = new Node\Arg($this->semStack[$stackPos - (1 - 1)], \false, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 314 => function ($stackPos) { + $this->semValue = new Node\Arg($this->semStack[$stackPos - (2 - 2)], \true, \false, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 315 => function ($stackPos) { + $this->semValue = new Node\Arg($this->semStack[$stackPos - (2 - 2)], \false, \true, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 316 => function ($stackPos) { + $this->semValue = new Node\Arg($this->semStack[$stackPos - (3 - 3)], \false, \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (3 - 1)]); + }, 317 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 318 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 319 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 320 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 321 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 322 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 323 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 324 => function ($stackPos) { + $this->semValue = new Stmt\StaticVar($this->semStack[$stackPos - (1 - 1)], null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 325 => function ($stackPos) { + $this->semValue = new Stmt\StaticVar($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 326 => function ($stackPos) { + if ($this->semStack[$stackPos - (2 - 2)] !== null) { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; } - while (($toToken = \reset($to)) !== $token) { - $diff[] = [\array_shift($to), self::ADDED]; + }, 327 => function ($stackPos) { + $this->semValue = array(); + }, 328 => function ($stackPos) { + $startAttributes = $this->lookaheadStartAttributes; + if (isset($startAttributes['comments'])) { + $nop = new Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); + } else { + $nop = null; } - $diff[] = [$token, self::OLD]; - \array_shift($from); - \array_shift($to); - } - while (($token = \array_shift($from)) !== null) { - $diff[] = [$token, self::REMOVED]; - } - while (($token = \array_shift($to)) !== null) { - $diff[] = [$token, self::ADDED]; - } - foreach ($end as $token) { - $diff[] = [$token, self::OLD]; - } - if ($this->detectUnmatchedLineEndings($diff)) { - \array_unshift($diff, ["#Warning: Strings contain different line endings!\n", self::DIFF_LINE_END_WARNING]); - } - return $diff; - } - /** - * Casts variable to string if it is not a string or array. - * - * @return array|string - */ - private function normalizeDiffInput($input) - { - if (!\is_array($input) && !\is_string($input)) { - return (string) $input; - } - return $input; - } - /** - * Checks if input is string, if so it will split it line-by-line. - */ - private function splitStringByLines(string $input) : array - { - return \preg_split('/(.*\\R)/', $input, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY); - } - private function selectLcsImplementation(array $from, array $to) : \PHPUnit\SebastianBergmann\Diff\LongestCommonSubsequenceCalculator - { - // We do not want to use the time-efficient implementation if its memory - // footprint will probably exceed this value. Note that the footprint - // calculation is only an estimation for the matrix and the LCS method - // will typically allocate a bit more memory than this. - $memoryLimit = 100 * 1024 * 1024; - if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) { - return new \PHPUnit\SebastianBergmann\Diff\MemoryEfficientLongestCommonSubsequenceCalculator(); - } - return new \PHPUnit\SebastianBergmann\Diff\TimeEfficientLongestCommonSubsequenceCalculator(); - } - /** - * Calculates the estimated memory footprint for the DP-based method. - * - * @return float|int - */ - private function calculateEstimatedFootprint(array $from, array $to) - { - $itemSize = \PHP_INT_SIZE === 4 ? 76 : 144; - return $itemSize * \min(\count($from), \count($to)) ** 2; - } - /** - * Returns true if line ends don't match in a diff. - */ - private function detectUnmatchedLineEndings(array $diff) : bool - { - $newLineBreaks = ['' => \true]; - $oldLineBreaks = ['' => \true]; - foreach ($diff as $entry) { - if (self::OLD === $entry[1]) { - $ln = $this->getLinebreak($entry[0]); - $oldLineBreaks[$ln] = \true; - $newLineBreaks[$ln] = \true; - } elseif (self::ADDED === $entry[1]) { - $newLineBreaks[$this->getLinebreak($entry[0])] = \true; - } elseif (self::REMOVED === $entry[1]) { - $oldLineBreaks[$this->getLinebreak($entry[0])] = \true; - } - } - // if either input or output is a single line without breaks than no warning should be raised - if (['' => \true] === $newLineBreaks || ['' => \true] === $oldLineBreaks) { - return \false; - } - // two way compare - foreach ($newLineBreaks as $break => $set) { - if (!isset($oldLineBreaks[$break])) { - return \true; + if ($nop !== null) { + $this->semStack[$stackPos - (1 - 1)][] = $nop; } - } - foreach ($oldLineBreaks as $break => $set) { - if (!isset($newLineBreaks[$break])) { - return \true; + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 329 => function ($stackPos) { + $this->semValue = new Stmt\Property($this->semStack[$stackPos - (5 - 2)], $this->semStack[$stackPos - (5 - 4)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 1)]); + $this->checkProperty($this->semValue, $stackPos - (5 - 2)); + }, 330 => function ($stackPos) { + $this->semValue = new Stmt\ClassConst($this->semStack[$stackPos - (5 - 4)], $this->semStack[$stackPos - (5 - 2)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (5 - 1)]); + $this->checkClassConst($this->semValue, $stackPos - (5 - 2)); + }, 331 => function ($stackPos) { + $this->semValue = new Stmt\ClassMethod($this->semStack[$stackPos - (10 - 5)], ['type' => $this->semStack[$stackPos - (10 - 2)], 'byRef' => $this->semStack[$stackPos - (10 - 4)], 'params' => $this->semStack[$stackPos - (10 - 7)], 'returnType' => $this->semStack[$stackPos - (10 - 9)], 'stmts' => $this->semStack[$stackPos - (10 - 10)], 'attrGroups' => $this->semStack[$stackPos - (10 - 1)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); + $this->checkClassMethod($this->semValue, $stackPos - (10 - 2)); + }, 332 => function ($stackPos) { + $this->semValue = new Stmt\TraitUse($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 333 => function ($stackPos) { + $this->semValue = new Stmt\EnumCase($this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 4)], $this->semStack[$stackPos - (5 - 1)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + }, 334 => function ($stackPos) { + $this->semValue = null; + /* will be skipped */ + }, 335 => function ($stackPos) { + $this->semValue = array(); + }, 336 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 337 => function ($stackPos) { + $this->semValue = array(); + }, 338 => function ($stackPos) { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 339 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Precedence($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 340 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (5 - 1)][0], $this->semStack[$stackPos - (5 - 1)][1], $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 4)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + }, 341 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], $this->semStack[$stackPos - (4 - 3)], null, $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 342 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 343 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 344 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)]); + }, 345 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 346 => function ($stackPos) { + $this->semValue = array(null, $this->semStack[$stackPos - (1 - 1)]); + }, 347 => function ($stackPos) { + $this->semValue = null; + }, 348 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 349 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 350 => function ($stackPos) { + $this->semValue = 0; + }, 351 => function ($stackPos) { + $this->semValue = 0; + }, 352 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 353 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 354 => function ($stackPos) { + $this->checkModifier($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $stackPos - (2 - 2)); + $this->semValue = $this->semStack[$stackPos - (2 - 1)] | $this->semStack[$stackPos - (2 - 2)]; + }, 355 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_PUBLIC; + }, 356 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_PROTECTED; + }, 357 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_PRIVATE; + }, 358 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_STATIC; + }, 359 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT; + }, 360 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_FINAL; + }, 361 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_READONLY; + }, 362 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 363 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 364 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 365 => function ($stackPos) { + $this->semValue = new Node\VarLikeIdentifier(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 366 => function ($stackPos) { + $this->semValue = new Stmt\PropertyProperty($this->semStack[$stackPos - (1 - 1)], null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 367 => function ($stackPos) { + $this->semValue = new Stmt\PropertyProperty($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 368 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 369 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 370 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 371 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 372 => function ($stackPos) { + $this->semValue = array(); + }, 373 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 374 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 375 => function ($stackPos) { + $this->semValue = new Expr\Assign($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 376 => function ($stackPos) { + $this->semValue = new Expr\Assign($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 377 => function ($stackPos) { + $this->semValue = new Expr\Assign($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 378 => function ($stackPos) { + $this->semValue = new Expr\AssignRef($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 379 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 380 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 381 => function ($stackPos) { + $this->semValue = new Expr\Clone_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 382 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Plus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 383 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Minus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 384 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Mul($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 385 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Div($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 386 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Concat($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 387 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Mod($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 388 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 389 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\BitwiseOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 390 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\BitwiseXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 391 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\ShiftLeft($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 392 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\ShiftRight($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 393 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Pow($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 394 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Coalesce($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 395 => function ($stackPos) { + $this->semValue = new Expr\PostInc($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 396 => function ($stackPos) { + $this->semValue = new Expr\PreInc($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 397 => function ($stackPos) { + $this->semValue = new Expr\PostDec($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 398 => function ($stackPos) { + $this->semValue = new Expr\PreDec($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 399 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BooleanOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 400 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BooleanAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 401 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 402 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 403 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 404 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 405 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 406 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 407 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 408 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Concat($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 409 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Plus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 410 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Minus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 411 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Mul($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 412 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Div($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 413 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Mod($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 414 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\ShiftLeft($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 415 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\ShiftRight($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 416 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Pow($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 417 => function ($stackPos) { + $this->semValue = new Expr\UnaryPlus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 418 => function ($stackPos) { + $this->semValue = new Expr\UnaryMinus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 419 => function ($stackPos) { + $this->semValue = new Expr\BooleanNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 420 => function ($stackPos) { + $this->semValue = new Expr\BitwiseNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 421 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Identical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 422 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\NotIdentical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 423 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Equal($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 424 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\NotEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 425 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Spaceship($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 426 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Smaller($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 427 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\SmallerOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 428 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Greater($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 429 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\GreaterOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 430 => function ($stackPos) { + $this->semValue = new Expr\Instanceof_($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 431 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 432 => function ($stackPos) { + $this->semValue = new Expr\Ternary($this->semStack[$stackPos - (5 - 1)], $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + }, 433 => function ($stackPos) { + $this->semValue = new Expr\Ternary($this->semStack[$stackPos - (4 - 1)], null, $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 434 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Coalesce($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 435 => function ($stackPos) { + $this->semValue = new Expr\Isset_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 436 => function ($stackPos) { + $this->semValue = new Expr\Empty_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 437 => function ($stackPos) { + $this->semValue = new Expr\Include_($this->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_INCLUDE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 438 => function ($stackPos) { + $this->semValue = new Expr\Include_($this->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_INCLUDE_ONCE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 439 => function ($stackPos) { + $this->semValue = new Expr\Eval_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 440 => function ($stackPos) { + $this->semValue = new Expr\Include_($this->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_REQUIRE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 441 => function ($stackPos) { + $this->semValue = new Expr\Include_($this->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_REQUIRE_ONCE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 442 => function ($stackPos) { + $this->semValue = new Expr\Cast\Int_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 443 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes; + $attrs['kind'] = $this->getFloatCastKind($this->semStack[$stackPos - (2 - 1)]); + $this->semValue = new Expr\Cast\Double($this->semStack[$stackPos - (2 - 2)], $attrs); + }, 444 => function ($stackPos) { + $this->semValue = new Expr\Cast\String_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 445 => function ($stackPos) { + $this->semValue = new Expr\Cast\Array_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 446 => function ($stackPos) { + $this->semValue = new Expr\Cast\Object_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 447 => function ($stackPos) { + $this->semValue = new Expr\Cast\Bool_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 448 => function ($stackPos) { + $this->semValue = new Expr\Cast\Unset_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 449 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes; + $attrs['kind'] = \strtolower($this->semStack[$stackPos - (2 - 1)]) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE; + $this->semValue = new Expr\Exit_($this->semStack[$stackPos - (2 - 2)], $attrs); + }, 450 => function ($stackPos) { + $this->semValue = new Expr\ErrorSuppress($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 451 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 452 => function ($stackPos) { + $this->semValue = new Expr\ShellExec($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 453 => function ($stackPos) { + $this->semValue = new Expr\Print_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 454 => function ($stackPos) { + $this->semValue = new Expr\Yield_(null, null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 455 => function ($stackPos) { + $this->semValue = new Expr\Yield_($this->semStack[$stackPos - (2 - 2)], null, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 456 => function ($stackPos) { + $this->semValue = new Expr\Yield_($this->semStack[$stackPos - (4 - 4)], $this->semStack[$stackPos - (4 - 2)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 457 => function ($stackPos) { + $this->semValue = new Expr\YieldFrom($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 458 => function ($stackPos) { + $this->semValue = new Expr\Throw_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 459 => function ($stackPos) { + $this->semValue = new Expr\ArrowFunction(['static' => \false, 'byRef' => $this->semStack[$stackPos - (8 - 2)], 'params' => $this->semStack[$stackPos - (8 - 4)], 'returnType' => $this->semStack[$stackPos - (8 - 6)], 'expr' => $this->semStack[$stackPos - (8 - 8)], 'attrGroups' => []], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); + }, 460 => function ($stackPos) { + $this->semValue = new Expr\ArrowFunction(['static' => \true, 'byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 5)], 'returnType' => $this->semStack[$stackPos - (9 - 7)], 'expr' => $this->semStack[$stackPos - (9 - 9)], 'attrGroups' => []], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); + }, 461 => function ($stackPos) { + $this->semValue = new Expr\Closure(['static' => \false, 'byRef' => $this->semStack[$stackPos - (8 - 2)], 'params' => $this->semStack[$stackPos - (8 - 4)], 'uses' => $this->semStack[$stackPos - (8 - 6)], 'returnType' => $this->semStack[$stackPos - (8 - 7)], 'stmts' => $this->semStack[$stackPos - (8 - 8)], 'attrGroups' => []], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); + }, 462 => function ($stackPos) { + $this->semValue = new Expr\Closure(['static' => \true, 'byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 5)], 'uses' => $this->semStack[$stackPos - (9 - 7)], 'returnType' => $this->semStack[$stackPos - (9 - 8)], 'stmts' => $this->semStack[$stackPos - (9 - 9)], 'attrGroups' => []], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); + }, 463 => function ($stackPos) { + $this->semValue = new Expr\ArrowFunction(['static' => \false, 'byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 5)], 'returnType' => $this->semStack[$stackPos - (9 - 7)], 'expr' => $this->semStack[$stackPos - (9 - 9)], 'attrGroups' => $this->semStack[$stackPos - (9 - 1)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); + }, 464 => function ($stackPos) { + $this->semValue = new Expr\ArrowFunction(['static' => \true, 'byRef' => $this->semStack[$stackPos - (10 - 4)], 'params' => $this->semStack[$stackPos - (10 - 6)], 'returnType' => $this->semStack[$stackPos - (10 - 8)], 'expr' => $this->semStack[$stackPos - (10 - 10)], 'attrGroups' => $this->semStack[$stackPos - (10 - 1)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); + }, 465 => function ($stackPos) { + $this->semValue = new Expr\Closure(['static' => \false, 'byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 5)], 'uses' => $this->semStack[$stackPos - (9 - 7)], 'returnType' => $this->semStack[$stackPos - (9 - 8)], 'stmts' => $this->semStack[$stackPos - (9 - 9)], 'attrGroups' => $this->semStack[$stackPos - (9 - 1)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); + }, 466 => function ($stackPos) { + $this->semValue = new Expr\Closure(['static' => \true, 'byRef' => $this->semStack[$stackPos - (10 - 4)], 'params' => $this->semStack[$stackPos - (10 - 6)], 'uses' => $this->semStack[$stackPos - (10 - 8)], 'returnType' => $this->semStack[$stackPos - (10 - 9)], 'stmts' => $this->semStack[$stackPos - (10 - 10)], 'attrGroups' => $this->semStack[$stackPos - (10 - 1)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); + }, 467 => function ($stackPos) { + $this->semValue = array(new Stmt\Class_(null, ['type' => 0, 'extends' => $this->semStack[$stackPos - (8 - 4)], 'implements' => $this->semStack[$stackPos - (8 - 5)], 'stmts' => $this->semStack[$stackPos - (8 - 7)], 'attrGroups' => $this->semStack[$stackPos - (8 - 1)]], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes), $this->semStack[$stackPos - (8 - 3)]); + $this->checkClass($this->semValue[0], -1); + }, 468 => function ($stackPos) { + $this->semValue = new Expr\New_($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 469 => function ($stackPos) { + list($class, $ctorArgs) = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = new Expr\New_($class, $ctorArgs, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 470 => function ($stackPos) { + $this->semValue = array(); + }, 471 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (4 - 3)]; + }, 472 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 473 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 474 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 475 => function ($stackPos) { + $this->semValue = new Expr\ClosureUse($this->semStack[$stackPos - (2 - 2)], $this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 476 => function ($stackPos) { + $this->semValue = new Expr\FuncCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 477 => function ($stackPos) { + $this->semValue = new Expr\FuncCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 478 => function ($stackPos) { + $this->semValue = new Expr\StaticCall($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 479 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 480 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 481 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 482 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 483 => function ($stackPos) { + $this->semValue = new Name\FullyQualified(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 484 => function ($stackPos) { + $this->semValue = new Name\Relative(\substr($this->semStack[$stackPos - (1 - 1)], 10), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 485 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 486 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 487 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 488 => function ($stackPos) { + $this->semValue = new Expr\Error($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + $this->errorState = 2; + }, 489 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 490 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 491 => function ($stackPos) { + $this->semValue = null; + }, 492 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 493 => function ($stackPos) { + $this->semValue = array(); + }, 494 => function ($stackPos) { + $this->semValue = array(new Scalar\EncapsedStringPart(Scalar\String_::parseEscapeSequences($this->semStack[$stackPos - (1 - 1)], '`'), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes)); + }, 495 => function ($stackPos) { + foreach ($this->semStack[$stackPos - (1 - 1)] as $s) { + if ($s instanceof Node\Scalar\EncapsedStringPart) { + $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', \true); + } } - } - return \false; - } - private function getLinebreak($line) : string - { - if (!\is_string($line)) { - return ''; - } - $lc = \substr($line, -1); - if ("\r" === $lc) { - return "\r"; - } - if ("\n" !== $lc) { - return ''; - } - if ("\r\n" === \substr($line, -2)) { - return "\r\n"; - } - return "\n"; - } - private static function getArrayDiffParted(array &$from, array &$to) : array - { - $start = []; - $end = []; - \reset($to); - foreach ($from as $k => $v) { - $toK = \key($to); - if ($toK === $k && $v === $to[$k]) { - $start[$k] = $v; - unset($from[$k], $to[$k]); - } else { - break; + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 496 => function ($stackPos) { + $this->semValue = array(); + }, 497 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 498 => function ($stackPos) { + $this->semValue = new Expr\ConstFetch($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 499 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Line($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 500 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\File($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 501 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Dir($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 502 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Class_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 503 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Trait_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 504 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Method($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 505 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Function_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 506 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Namespace_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 507 => function ($stackPos) { + $this->semValue = new Expr\ClassConstFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 508 => function ($stackPos) { + $this->semValue = new Expr\ClassConstFetch($this->semStack[$stackPos - (3 - 1)], new Expr\Error($this->startAttributeStack[$stackPos - (3 - 3)] + $this->endAttributeStack[$stackPos - (3 - 3)]), $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + $this->errorState = 2; + }, 509 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes; + $attrs['kind'] = Expr\Array_::KIND_SHORT; + $this->semValue = new Expr\Array_($this->semStack[$stackPos - (3 - 2)], $attrs); + }, 510 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes; + $attrs['kind'] = Expr\Array_::KIND_LONG; + $this->semValue = new Expr\Array_($this->semStack[$stackPos - (4 - 3)], $attrs); + }, 511 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 512 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes; + $attrs['kind'] = $this->semStack[$stackPos - (1 - 1)][0] === "'" || $this->semStack[$stackPos - (1 - 1)][1] === "'" && ($this->semStack[$stackPos - (1 - 1)][0] === 'b' || $this->semStack[$stackPos - (1 - 1)][0] === 'B') ? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED; + $this->semValue = new Scalar\String_(Scalar\String_::parse($this->semStack[$stackPos - (1 - 1)]), $attrs); + }, 513 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes; + $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED; + foreach ($this->semStack[$stackPos - (3 - 2)] as $s) { + if ($s instanceof Node\Scalar\EncapsedStringPart) { + $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', \true); + } } - } - \end($from); - \end($to); - do { - $fromK = \key($from); - $toK = \key($to); - if (null === $fromK || null === $toK || \current($from) !== \current($to)) { - break; + $this->semValue = new Scalar\Encapsed($this->semStack[$stackPos - (3 - 2)], $attrs); + }, 514 => function ($stackPos) { + $this->semValue = $this->parseLNumber($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 515 => function ($stackPos) { + $this->semValue = new Scalar\DNumber(Scalar\DNumber::parse($this->semStack[$stackPos - (1 - 1)]), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 516 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 517 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 518 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 519 => function ($stackPos) { + $this->semValue = $this->parseDocString($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (3 - 3)] + $this->endAttributeStack[$stackPos - (3 - 3)], \true); + }, 520 => function ($stackPos) { + $this->semValue = $this->parseDocString($this->semStack[$stackPos - (2 - 1)], '', $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (2 - 2)] + $this->endAttributeStack[$stackPos - (2 - 2)], \true); + }, 521 => function ($stackPos) { + $this->semValue = $this->parseDocString($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (3 - 3)] + $this->endAttributeStack[$stackPos - (3 - 3)], \true); + }, 522 => function ($stackPos) { + $this->semValue = null; + }, 523 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 524 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 525 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 526 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 527 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 528 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 529 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 530 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 531 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 532 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 533 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 534 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 535 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 536 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 537 => function ($stackPos) { + $this->semValue = new Expr\MethodCall($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 538 => function ($stackPos) { + $this->semValue = new Expr\NullsafeMethodCall($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 539 => function ($stackPos) { + $this->semValue = null; + }, 540 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 541 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 542 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 543 => function ($stackPos) { + $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 544 => function ($stackPos) { + $this->semValue = new Expr\NullsafePropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 545 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 546 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 547 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 548 => function ($stackPos) { + $this->semValue = new Expr\Variable(new Expr\Error($this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes), $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + $this->errorState = 2; + }, 549 => function ($stackPos) { + $var = $this->semStack[$stackPos - (1 - 1)]->name; + $this->semValue = \is_string($var) ? new Node\VarLikeIdentifier($var, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes) : $var; + }, 550 => function ($stackPos) { + $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 551 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 552 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 553 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 554 => function ($stackPos) { + $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 555 => function ($stackPos) { + $this->semValue = new Expr\NullsafePropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 556 => function ($stackPos) { + $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 557 => function ($stackPos) { + $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 558 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 559 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 560 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 561 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 562 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 563 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 564 => function ($stackPos) { + $this->semValue = new Expr\Error($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + $this->errorState = 2; + }, 565 => function ($stackPos) { + $this->semValue = new Expr\List_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 566 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + $end = \count($this->semValue) - 1; + if ($this->semValue[$end] === null) { + \array_pop($this->semValue); } - \prev($from); - \prev($to); - $end = [$fromK => $from[$fromK]] + $end; - unset($from[$fromK], $to[$toK]); - } while (\true); - return [$from, $to, $start, $end]; + }, 567 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 568 => function ($stackPos) { + /* do nothing -- prevent default action of $$=$this->semStack[$1]. See $551. */ + }, 569 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 570 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 571 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 572 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (2 - 2)], null, \true, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 573 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 574 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (3 - 3)], $this->semStack[$stackPos - (3 - 1)], \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 575 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (4 - 4)], $this->semStack[$stackPos - (4 - 1)], \true, $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 576 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (3 - 3)], $this->semStack[$stackPos - (3 - 1)], \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 577 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (2 - 2)], null, \false, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes, \true, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 578 => function ($stackPos) { + $this->semValue = null; + }, 579 => function ($stackPos) { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 580 => function ($stackPos) { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 581 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 582 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); + }, 583 => function ($stackPos) { + $this->semValue = new Scalar\EncapsedStringPart($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 584 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 585 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 586 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 587 => function ($stackPos) { + $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 588 => function ($stackPos) { + $this->semValue = new Expr\NullsafePropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 589 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 590 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 591 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 4)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); + }, 592 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 593 => function ($stackPos) { + $this->semValue = new Scalar\String_($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 594 => function ($stackPos) { + $this->semValue = $this->parseNumString($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 595 => function ($stackPos) { + $this->semValue = $this->parseNumString('-' . $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 596 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }]; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Diff; +namespace PHPUnit\PhpParser\Parser; -use Throwable; -interface Exception extends \Throwable +/* GENERATED file based on grammar/tokens.y */ +final class Tokens { + const YYERRTOK = 256; + const T_THROW = 257; + const T_INCLUDE = 258; + const T_INCLUDE_ONCE = 259; + const T_EVAL = 260; + const T_REQUIRE = 261; + const T_REQUIRE_ONCE = 262; + const T_LOGICAL_OR = 263; + const T_LOGICAL_XOR = 264; + const T_LOGICAL_AND = 265; + const T_PRINT = 266; + const T_YIELD = 267; + const T_DOUBLE_ARROW = 268; + const T_YIELD_FROM = 269; + const T_PLUS_EQUAL = 270; + const T_MINUS_EQUAL = 271; + const T_MUL_EQUAL = 272; + const T_DIV_EQUAL = 273; + const T_CONCAT_EQUAL = 274; + const T_MOD_EQUAL = 275; + const T_AND_EQUAL = 276; + const T_OR_EQUAL = 277; + const T_XOR_EQUAL = 278; + const T_SL_EQUAL = 279; + const T_SR_EQUAL = 280; + const T_POW_EQUAL = 281; + const T_COALESCE_EQUAL = 282; + const T_COALESCE = 283; + const T_BOOLEAN_OR = 284; + const T_BOOLEAN_AND = 285; + const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG = 286; + const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG = 287; + const T_IS_EQUAL = 288; + const T_IS_NOT_EQUAL = 289; + const T_IS_IDENTICAL = 290; + const T_IS_NOT_IDENTICAL = 291; + const T_SPACESHIP = 292; + const T_IS_SMALLER_OR_EQUAL = 293; + const T_IS_GREATER_OR_EQUAL = 294; + const T_SL = 295; + const T_SR = 296; + const T_INSTANCEOF = 297; + const T_INC = 298; + const T_DEC = 299; + const T_INT_CAST = 300; + const T_DOUBLE_CAST = 301; + const T_STRING_CAST = 302; + const T_ARRAY_CAST = 303; + const T_OBJECT_CAST = 304; + const T_BOOL_CAST = 305; + const T_UNSET_CAST = 306; + const T_POW = 307; + const T_NEW = 308; + const T_CLONE = 309; + const T_EXIT = 310; + const T_IF = 311; + const T_ELSEIF = 312; + const T_ELSE = 313; + const T_ENDIF = 314; + const T_LNUMBER = 315; + const T_DNUMBER = 316; + const T_STRING = 317; + const T_STRING_VARNAME = 318; + const T_VARIABLE = 319; + const T_NUM_STRING = 320; + const T_INLINE_HTML = 321; + const T_ENCAPSED_AND_WHITESPACE = 322; + const T_CONSTANT_ENCAPSED_STRING = 323; + const T_ECHO = 324; + const T_DO = 325; + const T_WHILE = 326; + const T_ENDWHILE = 327; + const T_FOR = 328; + const T_ENDFOR = 329; + const T_FOREACH = 330; + const T_ENDFOREACH = 331; + const T_DECLARE = 332; + const T_ENDDECLARE = 333; + const T_AS = 334; + const T_SWITCH = 335; + const T_MATCH = 336; + const T_ENDSWITCH = 337; + const T_CASE = 338; + const T_DEFAULT = 339; + const T_BREAK = 340; + const T_CONTINUE = 341; + const T_GOTO = 342; + const T_FUNCTION = 343; + const T_FN = 344; + const T_CONST = 345; + const T_RETURN = 346; + const T_TRY = 347; + const T_CATCH = 348; + const T_FINALLY = 349; + const T_USE = 350; + const T_INSTEADOF = 351; + const T_GLOBAL = 352; + const T_STATIC = 353; + const T_ABSTRACT = 354; + const T_FINAL = 355; + const T_PRIVATE = 356; + const T_PROTECTED = 357; + const T_PUBLIC = 358; + const T_READONLY = 359; + const T_VAR = 360; + const T_UNSET = 361; + const T_ISSET = 362; + const T_EMPTY = 363; + const T_HALT_COMPILER = 364; + const T_CLASS = 365; + const T_TRAIT = 366; + const T_INTERFACE = 367; + const T_ENUM = 368; + const T_EXTENDS = 369; + const T_IMPLEMENTS = 370; + const T_OBJECT_OPERATOR = 371; + const T_NULLSAFE_OBJECT_OPERATOR = 372; + const T_LIST = 373; + const T_ARRAY = 374; + const T_CALLABLE = 375; + const T_CLASS_C = 376; + const T_TRAIT_C = 377; + const T_METHOD_C = 378; + const T_FUNC_C = 379; + const T_LINE = 380; + const T_FILE = 381; + const T_START_HEREDOC = 382; + const T_END_HEREDOC = 383; + const T_DOLLAR_OPEN_CURLY_BRACES = 384; + const T_CURLY_OPEN = 385; + const T_PAAMAYIM_NEKUDOTAYIM = 386; + const T_NAMESPACE = 387; + const T_NS_C = 388; + const T_DIR = 389; + const T_NS_SEPARATOR = 390; + const T_ELLIPSIS = 391; + const T_NAME_FULLY_QUALIFIED = 392; + const T_NAME_QUALIFIED = 393; + const T_NAME_RELATIVE = 394; + const T_ATTRIBUTE = 395; } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Diff; +namespace PHPUnit\PhpParser\Parser; -class InvalidArgumentException extends \InvalidArgumentException implements \PHPUnit\SebastianBergmann\Diff\Exception +use PHPUnit\PhpParser\Error; +use PHPUnit\PhpParser\ErrorHandler; +use PHPUnit\PhpParser\Parser; +class Multiple implements Parser { -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Diff; - -use function get_class; -use function gettype; -use function is_object; -use function sprintf; -use Exception; -final class ConfigurationException extends \PHPUnit\SebastianBergmann\Diff\InvalidArgumentException -{ - public function __construct(string $option, string $expected, $value, int $code = 0, \Exception $previous = null) - { - parent::__construct(\sprintf('Option "%s" must be %s, got "%s".', $option, $expected, \is_object($value) ? \get_class($value) : (null === $value ? '' : \gettype($value) . '#' . $value)), $code, $previous); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Diff; - -final class Diff -{ - /** - * @var string - */ - private $from; - /** - * @var string - */ - private $to; - /** - * @var Chunk[] - */ - private $chunks; - /** - * @param Chunk[] $chunks - */ - public function __construct(string $from, string $to, array $chunks = []) - { - $this->from = $from; - $this->to = $to; - $this->chunks = $chunks; - } - public function getFrom() : string - { - return $this->from; - } - public function getTo() : string - { - return $this->to; - } - /** - * @return Chunk[] - */ - public function getChunks() : array - { - return $this->chunks; - } + /** @var Parser[] List of parsers to try, in order of preference */ + private $parsers; /** - * @param Chunk[] $chunks + * Create a parser which will try multiple parsers in an order of preference. + * + * Parsers will be invoked in the order they're provided to the constructor. If one of the + * parsers runs without throwing, it's output is returned. Otherwise the exception that the + * first parser generated is thrown. + * + * @param Parser[] $parsers */ - public function setChunks(array $chunks) : void + public function __construct(array $parsers) { - $this->chunks = $chunks; + $this->parsers = $parsers; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Diff; - -use function array_pop; -use function count; -use function max; -use function preg_match; -use function preg_split; -/** - * Unified diff parser. - */ -final class Parser -{ - /** - * @return Diff[] - */ - public function parse(string $string) : array + public function parse(string $code, ErrorHandler $errorHandler = null) { - $lines = \preg_split('(\\r\\n|\\r|\\n)', $string); - if (!empty($lines) && $lines[\count($lines) - 1] === '') { - \array_pop($lines); - } - $lineCount = \count($lines); - $diffs = []; - $diff = null; - $collected = []; - for ($i = 0; $i < $lineCount; ++$i) { - if (\preg_match('#^---\\h+"?(?P[^\\v\\t"]+)#', $lines[$i], $fromMatch) && \preg_match('#^\\+\\+\\+\\h+"?(?P[^\\v\\t"]+)#', $lines[$i + 1], $toMatch)) { - if ($diff !== null) { - $this->parseFileDiff($diff, $collected); - $diffs[] = $diff; - $collected = []; - } - $diff = new \PHPUnit\SebastianBergmann\Diff\Diff($fromMatch['file'], $toMatch['file']); - ++$i; - } else { - if (\preg_match('/^(?:diff --git |index [\\da-f\\.]+|[+-]{3} [ab])/', $lines[$i])) { - continue; - } - $collected[] = $lines[$i]; - } + if (null === $errorHandler) { + $errorHandler = new ErrorHandler\Throwing(); } - if ($diff !== null && \count($collected)) { - $this->parseFileDiff($diff, $collected); - $diffs[] = $diff; + list($firstStmts, $firstError) = $this->tryParse($this->parsers[0], $errorHandler, $code); + if ($firstError === null) { + return $firstStmts; } - return $diffs; - } - private function parseFileDiff(\PHPUnit\SebastianBergmann\Diff\Diff $diff, array $lines) : void - { - $chunks = []; - $chunk = null; - $diffLines = []; - foreach ($lines as $line) { - if (\preg_match('/^@@\\s+-(?P\\d+)(?:,\\s*(?P\\d+))?\\s+\\+(?P\\d+)(?:,\\s*(?P\\d+))?\\s+@@/', $line, $match)) { - $chunk = new \PHPUnit\SebastianBergmann\Diff\Chunk((int) $match['start'], isset($match['startrange']) ? \max(1, (int) $match['startrange']) : 1, (int) $match['end'], isset($match['endrange']) ? \max(1, (int) $match['endrange']) : 1); - $chunks[] = $chunk; - $diffLines = []; - continue; - } - if (\preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { - $type = \PHPUnit\SebastianBergmann\Diff\Line::UNCHANGED; - if ($match['type'] === '+') { - $type = \PHPUnit\SebastianBergmann\Diff\Line::ADDED; - } elseif ($match['type'] === '-') { - $type = \PHPUnit\SebastianBergmann\Diff\Line::REMOVED; - } - $diffLines[] = new \PHPUnit\SebastianBergmann\Diff\Line($type, $match['line']); - if (null !== $chunk) { - $chunk->setLines($diffLines); - } + for ($i = 1, $c = \count($this->parsers); $i < $c; ++$i) { + list($stmts, $error) = $this->tryParse($this->parsers[$i], $errorHandler, $code); + if ($error === null) { + return $stmts; } } - $diff->setChunks($chunks); + throw $firstError; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Diff\Output; - -use function count; -abstract class AbstractChunkOutputBuilder implements \PHPUnit\SebastianBergmann\Diff\Output\DiffOutputBuilderInterface -{ - /** - * Takes input of the diff array and returns the common parts. - * Iterates through diff line by line. - */ - protected function getCommonChunks(array $diff, int $lineThreshold = 5) : array + private function tryParse(Parser $parser, ErrorHandler $errorHandler, $code) { - $diffSize = \count($diff); - $capturing = \false; - $chunkStart = 0; - $chunkSize = 0; - $commonChunks = []; - for ($i = 0; $i < $diffSize; ++$i) { - if ($diff[$i][1] === 0) { - if ($capturing === \false) { - $capturing = \true; - $chunkStart = $i; - $chunkSize = 0; - } else { - ++$chunkSize; - } - } elseif ($capturing !== \false) { - if ($chunkSize >= $lineThreshold) { - $commonChunks[$chunkStart] = $chunkStart + $chunkSize; - } - $capturing = \false; - } - } - if ($capturing !== \false && $chunkSize >= $lineThreshold) { - $commonChunks[$chunkStart] = $chunkStart + $chunkSize; + $stmts = null; + $error = null; + try { + $stmts = $parser->parse($code, $errorHandler); + } catch (Error $error) { } - return $commonChunks; + return [$stmts, $error]; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Diff\Output; - -/** - * Defines how an output builder should take a generated - * diff array and return a string representation of that diff. - */ -interface DiffOutputBuilderInterface -{ - public function getDiff(array $diff) : string; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Diff\Output; +namespace PHPUnit\PhpParser\Parser; -use function array_splice; -use function count; -use function fclose; -use function fopen; -use function fwrite; -use function max; -use function min; -use function stream_get_contents; -use function strlen; -use function substr; -use PHPUnit\SebastianBergmann\Diff\Differ; -/** - * Builds a diff string representation in unified diff format in chunks. +use PHPUnit\PhpParser\Error; +use PHPUnit\PhpParser\Node; +use PHPUnit\PhpParser\Node\Expr; +use PHPUnit\PhpParser\Node\Name; +use PHPUnit\PhpParser\Node\Scalar; +use PHPUnit\PhpParser\Node\Stmt; +/* This is an automatically GENERATED file, which should not be manually edited. + * Instead edit one of the following: + * * the grammar files grammar/php5.y or grammar/php7.y + * * the skeleton file grammar/parser.template + * * the preprocessing script grammar/rebuildParsers.php */ -final class UnifiedDiffOutputBuilder extends \PHPUnit\SebastianBergmann\Diff\Output\AbstractChunkOutputBuilder +class Php5 extends \PHPUnit\PhpParser\ParserAbstract { - /** - * @var bool - */ - private $collapseRanges = \true; - /** - * @var int >= 0 - */ - private $commonLineThreshold = 6; - /** - * @var int >= 0 - */ - private $contextLines = 3; - /** - * @var string - */ - private $header; - /** - * @var bool - */ - private $addLineNumbers; - public function __construct(string $header = "--- Original\n+++ New\n", bool $addLineNumbers = \false) - { - $this->header = $header; - $this->addLineNumbers = $addLineNumbers; - } - public function getDiff(array $diff) : string - { - $buffer = \fopen('php://memory', 'r+b'); - if ('' !== $this->header) { - \fwrite($buffer, $this->header); - if ("\n" !== \substr($this->header, -1, 1)) { - \fwrite($buffer, "\n"); - } - } - if (0 !== \count($diff)) { - $this->writeDiffHunks($buffer, $diff); - } - $diff = \stream_get_contents($buffer, -1, 0); - \fclose($buffer); - // If the diff is non-empty and last char is not a linebreak: add it. - // This might happen when both the `from` and `to` do not have a trailing linebreak - $last = \substr($diff, -1); - return 0 !== \strlen($diff) && "\n" !== $last && "\r" !== $last ? $diff . "\n" : $diff; - } - private function writeDiffHunks($output, array $diff) : void - { - // detect "No newline at end of file" and insert into `$diff` if needed - $upperLimit = \count($diff); - if (0 === $diff[$upperLimit - 1][1]) { - $lc = \substr($diff[$upperLimit - 1][0], -1); - if ("\n" !== $lc) { - \array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", \PHPUnit\SebastianBergmann\Diff\Differ::NO_LINE_END_EOF_WARNING]]); - } - } else { - // search back for the last `+` and `-` line, - // check if has trailing linebreak, else add under it warning under it - $toFind = [1 => \true, 2 => \true]; - for ($i = $upperLimit - 1; $i >= 0; --$i) { - if (isset($toFind[$diff[$i][1]])) { - unset($toFind[$diff[$i][1]]); - $lc = \substr($diff[$i][0], -1); - if ("\n" !== $lc) { - \array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", \PHPUnit\SebastianBergmann\Diff\Differ::NO_LINE_END_EOF_WARNING]]); - } - if (!\count($toFind)) { - break; - } - } - } - } - // write hunks to output buffer - $cutOff = \max($this->commonLineThreshold, $this->contextLines); - $hunkCapture = \false; - $sameCount = $toRange = $fromRange = 0; - $toStart = $fromStart = 1; - $i = 0; - /** @var int $i */ - foreach ($diff as $i => $entry) { - if (0 === $entry[1]) { - // same - if (\false === $hunkCapture) { - ++$fromStart; - ++$toStart; - continue; - } - ++$sameCount; - ++$toRange; - ++$fromRange; - if ($sameCount === $cutOff) { - $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; - // note: $contextEndOffset = $this->contextLines; - // - // because we never go beyond the end of the diff. - // with the cutoff/contextlines here the follow is never true; - // - // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { - // $contextEndOffset = count($diff) - 1; - // } - // - // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop - $this->writeHunk($diff, $hunkCapture - $contextStartOffset, $i - $cutOff + $this->contextLines + 1, $fromStart - $contextStartOffset, $fromRange - $cutOff + $contextStartOffset + $this->contextLines, $toStart - $contextStartOffset, $toRange - $cutOff + $contextStartOffset + $this->contextLines, $output); - $fromStart += $fromRange; - $toStart += $toRange; - $hunkCapture = \false; - $sameCount = $toRange = $fromRange = 0; - } - continue; - } - $sameCount = 0; - if ($entry[1] === \PHPUnit\SebastianBergmann\Diff\Differ::NO_LINE_END_EOF_WARNING) { - continue; - } - if (\false === $hunkCapture) { - $hunkCapture = $i; - } - if (\PHPUnit\SebastianBergmann\Diff\Differ::ADDED === $entry[1]) { - ++$toRange; - } - if (\PHPUnit\SebastianBergmann\Diff\Differ::REMOVED === $entry[1]) { - ++$fromRange; - } - } - if (\false === $hunkCapture) { - return; - } - // we end here when cutoff (commonLineThreshold) was not reached, but we where capturing a hunk, - // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold - $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; - // prevent trying to write out more common lines than there are in the diff _and_ - // do not write more than configured through the context lines - $contextEndOffset = \min($sameCount, $this->contextLines); - $fromRange -= $sameCount; - $toRange -= $sameCount; - $this->writeHunk($diff, $hunkCapture - $contextStartOffset, $i - $sameCount + $contextEndOffset + 1, $fromStart - $contextStartOffset, $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, $output); - } - private function writeHunk(array $diff, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output) : void + protected $tokenToSymbolMapSize = 396; + protected $actionTableSize = 1093; + protected $gotoTableSize = 643; + protected $invalidSymbol = 168; + protected $errorSymbol = 1; + protected $defaultAction = -32766; + protected $unexpectedTokenRule = 32767; + protected $YY2TBLSTATE = 415; + protected $numNonLeafStates = 662; + protected $symbolToName = array("EOF", "error", "T_THROW", "T_INCLUDE", "T_INCLUDE_ONCE", "T_EVAL", "T_REQUIRE", "T_REQUIRE_ONCE", "','", "T_LOGICAL_OR", "T_LOGICAL_XOR", "T_LOGICAL_AND", "T_PRINT", "T_YIELD", "T_DOUBLE_ARROW", "T_YIELD_FROM", "'='", "T_PLUS_EQUAL", "T_MINUS_EQUAL", "T_MUL_EQUAL", "T_DIV_EQUAL", "T_CONCAT_EQUAL", "T_MOD_EQUAL", "T_AND_EQUAL", "T_OR_EQUAL", "T_XOR_EQUAL", "T_SL_EQUAL", "T_SR_EQUAL", "T_POW_EQUAL", "T_COALESCE_EQUAL", "'?'", "':'", "T_COALESCE", "T_BOOLEAN_OR", "T_BOOLEAN_AND", "'|'", "'^'", "T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG", "T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG", "T_IS_EQUAL", "T_IS_NOT_EQUAL", "T_IS_IDENTICAL", "T_IS_NOT_IDENTICAL", "T_SPACESHIP", "'<'", "T_IS_SMALLER_OR_EQUAL", "'>'", "T_IS_GREATER_OR_EQUAL", "T_SL", "T_SR", "'+'", "'-'", "'.'", "'*'", "'/'", "'%'", "'!'", "T_INSTANCEOF", "'~'", "T_INC", "T_DEC", "T_INT_CAST", "T_DOUBLE_CAST", "T_STRING_CAST", "T_ARRAY_CAST", "T_OBJECT_CAST", "T_BOOL_CAST", "T_UNSET_CAST", "'@'", "T_POW", "'['", "T_NEW", "T_CLONE", "T_EXIT", "T_IF", "T_ELSEIF", "T_ELSE", "T_ENDIF", "T_LNUMBER", "T_DNUMBER", "T_STRING", "T_STRING_VARNAME", "T_VARIABLE", "T_NUM_STRING", "T_INLINE_HTML", "T_ENCAPSED_AND_WHITESPACE", "T_CONSTANT_ENCAPSED_STRING", "T_ECHO", "T_DO", "T_WHILE", "T_ENDWHILE", "T_FOR", "T_ENDFOR", "T_FOREACH", "T_ENDFOREACH", "T_DECLARE", "T_ENDDECLARE", "T_AS", "T_SWITCH", "T_MATCH", "T_ENDSWITCH", "T_CASE", "T_DEFAULT", "T_BREAK", "T_CONTINUE", "T_GOTO", "T_FUNCTION", "T_FN", "T_CONST", "T_RETURN", "T_TRY", "T_CATCH", "T_FINALLY", "T_USE", "T_INSTEADOF", "T_GLOBAL", "T_STATIC", "T_ABSTRACT", "T_FINAL", "T_PRIVATE", "T_PROTECTED", "T_PUBLIC", "T_VAR", "T_UNSET", "T_ISSET", "T_EMPTY", "T_HALT_COMPILER", "T_CLASS", "T_TRAIT", "T_INTERFACE", "T_EXTENDS", "T_IMPLEMENTS", "T_OBJECT_OPERATOR", "T_LIST", "T_ARRAY", "T_CALLABLE", "T_CLASS_C", "T_TRAIT_C", "T_METHOD_C", "T_FUNC_C", "T_LINE", "T_FILE", "T_START_HEREDOC", "T_END_HEREDOC", "T_DOLLAR_OPEN_CURLY_BRACES", "T_CURLY_OPEN", "T_PAAMAYIM_NEKUDOTAYIM", "T_NAMESPACE", "T_NS_C", "T_DIR", "T_NS_SEPARATOR", "T_ELLIPSIS", "T_NAME_FULLY_QUALIFIED", "T_NAME_QUALIFIED", "T_NAME_RELATIVE", "';'", "'{'", "'}'", "'('", "')'", "'\$'", "'`'", "']'", "'\"'", "T_READONLY", "T_ENUM", "T_NULLSAFE_OBJECT_OPERATOR", "T_ATTRIBUTE"); + protected $tokenToSymbol = array(0, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 56, 163, 168, 160, 55, 168, 168, 158, 159, 53, 50, 8, 51, 52, 54, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 31, 155, 44, 16, 46, 30, 68, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 70, 168, 162, 36, 168, 161, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 156, 35, 157, 58, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 37, 38, 39, 40, 41, 42, 43, 45, 47, 48, 49, 57, 59, 60, 61, 62, 63, 64, 65, 66, 67, 69, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 164, 122, 123, 124, 125, 126, 127, 128, 129, 165, 130, 131, 132, 166, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 167); + protected $action = array(699, 669, 670, 671, 672, 673, 286, 674, 675, 676, 712, 713, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 0, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32767, -32767, -32767, -32767, 245, 246, 242, 243, 244, -32766, -32766, 677, -32766, 750, -32766, -32766, -32766, -32766, -32766, -32766, -32766, 1224, 245, 246, 1225, 678, 679, 680, 681, 682, 683, 684, -32766, 48, 746, -32766, -32766, -32766, -32766, -32766, -32766, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 715, 738, 716, 717, 718, 719, 707, 708, 709, 737, 710, 711, 696, 697, 698, 700, 701, 702, 740, 741, 742, 743, 744, 745, 703, 704, 705, 706, 736, 727, 725, 726, 722, 723, 751, 714, 720, 721, 728, 729, 731, 730, 732, 733, 55, 56, 425, 57, 58, 724, 735, 734, 1073, 59, 60, -224, 61, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, 121, -32767, -32767, -32767, -32767, 29, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 1043, 766, 1071, 767, 580, 62, 63, -32766, -32766, -32766, -32766, 64, 516, 65, 294, 295, 66, 67, 68, 69, 70, 71, 72, 73, 822, 25, 302, 74, 418, 981, 983, 1043, 1181, 1095, 1096, 1073, 748, 754, 1075, 1074, 1076, 469, -32766, -32766, -32766, 337, 823, 54, -32767, -32767, -32767, -32767, 98, 99, 100, 101, 102, 220, 221, 222, 78, 361, 1107, -32766, 341, -32766, -32766, -32766, -32766, -32766, 1107, 492, 949, 950, 951, 948, 947, 946, 207, 477, 478, 949, 950, 951, 948, 947, 946, 1043, 479, 480, 52, 1101, 1102, 1103, 1104, 1098, 1099, 319, 872, 668, 667, 27, -511, 1105, 1100, -32766, 130, 1075, 1074, 1076, 345, 668, 667, 41, 126, 341, 334, 369, 336, 426, -128, -128, -128, 896, 897, 468, 220, 221, 222, 811, 1195, 619, 40, 21, 427, -128, 470, -128, 471, -128, 472, -128, 802, 428, -4, 823, 54, 207, 33, 34, 429, 360, 317, 28, 35, 473, -32766, -32766, -32766, 211, 356, 357, 474, 475, -32766, -32766, -32766, 754, 476, 49, 313, 794, 843, 430, 431, 289, 125, -32766, 813, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32767, -32767, -32767, -32767, -32767, -32766, -32766, -32766, 769, 103, 104, 105, 327, 307, 825, 633, -128, 1075, 1074, 1076, 221, 222, 927, 748, 1146, 106, -32766, 129, -32766, -32766, -32766, -32766, 426, 823, 54, 902, 873, 302, 468, 75, 207, 359, 811, 668, 667, 40, 21, 427, 754, 470, 754, 471, 423, 472, 1043, 127, 428, 435, 1043, 341, 1043, 33, 34, 429, 360, 1181, 415, 35, 473, 122, 10, 315, 128, 356, 357, 474, 475, -32766, -32766, -32766, 768, 476, 668, 667, 758, 843, 430, 431, 754, 1043, 1147, -32766, -32766, -32766, 754, 419, 342, 1215, -32766, 131, -32766, -32766, -32766, 341, 363, 346, 426, 823, 54, 100, 101, 102, 468, 825, 633, -4, 811, 442, 903, 40, 21, 427, 754, 470, 435, 471, 341, 472, 341, 766, 428, 767, -209, -209, -209, 33, 34, 429, 360, 479, 1196, 35, 473, 345, -32766, -32766, -32766, 356, 357, 474, 475, 220, 221, 222, 421, 476, 32, 297, 794, 843, 430, 431, 754, 754, 435, -32766, 341, -32766, -32766, 9, 300, 51, 207, 249, 324, 753, 120, 220, 221, 222, 426, 30, 247, 941, 422, 424, 468, 825, 633, -209, 811, 1043, 1061, 40, 21, 427, 129, 470, 207, 471, 341, 472, 804, 20, 428, 124, -208, -208, -208, 33, 34, 429, 360, 479, 212, 35, 473, 923, -259, 823, 54, 356, 357, 474, 475, -32766, -32766, -32766, 1043, 476, 213, 806, 794, 843, 430, 431, -32766, -32766, 435, 435, 341, 341, 443, 79, 80, 81, -32766, 668, 667, 636, 344, 808, 668, 667, 239, 240, 241, 123, 214, 538, 250, 825, 633, -208, 36, 251, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 252, 307, 426, 220, 221, 222, 823, 54, 468, -32766, 222, 765, 811, 106, 134, 40, 21, 427, 571, 470, 207, 471, 445, 472, 207, -32766, 428, 896, 897, 207, 307, 33, 34, 429, 245, 246, 637, 35, 473, 452, 22, 809, 922, 356, 357, 457, 588, 135, 374, 595, 596, 476, -228, 759, 639, 938, 653, 926, 661, -86, 823, 54, 314, 644, 647, 821, 133, 836, 43, 106, 603, 44, 45, 46, 47, 748, 50, 53, 132, 426, 302, -32766, 520, 825, 633, 468, -84, 607, 577, 811, 641, 362, 40, 21, 427, -278, 470, 754, 471, 954, 472, 441, 627, 428, 823, 54, 574, 844, 33, 34, 429, 11, 615, 845, 35, 473, 444, 461, 285, -511, 356, 357, 592, -419, 593, 1106, 1153, -410, 476, 368, 838, 38, 658, 426, 645, 795, 1052, 0, 325, 468, 0, -32766, 0, 811, 0, 0, 40, 21, 427, 0, 470, 0, 471, 0, 472, 0, 322, 428, 823, 54, 825, 633, 33, 34, 429, 0, 326, 0, 35, 473, 323, 0, 316, 318, 356, 357, -512, 426, 0, 753, 531, 0, 476, 468, 6, 0, 0, 811, 650, 7, 40, 21, 427, 12, 470, 14, 471, 373, 472, -420, 562, 428, 823, 54, 78, -225, 33, 34, 429, 39, 656, 657, 35, 473, 859, 633, 764, 812, 356, 357, 820, 799, 814, 875, 866, 867, 476, 797, 860, 857, 855, 426, 933, 934, 931, 819, 803, 468, 805, 807, 810, 811, 930, 762, 40, 21, 427, 763, 470, 932, 471, 335, 472, 358, 634, 428, 638, 640, 825, 633, 33, 34, 429, 642, 643, 646, 35, 473, 648, 649, 651, 652, 356, 357, 635, 426, 1221, 1223, 761, 842, 476, 468, 248, 760, 841, 811, 1222, 840, 40, 21, 427, 1057, 470, 830, 471, 1045, 472, 839, 1046, 428, 828, 215, 216, 939, 33, 34, 429, 217, 864, 218, 35, 473, 825, 633, 24, 865, 356, 357, 456, 1220, 1189, 209, 1187, 1172, 476, 1185, 215, 216, 1086, 1095, 1096, 914, 217, 1193, 218, 1183, -224, 1097, 26, 31, 37, 42, 76, 77, 210, 288, 209, 292, 293, 308, 309, 310, 311, 339, 1095, 1096, 825, 633, 355, 291, 416, 1152, 1097, 16, 17, 18, 393, 453, 460, 462, 466, 552, 624, 1048, 1051, 904, 1111, 1047, 1023, 563, 1022, 1088, 0, 0, -429, 558, 1041, 1101, 1102, 1103, 1104, 1098, 1099, 398, 1054, 1053, 1056, 1055, 1070, 1105, 1100, 1186, 1171, 1167, 1184, 1085, 1218, 1112, 1166, 219, 558, 599, 1101, 1102, 1103, 1104, 1098, 1099, 398, 0, 0, 0, 0, 0, 1105, 1100, 0, 0, 0, 0, 0, 0, 0, 0, 219); + protected $actionCheck = array(2, 3, 4, 5, 6, 7, 14, 9, 10, 11, 12, 13, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 0, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 9, 10, 11, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 69, 70, 53, 54, 55, 9, 10, 57, 30, 80, 32, 33, 34, 35, 36, 37, 38, 80, 69, 70, 83, 71, 72, 73, 74, 75, 76, 77, 9, 70, 80, 33, 34, 35, 36, 37, 38, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 153, 133, 134, 135, 136, 137, 138, 139, 140, 141, 3, 4, 5, 6, 7, 147, 148, 149, 80, 12, 13, 159, 15, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 156, 44, 45, 46, 47, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 13, 106, 116, 108, 85, 50, 51, 33, 34, 35, 36, 56, 85, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 1, 70, 71, 72, 73, 59, 60, 13, 82, 78, 79, 80, 80, 82, 152, 153, 154, 86, 9, 10, 11, 8, 1, 2, 44, 45, 46, 47, 48, 49, 50, 51, 52, 9, 10, 11, 156, 106, 143, 30, 160, 32, 33, 34, 35, 36, 143, 116, 116, 117, 118, 119, 120, 121, 30, 124, 125, 116, 117, 118, 119, 120, 121, 13, 133, 134, 70, 136, 137, 138, 139, 140, 141, 142, 31, 37, 38, 8, 132, 148, 149, 116, 156, 152, 153, 154, 160, 37, 38, 158, 8, 160, 161, 8, 163, 74, 75, 76, 77, 134, 135, 80, 9, 10, 11, 84, 1, 80, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 155, 98, 0, 1, 2, 30, 103, 104, 105, 106, 132, 8, 109, 110, 9, 10, 11, 8, 115, 116, 117, 118, 9, 10, 11, 82, 123, 70, 8, 126, 127, 128, 129, 8, 156, 30, 155, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 9, 10, 11, 157, 53, 54, 55, 8, 57, 155, 156, 157, 152, 153, 154, 10, 11, 157, 80, 162, 69, 30, 151, 32, 33, 34, 35, 74, 1, 2, 159, 155, 71, 80, 151, 30, 8, 84, 37, 38, 87, 88, 89, 82, 91, 82, 93, 8, 95, 13, 156, 98, 158, 13, 160, 13, 103, 104, 105, 106, 82, 108, 109, 110, 156, 8, 113, 31, 115, 116, 117, 118, 9, 10, 11, 157, 123, 37, 38, 126, 127, 128, 129, 82, 13, 159, 33, 34, 35, 82, 127, 8, 85, 30, 156, 32, 33, 34, 160, 8, 147, 74, 1, 2, 50, 51, 52, 80, 155, 156, 157, 84, 31, 159, 87, 88, 89, 82, 91, 158, 93, 160, 95, 160, 106, 98, 108, 100, 101, 102, 103, 104, 105, 106, 133, 159, 109, 110, 160, 9, 10, 11, 115, 116, 117, 118, 9, 10, 11, 8, 123, 144, 145, 126, 127, 128, 129, 82, 82, 158, 30, 160, 32, 33, 108, 8, 70, 30, 31, 113, 152, 16, 9, 10, 11, 74, 14, 14, 122, 8, 8, 80, 155, 156, 157, 84, 13, 159, 87, 88, 89, 151, 91, 30, 93, 160, 95, 155, 159, 98, 14, 100, 101, 102, 103, 104, 105, 106, 133, 16, 109, 110, 155, 157, 1, 2, 115, 116, 117, 118, 9, 10, 11, 13, 123, 16, 155, 126, 127, 128, 129, 33, 34, 158, 158, 160, 160, 156, 9, 10, 11, 30, 37, 38, 31, 70, 155, 37, 38, 50, 51, 52, 156, 16, 81, 16, 155, 156, 157, 30, 16, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 16, 57, 74, 9, 10, 11, 1, 2, 80, 116, 11, 155, 84, 69, 156, 87, 88, 89, 160, 91, 30, 93, 132, 95, 30, 33, 98, 134, 135, 30, 57, 103, 104, 105, 69, 70, 31, 109, 110, 75, 76, 155, 155, 115, 116, 75, 76, 101, 102, 111, 112, 123, 159, 155, 156, 155, 156, 155, 156, 31, 1, 2, 31, 31, 31, 31, 31, 38, 70, 69, 77, 70, 70, 70, 70, 80, 70, 70, 70, 74, 71, 85, 85, 155, 156, 80, 97, 96, 100, 84, 31, 106, 87, 88, 89, 82, 91, 82, 93, 82, 95, 89, 92, 98, 1, 2, 90, 127, 103, 104, 105, 97, 94, 127, 109, 110, 97, 97, 97, 132, 115, 116, 100, 146, 113, 143, 143, 146, 123, 106, 151, 155, 157, 74, 31, 157, 162, -1, 114, 80, -1, 116, -1, 84, -1, -1, 87, 88, 89, -1, 91, -1, 93, -1, 95, -1, 130, 98, 1, 2, 155, 156, 103, 104, 105, -1, 130, -1, 109, 110, 131, -1, 132, 132, 115, 116, 132, 74, -1, 152, 150, -1, 123, 80, 146, -1, -1, 84, 31, 146, 87, 88, 89, 146, 91, 146, 93, 146, 95, 146, 150, 98, 1, 2, 156, 159, 103, 104, 105, 155, 155, 155, 109, 110, 155, 156, 155, 155, 115, 116, 155, 155, 155, 155, 155, 155, 123, 155, 155, 155, 155, 74, 155, 155, 155, 155, 155, 80, 155, 155, 155, 84, 155, 155, 87, 88, 89, 155, 91, 155, 93, 156, 95, 156, 156, 98, 156, 156, 155, 156, 103, 104, 105, 156, 156, 156, 109, 110, 156, 156, 156, 156, 115, 116, 156, 74, 157, 157, 157, 157, 123, 80, 31, 157, 157, 84, 157, 157, 87, 88, 89, 157, 91, 157, 93, 157, 95, 157, 157, 98, 157, 50, 51, 157, 103, 104, 105, 56, 157, 58, 109, 110, 155, 156, 158, 157, 115, 116, 157, 157, 157, 70, 157, 157, 123, 157, 50, 51, 157, 78, 79, 157, 56, 157, 58, 157, 159, 86, 158, 158, 158, 158, 158, 158, 158, 158, 70, 158, 158, 158, 158, 158, 158, 158, 78, 79, 155, 156, 158, 160, 158, 163, 86, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, -1, -1, 161, 134, 161, 136, 137, 138, 139, 140, 141, 142, 162, 162, 162, 162, 162, 148, 149, 162, 162, 162, 162, 162, 162, 162, 162, 158, 134, 162, 136, 137, 138, 139, 140, 141, 142, -1, -1, -1, -1, -1, 148, 149, -1, -1, -1, -1, -1, -1, -1, -1, 158); + protected $actionBase = array(0, 227, 326, 400, 474, 233, 132, 132, 752, -2, -2, 138, -2, -2, -2, 663, 761, 815, 761, 586, 717, 859, 859, 859, 244, 256, 256, 256, 413, 583, 583, 880, 546, 169, 415, 444, 409, 200, 200, 200, 200, 137, 137, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 249, 205, 738, 559, 535, 739, 741, 742, 876, 679, 877, 820, 821, 693, 823, 824, 826, 829, 832, 819, 834, 907, 836, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 67, 536, 299, 510, 230, 44, 652, 652, 652, 652, 652, 652, 652, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 378, 584, 584, 584, 657, 909, 648, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, 503, -21, -21, 436, 650, 364, 571, 215, 426, 156, 26, 26, 329, 329, 329, 329, 329, 46, 46, 5, 5, 5, 5, 152, 186, 186, 186, 186, 120, 120, 120, 120, 374, 374, 429, 448, 448, 334, 267, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 336, 427, 427, 572, 572, 408, 551, 551, 551, 551, 671, 171, 171, 391, 311, 311, 311, 109, 641, 856, 68, 68, 68, 68, 68, 68, 324, 324, 324, -3, -3, -3, 655, 77, 380, 77, 380, 683, 685, 86, 685, 654, -15, 516, 776, 281, 646, 809, 680, 816, 560, 711, 202, 578, 857, 643, -23, 578, 578, 578, 578, 857, 622, 628, 596, -23, 578, -23, 639, 454, 849, 351, 249, 558, 469, 631, 743, 514, 688, 746, 464, 544, 548, 556, 7, 412, 708, 750, 878, 879, 349, 702, 631, 631, 631, 327, 101, 7, -8, 623, 623, 623, 623, 219, 623, 623, 623, 623, 291, 430, 545, 401, 745, 653, 653, 675, 839, 814, 814, 653, 673, 653, 675, 841, 841, 841, 841, 653, 653, 653, 653, 814, 814, 667, 814, 275, 684, 694, 694, 841, 713, 714, 653, 653, 697, 814, 814, 814, 697, 687, 841, 669, 637, 333, 814, 841, 689, 673, 689, 653, 669, 689, 673, 673, 689, 22, 686, 656, 840, 842, 860, 756, 638, 644, 847, 848, 843, 845, 838, 692, 719, 720, 528, 659, 660, 661, 662, 696, 664, 698, 643, 658, 658, 658, 645, 701, 645, 658, 658, 658, 658, 658, 658, 658, 658, 632, 635, 709, 699, 670, 723, 566, 582, 758, 640, 636, 872, 865, 881, 883, 849, 870, 645, 890, 634, 288, 610, 850, 633, 753, 645, 851, 645, 759, 645, 873, 777, 666, 778, 779, 658, 874, 891, 892, 893, 894, 897, 898, 899, 900, 665, 901, 724, 674, 866, 344, 844, 639, 705, 677, 755, 725, 780, 372, 902, 784, 645, 645, 765, 706, 645, 766, 726, 712, 862, 727, 867, 903, 640, 678, 868, 645, 681, 785, 904, 372, 690, 651, 704, 649, 728, 858, 875, 853, 767, 612, 617, 787, 788, 792, 691, 730, 863, 864, 835, 731, 770, 642, 771, 676, 794, 772, 852, 732, 796, 798, 871, 647, 707, 682, 672, 668, 773, 799, 869, 733, 735, 736, 801, 737, 804, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 137, 137, 137, 137, -2, -2, -2, -2, 0, 0, -2, 0, 0, 0, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 0, 0, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 602, -21, -21, -21, -21, 602, -21, -21, -21, -21, -21, -21, -21, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, -21, 602, 602, 602, -21, 68, -21, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 602, 0, 0, 602, -21, 602, -21, 602, -21, -21, 602, 602, 602, 602, 602, 602, 602, -21, -21, -21, -21, -21, -21, 0, 324, 324, 324, 324, -21, -21, -21, -21, 68, 68, 147, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 324, 324, -3, -3, 68, 68, 68, 68, 68, 147, 68, 68, -23, 673, 673, 673, 380, 380, 380, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 380, -23, 0, -23, 0, 68, -23, 673, -23, 380, 673, 673, -23, 814, 604, 604, 604, 604, 372, 7, 0, 0, 673, 673, 0, 0, 0, 0, 0, 673, 0, 0, 0, 0, 0, 0, 814, 0, 653, 0, 0, 0, 0, 658, 288, 0, 677, 456, 0, 0, 0, 0, 0, 0, 677, 456, 530, 530, 0, 665, 658, 658, 658, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 372); + protected $actionDefault = array(3, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 540, 540, 495, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 297, 297, 297, 32767, 32767, 32767, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 32767, 32767, 32767, 32767, 32767, 32767, 381, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 387, 545, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 362, 363, 365, 366, 296, 548, 529, 245, 388, 544, 295, 247, 325, 499, 32767, 32767, 32767, 327, 122, 256, 201, 498, 125, 294, 232, 380, 382, 326, 301, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 300, 454, 359, 358, 357, 456, 32767, 455, 492, 492, 495, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 323, 483, 482, 324, 452, 328, 453, 331, 457, 460, 329, 330, 347, 348, 345, 346, 349, 458, 459, 476, 477, 474, 475, 299, 350, 351, 352, 353, 478, 479, 480, 481, 32767, 32767, 280, 539, 539, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 338, 339, 467, 468, 32767, 236, 236, 236, 236, 281, 236, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 333, 334, 332, 462, 463, 461, 428, 32767, 32767, 32767, 430, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 500, 32767, 32767, 32767, 32767, 32767, 513, 417, 171, 32767, 409, 32767, 171, 171, 171, 171, 32767, 220, 222, 167, 32767, 171, 32767, 486, 32767, 32767, 32767, 32767, 32767, 518, 343, 32767, 32767, 116, 32767, 32767, 32767, 555, 32767, 513, 32767, 116, 32767, 32767, 32767, 32767, 356, 335, 336, 337, 32767, 32767, 517, 511, 470, 471, 472, 473, 32767, 464, 465, 466, 469, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 425, 431, 431, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 516, 515, 32767, 410, 494, 186, 184, 184, 32767, 206, 206, 32767, 32767, 188, 487, 506, 32767, 188, 173, 32767, 398, 175, 494, 32767, 32767, 238, 32767, 238, 32767, 398, 238, 32767, 32767, 238, 32767, 411, 435, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 377, 378, 489, 502, 32767, 503, 32767, 409, 341, 342, 344, 320, 32767, 322, 367, 368, 369, 370, 371, 372, 373, 375, 32767, 415, 32767, 418, 32767, 32767, 32767, 255, 32767, 553, 32767, 32767, 304, 553, 32767, 32767, 32767, 547, 32767, 32767, 298, 32767, 32767, 32767, 32767, 251, 32767, 169, 32767, 537, 32767, 554, 32767, 511, 32767, 340, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 512, 32767, 32767, 32767, 32767, 227, 32767, 448, 32767, 116, 32767, 32767, 32767, 187, 32767, 32767, 302, 246, 32767, 32767, 546, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 114, 32767, 170, 32767, 32767, 32767, 189, 32767, 32767, 511, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 293, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 511, 32767, 32767, 231, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 411, 32767, 274, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 127, 127, 3, 127, 127, 258, 3, 258, 127, 258, 258, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 214, 217, 206, 206, 164, 127, 127, 266); + protected $goto = array(166, 140, 140, 140, 166, 187, 168, 144, 147, 141, 142, 143, 149, 163, 163, 163, 163, 144, 144, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 138, 159, 160, 161, 162, 184, 139, 185, 493, 494, 377, 495, 499, 500, 501, 502, 503, 504, 505, 506, 967, 164, 145, 146, 148, 171, 176, 186, 203, 253, 256, 258, 260, 263, 264, 265, 266, 267, 268, 269, 277, 278, 279, 280, 303, 304, 328, 329, 330, 394, 395, 396, 542, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 150, 151, 152, 167, 153, 169, 154, 204, 170, 155, 156, 157, 205, 158, 136, 620, 560, 756, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 1108, 628, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, 757, 888, 888, 508, 1200, 1200, 400, 606, 508, 536, 536, 568, 532, 534, 534, 496, 498, 524, 540, 569, 572, 583, 590, 852, 852, 852, 852, 847, 853, 174, 585, 519, 600, 601, 177, 178, 179, 401, 402, 403, 404, 173, 202, 206, 208, 257, 259, 261, 262, 270, 271, 272, 273, 274, 275, 281, 282, 283, 284, 305, 306, 331, 332, 333, 406, 407, 408, 409, 175, 180, 254, 255, 181, 182, 183, 497, 497, 785, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 509, 578, 582, 626, 749, 509, 544, 545, 546, 547, 548, 549, 550, 551, 553, 586, 338, 559, 321, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 530, 349, 655, 555, 587, 352, 414, 591, 575, 604, 885, 611, 612, 881, 616, 617, 623, 625, 630, 632, 298, 296, 296, 296, 298, 290, 299, 944, 610, 816, 1170, 613, 436, 436, 375, 436, 436, 436, 436, 436, 436, 436, 436, 436, 436, 436, 436, 436, 436, 1072, 1084, 1083, 945, 1065, 1072, 895, 895, 895, 895, 1178, 895, 895, 1212, 1212, 1178, 388, 858, 561, 755, 1072, 1072, 1072, 1072, 1072, 1072, 3, 4, 384, 384, 384, 1212, 874, 856, 854, 856, 654, 465, 511, 883, 878, 1089, 541, 384, 537, 384, 567, 384, 1026, 19, 15, 371, 384, 1226, 510, 1204, 1192, 1192, 1192, 510, 906, 372, 522, 533, 554, 912, 514, 1068, 1069, 13, 1065, 378, 912, 1158, 594, 23, 965, 386, 386, 386, 602, 1066, 1169, 1066, 937, 447, 449, 631, 752, 1177, 1067, 1109, 614, 935, 1177, 605, 1197, 391, 1211, 1211, 543, 892, 386, 1194, 1194, 1194, 399, 518, 1016, 901, 389, 771, 529, 752, 340, 752, 1211, 518, 518, 385, 781, 1214, 770, 772, 1063, 910, 774, 1058, 1176, 659, 953, 514, 782, 862, 915, 450, 573, 1155, 0, 463, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 513, 528, 0, 0, 0, 0, 513, 0, 528, 0, 350, 351, 0, 609, 512, 515, 438, 439, 1064, 618, 0, 0, 0, 0, 0, 0, 0, 0, 0, 779, 1219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 777, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 523, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 301, 301); + protected $gotoCheck = array(43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 57, 68, 15, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 126, 9, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 16, 76, 76, 68, 76, 76, 51, 51, 68, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 68, 68, 68, 68, 68, 68, 27, 66, 101, 66, 66, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 117, 117, 29, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 61, 61, 61, 6, 117, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 125, 57, 125, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 32, 71, 32, 32, 69, 69, 69, 32, 40, 40, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 5, 5, 5, 5, 5, 5, 5, 97, 62, 50, 81, 62, 57, 57, 62, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 124, 124, 97, 81, 57, 57, 57, 57, 57, 118, 57, 57, 142, 142, 118, 12, 33, 12, 14, 57, 57, 57, 57, 57, 57, 30, 30, 13, 13, 13, 142, 14, 14, 14, 14, 14, 57, 14, 14, 14, 34, 2, 13, 109, 13, 2, 13, 34, 34, 34, 34, 13, 13, 122, 140, 9, 9, 9, 122, 83, 58, 58, 58, 34, 13, 13, 81, 81, 58, 81, 46, 13, 131, 127, 34, 101, 123, 123, 123, 34, 81, 81, 81, 8, 8, 8, 8, 11, 119, 81, 8, 8, 8, 119, 49, 138, 48, 141, 141, 47, 78, 123, 119, 119, 119, 123, 47, 102, 80, 17, 23, 9, 11, 18, 11, 141, 47, 47, 11, 23, 141, 23, 24, 115, 84, 25, 113, 119, 73, 99, 13, 26, 70, 85, 64, 65, 130, -1, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 9, 9, -1, -1, -1, -1, 9, -1, 9, -1, 71, 71, -1, 13, 9, 9, 9, 9, 13, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, 9, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5, 5); + protected $gotoBase = array(0, 0, -184, 0, 0, 356, 290, 0, 488, 149, 0, 182, 85, 118, 426, 112, 203, 179, 208, 0, 0, 0, 0, 162, 190, 198, 120, 27, 0, 272, -224, 0, -274, 406, 32, 0, 0, 0, 0, 0, 330, 0, 0, -24, 0, 0, 440, 485, 213, 218, 371, -74, 0, 0, 0, 0, 0, 107, 110, 0, 0, -11, -72, 0, 104, 95, -405, 0, -94, 41, 119, -82, 0, 164, 0, 0, -79, 0, 197, 0, 204, 43, 0, 441, 171, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 115, 0, 195, 210, 0, 0, 0, 0, 0, 86, 427, 259, 0, 0, 116, 0, 174, 0, -5, 117, 196, 0, 0, 161, 170, 93, -21, -48, 273, 0, 0, 91, 271, 0, 0, 0, 0, 0, 0, 216, 0, 437, 187, 102, 0, 0); + protected $gotoDefault = array(-32768, 467, 663, 2, 664, 834, 739, 747, 597, 481, 629, 581, 380, 1188, 791, 792, 793, 381, 367, 482, 379, 410, 405, 780, 773, 775, 783, 172, 411, 786, 1, 788, 517, 824, 1017, 364, 796, 365, 589, 798, 526, 800, 801, 137, 382, 383, 527, 483, 390, 576, 815, 276, 387, 817, 366, 818, 827, 370, 464, 454, 459, 556, 608, 432, 446, 570, 564, 535, 1081, 565, 861, 348, 869, 660, 877, 880, 484, 557, 891, 451, 899, 1094, 397, 905, 911, 916, 287, 919, 417, 412, 584, 924, 925, 5, 929, 621, 622, 8, 312, 952, 598, 966, 420, 1036, 1038, 485, 486, 521, 458, 507, 525, 487, 1059, 440, 413, 1062, 488, 489, 433, 434, 1078, 354, 1163, 353, 448, 320, 1150, 579, 1113, 455, 1203, 1159, 347, 490, 491, 376, 1182, 392, 1198, 437, 1205, 1213, 343, 539, 566); + protected $ruleToNonTerminal = array(0, 1, 3, 3, 2, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 8, 8, 9, 10, 11, 11, 12, 12, 13, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 18, 18, 19, 19, 21, 21, 17, 17, 22, 22, 23, 23, 24, 24, 25, 25, 20, 20, 26, 28, 28, 29, 30, 30, 32, 31, 31, 31, 31, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 14, 14, 54, 54, 56, 55, 55, 48, 48, 58, 58, 59, 59, 60, 60, 15, 16, 16, 16, 63, 63, 63, 64, 64, 67, 67, 65, 65, 69, 69, 41, 41, 50, 50, 53, 53, 53, 52, 52, 70, 42, 42, 42, 42, 71, 71, 72, 72, 73, 73, 39, 39, 35, 35, 74, 37, 37, 75, 36, 36, 38, 38, 49, 49, 49, 61, 61, 77, 77, 78, 78, 80, 80, 80, 79, 79, 62, 62, 81, 81, 81, 82, 82, 83, 83, 83, 44, 44, 84, 84, 84, 45, 45, 85, 85, 86, 86, 66, 87, 87, 87, 87, 92, 92, 93, 93, 94, 94, 94, 94, 94, 95, 96, 96, 91, 91, 88, 88, 90, 90, 98, 98, 97, 97, 97, 97, 97, 97, 89, 89, 100, 99, 99, 46, 46, 40, 40, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 34, 34, 47, 47, 105, 105, 106, 106, 106, 106, 112, 101, 101, 108, 108, 114, 114, 115, 116, 116, 116, 116, 116, 116, 68, 68, 57, 57, 57, 57, 102, 102, 120, 120, 117, 117, 121, 121, 121, 121, 103, 103, 103, 107, 107, 107, 113, 113, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 27, 27, 27, 27, 27, 27, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 111, 111, 104, 104, 104, 104, 127, 127, 130, 130, 129, 129, 131, 131, 51, 51, 51, 51, 133, 133, 132, 132, 132, 132, 132, 134, 134, 119, 119, 122, 122, 118, 118, 136, 135, 135, 135, 135, 123, 123, 123, 123, 110, 110, 124, 124, 124, 124, 76, 137, 137, 138, 138, 138, 109, 109, 139, 139, 140, 140, 140, 140, 140, 125, 125, 125, 125, 142, 143, 141, 141, 141, 141, 141, 141, 141, 144, 144, 144); + protected $ruleToLength = array(1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 4, 3, 4, 2, 3, 1, 1, 7, 6, 3, 1, 3, 1, 3, 1, 1, 3, 1, 3, 1, 2, 3, 1, 3, 3, 1, 3, 2, 0, 1, 1, 1, 1, 1, 3, 5, 8, 3, 5, 9, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 1, 2, 2, 5, 7, 9, 5, 6, 3, 3, 2, 2, 1, 1, 1, 0, 2, 8, 0, 4, 1, 3, 0, 1, 0, 1, 0, 1, 10, 7, 6, 5, 1, 2, 2, 0, 2, 0, 2, 0, 2, 1, 3, 1, 4, 1, 4, 1, 1, 4, 1, 3, 3, 3, 4, 4, 5, 0, 2, 4, 3, 1, 1, 1, 4, 0, 2, 3, 0, 2, 4, 0, 2, 0, 3, 1, 2, 1, 1, 0, 1, 3, 4, 6, 1, 1, 1, 0, 1, 0, 2, 2, 3, 3, 1, 3, 1, 2, 2, 3, 1, 1, 2, 4, 3, 1, 1, 3, 2, 0, 1, 3, 3, 9, 3, 1, 3, 0, 2, 4, 5, 4, 4, 4, 3, 1, 1, 1, 3, 1, 1, 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3, 3, 1, 0, 1, 1, 3, 3, 4, 4, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 5, 4, 3, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 3, 2, 1, 2, 10, 11, 3, 3, 2, 4, 4, 3, 4, 4, 4, 4, 7, 3, 2, 0, 4, 1, 3, 2, 2, 4, 6, 2, 2, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 4, 4, 0, 2, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 3, 1, 4, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 4, 3, 1, 3, 1, 1, 3, 3, 0, 2, 0, 1, 3, 1, 3, 1, 1, 1, 1, 1, 6, 4, 3, 4, 2, 4, 4, 1, 3, 1, 2, 1, 1, 4, 1, 1, 3, 6, 4, 4, 4, 4, 1, 4, 0, 1, 1, 3, 1, 1, 4, 3, 1, 1, 1, 0, 0, 2, 3, 1, 3, 1, 4, 2, 2, 2, 2, 1, 2, 1, 1, 1, 4, 3, 3, 3, 6, 3, 1, 1, 1); + protected function initReduceCallbacks() { - if ($this->addLineNumbers) { - \fwrite($output, '@@ -' . $fromStart); - if (!$this->collapseRanges || 1 !== $fromRange) { - \fwrite($output, ',' . $fromRange); - } - \fwrite($output, ' +' . $toStart); - if (!$this->collapseRanges || 1 !== $toRange) { - \fwrite($output, ',' . $toRange); - } - \fwrite($output, " @@\n"); - } else { - \fwrite($output, "@@ @@\n"); - } - for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { - if ($diff[$i][1] === \PHPUnit\SebastianBergmann\Diff\Differ::ADDED) { - \fwrite($output, '+' . $diff[$i][0]); - } elseif ($diff[$i][1] === \PHPUnit\SebastianBergmann\Diff\Differ::REMOVED) { - \fwrite($output, '-' . $diff[$i][0]); - } elseif ($diff[$i][1] === \PHPUnit\SebastianBergmann\Diff\Differ::OLD) { - \fwrite($output, ' ' . $diff[$i][0]); - } elseif ($diff[$i][1] === \PHPUnit\SebastianBergmann\Diff\Differ::NO_LINE_END_EOF_WARNING) { - \fwrite($output, "\n"); - // $diff[$i][0] + $this->reduceCallbacks = [0 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 1 => function ($stackPos) { + $this->semValue = $this->handleNamespaces($this->semStack[$stackPos - (1 - 1)]); + }, 2 => function ($stackPos) { + if (\is_array($this->semStack[$stackPos - (2 - 2)])) { + $this->semValue = \array_merge($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); } else { - /* Not changed (old) Differ::OLD or Warning Differ::DIFF_LINE_END_WARNING */ - \fwrite($output, ' ' . $diff[$i][0]); - } - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Diff\Output; - -use function fclose; -use function fopen; -use function fwrite; -use function stream_get_contents; -use function substr; -use PHPUnit\SebastianBergmann\Diff\Differ; -/** - * Builds a diff string representation in a loose unified diff format - * listing only changes lines. Does not include line numbers. - */ -final class DiffOnlyOutputBuilder implements \PHPUnit\SebastianBergmann\Diff\Output\DiffOutputBuilderInterface -{ - /** - * @var string - */ - private $header; - public function __construct(string $header = "--- Original\n+++ New\n") - { - $this->header = $header; - } - public function getDiff(array $diff) : string - { - $buffer = \fopen('php://memory', 'r+b'); - if ('' !== $this->header) { - \fwrite($buffer, $this->header); - if ("\n" !== \substr($this->header, -1, 1)) { - \fwrite($buffer, "\n"); + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; } - } - foreach ($diff as $diffEntry) { - if ($diffEntry[1] === \PHPUnit\SebastianBergmann\Diff\Differ::ADDED) { - \fwrite($buffer, '+' . $diffEntry[0]); - } elseif ($diffEntry[1] === \PHPUnit\SebastianBergmann\Diff\Differ::REMOVED) { - \fwrite($buffer, '-' . $diffEntry[0]); - } elseif ($diffEntry[1] === \PHPUnit\SebastianBergmann\Diff\Differ::DIFF_LINE_END_WARNING) { - \fwrite($buffer, ' ' . $diffEntry[0]); - continue; - // Warnings should not be tested for line break, it will always be there + }, 3 => function ($stackPos) { + $this->semValue = array(); + }, 4 => function ($stackPos) { + $startAttributes = $this->lookaheadStartAttributes; + if (isset($startAttributes['comments'])) { + $nop = new Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); } else { - /* Not changed (old) 0 */ - continue; - // we didn't write the non changs line, so do not add a line break either + $nop = null; } - $lc = \substr($diffEntry[0], -1); - if ($lc !== "\n" && $lc !== "\r") { - \fwrite($buffer, "\n"); - // \No newline at end of file + if ($nop !== null) { + $this->semStack[$stackPos - (1 - 1)][] = $nop; } - } - $diff = \stream_get_contents($buffer, -1, 0); - \fclose($buffer); - return $diff; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Diff\Output; - -use function array_merge; -use function array_splice; -use function count; -use function fclose; -use function fopen; -use function fwrite; -use function is_bool; -use function is_int; -use function is_string; -use function max; -use function min; -use function sprintf; -use function stream_get_contents; -use function substr; -use PHPUnit\SebastianBergmann\Diff\ConfigurationException; -use PHPUnit\SebastianBergmann\Diff\Differ; -/** - * Strict Unified diff output builder. - * - * Generates (strict) Unified diff's (unidiffs) with hunks. - */ -final class StrictUnifiedDiffOutputBuilder implements \PHPUnit\SebastianBergmann\Diff\Output\DiffOutputBuilderInterface -{ - private static $default = [ - 'collapseRanges' => \true, - // ranges of length one are rendered with the trailing `,1` - 'commonLineThreshold' => 6, - // number of same lines before ending a new hunk and creating a new one (if needed) - 'contextLines' => 3, - // like `diff: -u, -U NUM, --unified[=NUM]`, for patch/git apply compatibility best to keep at least @ 3 - 'fromFile' => null, - 'fromFileDate' => null, - 'toFile' => null, - 'toFileDate' => null, - ]; - /** - * @var bool - */ - private $changed; - /** - * @var bool - */ - private $collapseRanges; - /** - * @var int >= 0 - */ - private $commonLineThreshold; - /** - * @var string - */ - private $header; - /** - * @var int >= 0 - */ - private $contextLines; - public function __construct(array $options = []) - { - $options = \array_merge(self::$default, $options); - if (!\is_bool($options['collapseRanges'])) { - throw new \PHPUnit\SebastianBergmann\Diff\ConfigurationException('collapseRanges', 'a bool', $options['collapseRanges']); - } - if (!\is_int($options['contextLines']) || $options['contextLines'] < 0) { - throw new \PHPUnit\SebastianBergmann\Diff\ConfigurationException('contextLines', 'an int >= 0', $options['contextLines']); - } - if (!\is_int($options['commonLineThreshold']) || $options['commonLineThreshold'] <= 0) { - throw new \PHPUnit\SebastianBergmann\Diff\ConfigurationException('commonLineThreshold', 'an int > 0', $options['commonLineThreshold']); - } - $this->assertString($options, 'fromFile'); - $this->assertString($options, 'toFile'); - $this->assertStringOrNull($options, 'fromFileDate'); - $this->assertStringOrNull($options, 'toFileDate'); - $this->header = \sprintf("--- %s%s\n+++ %s%s\n", $options['fromFile'], null === $options['fromFileDate'] ? '' : "\t" . $options['fromFileDate'], $options['toFile'], null === $options['toFileDate'] ? '' : "\t" . $options['toFileDate']); - $this->collapseRanges = $options['collapseRanges']; - $this->commonLineThreshold = $options['commonLineThreshold']; - $this->contextLines = $options['contextLines']; - } - public function getDiff(array $diff) : string - { - if (0 === \count($diff)) { - return ''; - } - $this->changed = \false; - $buffer = \fopen('php://memory', 'r+b'); - \fwrite($buffer, $this->header); - $this->writeDiffHunks($buffer, $diff); - if (!$this->changed) { - \fclose($buffer); - return ''; - } - $diff = \stream_get_contents($buffer, -1, 0); - \fclose($buffer); - // If the last char is not a linebreak: add it. - // This might happen when both the `from` and `to` do not have a trailing linebreak - $last = \substr($diff, -1); - return "\n" !== $last && "\r" !== $last ? $diff . "\n" : $diff; - } - private function writeDiffHunks($output, array $diff) : void - { - // detect "No newline at end of file" and insert into `$diff` if needed - $upperLimit = \count($diff); - if (0 === $diff[$upperLimit - 1][1]) { - $lc = \substr($diff[$upperLimit - 1][0], -1); - if ("\n" !== $lc) { - \array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", \PHPUnit\SebastianBergmann\Diff\Differ::NO_LINE_END_EOF_WARNING]]); - } - } else { - // search back for the last `+` and `-` line, - // check if has trailing linebreak, else add under it warning under it - $toFind = [1 => \true, 2 => \true]; - for ($i = $upperLimit - 1; $i >= 0; --$i) { - if (isset($toFind[$diff[$i][1]])) { - unset($toFind[$diff[$i][1]]); - $lc = \substr($diff[$i][0], -1); - if ("\n" !== $lc) { - \array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", \PHPUnit\SebastianBergmann\Diff\Differ::NO_LINE_END_EOF_WARNING]]); - } - if (!\count($toFind)) { - break; - } - } - } - } - // write hunks to output buffer - $cutOff = \max($this->commonLineThreshold, $this->contextLines); - $hunkCapture = \false; - $sameCount = $toRange = $fromRange = 0; - $toStart = $fromStart = 1; - $i = 0; - /** @var int $i */ - foreach ($diff as $i => $entry) { - if (0 === $entry[1]) { - // same - if (\false === $hunkCapture) { - ++$fromStart; - ++$toStart; - continue; - } - ++$sameCount; - ++$toRange; - ++$fromRange; - if ($sameCount === $cutOff) { - $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; - // note: $contextEndOffset = $this->contextLines; - // - // because we never go beyond the end of the diff. - // with the cutoff/contextlines here the follow is never true; - // - // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { - // $contextEndOffset = count($diff) - 1; - // } - // - // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop - $this->writeHunk($diff, $hunkCapture - $contextStartOffset, $i - $cutOff + $this->contextLines + 1, $fromStart - $contextStartOffset, $fromRange - $cutOff + $contextStartOffset + $this->contextLines, $toStart - $contextStartOffset, $toRange - $cutOff + $contextStartOffset + $this->contextLines, $output); - $fromStart += $fromRange; - $toStart += $toRange; - $hunkCapture = \false; - $sameCount = $toRange = $fromRange = 0; - } - continue; - } - $sameCount = 0; - if ($entry[1] === \PHPUnit\SebastianBergmann\Diff\Differ::NO_LINE_END_EOF_WARNING) { - continue; - } - $this->changed = \true; - if (\false === $hunkCapture) { - $hunkCapture = $i; - } - if (\PHPUnit\SebastianBergmann\Diff\Differ::ADDED === $entry[1]) { - // added - ++$toRange; - } - if (\PHPUnit\SebastianBergmann\Diff\Differ::REMOVED === $entry[1]) { - // removed - ++$fromRange; + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 5 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 6 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 7 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 8 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 9 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 10 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 11 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 12 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 13 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 14 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 15 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 16 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 17 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 18 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 19 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 20 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 21 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 22 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 23 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 24 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 25 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 26 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 27 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 28 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 29 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 30 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 31 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 32 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 33 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 34 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 35 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 36 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 37 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 38 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 39 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 40 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 41 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 42 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 43 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 44 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 45 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 46 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 47 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 48 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 49 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 50 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 51 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 52 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 53 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 54 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 55 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 56 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 57 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 58 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 59 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 60 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 61 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 62 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 63 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 64 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 65 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 66 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 67 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 68 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 69 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 70 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 71 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 72 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 73 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 74 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 75 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 76 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 77 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 78 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 79 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 80 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 81 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 82 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 83 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 84 => function ($stackPos) { + $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 85 => function ($stackPos) { + $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 86 => function ($stackPos) { + $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 87 => function ($stackPos) { + $this->semValue = new Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 88 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 89 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 90 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 91 => function ($stackPos) { + $this->semValue = new Name(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 92 => function ($stackPos) { + $this->semValue = new Expr\Variable(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 93 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 94 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 95 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 96 => function ($stackPos) { + $this->semValue = new Stmt\HaltCompiler($this->lexer->handleHaltCompiler(), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 97 => function ($stackPos) { + $this->semValue = new Stmt\Namespace_($this->semStack[$stackPos - (3 - 2)], null, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON); + $this->checkNamespace($this->semValue); + }, 98 => function ($stackPos) { + $this->semValue = new Stmt\Namespace_($this->semStack[$stackPos - (5 - 2)], $this->semStack[$stackPos - (5 - 4)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); + $this->checkNamespace($this->semValue); + }, 99 => function ($stackPos) { + $this->semValue = new Stmt\Namespace_(null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); + $this->checkNamespace($this->semValue); + }, 100 => function ($stackPos) { + $this->semValue = new Stmt\Use_($this->semStack[$stackPos - (3 - 2)], Stmt\Use_::TYPE_NORMAL, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 101 => function ($stackPos) { + $this->semValue = new Stmt\Use_($this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 2)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 102 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 103 => function ($stackPos) { + $this->semValue = new Stmt\Const_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 104 => function ($stackPos) { + $this->semValue = Stmt\Use_::TYPE_FUNCTION; + }, 105 => function ($stackPos) { + $this->semValue = Stmt\Use_::TYPE_CONSTANT; + }, 106 => function ($stackPos) { + $this->semValue = new Stmt\GroupUse($this->semStack[$stackPos - (7 - 3)], $this->semStack[$stackPos - (7 - 6)], $this->semStack[$stackPos - (7 - 2)], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); + }, 107 => function ($stackPos) { + $this->semValue = new Stmt\GroupUse($this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 5)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); + }, 108 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 109 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 110 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 111 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 112 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 113 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 114 => function ($stackPos) { + $this->semValue = new Stmt\UseUse($this->semStack[$stackPos - (1 - 1)], null, Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + $this->checkUseUse($this->semValue, $stackPos - (1 - 1)); + }, 115 => function ($stackPos) { + $this->semValue = new Stmt\UseUse($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + $this->checkUseUse($this->semValue, $stackPos - (3 - 3)); + }, 116 => function ($stackPos) { + $this->semValue = new Stmt\UseUse($this->semStack[$stackPos - (1 - 1)], null, Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + $this->checkUseUse($this->semValue, $stackPos - (1 - 1)); + }, 117 => function ($stackPos) { + $this->semValue = new Stmt\UseUse($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + $this->checkUseUse($this->semValue, $stackPos - (3 - 3)); + }, 118 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + $this->semValue->type = Stmt\Use_::TYPE_NORMAL; + }, 119 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue->type = $this->semStack[$stackPos - (2 - 1)]; + }, 120 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 121 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 122 => function ($stackPos) { + $this->semValue = new Node\Const_($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 123 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 124 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 125 => function ($stackPos) { + $this->semValue = new Node\Const_($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 126 => function ($stackPos) { + if (\is_array($this->semStack[$stackPos - (2 - 2)])) { + $this->semValue = \array_merge($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); + } else { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; } - } - if (\false === $hunkCapture) { - return; - } - // we end here when cutoff (commonLineThreshold) was not reached, but we where capturing a hunk, - // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold - $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; - // prevent trying to write out more common lines than there are in the diff _and_ - // do not write more than configured through the context lines - $contextEndOffset = \min($sameCount, $this->contextLines); - $fromRange -= $sameCount; - $toRange -= $sameCount; - $this->writeHunk($diff, $hunkCapture - $contextStartOffset, $i - $sameCount + $contextEndOffset + 1, $fromStart - $contextStartOffset, $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, $output); - } - private function writeHunk(array $diff, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output) : void - { - \fwrite($output, '@@ -' . $fromStart); - if (!$this->collapseRanges || 1 !== $fromRange) { - \fwrite($output, ',' . $fromRange); - } - \fwrite($output, ' +' . $toStart); - if (!$this->collapseRanges || 1 !== $toRange) { - \fwrite($output, ',' . $toRange); - } - \fwrite($output, " @@\n"); - for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { - if ($diff[$i][1] === \PHPUnit\SebastianBergmann\Diff\Differ::ADDED) { - $this->changed = \true; - \fwrite($output, '+' . $diff[$i][0]); - } elseif ($diff[$i][1] === \PHPUnit\SebastianBergmann\Diff\Differ::REMOVED) { - $this->changed = \true; - \fwrite($output, '-' . $diff[$i][0]); - } elseif ($diff[$i][1] === \PHPUnit\SebastianBergmann\Diff\Differ::OLD) { - \fwrite($output, ' ' . $diff[$i][0]); - } elseif ($diff[$i][1] === \PHPUnit\SebastianBergmann\Diff\Differ::NO_LINE_END_EOF_WARNING) { - $this->changed = \true; - \fwrite($output, $diff[$i][0]); + }, 127 => function ($stackPos) { + $this->semValue = array(); + }, 128 => function ($stackPos) { + $startAttributes = $this->lookaheadStartAttributes; + if (isset($startAttributes['comments'])) { + $nop = new Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); + } else { + $nop = null; } - //} elseif ($diff[$i][1] === Differ::DIFF_LINE_END_WARNING) { // custom comment inserted by PHPUnit/diff package - // skip - //} else { - // unknown/invalid - //} - } - } - private function assertString(array $options, string $option) : void - { - if (!\is_string($options[$option])) { - throw new \PHPUnit\SebastianBergmann\Diff\ConfigurationException($option, 'a string', $options[$option]); - } - } - private function assertStringOrNull(array $options, string $option) : void - { - if (null !== $options[$option] && !\is_string($options[$option])) { - throw new \PHPUnit\SebastianBergmann\Diff\ConfigurationException($option, 'a string or ', $options[$option]); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Diff; - -final class Line -{ - public const ADDED = 1; - public const REMOVED = 2; - public const UNCHANGED = 3; - /** - * @var int - */ - private $type; - /** - * @var string - */ - private $content; - public function __construct(int $type = self::UNCHANGED, string $content = '') - { - $this->type = $type; - $this->content = $content; - } - public function getContent() : string - { - return $this->content; - } - public function getType() : int - { - return $this->type; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Diff; - -use function array_reverse; -use function count; -use function max; -use SplFixedArray; -final class TimeEfficientLongestCommonSubsequenceCalculator implements \PHPUnit\SebastianBergmann\Diff\LongestCommonSubsequenceCalculator -{ - /** - * {@inheritdoc} - */ - public function calculate(array $from, array $to) : array - { - $common = []; - $fromLength = \count($from); - $toLength = \count($to); - $width = $fromLength + 1; - $matrix = new \SplFixedArray($width * ($toLength + 1)); - for ($i = 0; $i <= $fromLength; ++$i) { - $matrix[$i] = 0; - } - for ($j = 0; $j <= $toLength; ++$j) { - $matrix[$j * $width] = 0; - } - for ($i = 1; $i <= $fromLength; ++$i) { - for ($j = 1; $j <= $toLength; ++$j) { - $o = $j * $width + $i; - $matrix[$o] = \max($matrix[$o - 1], $matrix[$o - $width], $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0); + if ($nop !== null) { + $this->semStack[$stackPos - (1 - 1)][] = $nop; } - } - $i = $fromLength; - $j = $toLength; - while ($i > 0 && $j > 0) { - if ($from[$i - 1] === $to[$j - 1]) { - $common[] = $from[$i - 1]; - --$i; - --$j; + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 129 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 130 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 131 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 132 => function ($stackPos) { + throw new Error('__HALT_COMPILER() can only be used from the outermost scope', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 133 => function ($stackPos) { + if ($this->semStack[$stackPos - (3 - 2)]) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + $attrs = $this->startAttributeStack[$stackPos - (3 - 1)]; + $stmts = $this->semValue; + if (!empty($attrs['comments'])) { + $stmts[0]->setAttribute('comments', \array_merge($attrs['comments'], $stmts[0]->getAttribute('comments', []))); + } } else { - $o = $j * $width + $i; - if ($matrix[$o - $width] > $matrix[$o - 1]) { - --$j; + $startAttributes = $this->startAttributeStack[$stackPos - (3 - 1)]; + if (isset($startAttributes['comments'])) { + $this->semValue = new Stmt\Nop($startAttributes + $this->endAttributes); } else { - --$i; + $this->semValue = null; + } + if (null === $this->semValue) { + $this->semValue = array(); } } - } - return \array_reverse($common); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Diff; - -final class Chunk -{ - /** - * @var int - */ - private $start; - /** - * @var int - */ - private $startRange; - /** - * @var int - */ - private $end; - /** - * @var int - */ - private $endRange; - /** - * @var Line[] - */ - private $lines; - public function __construct(int $start = 0, int $startRange = 1, int $end = 0, int $endRange = 1, array $lines = []) - { - $this->start = $start; - $this->startRange = $startRange; - $this->end = $end; - $this->endRange = $endRange; - $this->lines = $lines; - } - public function getStart() : int - { - return $this->start; - } - public function getStartRange() : int - { - return $this->startRange; - } - public function getEnd() : int - { - return $this->end; - } - public function getEndRange() : int - { - return $this->endRange; - } - /** - * @return Line[] - */ - public function getLines() : array - { - return $this->lines; - } - /** - * @param Line[] $lines - */ - public function setLines(array $lines) : void - { - foreach ($lines as $line) { - if (!$line instanceof \PHPUnit\SebastianBergmann\Diff\Line) { - throw new \PHPUnit\SebastianBergmann\Diff\InvalidArgumentException(); + }, 134 => function ($stackPos) { + $this->semValue = new Stmt\If_($this->semStack[$stackPos - (5 - 2)], ['stmts' => \is_array($this->semStack[$stackPos - (5 - 3)]) ? $this->semStack[$stackPos - (5 - 3)] : array($this->semStack[$stackPos - (5 - 3)]), 'elseifs' => $this->semStack[$stackPos - (5 - 4)], 'else' => $this->semStack[$stackPos - (5 - 5)]], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + }, 135 => function ($stackPos) { + $this->semValue = new Stmt\If_($this->semStack[$stackPos - (8 - 2)], ['stmts' => $this->semStack[$stackPos - (8 - 4)], 'elseifs' => $this->semStack[$stackPos - (8 - 5)], 'else' => $this->semStack[$stackPos - (8 - 6)]], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); + }, 136 => function ($stackPos) { + $this->semValue = new Stmt\While_($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 137 => function ($stackPos) { + $this->semValue = new Stmt\Do_($this->semStack[$stackPos - (5 - 4)], \is_array($this->semStack[$stackPos - (5 - 2)]) ? $this->semStack[$stackPos - (5 - 2)] : array($this->semStack[$stackPos - (5 - 2)]), $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + }, 138 => function ($stackPos) { + $this->semValue = new Stmt\For_(['init' => $this->semStack[$stackPos - (9 - 3)], 'cond' => $this->semStack[$stackPos - (9 - 5)], 'loop' => $this->semStack[$stackPos - (9 - 7)], 'stmts' => $this->semStack[$stackPos - (9 - 9)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); + }, 139 => function ($stackPos) { + $this->semValue = new Stmt\Switch_($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 140 => function ($stackPos) { + $this->semValue = new Stmt\Break_(null, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 141 => function ($stackPos) { + $this->semValue = new Stmt\Break_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 142 => function ($stackPos) { + $this->semValue = new Stmt\Continue_(null, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 143 => function ($stackPos) { + $this->semValue = new Stmt\Continue_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 144 => function ($stackPos) { + $this->semValue = new Stmt\Return_(null, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 145 => function ($stackPos) { + $this->semValue = new Stmt\Return_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 146 => function ($stackPos) { + $this->semValue = new Stmt\Global_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 147 => function ($stackPos) { + $this->semValue = new Stmt\Static_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 148 => function ($stackPos) { + $this->semValue = new Stmt\Echo_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 149 => function ($stackPos) { + $this->semValue = new Stmt\InlineHTML($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 150 => function ($stackPos) { + $this->semValue = new Stmt\Expression($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 151 => function ($stackPos) { + $this->semValue = new Stmt\Expression($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 152 => function ($stackPos) { + $this->semValue = new Stmt\Unset_($this->semStack[$stackPos - (5 - 3)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + }, 153 => function ($stackPos) { + $this->semValue = new Stmt\Foreach_($this->semStack[$stackPos - (7 - 3)], $this->semStack[$stackPos - (7 - 5)][0], ['keyVar' => null, 'byRef' => $this->semStack[$stackPos - (7 - 5)][1], 'stmts' => $this->semStack[$stackPos - (7 - 7)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); + }, 154 => function ($stackPos) { + $this->semValue = new Stmt\Foreach_($this->semStack[$stackPos - (9 - 3)], $this->semStack[$stackPos - (9 - 7)][0], ['keyVar' => $this->semStack[$stackPos - (9 - 5)], 'byRef' => $this->semStack[$stackPos - (9 - 7)][1], 'stmts' => $this->semStack[$stackPos - (9 - 9)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); + }, 155 => function ($stackPos) { + $this->semValue = new Stmt\Declare_($this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + }, 156 => function ($stackPos) { + $this->semValue = new Stmt\TryCatch($this->semStack[$stackPos - (6 - 3)], $this->semStack[$stackPos - (6 - 5)], $this->semStack[$stackPos - (6 - 6)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); + $this->checkTryCatch($this->semValue); + }, 157 => function ($stackPos) { + $this->semValue = new Stmt\Throw_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 158 => function ($stackPos) { + $this->semValue = new Stmt\Goto_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 159 => function ($stackPos) { + $this->semValue = new Stmt\Label($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 160 => function ($stackPos) { + $this->semValue = new Stmt\Expression($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 161 => function ($stackPos) { + $this->semValue = array(); + /* means: no statement */ + }, 162 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 163 => function ($stackPos) { + $startAttributes = $this->startAttributeStack[$stackPos - (1 - 1)]; + if (isset($startAttributes['comments'])) { + $this->semValue = new Stmt\Nop($startAttributes + $this->endAttributes); + } else { + $this->semValue = null; } - } - $this->lines = $lines; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Diff; - -use function array_fill; -use function array_merge; -use function array_reverse; -use function array_slice; -use function count; -use function in_array; -use function max; -final class MemoryEfficientLongestCommonSubsequenceCalculator implements \PHPUnit\SebastianBergmann\Diff\LongestCommonSubsequenceCalculator -{ - /** - * {@inheritdoc} - */ - public function calculate(array $from, array $to) : array - { - $cFrom = \count($from); - $cTo = \count($to); - if ($cFrom === 0) { - return []; - } - if ($cFrom === 1) { - if (\in_array($from[0], $to, \true)) { - return [$from[0]]; + if ($this->semValue === null) { + $this->semValue = array(); } - return []; - } - $i = (int) ($cFrom / 2); - $fromStart = \array_slice($from, 0, $i); - $fromEnd = \array_slice($from, $i); - $llB = $this->length($fromStart, $to); - $llE = $this->length(\array_reverse($fromEnd), \array_reverse($to)); - $jMax = 0; - $max = 0; - for ($j = 0; $j <= $cTo; $j++) { - $m = $llB[$j] + $llE[$cTo - $j]; - if ($m >= $max) { - $max = $m; - $jMax = $j; + /* means: no statement */ + }, 164 => function ($stackPos) { + $this->semValue = array(); + }, 165 => function ($stackPos) { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 166 => function ($stackPos) { + $this->semValue = new Stmt\Catch_(array($this->semStack[$stackPos - (8 - 3)]), $this->semStack[$stackPos - (8 - 4)], $this->semStack[$stackPos - (8 - 7)], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); + }, 167 => function ($stackPos) { + $this->semValue = null; + }, 168 => function ($stackPos) { + $this->semValue = new Stmt\Finally_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 169 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 170 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 171 => function ($stackPos) { + $this->semValue = \false; + }, 172 => function ($stackPos) { + $this->semValue = \true; + }, 173 => function ($stackPos) { + $this->semValue = \false; + }, 174 => function ($stackPos) { + $this->semValue = \true; + }, 175 => function ($stackPos) { + $this->semValue = \false; + }, 176 => function ($stackPos) { + $this->semValue = \true; + }, 177 => function ($stackPos) { + $this->semValue = new Stmt\Function_($this->semStack[$stackPos - (10 - 3)], ['byRef' => $this->semStack[$stackPos - (10 - 2)], 'params' => $this->semStack[$stackPos - (10 - 5)], 'returnType' => $this->semStack[$stackPos - (10 - 7)], 'stmts' => $this->semStack[$stackPos - (10 - 9)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); + }, 178 => function ($stackPos) { + $this->semValue = new Stmt\Class_($this->semStack[$stackPos - (7 - 2)], ['type' => $this->semStack[$stackPos - (7 - 1)], 'extends' => $this->semStack[$stackPos - (7 - 3)], 'implements' => $this->semStack[$stackPos - (7 - 4)], 'stmts' => $this->semStack[$stackPos - (7 - 6)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); + $this->checkClass($this->semValue, $stackPos - (7 - 2)); + }, 179 => function ($stackPos) { + $this->semValue = new Stmt\Interface_($this->semStack[$stackPos - (6 - 2)], ['extends' => $this->semStack[$stackPos - (6 - 3)], 'stmts' => $this->semStack[$stackPos - (6 - 5)]], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); + $this->checkInterface($this->semValue, $stackPos - (6 - 2)); + }, 180 => function ($stackPos) { + $this->semValue = new Stmt\Trait_($this->semStack[$stackPos - (5 - 2)], ['stmts' => $this->semStack[$stackPos - (5 - 4)]], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + }, 181 => function ($stackPos) { + $this->semValue = 0; + }, 182 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT; + }, 183 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_FINAL; + }, 184 => function ($stackPos) { + $this->semValue = null; + }, 185 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 2)]; + }, 186 => function ($stackPos) { + $this->semValue = array(); + }, 187 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 2)]; + }, 188 => function ($stackPos) { + $this->semValue = array(); + }, 189 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 2)]; + }, 190 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 191 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 192 => function ($stackPos) { + $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); + }, 193 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (4 - 2)]; + }, 194 => function ($stackPos) { + $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); + }, 195 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (4 - 2)]; + }, 196 => function ($stackPos) { + $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); + }, 197 => function ($stackPos) { + $this->semValue = null; + }, 198 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (4 - 2)]; + }, 199 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 200 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 201 => function ($stackPos) { + $this->semValue = new Stmt\DeclareDeclare($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 202 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 203 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (4 - 3)]; + }, 204 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (4 - 2)]; + }, 205 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (5 - 3)]; + }, 206 => function ($stackPos) { + $this->semValue = array(); + }, 207 => function ($stackPos) { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 208 => function ($stackPos) { + $this->semValue = new Stmt\Case_($this->semStack[$stackPos - (4 - 2)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 209 => function ($stackPos) { + $this->semValue = new Stmt\Case_(null, $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 210 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 211 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 212 => function ($stackPos) { + $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); + }, 213 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (4 - 2)]; + }, 214 => function ($stackPos) { + $this->semValue = array(); + }, 215 => function ($stackPos) { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 216 => function ($stackPos) { + $this->semValue = new Stmt\ElseIf_($this->semStack[$stackPos - (3 - 2)], \is_array($this->semStack[$stackPos - (3 - 3)]) ? $this->semStack[$stackPos - (3 - 3)] : array($this->semStack[$stackPos - (3 - 3)]), $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 217 => function ($stackPos) { + $this->semValue = array(); + }, 218 => function ($stackPos) { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 219 => function ($stackPos) { + $this->semValue = new Stmt\ElseIf_($this->semStack[$stackPos - (4 - 2)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 220 => function ($stackPos) { + $this->semValue = null; + }, 221 => function ($stackPos) { + $this->semValue = new Stmt\Else_(\is_array($this->semStack[$stackPos - (2 - 2)]) ? $this->semStack[$stackPos - (2 - 2)] : array($this->semStack[$stackPos - (2 - 2)]), $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 222 => function ($stackPos) { + $this->semValue = null; + }, 223 => function ($stackPos) { + $this->semValue = new Stmt\Else_($this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 224 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)], \false); + }, 225 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (2 - 2)], \true); + }, 226 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)], \false); + }, 227 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 228 => function ($stackPos) { + $this->semValue = array(); + }, 229 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 230 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 231 => function ($stackPos) { + $this->semValue = new Node\Param($this->semStack[$stackPos - (4 - 4)], null, $this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 2)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + $this->checkParam($this->semValue); + }, 232 => function ($stackPos) { + $this->semValue = new Node\Param($this->semStack[$stackPos - (6 - 4)], $this->semStack[$stackPos - (6 - 6)], $this->semStack[$stackPos - (6 - 1)], $this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 3)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); + $this->checkParam($this->semValue); + }, 233 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 234 => function ($stackPos) { + $this->semValue = new Node\Identifier('array', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 235 => function ($stackPos) { + $this->semValue = new Node\Identifier('callable', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 236 => function ($stackPos) { + $this->semValue = null; + }, 237 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 238 => function ($stackPos) { + $this->semValue = null; + }, 239 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 2)]; + }, 240 => function ($stackPos) { + $this->semValue = array(); + }, 241 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 242 => function ($stackPos) { + $this->semValue = array(new Node\Arg($this->semStack[$stackPos - (3 - 2)], \false, \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes)); + }, 243 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 244 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 245 => function ($stackPos) { + $this->semValue = new Node\Arg($this->semStack[$stackPos - (1 - 1)], \false, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 246 => function ($stackPos) { + $this->semValue = new Node\Arg($this->semStack[$stackPos - (2 - 2)], \true, \false, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 247 => function ($stackPos) { + $this->semValue = new Node\Arg($this->semStack[$stackPos - (2 - 2)], \false, \true, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 248 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 249 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 250 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 251 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 252 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 253 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 254 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 255 => function ($stackPos) { + $this->semValue = new Stmt\StaticVar($this->semStack[$stackPos - (1 - 1)], null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 256 => function ($stackPos) { + $this->semValue = new Stmt\StaticVar($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 257 => function ($stackPos) { + if ($this->semStack[$stackPos - (2 - 2)] !== null) { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; } - } - $toStart = \array_slice($to, 0, $jMax); - $toEnd = \array_slice($to, $jMax); - return \array_merge($this->calculate($fromStart, $toStart), $this->calculate($fromEnd, $toEnd)); - } - private function length(array $from, array $to) : array - { - $current = \array_fill(0, \count($to) + 1, 0); - $cFrom = \count($from); - $cTo = \count($to); - for ($i = 0; $i < $cFrom; $i++) { - $prev = $current; - for ($j = 0; $j < $cTo; $j++) { - if ($from[$i] === $to[$j]) { - $current[$j + 1] = $prev[$j] + 1; - } else { - $current[$j + 1] = \max($current[$j], $prev[$j + 1]); - } + }, 258 => function ($stackPos) { + $this->semValue = array(); + }, 259 => function ($stackPos) { + $startAttributes = $this->lookaheadStartAttributes; + if (isset($startAttributes['comments'])) { + $nop = new Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); + } else { + $nop = null; } - } - return $current; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Diff; - -interface LongestCommonSubsequenceCalculator -{ - /** - * Calculates the longest common subsequence of two arrays. - */ - public function calculate(array $from, array $to) : array; -} -sebastian/diff - -Copyright (c) 2002-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; - -use function array_diff; -use function array_diff_key; -use function array_flip; -use function array_keys; -use function array_merge; -use function array_unique; -use function array_values; -use function count; -use function explode; -use function get_class; -use function is_array; -use function is_file; -use function sort; -use PHPUnit\Framework\TestCase; -use PHPUnit\Runner\PhptTestCase; -use PHPUnit\Util\Test; -use ReflectionClass; -use PHPUnit\SebastianBergmann\CodeCoverage\Driver\Driver; -use PHPUnit\SebastianBergmann\CodeCoverage\Node\Builder; -use PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory; -use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CachingCoveredFileAnalyser; -use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CachingUncoveredFileAnalyser; -use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CoveredFileAnalyser; -use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingCoveredFileAnalyser; -use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingUncoveredFileAnalyser; -use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\UncoveredFileAnalyser; -use PHPUnit\SebastianBergmann\CodeUnitReverseLookup\Wizard; -/** - * Provides collection functionality for PHP code coverage information. - */ -final class CodeCoverage -{ - private const UNCOVERED_FILES = 'UNCOVERED_FILES'; - /** - * @var Driver - */ - private $driver; - /** - * @var Filter - */ - private $filter; - /** - * @var Wizard - */ - private $wizard; - /** - * @var bool - */ - private $checkForUnintentionallyCoveredCode = \false; - /** - * @var bool - */ - private $includeUncoveredFiles = \true; - /** - * @var bool - */ - private $processUncoveredFiles = \false; - /** - * @var bool - */ - private $ignoreDeprecatedCode = \false; - /** - * @var PhptTestCase|string|TestCase - */ - private $currentId; - /** - * Code coverage data. - * - * @var ProcessedCodeCoverageData - */ - private $data; - /** - * @var bool - */ - private $useAnnotationsForIgnoringCode = \true; - /** - * Test data. - * - * @var array - */ - private $tests = []; - /** - * @psalm-var list - */ - private $parentClassesExcludedFromUnintentionallyCoveredCodeCheck = []; - /** - * @var ?CoveredFileAnalyser - */ - private $coveredFileAnalyser; - /** - * @var ?UncoveredFileAnalyser - */ - private $uncoveredFileAnalyser; - /** - * @var ?string - */ - private $cacheDirectory; - public function __construct(\PHPUnit\SebastianBergmann\CodeCoverage\Driver\Driver $driver, \PHPUnit\SebastianBergmann\CodeCoverage\Filter $filter) - { - $this->driver = $driver; - $this->filter = $filter; - $this->data = new \PHPUnit\SebastianBergmann\CodeCoverage\ProcessedCodeCoverageData(); - $this->wizard = new \PHPUnit\SebastianBergmann\CodeUnitReverseLookup\Wizard(); - } - /** - * Returns the code coverage information as a graph of node objects. - */ - public function getReport() : \PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory - { - return (new \PHPUnit\SebastianBergmann\CodeCoverage\Node\Builder($this->coveredFileAnalyser()))->build($this); - } - /** - * Clears collected code coverage data. - */ - public function clear() : void - { - $this->currentId = null; - $this->data = new \PHPUnit\SebastianBergmann\CodeCoverage\ProcessedCodeCoverageData(); - $this->tests = []; + if ($nop !== null) { + $this->semStack[$stackPos - (1 - 1)][] = $nop; + } + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 260 => function ($stackPos) { + $this->semValue = new Stmt\Property($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + $this->checkProperty($this->semValue, $stackPos - (3 - 1)); + }, 261 => function ($stackPos) { + $this->semValue = new Stmt\ClassConst($this->semStack[$stackPos - (3 - 2)], 0, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 262 => function ($stackPos) { + $this->semValue = new Stmt\ClassMethod($this->semStack[$stackPos - (9 - 4)], ['type' => $this->semStack[$stackPos - (9 - 1)], 'byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 6)], 'returnType' => $this->semStack[$stackPos - (9 - 8)], 'stmts' => $this->semStack[$stackPos - (9 - 9)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); + $this->checkClassMethod($this->semValue, $stackPos - (9 - 1)); + }, 263 => function ($stackPos) { + $this->semValue = new Stmt\TraitUse($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 264 => function ($stackPos) { + $this->semValue = array(); + }, 265 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 266 => function ($stackPos) { + $this->semValue = array(); + }, 267 => function ($stackPos) { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 268 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Precedence($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 269 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (5 - 1)][0], $this->semStack[$stackPos - (5 - 1)][1], $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 4)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + }, 270 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], $this->semStack[$stackPos - (4 - 3)], null, $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 271 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 272 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 273 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)]); + }, 274 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 275 => function ($stackPos) { + $this->semValue = array(null, $this->semStack[$stackPos - (1 - 1)]); + }, 276 => function ($stackPos) { + $this->semValue = null; + }, 277 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 278 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 279 => function ($stackPos) { + $this->semValue = 0; + }, 280 => function ($stackPos) { + $this->semValue = 0; + }, 281 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 282 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 283 => function ($stackPos) { + $this->checkModifier($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $stackPos - (2 - 2)); + $this->semValue = $this->semStack[$stackPos - (2 - 1)] | $this->semStack[$stackPos - (2 - 2)]; + }, 284 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_PUBLIC; + }, 285 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_PROTECTED; + }, 286 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_PRIVATE; + }, 287 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_STATIC; + }, 288 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT; + }, 289 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_FINAL; + }, 290 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 291 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 292 => function ($stackPos) { + $this->semValue = new Node\VarLikeIdentifier(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 293 => function ($stackPos) { + $this->semValue = new Stmt\PropertyProperty($this->semStack[$stackPos - (1 - 1)], null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 294 => function ($stackPos) { + $this->semValue = new Stmt\PropertyProperty($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 295 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 296 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 297 => function ($stackPos) { + $this->semValue = array(); + }, 298 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 299 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 300 => function ($stackPos) { + $this->semValue = new Expr\Assign($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 301 => function ($stackPos) { + $this->semValue = new Expr\Assign($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 302 => function ($stackPos) { + $this->semValue = new Expr\AssignRef($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 303 => function ($stackPos) { + $this->semValue = new Expr\AssignRef($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 304 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 305 => function ($stackPos) { + $this->semValue = new Expr\Clone_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 306 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Plus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 307 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Minus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 308 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Mul($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 309 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Div($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 310 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Concat($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 311 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Mod($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 312 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 313 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\BitwiseOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 314 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\BitwiseXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 315 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\ShiftLeft($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 316 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\ShiftRight($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 317 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Pow($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 318 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Coalesce($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 319 => function ($stackPos) { + $this->semValue = new Expr\PostInc($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 320 => function ($stackPos) { + $this->semValue = new Expr\PreInc($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 321 => function ($stackPos) { + $this->semValue = new Expr\PostDec($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 322 => function ($stackPos) { + $this->semValue = new Expr\PreDec($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 323 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BooleanOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 324 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BooleanAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 325 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 326 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 327 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 328 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 329 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 330 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 331 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 332 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Concat($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 333 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Plus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 334 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Minus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 335 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Mul($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 336 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Div($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 337 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Mod($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 338 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\ShiftLeft($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 339 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\ShiftRight($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 340 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Pow($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 341 => function ($stackPos) { + $this->semValue = new Expr\UnaryPlus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 342 => function ($stackPos) { + $this->semValue = new Expr\UnaryMinus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 343 => function ($stackPos) { + $this->semValue = new Expr\BooleanNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 344 => function ($stackPos) { + $this->semValue = new Expr\BitwiseNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 345 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Identical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 346 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\NotIdentical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 347 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Equal($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 348 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\NotEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 349 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Spaceship($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 350 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Smaller($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 351 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\SmallerOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 352 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Greater($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 353 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\GreaterOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 354 => function ($stackPos) { + $this->semValue = new Expr\Instanceof_($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 355 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 356 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 357 => function ($stackPos) { + $this->semValue = new Expr\Ternary($this->semStack[$stackPos - (5 - 1)], $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + }, 358 => function ($stackPos) { + $this->semValue = new Expr\Ternary($this->semStack[$stackPos - (4 - 1)], null, $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 359 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Coalesce($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 360 => function ($stackPos) { + $this->semValue = new Expr\Isset_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 361 => function ($stackPos) { + $this->semValue = new Expr\Empty_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 362 => function ($stackPos) { + $this->semValue = new Expr\Include_($this->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_INCLUDE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 363 => function ($stackPos) { + $this->semValue = new Expr\Include_($this->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_INCLUDE_ONCE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 364 => function ($stackPos) { + $this->semValue = new Expr\Eval_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 365 => function ($stackPos) { + $this->semValue = new Expr\Include_($this->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_REQUIRE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 366 => function ($stackPos) { + $this->semValue = new Expr\Include_($this->semStack[$stackPos - (2 - 2)], Expr\Include_::TYPE_REQUIRE_ONCE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 367 => function ($stackPos) { + $this->semValue = new Expr\Cast\Int_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 368 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes; + $attrs['kind'] = $this->getFloatCastKind($this->semStack[$stackPos - (2 - 1)]); + $this->semValue = new Expr\Cast\Double($this->semStack[$stackPos - (2 - 2)], $attrs); + }, 369 => function ($stackPos) { + $this->semValue = new Expr\Cast\String_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 370 => function ($stackPos) { + $this->semValue = new Expr\Cast\Array_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 371 => function ($stackPos) { + $this->semValue = new Expr\Cast\Object_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 372 => function ($stackPos) { + $this->semValue = new Expr\Cast\Bool_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 373 => function ($stackPos) { + $this->semValue = new Expr\Cast\Unset_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 374 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes; + $attrs['kind'] = \strtolower($this->semStack[$stackPos - (2 - 1)]) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE; + $this->semValue = new Expr\Exit_($this->semStack[$stackPos - (2 - 2)], $attrs); + }, 375 => function ($stackPos) { + $this->semValue = new Expr\ErrorSuppress($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 376 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 377 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 378 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 379 => function ($stackPos) { + $this->semValue = new Expr\ShellExec($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 380 => function ($stackPos) { + $this->semValue = new Expr\Print_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 381 => function ($stackPos) { + $this->semValue = new Expr\Yield_(null, null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 382 => function ($stackPos) { + $this->semValue = new Expr\YieldFrom($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 383 => function ($stackPos) { + $this->semValue = new Expr\Closure(['static' => \false, 'byRef' => $this->semStack[$stackPos - (10 - 2)], 'params' => $this->semStack[$stackPos - (10 - 4)], 'uses' => $this->semStack[$stackPos - (10 - 6)], 'returnType' => $this->semStack[$stackPos - (10 - 7)], 'stmts' => $this->semStack[$stackPos - (10 - 9)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); + }, 384 => function ($stackPos) { + $this->semValue = new Expr\Closure(['static' => \true, 'byRef' => $this->semStack[$stackPos - (11 - 3)], 'params' => $this->semStack[$stackPos - (11 - 5)], 'uses' => $this->semStack[$stackPos - (11 - 7)], 'returnType' => $this->semStack[$stackPos - (11 - 8)], 'stmts' => $this->semStack[$stackPos - (11 - 10)]], $this->startAttributeStack[$stackPos - (11 - 1)] + $this->endAttributes); + }, 385 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 386 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 387 => function ($stackPos) { + $this->semValue = new Expr\Yield_($this->semStack[$stackPos - (2 - 2)], null, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 388 => function ($stackPos) { + $this->semValue = new Expr\Yield_($this->semStack[$stackPos - (4 - 4)], $this->semStack[$stackPos - (4 - 2)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 389 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes; + $attrs['kind'] = Expr\Array_::KIND_LONG; + $this->semValue = new Expr\Array_($this->semStack[$stackPos - (4 - 3)], $attrs); + }, 390 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes; + $attrs['kind'] = Expr\Array_::KIND_SHORT; + $this->semValue = new Expr\Array_($this->semStack[$stackPos - (3 - 2)], $attrs); + }, 391 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 392 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes; + $attrs['kind'] = $this->semStack[$stackPos - (4 - 1)][0] === "'" || $this->semStack[$stackPos - (4 - 1)][1] === "'" && ($this->semStack[$stackPos - (4 - 1)][0] === 'b' || $this->semStack[$stackPos - (4 - 1)][0] === 'B') ? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED; + $this->semValue = new Expr\ArrayDimFetch(new Scalar\String_(Scalar\String_::parse($this->semStack[$stackPos - (4 - 1)]), $attrs), $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 393 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 394 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 395 => function ($stackPos) { + $this->semValue = array(new Stmt\Class_(null, ['type' => 0, 'extends' => $this->semStack[$stackPos - (7 - 3)], 'implements' => $this->semStack[$stackPos - (7 - 4)], 'stmts' => $this->semStack[$stackPos - (7 - 6)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes), $this->semStack[$stackPos - (7 - 2)]); + $this->checkClass($this->semValue[0], -1); + }, 396 => function ($stackPos) { + $this->semValue = new Expr\New_($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 397 => function ($stackPos) { + list($class, $ctorArgs) = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = new Expr\New_($class, $ctorArgs, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 398 => function ($stackPos) { + $this->semValue = array(); + }, 399 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (4 - 3)]; + }, 400 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 401 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 402 => function ($stackPos) { + $this->semValue = new Expr\ClosureUse($this->semStack[$stackPos - (2 - 2)], $this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 403 => function ($stackPos) { + $this->semValue = new Expr\FuncCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 404 => function ($stackPos) { + $this->semValue = new Expr\StaticCall($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 405 => function ($stackPos) { + $this->semValue = new Expr\StaticCall($this->semStack[$stackPos - (6 - 1)], $this->semStack[$stackPos - (6 - 4)], $this->semStack[$stackPos - (6 - 6)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); + }, 406 => function ($stackPos) { + $this->semValue = $this->fixupPhp5StaticPropCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 407 => function ($stackPos) { + $this->semValue = new Expr\FuncCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 408 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 409 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 410 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 411 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 412 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 413 => function ($stackPos) { + $this->semValue = new Name\FullyQualified(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 414 => function ($stackPos) { + $this->semValue = new Name\Relative(\substr($this->semStack[$stackPos - (1 - 1)], 10), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 415 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 416 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 417 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 418 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 419 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 420 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 421 => function ($stackPos) { + $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 422 => function ($stackPos) { + $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 423 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 424 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 425 => function ($stackPos) { + $this->semValue = null; + }, 426 => function ($stackPos) { + $this->semValue = null; + }, 427 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 428 => function ($stackPos) { + $this->semValue = array(); + }, 429 => function ($stackPos) { + $this->semValue = array(new Scalar\EncapsedStringPart(Scalar\String_::parseEscapeSequences($this->semStack[$stackPos - (1 - 1)], '`', \false), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes)); + }, 430 => function ($stackPos) { + foreach ($this->semStack[$stackPos - (1 - 1)] as $s) { + if ($s instanceof Node\Scalar\EncapsedStringPart) { + $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', \false); + } + } + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 431 => function ($stackPos) { + $this->semValue = array(); + }, 432 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 433 => function ($stackPos) { + $this->semValue = $this->parseLNumber($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes, \true); + }, 434 => function ($stackPos) { + $this->semValue = new Scalar\DNumber(Scalar\DNumber::parse($this->semStack[$stackPos - (1 - 1)]), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 435 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes; + $attrs['kind'] = $this->semStack[$stackPos - (1 - 1)][0] === "'" || $this->semStack[$stackPos - (1 - 1)][1] === "'" && ($this->semStack[$stackPos - (1 - 1)][0] === 'b' || $this->semStack[$stackPos - (1 - 1)][0] === 'B') ? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED; + $this->semValue = new Scalar\String_(Scalar\String_::parse($this->semStack[$stackPos - (1 - 1)], \false), $attrs); + }, 436 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Line($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 437 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\File($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 438 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Dir($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 439 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Class_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 440 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Trait_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 441 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Method($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 442 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Function_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 443 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Namespace_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 444 => function ($stackPos) { + $this->semValue = $this->parseDocString($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (3 - 3)] + $this->endAttributeStack[$stackPos - (3 - 3)], \false); + }, 445 => function ($stackPos) { + $this->semValue = $this->parseDocString($this->semStack[$stackPos - (2 - 1)], '', $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (2 - 2)] + $this->endAttributeStack[$stackPos - (2 - 2)], \false); + }, 446 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 447 => function ($stackPos) { + $this->semValue = new Expr\ClassConstFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 448 => function ($stackPos) { + $this->semValue = new Expr\ConstFetch($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 449 => function ($stackPos) { + $this->semValue = new Expr\Array_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 450 => function ($stackPos) { + $this->semValue = new Expr\Array_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 451 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 452 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BooleanOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 453 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BooleanAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 454 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 455 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 456 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 457 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 458 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 459 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 460 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 461 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Concat($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 462 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Plus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 463 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Minus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 464 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Mul($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 465 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Div($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 466 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Mod($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 467 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\ShiftLeft($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 468 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\ShiftRight($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 469 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Pow($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 470 => function ($stackPos) { + $this->semValue = new Expr\UnaryPlus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 471 => function ($stackPos) { + $this->semValue = new Expr\UnaryMinus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 472 => function ($stackPos) { + $this->semValue = new Expr\BooleanNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 473 => function ($stackPos) { + $this->semValue = new Expr\BitwiseNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 474 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Identical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 475 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\NotIdentical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 476 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Equal($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 477 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\NotEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 478 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Smaller($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 479 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\SmallerOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 480 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Greater($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 481 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\GreaterOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 482 => function ($stackPos) { + $this->semValue = new Expr\Ternary($this->semStack[$stackPos - (5 - 1)], $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); + }, 483 => function ($stackPos) { + $this->semValue = new Expr\Ternary($this->semStack[$stackPos - (4 - 1)], null, $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 484 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 485 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 486 => function ($stackPos) { + $this->semValue = new Expr\ConstFetch($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 487 => function ($stackPos) { + $this->semValue = new Expr\ClassConstFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 488 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 489 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 490 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes; + $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED; + foreach ($this->semStack[$stackPos - (3 - 2)] as $s) { + if ($s instanceof Node\Scalar\EncapsedStringPart) { + $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', \true); + } + } + $this->semValue = new Scalar\Encapsed($this->semStack[$stackPos - (3 - 2)], $attrs); + }, 491 => function ($stackPos) { + $this->semValue = $this->parseDocString($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (3 - 3)] + $this->endAttributeStack[$stackPos - (3 - 3)], \true); + }, 492 => function ($stackPos) { + $this->semValue = array(); + }, 493 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 494 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 495 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, 496 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 497 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 498 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (3 - 3)], $this->semStack[$stackPos - (3 - 1)], \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 499 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 500 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 501 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 502 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 503 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 504 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 5)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); + }, 505 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 506 => function ($stackPos) { + $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 507 => function ($stackPos) { + $this->semValue = new Expr\MethodCall($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 508 => function ($stackPos) { + $this->semValue = new Expr\FuncCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 509 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 510 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 511 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 512 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 513 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 514 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 515 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 516 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 517 => function ($stackPos) { + $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 518 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 519 => function ($stackPos) { + $var = \substr($this->semStack[$stackPos - (1 - 1)], 1); + $this->semValue = \is_string($var) ? new Node\VarLikeIdentifier($var, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes) : $var; + }, 520 => function ($stackPos) { + $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 521 => function ($stackPos) { + $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos - (6 - 1)], $this->semStack[$stackPos - (6 - 5)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); + }, 522 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 523 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 524 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 525 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 526 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 527 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 528 => function ($stackPos) { + $this->semValue = null; + }, 529 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 530 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 531 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 532 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 533 => function ($stackPos) { + $this->semValue = new Expr\Error($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + $this->errorState = 2; + }, 534 => function ($stackPos) { + $this->semValue = new Expr\List_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 535 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 536 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 537 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 538 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 539 => function ($stackPos) { + $this->semValue = null; + }, 540 => function ($stackPos) { + $this->semValue = array(); + }, 541 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 542 => function ($stackPos) { + $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; + $this->semValue = $this->semStack[$stackPos - (3 - 1)]; + }, 543 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 544 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (3 - 3)], $this->semStack[$stackPos - (3 - 1)], \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 545 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 546 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (4 - 4)], $this->semStack[$stackPos - (4 - 1)], \true, $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 547 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (2 - 2)], null, \true, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 548 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos - (2 - 2)], null, \false, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes, \true, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); + }, 549 => function ($stackPos) { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 550 => function ($stackPos) { + $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; + $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + }, 551 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); + }, 552 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); + }, 553 => function ($stackPos) { + $this->semValue = new Scalar\EncapsedStringPart($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 554 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 555 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }, 556 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); + }, 557 => function ($stackPos) { + $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 558 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 559 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); + }, 560 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 4)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); + }, 561 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (3 - 2)]; + }, 562 => function ($stackPos) { + $this->semValue = new Scalar\String_($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 563 => function ($stackPos) { + $this->semValue = $this->parseNumString($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); + }, 564 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos - (1 - 1)]; + }]; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Timer; + +use function array_pop; +use function hrtime; +final class Timer +{ /** - * Returns the filter object used. + * @psalm-var list */ - public function filter() : \PHPUnit\SebastianBergmann\CodeCoverage\Filter + private $startTimes = []; + public function start() : void { - return $this->filter; + $this->startTimes[] = (float) hrtime(\true); } /** - * Returns the collected code coverage data. + * @throws NoActiveTimerException */ - public function getData(bool $raw = \false) : \PHPUnit\SebastianBergmann\CodeCoverage\ProcessedCodeCoverageData + public function stop() : Duration { - if (!$raw) { - if ($this->processUncoveredFiles) { - $this->processUncoveredFilesFromFilter(); - } elseif ($this->includeUncoveredFiles) { - $this->addUncoveredFilesFromFilter(); - } + if (empty($this->startTimes)) { + throw new NoActiveTimerException('Timer::start() has to be called before Timer::stop()'); } - return $this->data; + return Duration::fromNanoseconds((float) hrtime(\true) - array_pop($this->startTimes)); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Timer; + +use function is_float; +use function memory_get_peak_usage; +use function microtime; +use function sprintf; +final class ResourceUsageFormatter +{ /** - * Sets the coverage data. + * @psalm-var array */ - public function setData(\PHPUnit\SebastianBergmann\CodeCoverage\ProcessedCodeCoverageData $data) : void + private const SIZES = ['GB' => 1073741824, 'MB' => 1048576, 'KB' => 1024]; + public function resourceUsage(Duration $duration) : string { - $this->data = $data; + return sprintf('Time: %s, Memory: %s', $duration->asString(), $this->bytesToString(memory_get_peak_usage(\true))); } /** - * Returns the test data. + * @throws TimeSinceStartOfRequestNotAvailableException */ - public function getTests() : array + public function resourceUsageSinceStartOfRequest() : string { - return $this->tests; + if (!isset($_SERVER['REQUEST_TIME_FLOAT'])) { + throw new TimeSinceStartOfRequestNotAvailableException('Cannot determine time at which the request started because $_SERVER[\'REQUEST_TIME_FLOAT\'] is not available'); + } + if (!is_float($_SERVER['REQUEST_TIME_FLOAT'])) { + throw new TimeSinceStartOfRequestNotAvailableException('Cannot determine time at which the request started because $_SERVER[\'REQUEST_TIME_FLOAT\'] is not of type float'); + } + return $this->resourceUsage(Duration::fromMicroseconds(1000000 * (microtime(\true) - $_SERVER['REQUEST_TIME_FLOAT']))); } - /** - * Sets the test data. - */ - public function setTests(array $tests) : void + private function bytesToString(int $bytes) : string { - $this->tests = $tests; - } - /** - * Start collection of code coverage information. - * - * @param PhptTestCase|string|TestCase $id - */ - public function start($id, bool $clear = \false) : void - { - if ($clear) { - $this->clear(); - } - $this->currentId = $id; - $this->driver->start(); - } - /** - * Stop collection of code coverage information. - * - * @param array|false $linesToBeCovered - */ - public function stop(bool $append = \true, $linesToBeCovered = [], array $linesToBeUsed = []) : \PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData - { - if (!\is_array($linesToBeCovered) && $linesToBeCovered !== \false) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\InvalidArgumentException('$linesToBeCovered must be an array or false'); - } - $data = $this->driver->stop(); - $this->append($data, null, $append, $linesToBeCovered, $linesToBeUsed); - $this->currentId = null; - return $data; - } - /** - * Appends code coverage data. - * - * @param PhptTestCase|string|TestCase $id - * @param array|false $linesToBeCovered - * - * @throws UnintentionallyCoveredCodeException - * @throws TestIdMissingException - * @throws ReflectionException - */ - public function append(\PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData $rawData, $id = null, bool $append = \true, $linesToBeCovered = [], array $linesToBeUsed = []) : void - { - if ($id === null) { - $id = $this->currentId; - } - if ($id === null) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\TestIdMissingException(); - } - $this->applyFilter($rawData); - if ($this->useAnnotationsForIgnoringCode) { - $this->applyIgnoredLinesFilter($rawData); - } - $this->data->initializeUnseenData($rawData); - if (!$append) { - return; - } - if ($id !== self::UNCOVERED_FILES) { - $this->applyCoversAnnotationFilter($rawData, $linesToBeCovered, $linesToBeUsed); - if (empty($rawData->lineCoverage())) { - return; - } - $size = 'unknown'; - $status = -1; - $fromTestcase = \false; - if ($id instanceof \PHPUnit\Framework\TestCase) { - $fromTestcase = \true; - $_size = $id->getSize(); - if ($_size === \PHPUnit\Util\Test::SMALL) { - $size = 'small'; - } elseif ($_size === \PHPUnit\Util\Test::MEDIUM) { - $size = 'medium'; - } elseif ($_size === \PHPUnit\Util\Test::LARGE) { - $size = 'large'; - } - $status = $id->getStatus(); - $id = \get_class($id) . '::' . $id->getName(); - } elseif ($id instanceof \PHPUnit\Runner\PhptTestCase) { - $fromTestcase = \true; - $size = 'large'; - $id = $id->getName(); + foreach (self::SIZES as $unit => $value) { + if ($bytes >= $value) { + return sprintf('%.2f %s', $bytes >= 1024 ? $bytes / $value : $bytes, $unit); } - $this->tests[$id] = ['size' => $size, 'status' => $status, 'fromTestcase' => $fromTestcase]; - $this->data->markCodeAsExecutedByTestCase($id, $rawData); } + // @codeCoverageIgnoreStart + return $bytes . ' byte' . ($bytes !== 1 ? 's' : ''); + // @codeCoverageIgnoreEnd } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Timer; + +use function floor; +use function sprintf; +/** + * @psalm-immutable + */ +final class Duration +{ /** - * Merges the data from another instance. + * @var float */ - public function merge(self $that) : void - { - $this->filter->includeFiles($that->filter()->files()); - $this->data->merge($that->data); - $this->tests = \array_merge($this->tests, $that->getTests()); - } - public function enableCheckForUnintentionallyCoveredCode() : void - { - $this->checkForUnintentionallyCoveredCode = \true; - } - public function disableCheckForUnintentionallyCoveredCode() : void - { - $this->checkForUnintentionallyCoveredCode = \false; - } - public function includeUncoveredFiles() : void - { - $this->includeUncoveredFiles = \true; - } - public function excludeUncoveredFiles() : void - { - $this->includeUncoveredFiles = \false; - } - public function processUncoveredFiles() : void - { - $this->processUncoveredFiles = \true; - } - public function doNotProcessUncoveredFiles() : void - { - $this->processUncoveredFiles = \false; - } - public function enableAnnotationsForIgnoringCode() : void - { - $this->useAnnotationsForIgnoringCode = \true; - } - public function disableAnnotationsForIgnoringCode() : void - { - $this->useAnnotationsForIgnoringCode = \false; - } - public function ignoreDeprecatedCode() : void - { - $this->ignoreDeprecatedCode = \true; - } - public function doNotIgnoreDeprecatedCode() : void - { - $this->ignoreDeprecatedCode = \false; - } + private $nanoseconds; /** - * @psalm-assert-if-true !null $this->cacheDirectory + * @var int */ - public function cachesStaticAnalysis() : bool - { - return $this->cacheDirectory !== null; - } - public function cacheStaticAnalysis(string $directory) : void - { - $this->cacheDirectory = $directory; - } - public function doNotCacheStaticAnalysis() : void - { - $this->cacheDirectory = null; - } + private $hours; /** - * @throws StaticAnalysisCacheNotConfiguredException + * @var int */ - public function cacheDirectory() : string - { - if (!$this->cachesStaticAnalysis()) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysisCacheNotConfiguredException('The static analysis cache is not configured'); - } - return $this->cacheDirectory; - } + private $minutes; /** - * @psalm-param class-string $className + * @var int */ - public function excludeSubclassesOfThisClassFromUnintentionallyCoveredCodeCheck(string $className) : void - { - $this->parentClassesExcludedFromUnintentionallyCoveredCodeCheck[] = $className; - } - public function enableBranchAndPathCoverage() : void - { - $this->driver->enableBranchAndPathCoverage(); - } - public function disableBranchAndPathCoverage() : void - { - $this->driver->disableBranchAndPathCoverage(); - } - public function collectsBranchAndPathCoverage() : bool - { - return $this->driver->collectsBranchAndPathCoverage(); - } - public function detectsDeadCode() : bool - { - return $this->driver->detectsDeadCode(); - } + private $seconds; /** - * Applies the @covers annotation filtering. - * - * @param array|false $linesToBeCovered - * - * @throws UnintentionallyCoveredCodeException - * @throws ReflectionException + * @var int */ - private function applyCoversAnnotationFilter(\PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData $rawData, $linesToBeCovered, array $linesToBeUsed) : void - { - if ($linesToBeCovered === \false) { - $rawData->clear(); - return; - } - if (empty($linesToBeCovered)) { - return; - } - if ($this->checkForUnintentionallyCoveredCode && (!$this->currentId instanceof \PHPUnit\Framework\TestCase || !$this->currentId->isMedium() && !$this->currentId->isLarge())) { - $this->performUnintentionallyCoveredCodeCheck($rawData, $linesToBeCovered, $linesToBeUsed); - } - $rawLineData = $rawData->lineCoverage(); - $filesWithNoCoverage = \array_diff_key($rawLineData, $linesToBeCovered); - foreach (\array_keys($filesWithNoCoverage) as $fileWithNoCoverage) { - $rawData->removeCoverageDataForFile($fileWithNoCoverage); - } - if (\is_array($linesToBeCovered)) { - foreach ($linesToBeCovered as $fileToBeCovered => $includedLines) { - $rawData->keepCoverageDataOnlyForLines($fileToBeCovered, $includedLines); - } - } - } - private function applyFilter(\PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData $data) : void - { - if ($this->filter->isEmpty()) { - return; - } - foreach (\array_keys($data->lineCoverage()) as $filename) { - if ($this->filter->isExcluded($filename)) { - $data->removeCoverageDataForFile($filename); - } - } - } - private function applyIgnoredLinesFilter(\PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData $data) : void + private $milliseconds; + public static function fromMicroseconds(float $microseconds) : self { - foreach (\array_keys($data->lineCoverage()) as $filename) { - if (!$this->filter->isFile($filename)) { - continue; - } - $data->removeCoverageDataForLines($filename, $this->coveredFileAnalyser()->ignoredLinesFor($filename)); - } + return new self($microseconds * 1000); } - /** - * @throws UnintentionallyCoveredCodeException - */ - private function addUncoveredFilesFromFilter() : void + public static function fromNanoseconds(float $nanoseconds) : self { - $uncoveredFiles = \array_diff($this->filter->files(), $this->data->coveredFiles()); - foreach ($uncoveredFiles as $uncoveredFile) { - if (\is_file($uncoveredFile)) { - $this->append(\PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData::fromUncoveredFile($uncoveredFile, $this->uncoveredFileAnalyser()), self::UNCOVERED_FILES); - } - } + return new self($nanoseconds); } - /** - * @throws UnintentionallyCoveredCodeException - */ - private function processUncoveredFilesFromFilter() : void + private function __construct(float $nanoseconds) { - $uncoveredFiles = \array_diff($this->filter->files(), $this->data->coveredFiles()); - $this->driver->start(); - foreach ($uncoveredFiles as $uncoveredFile) { - if (\is_file($uncoveredFile)) { - include_once $uncoveredFile; - } - } - $this->append($this->driver->stop(), self::UNCOVERED_FILES); + $this->nanoseconds = $nanoseconds; + $timeInMilliseconds = $nanoseconds / 1000000; + $hours = floor($timeInMilliseconds / 60 / 60 / 1000); + $hoursInMilliseconds = $hours * 60 * 60 * 1000; + $minutes = floor($timeInMilliseconds / 60 / 1000) % 60; + $minutesInMilliseconds = $minutes * 60 * 1000; + $seconds = floor(($timeInMilliseconds - $hoursInMilliseconds - $minutesInMilliseconds) / 1000); + $secondsInMilliseconds = $seconds * 1000; + $milliseconds = $timeInMilliseconds - $hoursInMilliseconds - $minutesInMilliseconds - $secondsInMilliseconds; + $this->hours = (int) $hours; + $this->minutes = $minutes; + $this->seconds = (int) $seconds; + $this->milliseconds = (int) $milliseconds; } - /** - * @throws UnintentionallyCoveredCodeException - * @throws ReflectionException - */ - private function performUnintentionallyCoveredCodeCheck(\PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData $data, array $linesToBeCovered, array $linesToBeUsed) : void + public function asNanoseconds() : float { - $allowedLines = $this->getAllowedLines($linesToBeCovered, $linesToBeUsed); - $unintentionallyCoveredUnits = []; - foreach ($data->lineCoverage() as $file => $_data) { - foreach ($_data as $line => $flag) { - if ($flag === 1 && !isset($allowedLines[$file][$line])) { - $unintentionallyCoveredUnits[] = $this->wizard->lookup($file, $line); - } - } - } - $unintentionallyCoveredUnits = $this->processUnintentionallyCoveredUnits($unintentionallyCoveredUnits); - if (!empty($unintentionallyCoveredUnits)) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException($unintentionallyCoveredUnits); - } + return $this->nanoseconds; } - private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed) : array + public function asMicroseconds() : float { - $allowedLines = []; - foreach (\array_keys($linesToBeCovered) as $file) { - if (!isset($allowedLines[$file])) { - $allowedLines[$file] = []; - } - $allowedLines[$file] = \array_merge($allowedLines[$file], $linesToBeCovered[$file]); - } - foreach (\array_keys($linesToBeUsed) as $file) { - if (!isset($allowedLines[$file])) { - $allowedLines[$file] = []; - } - $allowedLines[$file] = \array_merge($allowedLines[$file], $linesToBeUsed[$file]); - } - foreach (\array_keys($allowedLines) as $file) { - $allowedLines[$file] = \array_flip(\array_unique($allowedLines[$file])); - } - return $allowedLines; + return $this->nanoseconds / 1000; } - /** - * @throws ReflectionException - */ - private function processUnintentionallyCoveredUnits(array $unintentionallyCoveredUnits) : array + public function asMilliseconds() : float { - $unintentionallyCoveredUnits = \array_unique($unintentionallyCoveredUnits); - \sort($unintentionallyCoveredUnits); - foreach (\array_keys($unintentionallyCoveredUnits) as $k => $v) { - $unit = \explode('::', $unintentionallyCoveredUnits[$k]); - if (\count($unit) !== 2) { - continue; - } - try { - $class = new \ReflectionClass($unit[0]); - foreach ($this->parentClassesExcludedFromUnintentionallyCoveredCodeCheck as $parentClass) { - if ($class->isSubclassOf($parentClass)) { - unset($unintentionallyCoveredUnits[$k]); - break; - } - } - } catch (\ReflectionException $e) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); - } - } - return \array_values($unintentionallyCoveredUnits); + return $this->nanoseconds / 1000000; } - private function coveredFileAnalyser() : \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CoveredFileAnalyser + public function asSeconds() : float { - if ($this->coveredFileAnalyser !== null) { - return $this->coveredFileAnalyser; - } - $this->coveredFileAnalyser = new \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingCoveredFileAnalyser($this->useAnnotationsForIgnoringCode, $this->ignoreDeprecatedCode); - if ($this->cachesStaticAnalysis()) { - $this->coveredFileAnalyser = new \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CachingCoveredFileAnalyser($this->cacheDirectory, $this->coveredFileAnalyser); - } - return $this->coveredFileAnalyser; + return $this->nanoseconds / 1000000000; } - private function uncoveredFileAnalyser() : \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\UncoveredFileAnalyser + public function asString() : string { - if ($this->uncoveredFileAnalyser !== null) { - return $this->uncoveredFileAnalyser; + $result = ''; + if ($this->hours > 0) { + $result = sprintf('%02d', $this->hours) . ':'; } - $this->uncoveredFileAnalyser = new \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingUncoveredFileAnalyser(); - if ($this->cachesStaticAnalysis()) { - $this->uncoveredFileAnalyser = new \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CachingUncoveredFileAnalyser($this->cacheDirectory, $this->uncoveredFileAnalyser); + $result .= sprintf('%02d', $this->minutes) . ':'; + $result .= sprintf('%02d', $this->seconds); + if ($this->milliseconds > 0) { + $result .= '.' . sprintf('%03d', $this->milliseconds); } - return $this->uncoveredFileAnalyser; + return $result; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; +namespace PHPUnit\SebastianBergmann\Timer; -use const DIRECTORY_SEPARATOR; -use function file_get_contents; -use function file_put_contents; -use function filemtime; -use function hash; -use function is_file; -use function serialize; -use function unserialize; -use PHPUnit\SebastianBergmann\CodeCoverage\Directory; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -abstract class Cache +use LogicException; +final class NoActiveTimerException extends LogicException implements Exception { - /** - * @var string - */ - private $directory; - public function __construct(string $directory) - { - \PHPUnit\SebastianBergmann\CodeCoverage\Directory::create($directory); - $this->directory = $directory; - } - protected function has(string $filename, string $key) : bool - { - $cacheFile = $this->cacheFile($filename, $key); - if (!\is_file($cacheFile)) { - return \false; - } - if (\filemtime($cacheFile) < \filemtime($filename)) { - return \false; - } - return \true; - } - /** - * @psalm-param list $allowedClasses - * - * @return mixed - */ - protected function read(string $filename, string $key, array $allowedClasses = []) - { - $options = ['allowed_classes' => \false]; - if (!empty($allowedClasses)) { - $options = ['allowed_classes' => $allowedClasses]; - } - return \unserialize(\file_get_contents($this->cacheFile($filename, $key)), $options); - } - /** - * @param mixed $data - */ - protected function write(string $filename, string $key, $data) : void - { - \file_put_contents($this->cacheFile($filename, $key), \serialize($data)); - } - private function cacheFile(string $filename, string $key) : string - { - return $this->directory . \DIRECTORY_SEPARATOR . \hash('sha256', $filename . $key); - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; +namespace PHPUnit\SebastianBergmann\Timer; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class CachingUncoveredFileAnalyser extends \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\Cache implements \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\UncoveredFileAnalyser +use RuntimeException; +final class TimeSinceStartOfRequestNotAvailableException extends RuntimeException implements Exception { - /** - * @var UncoveredFileAnalyser - */ - private $uncoveredFileAnalyser; - public function __construct(string $directory, \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\UncoveredFileAnalyser $uncoveredFileAnalyser) - { - parent::__construct($directory); - $this->uncoveredFileAnalyser = $uncoveredFileAnalyser; - } - public function executableLinesIn(string $filename) : array - { - if ($this->has($filename, __METHOD__)) { - return $this->read($filename, __METHOD__); - } - $data = $this->uncoveredFileAnalyser->executableLinesIn($filename); - $this->write($filename, __METHOD__, $data); - return $data; - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; +namespace PHPUnit\SebastianBergmann\Timer; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -interface UncoveredFileAnalyser +use Throwable; +interface Exception extends Throwable { - public function executableLinesIn(string $filename) : array; } +phpunit/php-timer + +Copyright (c) 2010-2020, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; +namespace PHPUnit\SebastianBergmann\GlobalState; +use function array_keys; use function array_merge; -use function range; -use function strpos; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Stmt\Class_; -use PHPUnit\PhpParser\Node\Stmt\ClassMethod; -use PHPUnit\PhpParser\Node\Stmt\Function_; -use PHPUnit\PhpParser\Node\Stmt\Interface_; -use PHPUnit\PhpParser\Node\Stmt\Trait_; -use PHPUnit\PhpParser\NodeTraverser; -use PHPUnit\PhpParser\NodeVisitorAbstract; +use function array_reverse; +use function func_get_args; +use function get_declared_classes; +use function get_declared_interfaces; +use function get_declared_traits; +use function get_defined_constants; +use function get_defined_functions; +use function get_included_files; +use function in_array; +use function ini_get_all; +use function is_array; +use function is_object; +use function is_resource; +use function is_scalar; +use function serialize; +use function unserialize; +use ReflectionClass; +use PHPUnit\SebastianBergmann\ObjectReflector\ObjectReflector; +use PHPUnit\SebastianBergmann\RecursionContext\Context; +use Throwable; /** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * A snapshot of global state. */ -final class IgnoredLinesFindingVisitor extends \PHPUnit\PhpParser\NodeVisitorAbstract +class Snapshot { /** - * @psalm-var list + * @var ExcludeList */ - private $ignoredLines = []; + private $excludeList; /** - * @var bool + * @var array */ - private $useAnnotationsForIgnoringCode; + private $globalVariables = []; /** - * @var bool + * @var array */ - private $ignoreDeprecated; - public function __construct(bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecated) - { - $this->useAnnotationsForIgnoringCode = $useAnnotationsForIgnoringCode; - $this->ignoreDeprecated = $ignoreDeprecated; - } - public function enterNode(\PHPUnit\PhpParser\Node $node) : ?int - { - if (!$node instanceof \PHPUnit\PhpParser\Node\Stmt\Class_ && !$node instanceof \PHPUnit\PhpParser\Node\Stmt\Trait_ && !$node instanceof \PHPUnit\PhpParser\Node\Stmt\Interface_ && !$node instanceof \PHPUnit\PhpParser\Node\Stmt\ClassMethod && !$node instanceof \PHPUnit\PhpParser\Node\Stmt\Function_) { - return null; - } - if ($node instanceof \PHPUnit\PhpParser\Node\Stmt\Class_ && $node->isAnonymous()) { - return null; + private $superGlobalArrays = []; + /** + * @var array + */ + private $superGlobalVariables = []; + /** + * @var array + */ + private $staticAttributes = []; + /** + * @var array + */ + private $iniSettings = []; + /** + * @var array + */ + private $includedFiles = []; + /** + * @var array + */ + private $constants = []; + /** + * @var array + */ + private $functions = []; + /** + * @var array + */ + private $interfaces = []; + /** + * @var array + */ + private $classes = []; + /** + * @var array + */ + private $traits = []; + /** + * Creates a snapshot of the current global state. + */ + public function __construct(ExcludeList $excludeList = null, bool $includeGlobalVariables = \true, bool $includeStaticAttributes = \true, bool $includeConstants = \true, bool $includeFunctions = \true, bool $includeClasses = \true, bool $includeInterfaces = \true, bool $includeTraits = \true, bool $includeIniSettings = \true, bool $includeIncludedFiles = \true) + { + $this->excludeList = $excludeList ?: new ExcludeList(); + if ($includeConstants) { + $this->snapshotConstants(); } - // Workaround for https://bugs.xdebug.org/view.php?id=1798 - if ($node instanceof \PHPUnit\PhpParser\Node\Stmt\Class_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Trait_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Interface_) { - $this->ignoredLines[] = $node->getStartLine(); + if ($includeFunctions) { + $this->snapshotFunctions(); } - if (!$this->useAnnotationsForIgnoringCode) { - return null; + if ($includeClasses || $includeStaticAttributes) { + $this->snapshotClasses(); } - if ($node instanceof \PHPUnit\PhpParser\Node\Stmt\Interface_) { - return null; + if ($includeInterfaces) { + $this->snapshotInterfaces(); } - $docComment = $node->getDocComment(); - if ($docComment === null) { - return null; + if ($includeGlobalVariables) { + $this->setupSuperGlobalArrays(); + $this->snapshotGlobals(); } - if (\strpos($docComment->getText(), '@codeCoverageIgnore') !== \false) { - $this->ignoredLines = \array_merge($this->ignoredLines, \range($node->getStartLine(), $node->getEndLine())); + if ($includeStaticAttributes) { + $this->snapshotStaticAttributes(); } - if ($this->ignoreDeprecated && \strpos($docComment->getText(), '@deprecated') !== \false) { - $this->ignoredLines = \array_merge($this->ignoredLines, \range($node->getStartLine(), $node->getEndLine())); + if ($includeIniSettings) { + $this->iniSettings = ini_get_all(null, \false); } - if ($node instanceof \PHPUnit\PhpParser\Node\Stmt\ClassMethod || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Function_) { - return \PHPUnit\PhpParser\NodeTraverser::DONT_TRAVERSE_CHILDREN; + if ($includeIncludedFiles) { + $this->includedFiles = get_included_files(); } - return null; + $this->traits = get_declared_traits(); + } + public function excludeList() : ExcludeList + { + return $this->excludeList; + } + public function globalVariables() : array + { + return $this->globalVariables; + } + public function superGlobalVariables() : array + { + return $this->superGlobalVariables; + } + public function superGlobalArrays() : array + { + return $this->superGlobalArrays; + } + public function staticAttributes() : array + { + return $this->staticAttributes; + } + public function iniSettings() : array + { + return $this->iniSettings; + } + public function includedFiles() : array + { + return $this->includedFiles; + } + public function constants() : array + { + return $this->constants; + } + public function functions() : array + { + return $this->functions; + } + public function interfaces() : array + { + return $this->interfaces; + } + public function classes() : array + { + return $this->classes; + } + public function traits() : array + { + return $this->traits; } /** - * @psalm-return list + * Creates a snapshot user-defined constants. */ - public function ignoredLines() : array + private function snapshotConstants() : void { - return $this->ignoredLines; + $constants = get_defined_constants(\true); + if (isset($constants['user'])) { + $this->constants = $constants['user']; + } } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; - -use PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -interface CoveredFileAnalyser -{ - public function classesIn(string $filename) : array; - public function traitsIn(string $filename) : array; - public function functionsIn(string $filename) : array; - public function linesOfCodeFor(string $filename) : \PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode; - public function ignoredLinesFor(string $filename) : array; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; - -use PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class CachingCoveredFileAnalyser extends \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\Cache implements \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CoveredFileAnalyser -{ /** - * @var CoveredFileAnalyser + * Creates a snapshot user-defined functions. */ - private $coveredFileAnalyser; + private function snapshotFunctions() : void + { + $functions = get_defined_functions(); + $this->functions = $functions['user']; + } /** - * @var array + * Creates a snapshot user-defined classes. */ - private $cache = []; - public function __construct(string $directory, \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CoveredFileAnalyser $coveredFileAnalyser) + private function snapshotClasses() : void { - parent::__construct($directory); - $this->coveredFileAnalyser = $coveredFileAnalyser; + foreach (array_reverse(get_declared_classes()) as $className) { + $class = new ReflectionClass($className); + if (!$class->isUserDefined()) { + break; + } + $this->classes[] = $className; + } + $this->classes = array_reverse($this->classes); } - public function classesIn(string $filename) : array + /** + * Creates a snapshot user-defined interfaces. + */ + private function snapshotInterfaces() : void { - if (!isset($this->cache[$filename])) { - $this->process($filename); + foreach (array_reverse(get_declared_interfaces()) as $interfaceName) { + $class = new ReflectionClass($interfaceName); + if (!$class->isUserDefined()) { + break; + } + $this->interfaces[] = $interfaceName; } - return $this->cache[$filename]['classesIn']; + $this->interfaces = array_reverse($this->interfaces); } - public function traitsIn(string $filename) : array + /** + * Creates a snapshot of all global and super-global variables. + */ + private function snapshotGlobals() : void { - if (!isset($this->cache[$filename])) { - $this->process($filename); + $superGlobalArrays = $this->superGlobalArrays(); + foreach ($superGlobalArrays as $superGlobalArray) { + $this->snapshotSuperGlobalArray($superGlobalArray); + } + foreach (array_keys($GLOBALS) as $key) { + if ($key !== 'GLOBALS' && !in_array($key, $superGlobalArrays, \true) && $this->canBeSerialized($GLOBALS[$key]) && !$this->excludeList->isGlobalVariableExcluded($key)) { + /* @noinspection UnserializeExploitsInspection */ + $this->globalVariables[$key] = unserialize(serialize($GLOBALS[$key])); + } } - return $this->cache[$filename]['traitsIn']; } - public function functionsIn(string $filename) : array + /** + * Creates a snapshot a super-global variable array. + */ + private function snapshotSuperGlobalArray(string $superGlobalArray) : void { - if (!isset($this->cache[$filename])) { - $this->process($filename); + $this->superGlobalVariables[$superGlobalArray] = []; + if (isset($GLOBALS[$superGlobalArray]) && is_array($GLOBALS[$superGlobalArray])) { + foreach ($GLOBALS[$superGlobalArray] as $key => $value) { + /* @noinspection UnserializeExploitsInspection */ + $this->superGlobalVariables[$superGlobalArray][$key] = unserialize(serialize($value)); + } } - return $this->cache[$filename]['functionsIn']; } - public function linesOfCodeFor(string $filename) : \PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode + /** + * Creates a snapshot of all static attributes in user-defined classes. + */ + private function snapshotStaticAttributes() : void { - if (!isset($this->cache[$filename])) { - $this->process($filename); + foreach ($this->classes as $className) { + $class = new ReflectionClass($className); + $snapshot = []; + foreach ($class->getProperties() as $attribute) { + if ($attribute->isStatic()) { + $name = $attribute->getName(); + if ($this->excludeList->isStaticAttributeExcluded($className, $name)) { + continue; + } + $attribute->setAccessible(\true); + $value = $attribute->getValue(); + if ($this->canBeSerialized($value)) { + /* @noinspection UnserializeExploitsInspection */ + $snapshot[$name] = unserialize(serialize($value)); + } + } + } + if (!empty($snapshot)) { + $this->staticAttributes[$className] = $snapshot; + } } - return $this->cache[$filename]['linesOfCodeFor']; } - public function ignoredLinesFor(string $filename) : array + /** + * Returns a list of all super-global variable arrays. + */ + private function setupSuperGlobalArrays() : void { - if (!isset($this->cache[$filename])) { - $this->process($filename); + $this->superGlobalArrays = ['_ENV', '_POST', '_GET', '_COOKIE', '_SERVER', '_FILES', '_REQUEST']; + } + private function canBeSerialized($variable) : bool + { + if (is_scalar($variable) || $variable === null) { + return \true; } - return $this->cache[$filename]['ignoredLinesFor']; + if (is_resource($variable)) { + return \false; + } + foreach ($this->enumerateObjectsAndResources($variable) as $value) { + if (is_resource($value)) { + return \false; + } + if (is_object($value)) { + $class = new ReflectionClass($value); + if ($class->isAnonymous()) { + return \false; + } + try { + @serialize($value); + } catch (Throwable $t) { + return \false; + } + } + } + return \true; } - public function process(string $filename) : void + private function enumerateObjectsAndResources($variable) : array { - if ($this->has($filename, __CLASS__)) { - $this->cache[$filename] = $this->read($filename, __CLASS__, [\PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode::class]); - return; + if (isset(func_get_args()[1])) { + $processed = func_get_args()[1]; + } else { + $processed = new Context(); } - $this->cache[$filename] = ['classesIn' => $this->coveredFileAnalyser->classesIn($filename), 'traitsIn' => $this->coveredFileAnalyser->traitsIn($filename), 'functionsIn' => $this->coveredFileAnalyser->functionsIn($filename), 'linesOfCodeFor' => $this->coveredFileAnalyser->linesOfCodeFor($filename), 'ignoredLinesFor' => $this->coveredFileAnalyser->ignoredLinesFor($filename)]; - $this->write($filename, __CLASS__, $this->cache[$filename]); + $result = []; + if ($processed->contains($variable)) { + return $result; + } + $array = $variable; + $processed->add($variable); + if (is_array($variable)) { + foreach ($array as $element) { + if (!is_array($element) && !is_object($element) && !is_resource($element)) { + continue; + } + if (!is_resource($element)) { + /** @noinspection SlowArrayOperationsInLoopInspection */ + $result = array_merge($result, $this->enumerateObjectsAndResources($element, $processed)); + } else { + $result[] = $element; + } + } + } else { + $result[] = $variable; + foreach ((new ObjectReflector())->getAttributes($variable) as $value) { + if (!is_array($value) && !is_object($value) && !is_resource($value)) { + continue; + } + if (!is_resource($value)) { + /** @noinspection SlowArrayOperationsInLoopInspection */ + $result = array_merge($result, $this->enumerateObjectsAndResources($value, $processed)); + } else { + $result[] = $value; + } + } + } + return $result; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; +namespace PHPUnit\SebastianBergmann\GlobalState; -use function array_unique; -use function assert; -use function file_get_contents; +use const PHP_EOL; use function is_array; +use function is_scalar; +use function serialize; use function sprintf; -use function substr_count; -use function token_get_all; -use function trim; -use PHPUnit\PhpParser\Error; -use PHPUnit\PhpParser\Lexer; -use PHPUnit\PhpParser\NodeTraverser; -use PHPUnit\PhpParser\NodeVisitor\NameResolver; -use PHPUnit\PhpParser\NodeVisitor\ParentConnectingVisitor; -use PHPUnit\PhpParser\ParserFactory; -use PHPUnit\SebastianBergmann\CodeCoverage\ParserException; -use PHPUnit\SebastianBergmann\LinesOfCode\LineCountingVisitor; -use PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode; +use function var_export; /** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * Exports parts of a Snapshot as PHP code. */ -final class ParsingCoveredFileAnalyser implements \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CoveredFileAnalyser +final class CodeExporter { - /** - * @var array - */ - private $classes = []; - /** - * @var array - */ - private $traits = []; - /** - * @var array - */ - private $functions = []; - /** - * @var LinesOfCode[] - */ - private $linesOfCode = []; - /** - * @var array - */ - private $ignoredLines = []; - /** - * @var bool - */ - private $useAnnotationsForIgnoringCode; - /** - * @var bool - */ - private $ignoreDeprecatedCode; - public function __construct(bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode) - { - $this->useAnnotationsForIgnoringCode = $useAnnotationsForIgnoringCode; - $this->ignoreDeprecatedCode = $ignoreDeprecatedCode; - } - public function classesIn(string $filename) : array + public function constants(Snapshot $snapshot) : string { - $this->analyse($filename); - return $this->classes[$filename]; + $result = ''; + foreach ($snapshot->constants() as $name => $value) { + $result .= sprintf('if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", $name, $name, $this->exportVariable($value)); + } + return $result; } - public function traitsIn(string $filename) : array + public function globalVariables(Snapshot $snapshot) : string { - $this->analyse($filename); - return $this->traits[$filename]; - } - public function functionsIn(string $filename) : array + $result = <<<'EOT' +call_user_func( + function () { - $this->analyse($filename); - return $this->functions[$filename]; + foreach (array_keys($GLOBALS) as $key) { + unset($GLOBALS[$key]); + } } - public function linesOfCodeFor(string $filename) : \PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode - { - $this->analyse($filename); - return $this->linesOfCode[$filename]; +); + + +EOT; + foreach ($snapshot->globalVariables() as $name => $value) { + $result .= sprintf('$GLOBALS[%s] = %s;' . \PHP_EOL, $this->exportVariable($name), $this->exportVariable($value)); + } + return $result; } - public function ignoredLinesFor(string $filename) : array + public function iniSettings(Snapshot $snapshot) : string { - $this->analyse($filename); - return $this->ignoredLines[$filename]; + $result = ''; + foreach ($snapshot->iniSettings() as $key => $value) { + $result .= sprintf('@ini_set(%s, %s);' . "\n", $this->exportVariable($key), $this->exportVariable($value)); + } + return $result; } - /** - * @throws ParserException - */ - private function analyse(string $filename) : void + private function exportVariable($variable) : string { - if (isset($this->classes[$filename])) { - return; - } - $source = \file_get_contents($filename); - $linesOfCode = \substr_count($source, "\n"); - if ($linesOfCode === 0 && !empty($source)) { - $linesOfCode = 1; - } - $parser = (new \PHPUnit\PhpParser\ParserFactory())->create(\PHPUnit\PhpParser\ParserFactory::PREFER_PHP7, new \PHPUnit\PhpParser\Lexer()); - try { - $nodes = $parser->parse($source); - \assert($nodes !== null); - $traverser = new \PHPUnit\PhpParser\NodeTraverser(); - $codeUnitFindingVisitor = new \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor(); - $lineCountingVisitor = new \PHPUnit\SebastianBergmann\LinesOfCode\LineCountingVisitor($linesOfCode); - $ignoredLinesFindingVisitor = new \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\IgnoredLinesFindingVisitor($this->useAnnotationsForIgnoringCode, $this->ignoreDeprecatedCode); - $traverser->addVisitor(new \PHPUnit\PhpParser\NodeVisitor\NameResolver()); - $traverser->addVisitor(new \PHPUnit\PhpParser\NodeVisitor\ParentConnectingVisitor()); - $traverser->addVisitor($codeUnitFindingVisitor); - $traverser->addVisitor($lineCountingVisitor); - $traverser->addVisitor($ignoredLinesFindingVisitor); - /* @noinspection UnusedFunctionResultInspection */ - $traverser->traverse($nodes); - // @codeCoverageIgnoreStart - } catch (\PHPUnit\PhpParser\Error $error) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\ParserException(\sprintf('Cannot parse %s: %s', $filename, $error->getMessage()), (int) $error->getCode(), $error); + if (is_scalar($variable) || null === $variable || is_array($variable) && $this->arrayOnlyContainsScalars($variable)) { + return var_export($variable, \true); } - // @codeCoverageIgnoreEnd - $this->classes[$filename] = $codeUnitFindingVisitor->classes(); - $this->traits[$filename] = $codeUnitFindingVisitor->traits(); - $this->functions[$filename] = $codeUnitFindingVisitor->functions(); - $this->linesOfCode[$filename] = $lineCountingVisitor->result(); - $this->ignoredLines[$filename] = []; - $this->findLinesIgnoredByLineBasedAnnotations($filename, $source, $this->useAnnotationsForIgnoringCode); - $this->ignoredLines[$filename] = \array_unique(\array_merge($this->ignoredLines[$filename], $ignoredLinesFindingVisitor->ignoredLines())); - \sort($this->ignoredLines[$filename]); + return 'unserialize(' . var_export(serialize($variable), \true) . ')'; } - private function findLinesIgnoredByLineBasedAnnotations(string $filename, string $source, bool $useAnnotationsForIgnoringCode) : void + private function arrayOnlyContainsScalars(array $array) : bool { - $ignore = \false; - $stop = \false; - foreach (\token_get_all($source) as $token) { - if (!\is_array($token)) { - continue; - } - switch ($token[0]) { - case \T_COMMENT: - case \T_DOC_COMMENT: - if (!$useAnnotationsForIgnoringCode) { - break; - } - $comment = \trim($token[1]); - if ($comment === '// @codeCoverageIgnore' || $comment === '//@codeCoverageIgnore') { - $ignore = \true; - $stop = \true; - } elseif ($comment === '// @codeCoverageIgnoreStart' || $comment === '//@codeCoverageIgnoreStart') { - $ignore = \true; - } elseif ($comment === '// @codeCoverageIgnoreEnd' || $comment === '//@codeCoverageIgnoreEnd') { - $stop = \true; - } - break; + $result = \true; + foreach ($array as $element) { + if (is_array($element)) { + $result = $this->arrayOnlyContainsScalars($element); + } elseif (!is_scalar($element) && null !== $element) { + $result = \false; } - if ($ignore) { - $this->ignoredLines[$filename][] = $token[2]; - if ($stop) { - $ignore = \false; - $stop = \false; - } + if ($result === \false) { + break; } } + return $result; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; +namespace PHPUnit\SebastianBergmann\GlobalState; -use function implode; -use function rtrim; -use function trim; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Identifier; -use PHPUnit\PhpParser\Node\Name; -use PHPUnit\PhpParser\Node\NullableType; -use PHPUnit\PhpParser\Node\Stmt\Class_; -use PHPUnit\PhpParser\Node\Stmt\ClassMethod; -use PHPUnit\PhpParser\Node\Stmt\Function_; -use PHPUnit\PhpParser\Node\Stmt\Interface_; -use PHPUnit\PhpParser\Node\Stmt\Trait_; -use PHPUnit\PhpParser\Node\UnionType; -use PHPUnit\PhpParser\NodeTraverser; -use PHPUnit\PhpParser\NodeVisitorAbstract; -use PHPUnit\SebastianBergmann\Complexity\CyclomaticComplexityCalculatingVisitor; +use function array_diff; +use function array_key_exists; +use function array_keys; +use function array_merge; +use function function_exists; +use function get_defined_functions; +use function in_array; +use function is_array; +use ReflectionClass; +use ReflectionProperty; /** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * Restorer of snapshots of global state. */ -final class CodeUnitFindingVisitor extends \PHPUnit\PhpParser\NodeVisitorAbstract +class Restorer { /** - * @var array - */ - private $classes = []; - /** - * @var array - */ - private $traits = []; - /** - * @var array + * Deletes function definitions that are not defined in a snapshot. + * + * @throws RuntimeException when the uopz_delete() function is not available + * + * @see https://github.com/krakjoe/uopz */ - private $functions = []; - public function enterNode(\PHPUnit\PhpParser\Node $node) + public function restoreFunctions(Snapshot $snapshot) : void { - if ($node instanceof \PHPUnit\PhpParser\Node\Stmt\Class_) { - if ($node->isAnonymous()) { - return; - } - $this->processClass($node); + if (!function_exists('PHPUnit\\uopz_delete')) { + throw new RuntimeException('The uopz_delete() function is required for this operation'); } - if ($node instanceof \PHPUnit\PhpParser\Node\Stmt\Trait_) { - $this->processTrait($node); + $functions = get_defined_functions(); + foreach (array_diff($functions['user'], $snapshot->functions()) as $function) { + uopz_delete($function); } - if (!$node instanceof \PHPUnit\PhpParser\Node\Stmt\ClassMethod && !$node instanceof \PHPUnit\PhpParser\Node\Stmt\Function_) { - return null; - } - if ($node instanceof \PHPUnit\PhpParser\Node\Stmt\ClassMethod) { - $parentNode = $node->getAttribute('parent'); - if ($parentNode instanceof \PHPUnit\PhpParser\Node\Stmt\Class_ && $parentNode->isAnonymous()) { - return; - } - $this->processMethod($node); - return; - } - $this->processFunction($node); - } - public function classes() : array - { - return $this->classes; - } - public function traits() : array - { - return $this->traits; - } - public function functions() : array - { - return $this->functions; } /** - * @psalm-param ClassMethod|Function_ $node + * Restores all global and super-global variables from a snapshot. */ - private function cyclomaticComplexity(\PHPUnit\PhpParser\Node $node) : int + public function restoreGlobalVariables(Snapshot $snapshot) : void { - \assert($node instanceof \PHPUnit\PhpParser\Node\Stmt\ClassMethod || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Function_); - $nodes = $node->getStmts(); - if ($nodes === null) { - return 0; + $superGlobalArrays = $snapshot->superGlobalArrays(); + foreach ($superGlobalArrays as $superGlobalArray) { + $this->restoreSuperGlobalArray($snapshot, $superGlobalArray); + } + $globalVariables = $snapshot->globalVariables(); + foreach (array_keys($GLOBALS) as $key) { + if ($key !== 'GLOBALS' && !in_array($key, $superGlobalArrays, \true) && !$snapshot->excludeList()->isGlobalVariableExcluded($key)) { + if (array_key_exists($key, $globalVariables)) { + $GLOBALS[$key] = $globalVariables[$key]; + } else { + unset($GLOBALS[$key]); + } + } } - $traverser = new \PHPUnit\PhpParser\NodeTraverser(); - $cyclomaticComplexityCalculatingVisitor = new \PHPUnit\SebastianBergmann\Complexity\CyclomaticComplexityCalculatingVisitor(); - $traverser->addVisitor($cyclomaticComplexityCalculatingVisitor); - /* @noinspection UnusedFunctionResultInspection */ - $traverser->traverse($nodes); - return $cyclomaticComplexityCalculatingVisitor->cyclomaticComplexity(); } /** - * @psalm-param ClassMethod|Function_ $node + * Restores all static attributes in user-defined classes from this snapshot. */ - private function signature(\PHPUnit\PhpParser\Node $node) : string + public function restoreStaticAttributes(Snapshot $snapshot) : void { - \assert($node instanceof \PHPUnit\PhpParser\Node\Stmt\ClassMethod || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Function_); - $signature = ($node->returnsByRef() ? '&' : '') . $node->name->toString() . '('; - $parameters = []; - foreach ($node->getParams() as $parameter) { - \assert(isset($parameter->var->name)); - $parameterAsString = ''; - if ($parameter->type !== null) { - $parameterAsString = $this->type($parameter->type) . ' '; + $current = new Snapshot($snapshot->excludeList(), \false, \false, \false, \false, \true, \false, \false, \false, \false); + $newClasses = array_diff($current->classes(), $snapshot->classes()); + unset($current); + foreach ($snapshot->staticAttributes() as $className => $staticAttributes) { + foreach ($staticAttributes as $name => $value) { + $reflector = new ReflectionProperty($className, $name); + $reflector->setAccessible(\true); + $reflector->setValue($value); } - $parameterAsString .= '$' . $parameter->var->name; - /* @todo Handle default values */ - $parameters[] = $parameterAsString; } - $signature .= \implode(', ', $parameters) . ')'; - $returnType = $node->getReturnType(); - if ($returnType !== null) { - $signature .= ': ' . $this->type($returnType); + foreach ($newClasses as $className) { + $class = new ReflectionClass($className); + $defaults = $class->getDefaultProperties(); + foreach ($class->getProperties() as $attribute) { + if (!$attribute->isStatic()) { + continue; + } + $name = $attribute->getName(); + if ($snapshot->excludeList()->isStaticAttributeExcluded($className, $name)) { + continue; + } + if (!isset($defaults[$name])) { + continue; + } + $attribute->setAccessible(\true); + $attribute->setValue($defaults[$name]); + } } - return $signature; } /** - * @psalm-param Identifier|Name|NullableType|UnionType $type + * Restores a super-global variable array from this snapshot. */ - private function type(\PHPUnit\PhpParser\Node $type) : string + private function restoreSuperGlobalArray(Snapshot $snapshot, string $superGlobalArray) : void { - \assert($type instanceof \PHPUnit\PhpParser\Node\Identifier || $type instanceof \PHPUnit\PhpParser\Node\Name || $type instanceof \PHPUnit\PhpParser\Node\NullableType || $type instanceof \PHPUnit\PhpParser\Node\UnionType); - if ($type instanceof \PHPUnit\PhpParser\Node\NullableType) { - return '?' . $type->type; - } - if ($type instanceof \PHPUnit\PhpParser\Node\UnionType) { - $types = []; - foreach ($type->types as $_type) { - $types[] = $_type->toString(); + $superGlobalVariables = $snapshot->superGlobalVariables(); + if (isset($GLOBALS[$superGlobalArray]) && is_array($GLOBALS[$superGlobalArray]) && isset($superGlobalVariables[$superGlobalArray])) { + $keys = array_keys(array_merge($GLOBALS[$superGlobalArray], $superGlobalVariables[$superGlobalArray])); + foreach ($keys as $key) { + if (isset($superGlobalVariables[$superGlobalArray][$key])) { + $GLOBALS[$superGlobalArray][$key] = $superGlobalVariables[$superGlobalArray][$key]; + } else { + unset($GLOBALS[$superGlobalArray][$key]); + } } - return \implode('|', $types); - } - return $type->toString(); - } - private function visibility(\PHPUnit\PhpParser\Node\Stmt\ClassMethod $node) : string - { - if ($node->isPrivate()) { - return 'private'; } - if ($node->isProtected()) { - return 'protected'; - } - return 'public'; - } - private function processClass(\PHPUnit\PhpParser\Node\Stmt\Class_ $node) : void - { - $name = $node->name->toString(); - $namespacedName = $node->namespacedName->toString(); - $this->classes[$namespacedName] = ['name' => $name, 'namespacedName' => $namespacedName, 'namespace' => $this->namespace($namespacedName, $name), 'startLine' => $node->getStartLine(), 'endLine' => $node->getEndLine(), 'methods' => []]; - } - private function processTrait(\PHPUnit\PhpParser\Node\Stmt\Trait_ $node) : void - { - $name = $node->name->toString(); - $namespacedName = $node->namespacedName->toString(); - $this->traits[$namespacedName] = ['name' => $name, 'namespacedName' => $namespacedName, 'namespace' => $this->namespace($namespacedName, $name), 'startLine' => $node->getStartLine(), 'endLine' => $node->getEndLine(), 'methods' => []]; - } - private function processMethod(\PHPUnit\PhpParser\Node\Stmt\ClassMethod $node) : void - { - $parentNode = $node->getAttribute('parent'); - if ($parentNode instanceof \PHPUnit\PhpParser\Node\Stmt\Interface_) { - return; - } - \assert($parentNode instanceof \PHPUnit\PhpParser\Node\Stmt\Class_ || $parentNode instanceof \PHPUnit\PhpParser\Node\Stmt\Trait_); - \assert(isset($parentNode->name)); - \assert(isset($parentNode->namespacedName)); - \assert($parentNode->namespacedName instanceof \PHPUnit\PhpParser\Node\Name); - $parentName = $parentNode->name->toString(); - $parentNamespacedName = $parentNode->namespacedName->toString(); - if ($parentNode instanceof \PHPUnit\PhpParser\Node\Stmt\Class_) { - $storage =& $this->classes; - } else { - $storage =& $this->traits; - } - if (!isset($storage[$parentNamespacedName])) { - $storage[$parentNamespacedName] = ['name' => $parentName, 'namespacedName' => $parentNamespacedName, 'namespace' => $this->namespace($parentNamespacedName, $parentName), 'startLine' => $parentNode->getStartLine(), 'endLine' => $parentNode->getEndLine(), 'methods' => []]; - } - $storage[$parentNamespacedName]['methods'][$node->name->toString()] = ['methodName' => $node->name->toString(), 'signature' => $this->signature($node), 'visibility' => $this->visibility($node), 'startLine' => $node->getStartLine(), 'endLine' => $node->getEndLine(), 'ccn' => $this->cyclomaticComplexity($node)]; - } - private function processFunction(\PHPUnit\PhpParser\Node\Stmt\Function_ $node) : void - { - \assert(isset($node->name)); - \assert(isset($node->namespacedName)); - \assert($node->namespacedName instanceof \PHPUnit\PhpParser\Node\Name); - $name = $node->name->toString(); - $namespacedName = $node->namespacedName->toString(); - $this->functions[$namespacedName] = ['name' => $name, 'namespacedName' => $namespacedName, 'namespace' => $this->namespace($namespacedName, $name), 'signature' => $this->signature($node), 'startLine' => $node->getStartLine(), 'endLine' => $node->getEndLine(), 'ccn' => $this->cyclomaticComplexity($node)]; - } - private function namespace(string $namespacedName, string $name) : string - { - return \trim(\rtrim($namespacedName, $name), '\\'); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; +namespace PHPUnit\SebastianBergmann\GlobalState; -use PHPUnit\PhpParser\Error; -use PHPUnit\PhpParser\Lexer; -use PHPUnit\PhpParser\NodeTraverser; -use PHPUnit\PhpParser\ParserFactory; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class ParsingUncoveredFileAnalyser implements \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\UncoveredFileAnalyser +final class RuntimeException extends \RuntimeException implements Exception { - public function executableLinesIn(string $filename) : array - { - $parser = (new \PHPUnit\PhpParser\ParserFactory())->create(\PHPUnit\PhpParser\ParserFactory::PREFER_PHP7, new \PHPUnit\PhpParser\Lexer()); - try { - $nodes = $parser->parse(\file_get_contents($filename)); - \assert($nodes !== null); - $traverser = new \PHPUnit\PhpParser\NodeTraverser(); - $visitor = new \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\ExecutableLinesFindingVisitor(); - $traverser->addVisitor($visitor); - /* @noinspection UnusedFunctionResultInspection */ - $traverser->traverse($nodes); - return $visitor->executableLines(); - // @codeCoverageIgnoreStart - } catch (\PHPUnit\PhpParser\Error $error) { - } - // @codeCoverageIgnoreEnd - return []; - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; +namespace PHPUnit\SebastianBergmann\GlobalState; -use PHPUnit\SebastianBergmann\CodeCoverage\Filter; -final class CacheWarmer +use Throwable; +interface Exception extends Throwable { - public function warmCache(string $cacheDirectory, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode, \PHPUnit\SebastianBergmann\CodeCoverage\Filter $filter) : void - { - $coveredFileAnalyser = new \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CachingCoveredFileAnalyser($cacheDirectory, new \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingCoveredFileAnalyser($useAnnotationsForIgnoringCode, $ignoreDeprecatedCode)); - $uncoveredFileAnalyser = new \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CachingUncoveredFileAnalyser($cacheDirectory, new \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingUncoveredFileAnalyser()); - foreach ($filter->files() as $file) { - $coveredFileAnalyser->process($file); - /* @noinspection UnusedFunctionResultInspection */ - $uncoveredFileAnalyser->executableLinesIn($file); - } - } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; +Copyright (c) 2001-2021, Sebastian Bergmann . +All rights reserved. -use function array_unique; -use function sort; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Stmt\Break_; -use PHPUnit\PhpParser\Node\Stmt\Case_; -use PHPUnit\PhpParser\Node\Stmt\Catch_; -use PHPUnit\PhpParser\Node\Stmt\Continue_; -use PHPUnit\PhpParser\Node\Stmt\Do_; -use PHPUnit\PhpParser\Node\Stmt\Echo_; -use PHPUnit\PhpParser\Node\Stmt\Else_; -use PHPUnit\PhpParser\Node\Stmt\ElseIf_; -use PHPUnit\PhpParser\Node\Stmt\Expression; -use PHPUnit\PhpParser\Node\Stmt\Finally_; -use PHPUnit\PhpParser\Node\Stmt\For_; -use PHPUnit\PhpParser\Node\Stmt\Foreach_; -use PHPUnit\PhpParser\Node\Stmt\Goto_; -use PHPUnit\PhpParser\Node\Stmt\If_; -use PHPUnit\PhpParser\Node\Stmt\Return_; -use PHPUnit\PhpParser\Node\Stmt\Switch_; -use PHPUnit\PhpParser\Node\Stmt\Throw_; -use PHPUnit\PhpParser\Node\Stmt\TryCatch; -use PHPUnit\PhpParser\Node\Stmt\Unset_; -use PHPUnit\PhpParser\Node\Stmt\While_; -use PHPUnit\PhpParser\NodeVisitorAbstract; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class ExecutableLinesFindingVisitor extends \PHPUnit\PhpParser\NodeVisitorAbstract -{ - /** - * @psalm-var list - */ - private $executableLines = []; - public function enterNode(\PHPUnit\PhpParser\Node $node) : void - { - if (!$this->isExecutable($node)) { - return; - } - $this->executableLines[] = $node->getStartLine(); - } - /** - * @psalm-return list - */ - public function executableLines() : array - { - $executableLines = \array_unique($this->executableLines); - \sort($executableLines); - return $executableLines; - } - private function isExecutable(\PHPUnit\PhpParser\Node $node) : bool - { - return $node instanceof \PHPUnit\PhpParser\Node\Stmt\Break_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Case_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Catch_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Continue_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Do_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Echo_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\ElseIf_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Else_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Expression || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Finally_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Foreach_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\For_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Goto_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\If_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Return_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Switch_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Throw_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\TryCatch || $node instanceof \PHPUnit\PhpParser\Node\Stmt\Unset_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\While_; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. -use function dirname; -use PHPUnit\SebastianBergmann\Version as VersionId; -final class Version -{ - /** - * @var string - */ - private static $version; - public static function id() : string - { - if (self::$version === null) { - self::$version = (new \PHPUnit\SebastianBergmann\Version('9.2.5', \dirname(__DIR__)))->getVersion(); - } - return self::$version; - } -} + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Node; +namespace PHPUnit\SebastianBergmann\GlobalState; -use const DIRECTORY_SEPARATOR; -use function array_merge; -use function str_replace; -use function substr; -use Countable; -use PHPUnit\SebastianBergmann\CodeCoverage\Percentage; -use PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -abstract class AbstractNode implements \Countable +use function in_array; +use function strpos; +use ReflectionClass; +final class ExcludeList { /** - * @var string + * @var array */ - private $name; + private $globalVariables = []; /** - * @var string + * @var string[] */ - private $pathAsString; + private $classes = []; /** - * @var array + * @var string[] */ - private $pathAsArray; + private $classNamePrefixes = []; /** - * @var AbstractNode + * @var string[] */ - private $parent; + private $parentClasses = []; /** - * @var string + * @var string[] */ - private $id; - public function __construct(string $name, self $parent = null) - { - if (\substr($name, -1) === \DIRECTORY_SEPARATOR) { - $name = \substr($name, 0, -1); - } - $this->name = $name; - $this->parent = $parent; - } - public function name() : string - { - return $this->name; - } - public function id() : string - { - if ($this->id === null) { - $parent = $this->parent(); - if ($parent === null) { - $this->id = 'index'; - } else { - $parentId = $parent->id(); - if ($parentId === 'index') { - $this->id = \str_replace(':', '_', $this->name); - } else { - $this->id = $parentId . '/' . $this->name; - } - } - } - return $this->id; - } - public function pathAsString() : string - { - if ($this->pathAsString === null) { - if ($this->parent === null) { - $this->pathAsString = $this->name; - } else { - $this->pathAsString = $this->parent->pathAsString() . \DIRECTORY_SEPARATOR . $this->name; - } - } - return $this->pathAsString; - } - public function pathAsArray() : array - { - if ($this->pathAsArray === null) { - if ($this->parent === null) { - $this->pathAsArray = []; - } else { - $this->pathAsArray = $this->parent->pathAsArray(); - } - $this->pathAsArray[] = $this; - } - return $this->pathAsArray; - } - public function parent() : ?self - { - return $this->parent; - } - public function percentageOfTestedClasses() : \PHPUnit\SebastianBergmann\CodeCoverage\Percentage - { - return \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($this->numberOfTestedClasses(), $this->numberOfClasses()); - } - public function percentageOfTestedTraits() : \PHPUnit\SebastianBergmann\CodeCoverage\Percentage - { - return \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($this->numberOfTestedTraits(), $this->numberOfTraits()); - } - public function percentageOfTestedClassesAndTraits() : \PHPUnit\SebastianBergmann\CodeCoverage\Percentage - { - return \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($this->numberOfTestedClassesAndTraits(), $this->numberOfClassesAndTraits()); - } - public function percentageOfTestedFunctions() : \PHPUnit\SebastianBergmann\CodeCoverage\Percentage - { - return \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($this->numberOfTestedFunctions(), $this->numberOfFunctions()); - } - public function percentageOfTestedMethods() : \PHPUnit\SebastianBergmann\CodeCoverage\Percentage - { - return \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($this->numberOfTestedMethods(), $this->numberOfMethods()); - } - public function percentageOfTestedFunctionsAndMethods() : \PHPUnit\SebastianBergmann\CodeCoverage\Percentage - { - return \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($this->numberOfTestedFunctionsAndMethods(), $this->numberOfFunctionsAndMethods()); - } - public function percentageOfExecutedLines() : \PHPUnit\SebastianBergmann\CodeCoverage\Percentage + private $interfaces = []; + /** + * @var array + */ + private $staticAttributes = []; + public function addGlobalVariable(string $variableName) : void { - return \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($this->numberOfExecutedLines(), $this->numberOfExecutableLines()); + $this->globalVariables[$variableName] = \true; } - public function percentageOfExecutedBranches() : \PHPUnit\SebastianBergmann\CodeCoverage\Percentage + public function addClass(string $className) : void { - return \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($this->numberOfExecutedBranches(), $this->numberOfExecutableBranches()); + $this->classes[] = $className; } - public function percentageOfExecutedPaths() : \PHPUnit\SebastianBergmann\CodeCoverage\Percentage + public function addSubclassesOf(string $className) : void { - return \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($this->numberOfExecutedPaths(), $this->numberOfExecutablePaths()); + $this->parentClasses[] = $className; } - public function numberOfClassesAndTraits() : int + public function addImplementorsOf(string $interfaceName) : void { - return $this->numberOfClasses() + $this->numberOfTraits(); + $this->interfaces[] = $interfaceName; } - public function numberOfTestedClassesAndTraits() : int + public function addClassNamePrefix(string $classNamePrefix) : void { - return $this->numberOfTestedClasses() + $this->numberOfTestedTraits(); + $this->classNamePrefixes[] = $classNamePrefix; } - public function classesAndTraits() : array + public function addStaticAttribute(string $className, string $attributeName) : void { - return \array_merge($this->classes(), $this->traits()); + if (!isset($this->staticAttributes[$className])) { + $this->staticAttributes[$className] = []; + } + $this->staticAttributes[$className][$attributeName] = \true; } - public function numberOfFunctionsAndMethods() : int + public function isGlobalVariableExcluded(string $variableName) : bool { - return $this->numberOfFunctions() + $this->numberOfMethods(); + return isset($this->globalVariables[$variableName]); } - public function numberOfTestedFunctionsAndMethods() : int + public function isStaticAttributeExcluded(string $className, string $attributeName) : bool { - return $this->numberOfTestedFunctions() + $this->numberOfTestedMethods(); + if (in_array($className, $this->classes, \true)) { + return \true; + } + foreach ($this->classNamePrefixes as $prefix) { + if (strpos($className, $prefix) === 0) { + return \true; + } + } + $class = new ReflectionClass($className); + foreach ($this->parentClasses as $type) { + if ($class->isSubclassOf($type)) { + return \true; + } + } + foreach ($this->interfaces as $type) { + if ($class->implementsInterface($type)) { + return \true; + } + } + if (isset($this->staticAttributes[$className][$attributeName])) { + return \true; + } + return \false; } - public abstract function classes() : array; - public abstract function traits() : array; - public abstract function functions() : array; - public abstract function linesOfCode() : \PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode; - public abstract function numberOfExecutableLines() : int; - public abstract function numberOfExecutedLines() : int; - public abstract function numberOfExecutableBranches() : int; - public abstract function numberOfExecutedBranches() : int; - public abstract function numberOfExecutablePaths() : int; - public abstract function numberOfExecutedPaths() : int; - public abstract function numberOfClasses() : int; - public abstract function numberOfTestedClasses() : int; - public abstract function numberOfTraits() : int; - public abstract function numberOfTestedTraits() : int; - public abstract function numberOfMethods() : int; - public abstract function numberOfTestedMethods() : int; - public abstract function numberOfFunctions() : int; - public abstract function numberOfTestedFunctions() : int; } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Node; +namespace PHPUnit\DeepCopy\Matcher; -use function count; -use RecursiveIterator; /** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * @final */ -final class Iterator implements \RecursiveIterator +class PropertyNameMatcher implements Matcher { /** - * @var int + * @var string */ - private $position; + private $property; /** - * @var AbstractNode[] + * @param string $property Property name */ - private $nodes; - public function __construct(\PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory $node) + public function __construct($property) { - $this->nodes = $node->children(); + $this->property = $property; } /** - * Rewinds the Iterator to the first element. + * Matches a property by its name. + * + * {@inheritdoc} */ - public function rewind() : void + public function matches($object, $property) { - $this->position = 0; + return $property == $this->property; } +} +position < \count($this->nodes); - } + private $class; /** - * Returns the key of the current element. + * @var string */ - public function key() : int - { - return $this->position; - } + private $property; /** - * Returns the current element. + * @param string $class Class name + * @param string $property Property name */ - public function current() : ?\PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode + public function __construct($class, $property) { - return $this->valid() ? $this->nodes[$this->position] : null; + $this->class = $class; + $this->property = $property; } /** - * Moves forward to next element. + * Matches a specific property of a specific class. + * + * {@inheritdoc} */ - public function next() : void + public function matches($object, $property) { - $this->position++; + return $object instanceof $this->class && $property == $this->property; } +} +nodes[$this->position]); - } + public function matches($object, $property); +} +nodes[$this->position] instanceof \PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory; + return $object instanceof Proxy; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Node; +namespace PHPUnit\DeepCopy\Matcher; -use const DIRECTORY_SEPARATOR; -use function array_shift; -use function basename; -use function count; -use function dirname; -use function explode; -use function implode; -use function is_file; -use function str_replace; -use function strpos; -use function substr; -use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; -use PHPUnit\SebastianBergmann\CodeCoverage\ProcessedCodeCoverageData; -use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CoveredFileAnalyser; +use PHPUnit\DeepCopy\Reflection\ReflectionHelper; +use ReflectionException; /** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * Matches a property by its type. + * + * It is recommended to use {@see DeepCopy\TypeFilter\TypeFilter} instead, as it applies on all occurrences + * of given type in copied context (eg. array elements), not just on object properties. + * + * @final */ -final class Builder +class PropertyTypeMatcher implements Matcher { /** - * @var CoveredFileAnalyser + * @var string */ - private $coveredFileAnalyser; - public function __construct(\PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CoveredFileAnalyser $coveredFileAnalyser) - { - $this->coveredFileAnalyser = $coveredFileAnalyser; - } - public function build(\PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage $coverage) : \PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory - { - $data = clone $coverage->getData(); - // clone because path munging is destructive to the original data - $commonPath = $this->reducePaths($data); - $root = new \PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory($commonPath, null); - $this->addItems($root, $this->buildDirectoryStructure($data), $coverage->getTests()); - return $root; - } - private function addItems(\PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory $root, array $items, array $tests) : void + private $propertyType; + /** + * @param string $propertyType Property type + */ + public function __construct($propertyType) { - foreach ($items as $key => $value) { - $key = (string) $key; - if (\substr($key, -2) === '/f') { - $key = \substr($key, 0, -2); - $filename = $root->pathAsString() . \DIRECTORY_SEPARATOR . $key; - if (\is_file($filename)) { - $root->addFile(new \PHPUnit\SebastianBergmann\CodeCoverage\Node\File($key, $root, $value['lineCoverage'], $value['functionCoverage'], $tests, $this->coveredFileAnalyser->classesIn($filename), $this->coveredFileAnalyser->traitsIn($filename), $this->coveredFileAnalyser->functionsIn($filename), $this->coveredFileAnalyser->linesOfCodeFor($filename))); - } - } else { - $child = $root->addDirectory($key); - $this->addItems($child, $value, $tests); - } - } + $this->propertyType = $propertyType; } /** - * Builds an array representation of the directory structure. - * - * For instance, - * - * - * Array - * ( - * [Money.php] => Array - * ( - * ... - * ) - * - * [MoneyBag.php] => Array - * ( - * ... - * ) - * ) - * - * - * is transformed into - * - * - * Array - * ( - * [.] => Array - * ( - * [Money.php] => Array - * ( - * ... - * ) - * - * [MoneyBag.php] => Array - * ( - * ... - * ) - * ) - * ) - * + * {@inheritdoc} */ - private function buildDirectoryStructure(\PHPUnit\SebastianBergmann\CodeCoverage\ProcessedCodeCoverageData $data) : array + public function matches($object, $property) { - $result = []; - foreach ($data->coveredFiles() as $originalPath) { - $path = \explode(\DIRECTORY_SEPARATOR, $originalPath); - $pointer =& $result; - $max = \count($path); - for ($i = 0; $i < $max; $i++) { - $type = ''; - if ($i === $max - 1) { - $type = '/f'; - } - $pointer =& $pointer[$path[$i] . $type]; - } - $pointer = ['lineCoverage' => $data->lineCoverage()[$originalPath] ?? [], 'functionCoverage' => $data->functionCoverage()[$originalPath] ?? []]; + try { + $reflectionProperty = ReflectionHelper::getProperty($object, $property); + } catch (ReflectionException $exception) { + return \false; } - return $result; + $reflectionProperty->setAccessible(\true); + // Uninitialized properties (for PHP >7.4) + if (\method_exists($reflectionProperty, 'isInitialized') && !$reflectionProperty->isInitialized($object)) { + // null instanceof $this->propertyType + return \false; + } + return $reflectionProperty->getValue($object) instanceof $this->propertyType; } +} + - * Array - * ( - * [/home/sb/Money/Money.php] => Array - * ( - * ... - * ) - * - * [/home/sb/Money/MoneyBag.php] => Array - * ( - * ... - * ) - * ) - * - * - * is reduced to + * Deep copies the given value. * - * - * Array - * ( - * [Money.php] => Array - * ( - * ... - * ) + * @param mixed $value + * @param bool $useCloneMethod * - * [MoneyBag.php] => Array - * ( - * ... - * ) - * ) - * + * @return mixed */ - private function reducePaths(\PHPUnit\SebastianBergmann\CodeCoverage\ProcessedCodeCoverageData $coverage) : string + function deep_copy($value, $useCloneMethod = \false) { - if (empty($coverage->coveredFiles())) { - return '.'; - } - $commonPath = ''; - $paths = $coverage->coveredFiles(); - if (\count($paths) === 1) { - $commonPath = \dirname($paths[0]) . \DIRECTORY_SEPARATOR; - $coverage->renameFile($paths[0], \basename($paths[0])); - return $commonPath; - } - $max = \count($paths); - for ($i = 0; $i < $max; $i++) { - // strip phar:// prefixes - if (\strpos($paths[$i], 'phar://') === 0) { - $paths[$i] = \substr($paths[$i], 7); - $paths[$i] = \str_replace('/', \DIRECTORY_SEPARATOR, $paths[$i]); - } - $paths[$i] = \explode(\DIRECTORY_SEPARATOR, $paths[$i]); - if (empty($paths[$i][0])) { - $paths[$i][0] = \DIRECTORY_SEPARATOR; - } - } - $done = \false; - $max = \count($paths); - while (!$done) { - for ($i = 0; $i < $max - 1; $i++) { - if (!isset($paths[$i][0]) || !isset($paths[$i + 1][0]) || $paths[$i][0] !== $paths[$i + 1][0]) { - $done = \true; - break; - } - } - if (!$done) { - $commonPath .= $paths[0][0]; - if ($paths[0][0] !== \DIRECTORY_SEPARATOR) { - $commonPath .= \DIRECTORY_SEPARATOR; - } - for ($i = 0; $i < $max; $i++) { - \array_shift($paths[$i]); - } - } - } - $original = $coverage->coveredFiles(); - $max = \count($original); - for ($i = 0; $i < $max; $i++) { - $coverage->renameFile($original[$i], \implode(\DIRECTORY_SEPARATOR, $paths[$i])); - } - return \substr($commonPath, 0, -1); + return (new DeepCopy($useCloneMethod))->copy($value); } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Node; +namespace PHPUnit\DeepCopy\Filter; -use function array_merge; -use function count; -use IteratorAggregate; -use RecursiveIteratorIterator; -use PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode; +use PHPUnit\DeepCopy\Reflection\ReflectionHelper; /** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * @final */ -final class Directory extends \PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode implements \IteratorAggregate +class ReplaceFilter implements Filter { /** - * @var AbstractNode[] + * @var callable */ - private $children = []; + protected $callback; /** - * @var Directory[] + * @param callable $callable Will be called to get the new value for each property to replace */ - private $directories = []; + public function __construct(callable $callable) + { + $this->callback = $callable; + } /** - * @var File[] + * Replaces the object property by the result of the callback called with the object property. + * + * {@inheritdoc} */ - private $files = []; + public function apply($object, $property, $objectCopier) + { + $reflectionProperty = ReflectionHelper::getProperty($object, $property); + $reflectionProperty->setAccessible(\true); + $value = \call_user_func($this->callback, $reflectionProperty->getValue($object)); + $reflectionProperty->setValue($object, $value); + } +} +setAccessible(\true); + $reflectionProperty->setValue($object, null); + } +} +setAccessible(\true); + $oldCollection = $reflectionProperty->getValue($object); + $newCollection = $oldCollection->map(function ($item) use($objectCopier) { + return $objectCopier($item); + }); + $reflectionProperty->setValue($object, $newCollection); + } +} +setAccessible(\true); + $reflectionProperty->setValue($object, new ArrayCollection()); + } +} +__load(); + } +} +type = $type; + } /** - * @var int + * @param mixed $element + * + * @return boolean */ - private $numExecutedBranches = -1; + public function matches($element) + { + return \is_object($element) ? \is_a($element, $this->type) : \gettype($element) === $this->type; + } +} +callback = $callable; + } /** - * @var int + * {@inheritdoc} */ - private $numClasses = -1; + public function apply($element) + { + return \call_user_func($this->callback, $element); + } +} +copier = $copier; + } /** - * @var int + * {@inheritdoc} */ - private $numTestedFunctions = -1; - public function count() : int + public function apply($arrayObject) { - if ($this->numFiles === -1) { - $this->numFiles = 0; - foreach ($this->children as $child) { - $this->numFiles += \count($child); - } + $clone = clone $arrayObject; + foreach ($arrayObject->getArrayCopy() as $k => $v) { + $clone->offsetSet($k, $this->copier->copy($v)); } - return $this->numFiles; - } - public function getIterator() : \RecursiveIteratorIterator - { - return new \RecursiveIteratorIterator(new \PHPUnit\SebastianBergmann\CodeCoverage\Node\Iterator($this), \RecursiveIteratorIterator::SELF_FIRST); - } - public function addDirectory(string $name) : self - { - $directory = new self($name, $this); - $this->children[] = $directory; - $this->directories[] =& $this->children[\count($this->children) - 1]; - return $directory; + return $clone; } - public function addFile(\PHPUnit\SebastianBergmann\CodeCoverage\Node\File $file) : void +} +children[] = $file; - $this->files[] =& $this->children[\count($this->children) - 1]; - $this->numExecutableLines = -1; - $this->numExecutedLines = -1; + $this->copier = $copier; } - public function directories() : array + /** + * {@inheritdoc} + */ + public function apply($element) { - return $this->directories; + $newElement = clone $element; + $copy = $this->createCopyClosure(); + return $copy($newElement); } - public function files() : array + private function createCopyClosure() { - return $this->files; + $copier = $this->copier; + $copy = function (SplDoublyLinkedList $list) use($copier) { + // Replace each element in the list with a deep copy of itself + for ($i = 1; $i <= $list->count(); $i++) { + $copy = $copier->recursiveCopy($list->shift()); + $list->push($copy); + } + return $list; + }; + return Closure::bind($copy, null, DeepCopy::class); } - public function children() : array +} +children; + return clone $element; } - public function classes() : array +} +classes === null) { - $this->classes = []; - foreach ($this->children as $child) { - $this->classes = \array_merge($this->classes, $child->classes()); - } + $copy = new DateInterval('P0D'); + foreach ($element as $propertyName => $propertyValue) { + $copy->{$propertyName} = $propertyValue; } - return $this->classes; + return $copy; } - public function traits() : array +} +getProperties() does not return private properties from ancestor classes. + * + * @author muratyaman@gmail.com + * @see http://php.net/manual/en/reflectionclass.getproperties.php + * + * @param ReflectionClass $ref + * + * @return ReflectionProperty[] + */ + public static function getProperties(ReflectionClass $ref) { - if ($this->traits === null) { - $this->traits = []; - foreach ($this->children as $child) { - $this->traits = \array_merge($this->traits, $child->traits()); - } + $props = $ref->getProperties(); + $propsArr = array(); + foreach ($props as $prop) { + $propertyName = $prop->getName(); + $propsArr[$propertyName] = $prop; } - return $this->traits; - } - public function functions() : array - { - if ($this->functions === null) { - $this->functions = []; - foreach ($this->children as $child) { - $this->functions = \array_merge($this->functions, $child->functions()); + if ($parentClass = $ref->getParentClass()) { + $parentPropsArr = self::getProperties($parentClass); + foreach ($propsArr as $key => $property) { + $parentPropsArr[$key] = $property; } + return $parentPropsArr; } - return $this->functions; + return $propsArr; } - public function linesOfCode() : \PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode + /** + * Retrieves property by name from object and all its ancestors. + * + * @param object|string $object + * @param string $name + * + * @throws PropertyException + * @throws ReflectionException + * + * @return ReflectionProperty + */ + public static function getProperty($object, $name) { - if ($this->linesOfCode === null) { - $this->linesOfCode = new \PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode(0, 0, 0, 0); - foreach ($this->children as $child) { - $this->linesOfCode = $this->linesOfCode->plus($child->linesOfCode()); - } + $reflection = \is_object($object) ? new ReflectionObject($object) : new ReflectionClass($object); + if ($reflection->hasProperty($name)) { + return $reflection->getProperty($name); } - return $this->linesOfCode; - } - public function numberOfExecutableLines() : int - { - if ($this->numExecutableLines === -1) { - $this->numExecutableLines = 0; - foreach ($this->children as $child) { - $this->numExecutableLines += $child->numberOfExecutableLines(); - } + if ($parentClass = $reflection->getParentClass()) { + return self::getProperty($parentClass->getName(), $name); } - return $this->numExecutableLines; + throw new PropertyException(\sprintf('The class "%s" doesn\'t have a property with the given name: "%s".', \is_object($object) ? \get_class($object) : $object, $name)); } - public function numberOfExecutedLines() : int +} + Filter, 'matcher' => Matcher] pairs. + */ + private $filters = []; + /** + * Type Filters to apply. + * + * @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs. + */ + private $typeFilters = []; + /** + * @var bool + */ + private $skipUncloneable = \false; + /** + * @var bool + */ + private $useCloneMethod; + /** + * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will be used + * instead of the regular deep cloning. + */ + public function __construct($useCloneMethod = \false) { - if ($this->numExecutedLines === -1) { - $this->numExecutedLines = 0; - foreach ($this->children as $child) { - $this->numExecutedLines += $child->numberOfExecutedLines(); - } - } - return $this->numExecutedLines; + $this->useCloneMethod = $useCloneMethod; + $this->addTypeFilter(new ArrayObjectFilter($this), new TypeMatcher(ArrayObject::class)); + $this->addTypeFilter(new DateIntervalFilter(), new TypeMatcher(DateInterval::class)); + $this->addTypeFilter(new SplDoublyLinkedListFilter($this), new TypeMatcher(SplDoublyLinkedList::class)); } - public function numberOfExecutableBranches() : int + /** + * If enabled, will not throw an exception when coming across an uncloneable property. + * + * @param $skipUncloneable + * + * @return $this + */ + public function skipUncloneable($skipUncloneable = \true) { - if ($this->numExecutableBranches === -1) { - $this->numExecutableBranches = 0; - foreach ($this->children as $child) { - $this->numExecutableBranches += $child->numberOfExecutableBranches(); - } - } - return $this->numExecutableBranches; + $this->skipUncloneable = $skipUncloneable; + return $this; } - public function numberOfExecutedBranches() : int + /** + * Deep copies the given object. + * + * @param mixed $object + * + * @return mixed + */ + public function copy($object) { - if ($this->numExecutedBranches === -1) { - $this->numExecutedBranches = 0; - foreach ($this->children as $child) { - $this->numExecutedBranches += $child->numberOfExecutedBranches(); - } - } - return $this->numExecutedBranches; + $this->hashMap = []; + return $this->recursiveCopy($object); } - public function numberOfExecutablePaths() : int + public function addFilter(Filter $filter, Matcher $matcher) { - if ($this->numExecutablePaths === -1) { - $this->numExecutablePaths = 0; - foreach ($this->children as $child) { - $this->numExecutablePaths += $child->numberOfExecutablePaths(); - } - } - return $this->numExecutablePaths; + $this->filters[] = ['matcher' => $matcher, 'filter' => $filter]; } - public function numberOfExecutedPaths() : int + public function prependFilter(Filter $filter, Matcher $matcher) { - if ($this->numExecutedPaths === -1) { - $this->numExecutedPaths = 0; - foreach ($this->children as $child) { - $this->numExecutedPaths += $child->numberOfExecutedPaths(); - } - } - return $this->numExecutedPaths; + \array_unshift($this->filters, ['matcher' => $matcher, 'filter' => $filter]); } - public function numberOfClasses() : int + public function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher) { - if ($this->numClasses === -1) { - $this->numClasses = 0; - foreach ($this->children as $child) { - $this->numClasses += $child->numberOfClasses(); - } - } - return $this->numClasses; + $this->typeFilters[] = ['matcher' => $matcher, 'filter' => $filter]; } - public function numberOfTestedClasses() : int + private function recursiveCopy($var) { - if ($this->numTestedClasses === -1) { - $this->numTestedClasses = 0; - foreach ($this->children as $child) { - $this->numTestedClasses += $child->numberOfTestedClasses(); - } + // Matches Type Filter + if ($filter = $this->getFirstMatchedTypeFilter($this->typeFilters, $var)) { + return $filter->apply($var); } - return $this->numTestedClasses; - } - public function numberOfTraits() : int - { - if ($this->numTraits === -1) { - $this->numTraits = 0; - foreach ($this->children as $child) { - $this->numTraits += $child->numberOfTraits(); - } + // Resource + if (\is_resource($var)) { + return $var; } - return $this->numTraits; + // Array + if (\is_array($var)) { + return $this->copyArray($var); + } + // Scalar + if (!\is_object($var)) { + return $var; + } + // Object + return $this->copyObject($var); } - public function numberOfTestedTraits() : int + /** + * Copy an array + * @param array $array + * @return array + */ + private function copyArray(array $array) { - if ($this->numTestedTraits === -1) { - $this->numTestedTraits = 0; - foreach ($this->children as $child) { - $this->numTestedTraits += $child->numberOfTestedTraits(); - } + foreach ($array as $key => $value) { + $array[$key] = $this->recursiveCopy($value); } - return $this->numTestedTraits; + return $array; } - public function numberOfMethods() : int + /** + * Copies an object. + * + * @param object $object + * + * @throws CloneException + * + * @return object + */ + private function copyObject($object) { - if ($this->numMethods === -1) { - $this->numMethods = 0; - foreach ($this->children as $child) { - $this->numMethods += $child->numberOfMethods(); + $objectHash = \spl_object_hash($object); + if (isset($this->hashMap[$objectHash])) { + return $this->hashMap[$objectHash]; + } + $reflectedObject = new ReflectionObject($object); + $isCloneable = $reflectedObject->isCloneable(); + if (\false === $isCloneable) { + if ($this->skipUncloneable) { + $this->hashMap[$objectHash] = $object; + return $object; } + throw new CloneException(\sprintf('The class "%s" is not cloneable.', $reflectedObject->getName())); } - return $this->numMethods; + $newObject = clone $object; + $this->hashMap[$objectHash] = $newObject; + if ($this->useCloneMethod && $reflectedObject->hasMethod('__clone')) { + return $newObject; + } + if ($newObject instanceof DateTimeInterface || $newObject instanceof DateTimeZone) { + return $newObject; + } + foreach (ReflectionHelper::getProperties($reflectedObject) as $property) { + $this->copyObjectProperty($newObject, $property); + } + return $newObject; } - public function numberOfTestedMethods() : int + private function copyObjectProperty($object, ReflectionProperty $property) { - if ($this->numTestedMethods === -1) { - $this->numTestedMethods = 0; - foreach ($this->children as $child) { - $this->numTestedMethods += $child->numberOfTestedMethods(); + // Ignore static properties + if ($property->isStatic()) { + return; + } + // Apply the filters + foreach ($this->filters as $item) { + /** @var Matcher $matcher */ + $matcher = $item['matcher']; + /** @var Filter $filter */ + $filter = $item['filter']; + if ($matcher->matches($object, $property->getName())) { + $filter->apply($object, $property->getName(), function ($object) { + return $this->recursiveCopy($object); + }); + // If a filter matches, we stop processing this property + return; } } - return $this->numTestedMethods; + $property->setAccessible(\true); + // Ignore uninitialized properties (for PHP >7.4) + if (\method_exists($property, 'isInitialized') && !$property->isInitialized($object)) { + return; + } + $propertyValue = $property->getValue($object); + // Copy the property + $property->setValue($object, $this->recursiveCopy($propertyValue)); } - public function numberOfFunctions() : int + /** + * Returns first filter that matches variable, `null` if no such filter found. + * + * @param array $filterRecords Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and + * 'matcher' with value of type {@see TypeMatcher} + * @param mixed $var + * + * @return TypeFilter|null + */ + private function getFirstMatchedTypeFilter(array $filterRecords, $var) { - if ($this->numFunctions === -1) { - $this->numFunctions = 0; - foreach ($this->children as $child) { - $this->numFunctions += $child->numberOfFunctions(); - } - } - return $this->numFunctions; + $matched = $this->first($filterRecords, function (array $record) use($var) { + /* @var TypeMatcher $matcher */ + $matcher = $record['matcher']; + return $matcher->matches($var); + }); + return isset($matched) ? $matched['filter'] : null; } - public function numberOfTestedFunctions() : int + /** + * Returns first element that matches predicate, `null` if no such element found. + * + * @param array $elements Array of ['filter' => Filter, 'matcher' => Matcher] pairs. + * @param callable $predicate Predicate arguments are: element. + * + * @return array|null Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and 'matcher' + * with value of type {@see TypeMatcher} or `null`. + */ + private function first(array $elements, callable $predicate) { - if ($this->numTestedFunctions === -1) { - $this->numTestedFunctions = 0; - foreach ($this->children as $child) { - $this->numTestedFunctions += $child->numberOfTestedFunctions(); + foreach ($elements as $element) { + if (\call_user_func($predicate, $element)) { + return $element; } } - return $this->numTestedFunctions; + return null; } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Node; +Copyright (c) 2013 My C-Sense -use function array_filter; -use function count; -use function range; -use PHPUnit\SebastianBergmann\CodeCoverage\CrapIndex; -use PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -final class File extends \PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode +namespace PHPUnit\SebastianBergmann\Type; + +final class UnknownType extends Type { - /** - * @var array - */ - private $lineCoverageData; - /** - * @var array - */ - private $functionCoverageData; - /** - * @var array - */ - private $testData; - /** - * @var int - */ - private $numExecutableLines = 0; - /** - * @var int - */ - private $numExecutedLines = 0; - /** - * @var int - */ - private $numExecutableBranches = 0; - /** - * @var int - */ - private $numExecutedBranches = 0; - /** - * @var int - */ - private $numExecutablePaths = 0; - /** - * @var int - */ - private $numExecutedPaths = 0; - /** - * @var array - */ - private $classes = []; - /** - * @var array - */ - private $traits = []; - /** - * @var array - */ - private $functions = []; - /** - * @var LinesOfCode - */ - private $linesOfCode; - /** - * @var int - */ - private $numClasses; - /** - * @var int - */ - private $numTestedClasses = 0; - /** - * @var int - */ - private $numTraits; - /** - * @var int - */ - private $numTestedTraits = 0; - /** - * @var int - */ - private $numMethods; - /** - * @var int - */ - private $numTestedMethods; - /** - * @var int - */ - private $numTestedFunctions; - /** - * @var array - */ - private $codeUnitsByLine = []; - public function __construct(string $name, \PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode $parent, array $lineCoverageData, array $functionCoverageData, array $testData, array $classes, array $traits, array $functions, \PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode $linesOfCode) - { - parent::__construct($name, $parent); - $this->lineCoverageData = $lineCoverageData; - $this->functionCoverageData = $functionCoverageData; - $this->testData = $testData; - $this->linesOfCode = $linesOfCode; - $this->calculateStatistics($classes, $traits, $functions); - } - public function count() : int + public function isAssignable(Type $other) : bool { - return 1; - } - public function lineCoverageData() : array - { - return $this->lineCoverageData; - } - public function functionCoverageData() : array - { - return $this->functionCoverageData; - } - public function testData() : array - { - return $this->testData; - } - public function classes() : array - { - return $this->classes; - } - public function traits() : array - { - return $this->traits; - } - public function functions() : array - { - return $this->functions; - } - public function linesOfCode() : \PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode - { - return $this->linesOfCode; - } - public function numberOfExecutableLines() : int - { - return $this->numExecutableLines; + return \true; } - public function numberOfExecutedLines() : int + public function name() : string { - return $this->numExecutedLines; + return 'unknown type'; } - public function numberOfExecutableBranches() : int + public function asString() : string { - return $this->numExecutableBranches; + return ''; } - public function numberOfExecutedBranches() : int + /** + * @deprecated + * + * @codeCoverageIgnore + */ + public function getReturnTypeDeclaration() : string { - return $this->numExecutedBranches; + return ''; } - public function numberOfExecutablePaths() : int + public function allowsNull() : bool { - return $this->numExecutablePaths; + return \true; } - public function numberOfExecutedPaths() : int +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Type; + +final class GenericObjectType extends Type +{ + /** + * @var bool + */ + private $allowsNull; + public function __construct(bool $nullable) { - return $this->numExecutedPaths; + $this->allowsNull = $nullable; } - public function numberOfClasses() : int + public function isAssignable(Type $other) : bool { - if ($this->numClasses === null) { - $this->numClasses = 0; - foreach ($this->classes as $class) { - foreach ($class['methods'] as $method) { - if ($method['executableLines'] > 0) { - $this->numClasses++; - continue 2; - } - } - } + if ($this->allowsNull && $other instanceof NullType) { + return \true; } - return $this->numClasses; - } - public function numberOfTestedClasses() : int - { - return $this->numTestedClasses; + if (!$other instanceof ObjectType) { + return \false; + } + return \true; } - public function numberOfTraits() : int + public function name() : string { - if ($this->numTraits === null) { - $this->numTraits = 0; - foreach ($this->traits as $trait) { - foreach ($trait['methods'] as $method) { - if ($method['executableLines'] > 0) { - $this->numTraits++; - continue 2; - } - } - } - } - return $this->numTraits; + return 'object'; } - public function numberOfTestedTraits() : int + public function allowsNull() : bool { - return $this->numTestedTraits; + return $this->allowsNull; } - public function numberOfMethods() : int +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Type; + +use function count; +use function implode; +use function sort; +final class UnionType extends Type +{ + /** + * @psalm-var list + */ + private $types; + /** + * @throws RuntimeException + */ + public function __construct(Type ...$types) { - if ($this->numMethods === null) { - $this->numMethods = 0; - foreach ($this->classes as $class) { - foreach ($class['methods'] as $method) { - if ($method['executableLines'] > 0) { - $this->numMethods++; - } - } - } - foreach ($this->traits as $trait) { - foreach ($trait['methods'] as $method) { - if ($method['executableLines'] > 0) { - $this->numMethods++; - } - } - } - } - return $this->numMethods; + $this->ensureMinimumOfTwoTypes(...$types); + $this->ensureOnlyValidTypes(...$types); + $this->types = $types; } - public function numberOfTestedMethods() : int + public function isAssignable(Type $other) : bool { - if ($this->numTestedMethods === null) { - $this->numTestedMethods = 0; - foreach ($this->classes as $class) { - foreach ($class['methods'] as $method) { - if ($method['executableLines'] > 0 && $method['coverage'] === 100) { - $this->numTestedMethods++; - } - } - } - foreach ($this->traits as $trait) { - foreach ($trait['methods'] as $method) { - if ($method['executableLines'] > 0 && $method['coverage'] === 100) { - $this->numTestedMethods++; - } - } + foreach ($this->types as $type) { + if ($type->isAssignable($other)) { + return \true; } } - return $this->numTestedMethods; + return \false; } - public function numberOfFunctions() : int + public function asString() : string { - return \count($this->functions); + return $this->name(); } - public function numberOfTestedFunctions() : int + /** + * @deprecated + * + * @codeCoverageIgnore + */ + public function getReturnTypeDeclaration() : string { - if ($this->numTestedFunctions === null) { - $this->numTestedFunctions = 0; - foreach ($this->functions as $function) { - if ($function['executableLines'] > 0 && $function['coverage'] === 100) { - $this->numTestedFunctions++; - } - } - } - return $this->numTestedFunctions; + return ': ' . $this->name(); } - private function calculateStatistics(array $classes, array $traits, array $functions) : void + public function name() : string { - foreach (\range(1, $this->linesOfCode->linesOfCode()) as $lineNumber) { - $this->codeUnitsByLine[$lineNumber] = []; - } - $this->processClasses($classes); - $this->processTraits($traits); - $this->processFunctions($functions); - foreach (\range(1, $this->linesOfCode->linesOfCode()) as $lineNumber) { - if (isset($this->lineCoverageData[$lineNumber])) { - foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) { - $codeUnit['executableLines']++; - } - unset($codeUnit); - $this->numExecutableLines++; - if (\count($this->lineCoverageData[$lineNumber]) > 0) { - foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) { - $codeUnit['executedLines']++; - } - unset($codeUnit); - $this->numExecutedLines++; - } - } - } - foreach ($this->traits as &$trait) { - foreach ($trait['methods'] as &$method) { - $methodLineCoverage = $method['executableLines'] ? $method['executedLines'] / $method['executableLines'] * 100 : 100; - $methodBranchCoverage = $method['executableBranches'] ? $method['executedBranches'] / $method['executableBranches'] * 100 : 0; - $methodPathCoverage = $method['executablePaths'] ? $method['executedPaths'] / $method['executablePaths'] * 100 : 0; - $method['coverage'] = $methodBranchCoverage ?: $methodLineCoverage; - $method['crap'] = (new \PHPUnit\SebastianBergmann\CodeCoverage\CrapIndex($method['ccn'], $methodPathCoverage ?: $methodLineCoverage))->asString(); - $trait['ccn'] += $method['ccn']; - } - unset($method); - $traitLineCoverage = $trait['executableLines'] ? $trait['executedLines'] / $trait['executableLines'] * 100 : 100; - $traitBranchCoverage = $trait['executableBranches'] ? $trait['executedBranches'] / $trait['executableBranches'] * 100 : 0; - $traitPathCoverage = $trait['executablePaths'] ? $trait['executedPaths'] / $trait['executablePaths'] * 100 : 0; - $trait['coverage'] = $traitBranchCoverage ?: $traitLineCoverage; - $trait['crap'] = (new \PHPUnit\SebastianBergmann\CodeCoverage\CrapIndex($trait['ccn'], $traitPathCoverage ?: $traitLineCoverage))->asString(); - if ($trait['executableLines'] > 0 && $trait['coverage'] === 100) { - $this->numTestedClasses++; - } - } - unset($trait); - foreach ($this->classes as &$class) { - foreach ($class['methods'] as &$method) { - $methodLineCoverage = $method['executableLines'] ? $method['executedLines'] / $method['executableLines'] * 100 : 100; - $methodBranchCoverage = $method['executableBranches'] ? $method['executedBranches'] / $method['executableBranches'] * 100 : 0; - $methodPathCoverage = $method['executablePaths'] ? $method['executedPaths'] / $method['executablePaths'] * 100 : 0; - $method['coverage'] = $methodBranchCoverage ?: $methodLineCoverage; - $method['crap'] = (new \PHPUnit\SebastianBergmann\CodeCoverage\CrapIndex($method['ccn'], $methodPathCoverage ?: $methodLineCoverage))->asString(); - $class['ccn'] += $method['ccn']; - } - unset($method); - $classLineCoverage = $class['executableLines'] ? $class['executedLines'] / $class['executableLines'] * 100 : 100; - $classBranchCoverage = $class['executableBranches'] ? $class['executedBranches'] / $class['executableBranches'] * 100 : 0; - $classPathCoverage = $class['executablePaths'] ? $class['executedPaths'] / $class['executablePaths'] * 100 : 0; - $class['coverage'] = $classBranchCoverage ?: $classLineCoverage; - $class['crap'] = (new \PHPUnit\SebastianBergmann\CodeCoverage\CrapIndex($class['ccn'], $classPathCoverage ?: $classLineCoverage))->asString(); - if ($class['executableLines'] > 0 && $class['coverage'] === 100) { - $this->numTestedClasses++; - } - } - unset($class); - foreach ($this->functions as &$function) { - $functionLineCoverage = $function['executableLines'] ? $function['executedLines'] / $function['executableLines'] * 100 : 100; - $functionBranchCoverage = $function['executableBranches'] ? $function['executedBranches'] / $function['executableBranches'] * 100 : 0; - $functionPathCoverage = $function['executablePaths'] ? $function['executedPaths'] / $function['executablePaths'] * 100 : 0; - $function['coverage'] = $functionBranchCoverage ?: $functionLineCoverage; - $function['crap'] = (new \PHPUnit\SebastianBergmann\CodeCoverage\CrapIndex($function['ccn'], $functionPathCoverage ?: $functionLineCoverage))->asString(); - if ($function['coverage'] === 100) { - $this->numTestedFunctions++; - } + $types = []; + foreach ($this->types as $type) { + $types[] = $type->name(); } + sort($types); + return implode('|', $types); } - private function processClasses(array $classes) : void + public function allowsNull() : bool { - $link = $this->id() . '.html#'; - foreach ($classes as $className => $class) { - $this->classes[$className] = ['className' => $className, 'namespace' => $class['namespace'], 'methods' => [], 'startLine' => $class['startLine'], 'executableLines' => 0, 'executedLines' => 0, 'executableBranches' => 0, 'executedBranches' => 0, 'executablePaths' => 0, 'executedPaths' => 0, 'ccn' => 0, 'coverage' => 0, 'crap' => 0, 'link' => $link . $class['startLine']]; - foreach ($class['methods'] as $methodName => $method) { - $methodData = $this->newMethod($className, $methodName, $method, $link); - $this->classes[$className]['methods'][$methodName] = $methodData; - $this->classes[$className]['executableBranches'] += $methodData['executableBranches']; - $this->classes[$className]['executedBranches'] += $methodData['executedBranches']; - $this->classes[$className]['executablePaths'] += $methodData['executablePaths']; - $this->classes[$className]['executedPaths'] += $methodData['executedPaths']; - $this->numExecutableBranches += $methodData['executableBranches']; - $this->numExecutedBranches += $methodData['executedBranches']; - $this->numExecutablePaths += $methodData['executablePaths']; - $this->numExecutedPaths += $methodData['executedPaths']; - foreach (\range($method['startLine'], $method['endLine']) as $lineNumber) { - $this->codeUnitsByLine[$lineNumber] = [&$this->classes[$className], &$this->classes[$className]['methods'][$methodName]]; - } + foreach ($this->types as $type) { + if ($type instanceof NullType) { + return \true; } } + return \false; } - private function processTraits(array $traits) : void + /** + * @throws RuntimeException + */ + private function ensureMinimumOfTwoTypes(Type ...$types) : void { - $link = $this->id() . '.html#'; - foreach ($traits as $traitName => $trait) { - $this->traits[$traitName] = ['traitName' => $traitName, 'namespace' => $trait['namespace'], 'methods' => [], 'startLine' => $trait['startLine'], 'executableLines' => 0, 'executedLines' => 0, 'executableBranches' => 0, 'executedBranches' => 0, 'executablePaths' => 0, 'executedPaths' => 0, 'ccn' => 0, 'coverage' => 0, 'crap' => 0, 'link' => $link . $trait['startLine']]; - foreach ($trait['methods'] as $methodName => $method) { - $methodData = $this->newMethod($traitName, $methodName, $method, $link); - $this->traits[$traitName]['methods'][$methodName] = $methodData; - $this->traits[$traitName]['executableBranches'] += $methodData['executableBranches']; - $this->traits[$traitName]['executedBranches'] += $methodData['executedBranches']; - $this->traits[$traitName]['executablePaths'] += $methodData['executablePaths']; - $this->traits[$traitName]['executedPaths'] += $methodData['executedPaths']; - $this->numExecutableBranches += $methodData['executableBranches']; - $this->numExecutedBranches += $methodData['executedBranches']; - $this->numExecutablePaths += $methodData['executablePaths']; - $this->numExecutedPaths += $methodData['executedPaths']; - foreach (\range($method['startLine'], $method['endLine']) as $lineNumber) { - $this->codeUnitsByLine[$lineNumber] = [&$this->traits[$traitName], &$this->traits[$traitName]['methods'][$methodName]]; - } - } + if (count($types) < 2) { + throw new RuntimeException('A union type must be composed of at least two types'); } } - private function processFunctions(array $functions) : void + /** + * @throws RuntimeException + */ + private function ensureOnlyValidTypes(Type ...$types) : void { - $link = $this->id() . '.html#'; - foreach ($functions as $functionName => $function) { - $this->functions[$functionName] = ['functionName' => $functionName, 'namespace' => $function['namespace'], 'signature' => $function['signature'], 'startLine' => $function['startLine'], 'endLine' => $function['endLine'], 'executableLines' => 0, 'executedLines' => 0, 'executableBranches' => 0, 'executedBranches' => 0, 'executablePaths' => 0, 'executedPaths' => 0, 'ccn' => $function['ccn'], 'coverage' => 0, 'crap' => 0, 'link' => $link . $function['startLine']]; - foreach (\range($function['startLine'], $function['endLine']) as $lineNumber) { - $this->codeUnitsByLine[$lineNumber] = [&$this->functions[$functionName]]; - } - if (isset($this->functionCoverageData[$functionName]['branches'])) { - $this->functions[$functionName]['executableBranches'] = \count($this->functionCoverageData[$functionName]['branches']); - $this->functions[$functionName]['executedBranches'] = \count(\array_filter($this->functionCoverageData[$functionName]['branches'], static function (array $branch) { - return (bool) $branch['hit']; - })); + foreach ($types as $type) { + if ($type instanceof UnknownType) { + throw new RuntimeException('A union type must not be composed of an unknown type'); } - if (isset($this->functionCoverageData[$functionName]['paths'])) { - $this->functions[$functionName]['executablePaths'] = \count($this->functionCoverageData[$functionName]['paths']); - $this->functions[$functionName]['executedPaths'] = \count(\array_filter($this->functionCoverageData[$functionName]['paths'], static function (array $path) { - return (bool) $path['hit']; - })); + if ($type instanceof VoidType) { + throw new RuntimeException('A union type must not be composed of a void type'); } - $this->numExecutableBranches += $this->functions[$functionName]['executableBranches']; - $this->numExecutedBranches += $this->functions[$functionName]['executedBranches']; - $this->numExecutablePaths += $this->functions[$functionName]['executablePaths']; - $this->numExecutedPaths += $this->functions[$functionName]['executedPaths']; - } - } - private function newMethod(string $className, string $methodName, array $method, string $link) : array - { - $methodData = ['methodName' => $methodName, 'visibility' => $method['visibility'], 'signature' => $method['signature'], 'startLine' => $method['startLine'], 'endLine' => $method['endLine'], 'executableLines' => 0, 'executedLines' => 0, 'executableBranches' => 0, 'executedBranches' => 0, 'executablePaths' => 0, 'executedPaths' => 0, 'ccn' => $method['ccn'], 'coverage' => 0, 'crap' => 0, 'link' => $link . $method['startLine']]; - $key = $className . '->' . $methodName; - if (isset($this->functionCoverageData[$key]['branches'])) { - $methodData['executableBranches'] = \count($this->functionCoverageData[$key]['branches']); - $methodData['executedBranches'] = \count(\array_filter($this->functionCoverageData[$key]['branches'], static function (array $branch) { - return (bool) $branch['hit']; - })); - } - if (isset($this->functionCoverageData[$key]['paths'])) { - $methodData['executablePaths'] = \count($this->functionCoverageData[$key]['paths']); - $methodData['executedPaths'] = \count(\array_filter($this->functionCoverageData[$key]['paths'], static function (array $path) { - return (bool) $path['hit']; - })); } - return $methodData; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; +namespace PHPUnit\SebastianBergmann\Type; -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class Project extends \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Node +final class MixedType extends Type { - public function __construct(string $directory) - { - $this->init(); - $this->setProjectSourceDirectory($directory); - } - public function projectSourceDirectory() : string - { - return $this->contextNode()->getAttribute('source'); - } - public function buildInformation() : \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\BuildInformation + public function isAssignable(Type $other) : bool { - $buildNode = $this->dom()->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'build')->item(0); - if (!$buildNode) { - $buildNode = $this->dom()->documentElement->appendChild($this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', 'build')); - } - return new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\BuildInformation($buildNode); + return !$other instanceof VoidType; } - public function tests() : \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Tests + public function asString() : string { - $testsNode = $this->contextNode()->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'tests')->item(0); - if (!$testsNode) { - $testsNode = $this->contextNode()->appendChild($this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', 'tests')); - } - return new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Tests($testsNode); + return 'mixed'; } - public function asDom() : \DOMDocument + public function name() : string { - return $this->dom(); + return 'mixed'; } - private function init() : void + public function allowsNull() : bool { - $dom = new \DOMDocument(); - $dom->loadXML(''); - $this->setContextNode($dom->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'project')->item(0)); - } - private function setProjectSourceDirectory(string $name) : void - { - $this->contextNode()->setAttribute('source', $name); + return \true; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; +namespace PHPUnit\SebastianBergmann\Type; -use DOMElement; -use PHPUnit\TheSeer\Tokenizer\NamespaceUri; -use PHPUnit\TheSeer\Tokenizer\Tokenizer; -use PHPUnit\TheSeer\Tokenizer\XMLSerializer; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class Source +use function assert; +use function class_exists; +use function count; +use function explode; +use function function_exists; +use function is_array; +use function is_object; +use function is_string; +use function strpos; +use Closure; +use ReflectionClass; +use ReflectionException; +use ReflectionObject; +final class CallableType extends Type { - /** @var DOMElement */ - private $context; - public function __construct(\DOMElement $context) + /** + * @var bool + */ + private $allowsNull; + public function __construct(bool $nullable) { - $this->context = $context; + $this->allowsNull = $nullable; } - public function setSourceCode(string $source) : void + /** + * @throws RuntimeException + */ + public function isAssignable(Type $other) : bool { - $context = $this->context; - $tokens = (new \PHPUnit\TheSeer\Tokenizer\Tokenizer())->parse($source); - $srcDom = (new \PHPUnit\TheSeer\Tokenizer\XMLSerializer(new \PHPUnit\TheSeer\Tokenizer\NamespaceUri($context->namespaceURI)))->toDom($tokens); - $context->parentNode->replaceChild($context->ownerDocument->importNode($srcDom->documentElement, \true), $context); + if ($this->allowsNull && $other instanceof NullType) { + return \true; + } + if ($other instanceof self) { + return \true; + } + if ($other instanceof ObjectType) { + if ($this->isClosure($other)) { + return \true; + } + if ($this->hasInvokeMethod($other)) { + return \true; + } + } + if ($other instanceof SimpleType) { + if ($this->isFunction($other)) { + return \true; + } + if ($this->isClassCallback($other)) { + return \true; + } + if ($this->isObjectCallback($other)) { + return \true; + } + } + return \false; + } + public function name() : string + { + return 'callable'; + } + public function allowsNull() : bool + { + return $this->allowsNull; + } + private function isClosure(ObjectType $type) : bool + { + return !$type->className()->isNamespaced() && $type->className()->simpleName() === Closure::class; + } + /** + * @throws RuntimeException + */ + private function hasInvokeMethod(ObjectType $type) : bool + { + $className = $type->className()->qualifiedName(); + assert(class_exists($className)); + try { + $class = new ReflectionClass($className); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new RuntimeException($e->getMessage(), (int) $e->getCode(), $e); + // @codeCoverageIgnoreEnd + } + if ($class->hasMethod('__invoke')) { + return \true; + } + return \false; + } + private function isFunction(SimpleType $type) : bool + { + if (!is_string($type->value())) { + return \false; + } + return function_exists($type->value()); + } + private function isObjectCallback(SimpleType $type) : bool + { + if (!is_array($type->value())) { + return \false; + } + if (count($type->value()) !== 2) { + return \false; + } + if (!is_object($type->value()[0]) || !is_string($type->value()[1])) { + return \false; + } + [$object, $methodName] = $type->value(); + return (new ReflectionObject($object))->hasMethod($methodName); + } + private function isClassCallback(SimpleType $type) : bool + { + if (!is_string($type->value()) && !is_array($type->value())) { + return \false; + } + if (is_string($type->value())) { + if (strpos($type->value(), '::') === \false) { + return \false; + } + [$className, $methodName] = explode('::', $type->value()); + } + if (is_array($type->value())) { + if (count($type->value()) !== 2) { + return \false; + } + if (!is_string($type->value()[0]) || !is_string($type->value()[1])) { + return \false; + } + [$className, $methodName] = $type->value(); + } + assert(isset($className) && is_string($className) && class_exists($className)); + assert(isset($methodName) && is_string($methodName)); + try { + $class = new ReflectionClass($className); + if ($class->hasMethod($methodName)) { + $method = $class->getMethod($methodName); + return $method->isPublic() && $method->isStatic(); + } + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new RuntimeException($e->getMessage(), (int) $e->getCode(), $e); + // @codeCoverageIgnoreEnd + } + return \false; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; +namespace PHPUnit\SebastianBergmann\Type; -use function basename; -use function dirname; -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class Report extends \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\File +final class VoidType extends Type { - public function __construct(string $name) + public function isAssignable(Type $other) : bool { - $dom = new \DOMDocument(); - $dom->loadXML(''); - $contextNode = $dom->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'file')->item(0); - parent::__construct($contextNode); - $this->setName($name); + return $other instanceof self; } - public function asDom() : \DOMDocument + public function name() : string { - return $this->dom(); + return 'void'; } - public function functionObject($name) : \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Method + public function allowsNull() : bool { - $node = $this->contextNode()->appendChild($this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', 'function')); - return new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Method($node, $name); + return \false; } - public function classObject($name) : \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Unit +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Type; + +final class NullType extends Type +{ + public function isAssignable(Type $other) : bool { - return $this->unitObject('class', $name); + return !$other instanceof VoidType; } - public function traitObject($name) : \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Unit + public function name() : string { - return $this->unitObject('trait', $name); + return 'null'; } - public function source() : \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Source + public function asString() : string { - $source = $this->contextNode()->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'source')->item(0); - if (!$source) { - $source = $this->contextNode()->appendChild($this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', 'source')); - } - return new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Source($source); + return 'null'; } - private function setName(string $name) : void + /** + * @deprecated + * + * @codeCoverageIgnore + */ + public function getReturnTypeDeclaration() : string { - $this->contextNode()->setAttribute('name', \basename($name)); - $this->contextNode()->setAttribute('path', \dirname($name)); + return ''; } - private function unitObject(string $tagName, $name) : \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Unit + public function allowsNull() : bool { - $node = $this->contextNode()->appendChild($this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', $tagName)); - return new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Unit($node, $name); + return \true; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; +namespace PHPUnit\SebastianBergmann\Type; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class Unit +use function is_subclass_of; +use function strcasecmp; +final class ObjectType extends Type { /** - * @var DOMElement + * @var TypeName */ - private $contextNode; - public function __construct(\DOMElement $context, string $name) - { - $this->contextNode = $context; - $this->setName($name); - } - public function setLines(int $start, int $executable, int $executed) : void + private $className; + /** + * @var bool + */ + private $allowsNull; + public function __construct(TypeName $className, bool $allowsNull) { - $this->contextNode->setAttribute('start', (string) $start); - $this->contextNode->setAttribute('executable', (string) $executable); - $this->contextNode->setAttribute('executed', (string) $executed); + $this->className = $className; + $this->allowsNull = $allowsNull; } - public function setCrap(float $crap) : void + public function isAssignable(Type $other) : bool { - $this->contextNode->setAttribute('crap', (string) $crap); + if ($this->allowsNull && $other instanceof NullType) { + return \true; + } + if ($other instanceof self) { + if (0 === strcasecmp($this->className->qualifiedName(), $other->className->qualifiedName())) { + return \true; + } + if (is_subclass_of($other->className->qualifiedName(), $this->className->qualifiedName(), \true)) { + return \true; + } + } + return \false; } - public function setNamespace(string $namespace) : void + public function name() : string { - $node = $this->contextNode->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'namespace')->item(0); - if (!$node) { - $node = $this->contextNode->appendChild($this->contextNode->ownerDocument->createElementNS('https://schema.phpunit.de/coverage/1.0', 'namespace')); - } - $node->setAttribute('name', $namespace); + return $this->className->qualifiedName(); } - public function addMethod(string $name) : \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Method + public function allowsNull() : bool { - $node = $this->contextNode->appendChild($this->contextNode->ownerDocument->createElementNS('https://schema.phpunit.de/coverage/1.0', 'method')); - return new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Method($node, $name); + return $this->allowsNull; } - private function setName(string $name) : void + public function className() : TypeName { - $this->contextNode->setAttribute('name', $name); + return $this->className; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; +namespace PHPUnit\SebastianBergmann\Type; -use DOMDocument; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -abstract class Node +use function strtolower; +final class SimpleType extends Type { /** - * @var DOMDocument + * @var string */ - private $dom; + private $name; /** - * @var DOMElement + * @var bool */ - private $contextNode; - public function __construct(\DOMElement $context) - { - $this->setContextNode($context); - } - public function dom() : \DOMDocument + private $allowsNull; + /** + * @var mixed + */ + private $value; + public function __construct(string $name, bool $nullable, $value = null) { - return $this->dom; + $this->name = $this->normalize($name); + $this->allowsNull = $nullable; + $this->value = $value; } - public function totals() : \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Totals + public function isAssignable(Type $other) : bool { - $totalsContainer = $this->contextNode()->firstChild; - if (!$totalsContainer) { - $totalsContainer = $this->contextNode()->appendChild($this->dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'totals')); + if ($this->allowsNull && $other instanceof NullType) { + return \true; } - return new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Totals($totalsContainer); + if ($this->name === 'bool' && $other->name() === 'false') { + return \true; + } + if ($other instanceof self) { + return $this->name === $other->name; + } + return \false; } - public function addDirectory(string $name) : \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Directory + public function name() : string { - $dirNode = $this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', 'directory'); - $dirNode->setAttribute('name', $name); - $this->contextNode()->appendChild($dirNode); - return new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Directory($dirNode); + return $this->name; } - public function addFile(string $name, string $href) : \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\File + public function allowsNull() : bool { - $fileNode = $this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', 'file'); - $fileNode->setAttribute('name', $name); - $fileNode->setAttribute('href', $href); - $this->contextNode()->appendChild($fileNode); - return new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\File($fileNode); + return $this->allowsNull; } - protected function setContextNode(\DOMElement $context) : void + public function value() { - $this->dom = $context->ownerDocument; - $this->contextNode = $context; + return $this->value; } - protected function contextNode() : \DOMElement + private function normalize(string $name) : string { - return $this->contextNode; + $name = strtolower($name); + switch ($name) { + case 'boolean': + return 'bool'; + case 'real': + case 'double': + return 'float'; + case 'integer': + return 'int'; + case '[]': + return 'array'; + default: + return $name; + } } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; +namespace PHPUnit\SebastianBergmann\Type; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class Directory extends \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Node +use function array_pop; +use function explode; +use function implode; +use function substr; +use ReflectionClass; +final class TypeName { + /** + * @var ?string + */ + private $namespaceName; + /** + * @var string + */ + private $simpleName; + public static function fromQualifiedName(string $fullClassName) : self + { + if ($fullClassName[0] === '\\') { + $fullClassName = substr($fullClassName, 1); + } + $classNameParts = explode('\\', $fullClassName); + $simpleName = array_pop($classNameParts); + $namespaceName = implode('\\', $classNameParts); + return new self($namespaceName, $simpleName); + } + public static function fromReflection(ReflectionClass $type) : self + { + return new self($type->getNamespaceName(), $type->getShortName()); + } + public function __construct(?string $namespaceName, string $simpleName) + { + if ($namespaceName === '') { + $namespaceName = null; + } + $this->namespaceName = $namespaceName; + $this->simpleName = $simpleName; + } + public function namespaceName() : ?string + { + return $this->namespaceName; + } + public function simpleName() : string + { + return $this->simpleName; + } + public function qualifiedName() : string + { + return $this->namespaceName === null ? $this->simpleName : $this->namespaceName . '\\' . $this->simpleName; + } + /** + * @deprecated Use namespaceName() instead + * + * @codeCoverageIgnore + */ + public function getNamespaceName() : ?string + { + return $this->namespaceName(); + } + /** + * @deprecated Use simpleName() instead + * + * @codeCoverageIgnore + */ + public function getSimpleName() : string + { + return $this->simpleName(); + } + /** + * @deprecated Use qualifiedName() instead + * + * @codeCoverageIgnore + */ + public function getQualifiedName() : string + { + return $this->qualifiedName(); + } + public function isNamespaced() : bool + { + return $this->namespaceName !== null; + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; +namespace PHPUnit\SebastianBergmann\Type; -use DOMElement; -use PHPUnit\SebastianBergmann\CodeCoverage\ReportAlreadyFinalizedException; -use XMLWriter; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class Coverage +final class StaticType extends Type { /** - * @var XMLWriter - */ - private $writer; - /** - * @var DOMElement + * @var TypeName */ - private $contextNode; + private $className; /** * @var bool */ - private $finalized = \false; - public function __construct(\DOMElement $context, string $line) + private $allowsNull; + public function __construct(TypeName $className, bool $allowsNull) { - $this->contextNode = $context; - $this->writer = new \XMLWriter(); - $this->writer->openMemory(); - $this->writer->startElementNS(null, $context->nodeName, 'https://schema.phpunit.de/coverage/1.0'); - $this->writer->writeAttribute('nr', $line); + $this->className = $className; + $this->allowsNull = $allowsNull; } - /** - * @throws ReportAlreadyFinalizedException - */ - public function addTest(string $test) : void + public function isAssignable(Type $other) : bool { - if ($this->finalized) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\ReportAlreadyFinalizedException(); + if ($this->allowsNull && $other instanceof NullType) { + return \true; } - $this->writer->startElement('covered'); - $this->writer->writeAttribute('by', $test); - $this->writer->endElement(); + if (!$other instanceof ObjectType) { + return \false; + } + if (0 === \strcasecmp($this->className->qualifiedName(), $other->className()->qualifiedName())) { + return \true; + } + if (\is_subclass_of($other->className()->qualifiedName(), $this->className->qualifiedName(), \true)) { + return \true; + } + return \false; } - public function finalize() : void + public function name() : string { - $this->writer->endElement(); - $fragment = $this->contextNode->ownerDocument->createDocumentFragment(); - $fragment->appendXML($this->writer->outputMemory()); - $this->contextNode->parentNode->replaceChild($fragment, $this->contextNode); - $this->finalized = \true; + return 'static'; + } + public function allowsNull() : bool + { + return $this->allowsNull; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; +namespace PHPUnit\SebastianBergmann\Type; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class Method +final class FalseType extends Type { - /** - * @var DOMElement - */ - private $contextNode; - public function __construct(\DOMElement $context, string $name) - { - $this->contextNode = $context; - $this->setName($name); - } - public function setSignature(string $signature) : void - { - $this->contextNode->setAttribute('signature', $signature); - } - public function setLines(string $start, ?string $end = null) : void + public function isAssignable(Type $other) : bool { - $this->contextNode->setAttribute('start', $start); - if ($end !== null) { - $this->contextNode->setAttribute('end', $end); + if ($other instanceof self) { + return \true; } + return $other instanceof SimpleType && $other->name() === 'bool' && $other->value() === \false; } - public function setTotals(string $executable, string $executed, string $coverage) : void + public function name() : string { - $this->contextNode->setAttribute('executable', $executable); - $this->contextNode->setAttribute('executed', $executed); - $this->contextNode->setAttribute('coverage', $coverage); + return 'false'; } - public function setCrap(string $crap) : void + public function allowsNull() : bool { - $this->contextNode->setAttribute('crap', $crap); + return \false; } - private function setName(string $name) : void + /** + * @deprecated + * + * @codeCoverageIgnore + * + * @throws LogicException + */ + public function getReturnTypeDeclaration() : string { - $this->contextNode->setAttribute('name', $name); + throw new LogicException(); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; +namespace PHPUnit\SebastianBergmann\Type; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class Tests +use function assert; +use function sprintf; +use ReflectionMethod; +use ReflectionNamedType; +use ReflectionType; +use ReflectionUnionType; +final class ReflectionMapper { - private $contextNode; - private $codeMap = [ - -1 => 'UNKNOWN', - // PHPUnit_Runner_BaseTestRunner::STATUS_UNKNOWN - 0 => 'PASSED', - // PHPUnit_Runner_BaseTestRunner::STATUS_PASSED - 1 => 'SKIPPED', - // PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED - 2 => 'INCOMPLETE', - // PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE - 3 => 'FAILURE', - // PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE - 4 => 'ERROR', - // PHPUnit_Runner_BaseTestRunner::STATUS_ERROR - 5 => 'RISKY', - // PHPUnit_Runner_BaseTestRunner::STATUS_RISKY - 6 => 'WARNING', - ]; - public function __construct(\DOMElement $context) + public function fromMethodReturnType(ReflectionMethod $method) : Type { - $this->contextNode = $context; + if (!$this->reflectionMethodHasReturnType($method)) { + return new UnknownType(); + } + $returnType = $this->reflectionMethodGetReturnType($method); + assert($returnType instanceof ReflectionNamedType || $returnType instanceof ReflectionUnionType); + if ($returnType instanceof ReflectionNamedType) { + if ($returnType->getName() === 'self') { + return ObjectType::fromName($method->getDeclaringClass()->getName(), $returnType->allowsNull()); + } + if ($returnType->getName() === 'static') { + return new StaticType(TypeName::fromReflection($method->getDeclaringClass()), $returnType->allowsNull()); + } + if ($returnType->getName() === 'mixed') { + return new MixedType(); + } + if ($returnType->getName() === 'parent') { + $parentClass = $method->getDeclaringClass()->getParentClass(); + // @codeCoverageIgnoreStart + if ($parentClass === \false) { + throw new RuntimeException(sprintf('%s::%s() has a "parent" return type declaration but %s does not have a parent class', $method->getDeclaringClass()->getName(), $method->getName(), $method->getDeclaringClass()->getName())); + } + // @codeCoverageIgnoreEnd + return ObjectType::fromName($parentClass->getName(), $returnType->allowsNull()); + } + return Type::fromName($returnType->getName(), $returnType->allowsNull()); + } + assert($returnType instanceof ReflectionUnionType); + $types = []; + foreach ($returnType->getTypes() as $type) { + assert($type instanceof ReflectionNamedType); + if ($type->getName() === 'self') { + $types[] = ObjectType::fromName($method->getDeclaringClass()->getName(), \false); + } else { + $types[] = Type::fromName($type->getName(), \false); + } + } + return new UnionType(...$types); } - public function addTest(string $test, array $result) : void + private function reflectionMethodHasReturnType(ReflectionMethod $method) : bool { - $node = $this->contextNode->appendChild($this->contextNode->ownerDocument->createElementNS('https://schema.phpunit.de/coverage/1.0', 'test')); - $node->setAttribute('name', $test); - $node->setAttribute('size', $result['size']); - $node->setAttribute('result', (string) $result['status']); - $node->setAttribute('status', $this->codeMap[(int) $result['status']]); + if ($method->hasReturnType()) { + return \true; + } + if (!\method_exists($method, 'hasTentativeReturnType')) { + return \false; + } + return $method->hasTentativeReturnType(); + } + private function reflectionMethodGetReturnType(ReflectionMethod $method) : ?ReflectionType + { + if ($method->hasReturnType()) { + return $method->getReturnType(); + } + if (!\method_exists($method, 'getTentativeReturnType')) { + return null; + } + return $method->getTentativeReturnType(); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; +namespace PHPUnit\SebastianBergmann\Type; -use const DIRECTORY_SEPARATOR; -use const PHP_EOL; -use function count; -use function dirname; -use function file_get_contents; -use function file_put_contents; -use function is_array; -use function is_dir; -use function is_file; -use function is_writable; -use function libxml_clear_errors; -use function libxml_get_errors; -use function libxml_use_internal_errors; -use function sprintf; -use function strlen; -use function substr; -use DateTimeImmutable; -use DOMDocument; -use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; -use PHPUnit\SebastianBergmann\CodeCoverage\Directory as DirectoryUtil; -use PHPUnit\SebastianBergmann\CodeCoverage\Driver\PathExistsButIsNotDirectoryException; -use PHPUnit\SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException; -use PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode; -use PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; -use PHPUnit\SebastianBergmann\CodeCoverage\Node\File as FileNode; -use PHPUnit\SebastianBergmann\CodeCoverage\Version; -use PHPUnit\SebastianBergmann\CodeCoverage\XmlException; -use PHPUnit\SebastianBergmann\Environment\Runtime; -final class Facade +use function assert; +use function class_exists; +use function is_iterable; +use ReflectionClass; +use ReflectionException; +final class IterableType extends Type { /** - * @var string - */ - private $target; - /** - * @var Project - */ - private $project; - /** - * @var string - */ - private $phpUnitVersion; - public function __construct(string $version) - { - $this->phpUnitVersion = $version; - } - /** - * @throws XmlException - */ - public function process(\PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage $coverage, string $target) : void - { - if (\substr($target, -1, 1) !== \DIRECTORY_SEPARATOR) { - $target .= \DIRECTORY_SEPARATOR; - } - $this->target = $target; - $this->initTargetDirectory($target); - $report = $coverage->getReport(); - $this->project = new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Project($coverage->getReport()->name()); - $this->setBuildInformation(); - $this->processTests($coverage->getTests()); - $this->processDirectory($report, $this->project); - $this->saveDocument($this->project->asDom(), 'index'); - } - private function setBuildInformation() : void - { - $buildNode = $this->project->buildInformation(); - $buildNode->setRuntimeInformation(new \PHPUnit\SebastianBergmann\Environment\Runtime()); - $buildNode->setBuildTime(new \DateTimeImmutable()); - $buildNode->setGeneratorVersions($this->phpUnitVersion, \PHPUnit\SebastianBergmann\CodeCoverage\Version::id()); - } - /** - * @throws PathExistsButIsNotDirectoryException - * @throws WriteOperationFailedException + * @var bool */ - private function initTargetDirectory(string $directory) : void + private $allowsNull; + public function __construct(bool $nullable) { - if (\is_file($directory)) { - if (!\is_dir($directory)) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\PathExistsButIsNotDirectoryException($directory); - } - if (!\is_writable($directory)) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException($directory); - } - } - \PHPUnit\SebastianBergmann\CodeCoverage\Directory::create($directory); + $this->allowsNull = $nullable; } /** - * @throws XmlException + * @throws RuntimeException */ - private function processDirectory(\PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory $directory, \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Node $context) : void + public function isAssignable(Type $other) : bool { - $directoryName = $directory->name(); - if ($this->project->projectSourceDirectory() === $directoryName) { - $directoryName = '/'; - } - $directoryObject = $context->addDirectory($directoryName); - $this->setTotals($directory, $directoryObject->totals()); - foreach ($directory->directories() as $node) { - $this->processDirectory($node, $directoryObject); - } - foreach ($directory->files() as $node) { - $this->processFile($node, $directoryObject); + if ($this->allowsNull && $other instanceof NullType) { + return \true; } - } - /** - * @throws XmlException - */ - private function processFile(\PHPUnit\SebastianBergmann\CodeCoverage\Node\File $file, \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Directory $context) : void - { - $fileObject = $context->addFile($file->name(), $file->id() . '.xml'); - $this->setTotals($file, $fileObject->totals()); - $path = \substr($file->pathAsString(), \strlen($this->project->projectSourceDirectory())); - $fileReport = new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Report($path); - $this->setTotals($file, $fileReport->totals()); - foreach ($file->classesAndTraits() as $unit) { - $this->processUnit($unit, $fileReport); + if ($other instanceof self) { + return \true; } - foreach ($file->functions() as $function) { - $this->processFunction($function, $fileReport); + if ($other instanceof SimpleType) { + return is_iterable($other->value()); } - foreach ($file->lineCoverageData() as $line => $tests) { - if (!\is_array($tests) || \count($tests) === 0) { - continue; - } - $coverage = $fileReport->lineCoverage((string) $line); - foreach ($tests as $test) { - $coverage->addTest($test); + if ($other instanceof ObjectType) { + $className = $other->className()->qualifiedName(); + assert(class_exists($className)); + try { + return (new ReflectionClass($className))->isIterable(); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new RuntimeException($e->getMessage(), (int) $e->getCode(), $e); + // @codeCoverageIgnoreEnd } - $coverage->finalize(); } - $fileReport->source()->setSourceCode(\file_get_contents($file->pathAsString())); - $this->saveDocument($fileReport->asDom(), $file->id()); + return \false; } - private function processUnit(array $unit, \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Report $report) : void + public function name() : string { - if (isset($unit['className'])) { - $unitObject = $report->classObject($unit['className']); - } else { - $unitObject = $report->traitObject($unit['traitName']); - } - $unitObject->setLines($unit['startLine'], $unit['executableLines'], $unit['executedLines']); - $unitObject->setCrap((float) $unit['crap']); - $unitObject->setNamespace($unit['namespace']); - foreach ($unit['methods'] as $method) { - $methodObject = $unitObject->addMethod($method['methodName']); - $methodObject->setSignature($method['signature']); - $methodObject->setLines((string) $method['startLine'], (string) $method['endLine']); - $methodObject->setCrap($method['crap']); - $methodObject->setTotals((string) $method['executableLines'], (string) $method['executedLines'], (string) $method['coverage']); - } + return 'iterable'; } - private function processFunction(array $function, \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Report $report) : void + public function allowsNull() : bool { - $functionObject = $report->functionObject($function['functionName']); - $functionObject->setSignature($function['signature']); - $functionObject->setLines((string) $function['startLine']); - $functionObject->setCrap($function['crap']); - $functionObject->setTotals((string) $function['executableLines'], (string) $function['executedLines'], (string) $function['coverage']); + return $this->allowsNull; } - private function processTests(array $tests) : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Type; + +use function get_class; +use function gettype; +use function strtolower; +abstract class Type +{ + public static function fromValue($value, bool $allowsNull) : self { - $testsObject = $this->project->tests(); - foreach ($tests as $test => $result) { - $testsObject->addTest($test, $result); + if ($value === \false) { + return new FalseType(); } + $typeName = gettype($value); + if ($typeName === 'object') { + return new ObjectType(TypeName::fromQualifiedName(get_class($value)), $allowsNull); + } + $type = self::fromName($typeName, $allowsNull); + if ($type instanceof SimpleType) { + $type = new SimpleType($typeName, $allowsNull, $value); + } + return $type; } - private function setTotals(\PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode $node, \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Totals $totals) : void - { - $loc = $node->linesOfCode(); - $totals->setNumLines($loc->linesOfCode(), $loc->commentLinesOfCode(), $loc->nonCommentLinesOfCode(), $node->numberOfExecutableLines(), $node->numberOfExecutedLines()); - $totals->setNumClasses($node->numberOfClasses(), $node->numberOfTestedClasses()); - $totals->setNumTraits($node->numberOfTraits(), $node->numberOfTestedTraits()); - $totals->setNumMethods($node->numberOfMethods(), $node->numberOfTestedMethods()); - $totals->setNumFunctions($node->numberOfFunctions(), $node->numberOfTestedFunctions()); - } - private function targetDirectory() : string + public static function fromName(string $typeName, bool $allowsNull) : self { - return $this->target; + switch (strtolower($typeName)) { + case 'callable': + return new CallableType($allowsNull); + case 'false': + return new FalseType(); + case 'iterable': + return new IterableType($allowsNull); + case 'null': + return new NullType(); + case 'object': + return new GenericObjectType($allowsNull); + case 'unknown type': + return new UnknownType(); + case 'void': + return new VoidType(); + case 'array': + case 'bool': + case 'boolean': + case 'double': + case 'float': + case 'int': + case 'integer': + case 'real': + case 'resource': + case 'resource (closed)': + case 'string': + return new SimpleType($typeName, $allowsNull); + default: + return new ObjectType(TypeName::fromQualifiedName($typeName), $allowsNull); + } } - /** - * @throws XmlException - */ - private function saveDocument(\DOMDocument $document, string $name) : void + public function asString() : string { - $filename = \sprintf('%s/%s.xml', $this->targetDirectory(), $name); - $document->formatOutput = \true; - $document->preserveWhiteSpace = \false; - $this->initTargetDirectory(\dirname($filename)); - \file_put_contents($filename, $this->documentAsString($document)); + return ($this->allowsNull() ? '?' : '') . $this->name(); } /** - * @throws XmlException + * @deprecated * - * @see https://bugs.php.net/bug.php?id=79191 + * @codeCoverageIgnore */ - private function documentAsString(\DOMDocument $document) : string + public function getReturnTypeDeclaration() : string { - $xmlErrorHandling = \libxml_use_internal_errors(\true); - $xml = $document->saveXML(); - if ($xml === \false) { - $message = 'Unable to generate the XML'; - foreach (\libxml_get_errors() as $error) { - $message .= \PHP_EOL . $error->message; - } - throw new \PHPUnit\SebastianBergmann\CodeCoverage\XmlException($message); - } - \libxml_clear_errors(); - \libxml_use_internal_errors($xmlErrorHandling); - return $xml; + return ': ' . $this->asString(); } + public abstract function isAssignable(Type $other) : bool; + public abstract function name() : string; + public abstract function allowsNull() : bool; } +sebastian/type + +Copyright (c) 2019-2020, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; +namespace PHPUnit\SebastianBergmann\Type; -use function sprintf; -use DOMElement; -use DOMNode; -use PHPUnit\SebastianBergmann\CodeCoverage\Percentage; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class Totals +final class RuntimeException extends \RuntimeException implements Exception { - /** - * @var DOMNode - */ - private $container; - /** - * @var DOMElement - */ - private $linesNode; - /** - * @var DOMElement - */ - private $methodsNode; - /** - * @var DOMElement - */ - private $functionsNode; - /** - * @var DOMElement - */ - private $classesNode; - /** - * @var DOMElement - */ - private $traitsNode; - public function __construct(\DOMElement $container) - { - $this->container = $container; - $dom = $container->ownerDocument; - $this->linesNode = $dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'lines'); - $this->methodsNode = $dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'methods'); - $this->functionsNode = $dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'functions'); - $this->classesNode = $dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'classes'); - $this->traitsNode = $dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'traits'); - $container->appendChild($this->linesNode); - $container->appendChild($this->methodsNode); - $container->appendChild($this->functionsNode); - $container->appendChild($this->classesNode); - $container->appendChild($this->traitsNode); - } - public function container() : \DOMNode - { - return $this->container; - } - public function setNumLines(int $loc, int $cloc, int $ncloc, int $executable, int $executed) : void - { - $this->linesNode->setAttribute('total', (string) $loc); - $this->linesNode->setAttribute('comments', (string) $cloc); - $this->linesNode->setAttribute('code', (string) $ncloc); - $this->linesNode->setAttribute('executable', (string) $executable); - $this->linesNode->setAttribute('executed', (string) $executed); - $this->linesNode->setAttribute('percent', $executable === 0 ? '0' : \sprintf('%01.2F', \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($executed, $executable)->asFloat())); - } - public function setNumClasses(int $count, int $tested) : void - { - $this->classesNode->setAttribute('count', (string) $count); - $this->classesNode->setAttribute('tested', (string) $tested); - $this->classesNode->setAttribute('percent', $count === 0 ? '0' : \sprintf('%01.2F', \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($tested, $count)->asFloat())); - } - public function setNumTraits(int $count, int $tested) : void - { - $this->traitsNode->setAttribute('count', (string) $count); - $this->traitsNode->setAttribute('tested', (string) $tested); - $this->traitsNode->setAttribute('percent', $count === 0 ? '0' : \sprintf('%01.2F', \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($tested, $count)->asFloat())); - } - public function setNumMethods(int $count, int $tested) : void - { - $this->methodsNode->setAttribute('count', (string) $count); - $this->methodsNode->setAttribute('tested', (string) $tested); - $this->methodsNode->setAttribute('percent', $count === 0 ? '0' : \sprintf('%01.2F', \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($tested, $count)->asFloat())); - } - public function setNumFunctions(int $count, int $tested) : void - { - $this->functionsNode->setAttribute('count', (string) $count); - $this->functionsNode->setAttribute('tested', (string) $tested); - $this->functionsNode->setAttribute('percent', $count === 0 ? '0' : \sprintf('%01.2F', \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($tested, $count)->asFloat())); - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; +namespace PHPUnit\SebastianBergmann\Type; -use function constant; -use function phpversion; -use DateTimeImmutable; -use DOMElement; -use PHPUnit\SebastianBergmann\Environment\Runtime; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class BuildInformation +final class LogicException extends \LogicException implements Exception { - /** - * @var DOMElement - */ - private $contextNode; - public function __construct(\DOMElement $contextNode) - { - $this->contextNode = $contextNode; - } - public function setRuntimeInformation(\PHPUnit\SebastianBergmann\Environment\Runtime $runtime) : void - { - $runtimeNode = $this->nodeByName('runtime'); - $runtimeNode->setAttribute('name', $runtime->getName()); - $runtimeNode->setAttribute('version', $runtime->getVersion()); - $runtimeNode->setAttribute('url', $runtime->getVendorUrl()); - $driverNode = $this->nodeByName('driver'); - if ($runtime->hasPHPDBGCodeCoverage()) { - $driverNode->setAttribute('name', 'phpdbg'); - $driverNode->setAttribute('version', \constant('PHPDBG_VERSION')); - } - if ($runtime->hasXdebug()) { - $driverNode->setAttribute('name', 'xdebug'); - $driverNode->setAttribute('version', \phpversion('xdebug')); - } - if ($runtime->hasPCOV()) { - $driverNode->setAttribute('name', 'pcov'); - $driverNode->setAttribute('version', \phpversion('pcov')); - } - } - public function setBuildTime(\DateTimeImmutable $date) : void - { - $this->contextNode->setAttribute('time', $date->format('D M j G:i:s T Y')); - } - public function setGeneratorVersions(string $phpUnitVersion, string $coverageVersion) : void - { - $this->contextNode->setAttribute('phpunit', $phpUnitVersion); - $this->contextNode->setAttribute('coverage', $coverageVersion); - } - private function nodeByName(string $name) : \DOMElement - { - $node = $this->contextNode->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', $name)->item(0); - if (!$node) { - $node = $this->contextNode->appendChild($this->contextNode->ownerDocument->createElementNS('https://schema.phpunit.de/coverage/1.0', $name)); - } - return $node; - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; +namespace PHPUnit\SebastianBergmann\Type; -use DOMDocument; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -class File +use Throwable; +interface Exception extends Throwable { - /** - * @var DOMDocument - */ - private $dom; - /** - * @var DOMElement - */ - private $contextNode; - public function __construct(\DOMElement $context) - { - $this->dom = $context->ownerDocument; - $this->contextNode = $context; - } - public function totals() : \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Totals - { - $totalsContainer = $this->contextNode->firstChild; - if (!$totalsContainer) { - $totalsContainer = $this->contextNode->appendChild($this->dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'totals')); - } - return new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Totals($totalsContainer); - } - public function lineCoverage(string $line) : \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Coverage - { - $coverage = $this->contextNode->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'coverage')->item(0); - if (!$coverage) { - $coverage = $this->contextNode->appendChild($this->dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'coverage')); - } - $lineNode = $coverage->appendChild($this->dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'line')); - return new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Coverage($lineNode, $line); - } - protected function contextNode() : \DOMElement - { - return $this->contextNode; - } - protected function dom() : \DOMDocument - { - return $this->dom; - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Html; +namespace PHPUnit\SebastianBergmann; -use function array_pop; -use function count; -use function phpversion; -use function sprintf; -use function str_repeat; -use function substr_count; -use PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode; -use PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; -use PHPUnit\SebastianBergmann\CodeCoverage\Node\File as FileNode; -use PHPUnit\SebastianBergmann\CodeCoverage\Version; -use PHPUnit\SebastianBergmann\Environment\Runtime; -use PHPUnit\SebastianBergmann\Template\Template; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -abstract class Renderer +final class Version { /** * @var string */ - protected $templatePath; - /** - * @var string - */ - protected $generator; + private $path; /** * @var string */ - protected $date; - /** - * @var int - */ - protected $lowUpperBound; - /** - * @var int - */ - protected $highLowerBound; - /** - * @var bool - */ - protected $hasBranchCoverage; + private $release; /** * @var string */ - protected $version; - public function __construct(string $templatePath, string $generator, string $date, int $lowUpperBound, int $highLowerBound, bool $hasBranchCoverage) - { - $this->templatePath = $templatePath; - $this->generator = $generator; - $this->date = $date; - $this->lowUpperBound = $lowUpperBound; - $this->highLowerBound = $highLowerBound; - $this->version = \PHPUnit\SebastianBergmann\CodeCoverage\Version::id(); - $this->hasBranchCoverage = $hasBranchCoverage; - } - protected function renderItemTemplate(\PHPUnit\SebastianBergmann\Template\Template $template, array $data) : string - { - $numSeparator = ' / '; - if (isset($data['numClasses']) && $data['numClasses'] > 0) { - $classesLevel = $this->colorLevel($data['testedClassesPercent']); - $classesNumber = $data['numTestedClasses'] . $numSeparator . $data['numClasses']; - $classesBar = $this->coverageBar($data['testedClassesPercent']); - } else { - $classesLevel = ''; - $classesNumber = '0' . $numSeparator . '0'; - $classesBar = ''; - $data['testedClassesPercentAsString'] = 'n/a'; - } - if ($data['numMethods'] > 0) { - $methodsLevel = $this->colorLevel($data['testedMethodsPercent']); - $methodsNumber = $data['numTestedMethods'] . $numSeparator . $data['numMethods']; - $methodsBar = $this->coverageBar($data['testedMethodsPercent']); - } else { - $methodsLevel = ''; - $methodsNumber = '0' . $numSeparator . '0'; - $methodsBar = ''; - $data['testedMethodsPercentAsString'] = 'n/a'; - } - if ($data['numExecutableLines'] > 0) { - $linesLevel = $this->colorLevel($data['linesExecutedPercent']); - $linesNumber = $data['numExecutedLines'] . $numSeparator . $data['numExecutableLines']; - $linesBar = $this->coverageBar($data['linesExecutedPercent']); - } else { - $linesLevel = ''; - $linesNumber = '0' . $numSeparator . '0'; - $linesBar = ''; - $data['linesExecutedPercentAsString'] = 'n/a'; - } - if ($data['numExecutablePaths'] > 0) { - $pathsLevel = $this->colorLevel($data['pathsExecutedPercent']); - $pathsNumber = $data['numExecutedPaths'] . $numSeparator . $data['numExecutablePaths']; - $pathsBar = $this->coverageBar($data['pathsExecutedPercent']); - } else { - $pathsLevel = ''; - $pathsNumber = '0' . $numSeparator . '0'; - $pathsBar = ''; - $data['pathsExecutedPercentAsString'] = 'n/a'; - } - if ($data['numExecutableBranches'] > 0) { - $branchesLevel = $this->colorLevel($data['branchesExecutedPercent']); - $branchesNumber = $data['numExecutedBranches'] . $numSeparator . $data['numExecutableBranches']; - $branchesBar = $this->coverageBar($data['branchesExecutedPercent']); - } else { - $branchesLevel = ''; - $branchesNumber = '0' . $numSeparator . '0'; - $branchesBar = ''; - $data['branchesExecutedPercentAsString'] = 'n/a'; - } - $template->setVar(['icon' => $data['icon'] ?? '', 'crap' => $data['crap'] ?? '', 'name' => $data['name'], 'lines_bar' => $linesBar, 'lines_executed_percent' => $data['linesExecutedPercentAsString'], 'lines_level' => $linesLevel, 'lines_number' => $linesNumber, 'paths_bar' => $pathsBar, 'paths_executed_percent' => $data['pathsExecutedPercentAsString'], 'paths_level' => $pathsLevel, 'paths_number' => $pathsNumber, 'branches_bar' => $branchesBar, 'branches_executed_percent' => $data['branchesExecutedPercentAsString'], 'branches_level' => $branchesLevel, 'branches_number' => $branchesNumber, 'methods_bar' => $methodsBar, 'methods_tested_percent' => $data['testedMethodsPercentAsString'], 'methods_level' => $methodsLevel, 'methods_number' => $methodsNumber, 'classes_bar' => $classesBar, 'classes_tested_percent' => $data['testedClassesPercentAsString'] ?? '', 'classes_level' => $classesLevel, 'classes_number' => $classesNumber]); - return $template->render(); - } - protected function setCommonTemplateVariables(\PHPUnit\SebastianBergmann\Template\Template $template, \PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode $node) : void - { - $template->setVar(['id' => $node->id(), 'full_path' => $node->pathAsString(), 'path_to_root' => $this->pathToRoot($node), 'breadcrumbs' => $this->breadcrumbs($node), 'date' => $this->date, 'version' => $this->version, 'runtime' => $this->runtimeString(), 'generator' => $this->generator, 'low_upper_bound' => $this->lowUpperBound, 'high_lower_bound' => $this->highLowerBound]); - } - protected function breadcrumbs(\PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode $node) : string - { - $breadcrumbs = ''; - $path = $node->pathAsArray(); - $pathToRoot = []; - $max = \count($path); - if ($node instanceof \PHPUnit\SebastianBergmann\CodeCoverage\Node\File) { - $max--; - } - for ($i = 0; $i < $max; $i++) { - $pathToRoot[] = \str_repeat('../', $i); - } - foreach ($path as $step) { - if ($step !== $node) { - $breadcrumbs .= $this->inactiveBreadcrumb($step, \array_pop($pathToRoot)); - } else { - $breadcrumbs .= $this->activeBreadcrumb($step); - } - } - return $breadcrumbs; - } - protected function activeBreadcrumb(\PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode $node) : string - { - $buffer = \sprintf(' ' . "\n", $node->name()); - if ($node instanceof \PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory) { - $buffer .= ' ' . "\n"; - } - return $buffer; - } - protected function inactiveBreadcrumb(\PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode $node, string $pathToRoot) : string - { - return \sprintf(' ' . "\n", $pathToRoot, $node->name()); - } - protected function pathToRoot(\PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode $node) : string - { - $id = $node->id(); - $depth = \substr_count($id, '/'); - if ($id !== 'index' && $node instanceof \PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory) { - $depth++; - } - return \str_repeat('../', $depth); - } - protected function coverageBar(float $percent) : string - { - $level = $this->colorLevel($percent); - $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'coverage_bar_branch.html' : 'coverage_bar.html'); - $template = new \PHPUnit\SebastianBergmann\Template\Template($templateName, '{{', '}}'); - $template->setVar(['level' => $level, 'percent' => \sprintf('%.2F', $percent)]); - return $template->render(); - } - protected function colorLevel(float $percent) : string - { - if ($percent <= $this->lowUpperBound) { - return 'danger'; - } - if ($percent > $this->lowUpperBound && $percent < $this->highLowerBound) { - return 'warning'; - } - return 'success'; - } - private function runtimeString() : string - { - $runtime = new \PHPUnit\SebastianBergmann\Environment\Runtime(); - $buffer = \sprintf('%s %s', $runtime->getVendorUrl(), $runtime->getName(), $runtime->getVersion()); - if ($runtime->hasPHPDBGCodeCoverage()) { - return $buffer; - } - if ($runtime->hasPCOV()) { - $buffer .= \sprintf(' with PCOV %s', \phpversion('pcov')); - } elseif ($runtime->hasXdebug()) { - $buffer .= \sprintf(' with Xdebug %s', \phpversion('xdebug')); - } - return $buffer; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Html; - -use function count; -use function sprintf; -use function str_repeat; -use PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode as Node; -use PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; -use PHPUnit\SebastianBergmann\Template\Template; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class Directory extends \PHPUnit\SebastianBergmann\CodeCoverage\Report\Html\Renderer -{ - public function render(\PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory $node, string $file) : void + private $version; + public function __construct(string $release, string $path) { - $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'directory_branch.html' : 'directory.html'); - $template = new \PHPUnit\SebastianBergmann\Template\Template($templateName, '{{', '}}'); - $this->setCommonTemplateVariables($template, $node); - $items = $this->renderItem($node, \true); - foreach ($node->directories() as $item) { - $items .= $this->renderItem($item); - } - foreach ($node->files() as $item) { - $items .= $this->renderItem($item); - } - $template->setVar(['id' => $node->id(), 'items' => $items]); - $template->renderTo($file); + $this->release = $release; + $this->path = $path; } - private function renderItem(\PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode $node, bool $total = \false) : string + public function getVersion() : string { - $data = ['numClasses' => $node->numberOfClassesAndTraits(), 'numTestedClasses' => $node->numberOfTestedClassesAndTraits(), 'numMethods' => $node->numberOfFunctionsAndMethods(), 'numTestedMethods' => $node->numberOfTestedFunctionsAndMethods(), 'linesExecutedPercent' => $node->percentageOfExecutedLines()->asFloat(), 'linesExecutedPercentAsString' => $node->percentageOfExecutedLines()->asString(), 'numExecutedLines' => $node->numberOfExecutedLines(), 'numExecutableLines' => $node->numberOfExecutableLines(), 'branchesExecutedPercent' => $node->percentageOfExecutedBranches()->asFloat(), 'branchesExecutedPercentAsString' => $node->percentageOfExecutedBranches()->asString(), 'numExecutedBranches' => $node->numberOfExecutedBranches(), 'numExecutableBranches' => $node->numberOfExecutableBranches(), 'pathsExecutedPercent' => $node->percentageOfExecutedPaths()->asFloat(), 'pathsExecutedPercentAsString' => $node->percentageOfExecutedPaths()->asString(), 'numExecutedPaths' => $node->numberOfExecutedPaths(), 'numExecutablePaths' => $node->numberOfExecutablePaths(), 'testedMethodsPercent' => $node->percentageOfTestedFunctionsAndMethods()->asFloat(), 'testedMethodsPercentAsString' => $node->percentageOfTestedFunctionsAndMethods()->asString(), 'testedClassesPercent' => $node->percentageOfTestedClassesAndTraits()->asFloat(), 'testedClassesPercentAsString' => $node->percentageOfTestedClassesAndTraits()->asString()]; - if ($total) { - $data['name'] = 'Total'; - } else { - $up = \str_repeat('../', \count($node->pathAsArray()) - 2); - $data['icon'] = \sprintf('', $up); - if ($node instanceof \PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory) { - $data['name'] = \sprintf('%s', $node->name(), $node->name()); - $data['icon'] = \sprintf('', $up); - } elseif ($this->hasBranchCoverage) { - $data['name'] = \sprintf('%s [line] [branch] [path]', $node->name(), $node->name(), $node->name(), $node->name()); + if ($this->version === null) { + if (\substr_count($this->release, '.') + 1 === 3) { + $this->version = $this->release; } else { - $data['name'] = \sprintf('%s', $node->name(), $node->name()); - } - } - $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'directory_item_branch.html' : 'directory_item.html'); - return $this->renderItemTemplate(new \PHPUnit\SebastianBergmann\Template\Template($templateName, '{{', '}}'), $data); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Html; - -use function array_values; -use function arsort; -use function asort; -use function count; -use function explode; -use function floor; -use function json_encode; -use function sprintf; -use function str_replace; -use PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode; -use PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; -use PHPUnit\SebastianBergmann\Template\Template; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class Dashboard extends \PHPUnit\SebastianBergmann\CodeCoverage\Report\Html\Renderer -{ - public function render(\PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory $node, string $file) : void - { - $classes = $node->classesAndTraits(); - $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'dashboard_branch.html' : 'dashboard.html'); - $template = new \PHPUnit\SebastianBergmann\Template\Template($templateName, '{{', '}}'); - $this->setCommonTemplateVariables($template, $node); - $baseLink = $node->id() . '/'; - $complexity = $this->complexity($classes, $baseLink); - $coverageDistribution = $this->coverageDistribution($classes); - $insufficientCoverage = $this->insufficientCoverage($classes, $baseLink); - $projectRisks = $this->projectRisks($classes, $baseLink); - $template->setVar(['insufficient_coverage_classes' => $insufficientCoverage['class'], 'insufficient_coverage_methods' => $insufficientCoverage['method'], 'project_risks_classes' => $projectRisks['class'], 'project_risks_methods' => $projectRisks['method'], 'complexity_class' => $complexity['class'], 'complexity_method' => $complexity['method'], 'class_coverage_distribution' => $coverageDistribution['class'], 'method_coverage_distribution' => $coverageDistribution['method']]); - $template->renderTo($file); - } - protected function activeBreadcrumb(\PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode $node) : string - { - return \sprintf(' ' . "\n" . ' ' . "\n", $node->name()); - } - /** - * Returns the data for the Class/Method Complexity charts. - */ - private function complexity(array $classes, string $baseLink) : array - { - $result = ['class' => [], 'method' => []]; - foreach ($classes as $className => $class) { - foreach ($class['methods'] as $methodName => $method) { - if ($className !== '*') { - $methodName = $className . '::' . $methodName; - } - $result['method'][] = [$method['coverage'], $method['ccn'], \sprintf('%s', \str_replace($baseLink, '', $method['link']), $methodName)]; + $this->version = $this->release . '-dev'; } - $result['class'][] = [$class['coverage'], $class['ccn'], \sprintf('%s', \str_replace($baseLink, '', $class['link']), $className)]; - } - return ['class' => \json_encode($result['class']), 'method' => \json_encode($result['method'])]; - } - /** - * Returns the data for the Class / Method Coverage Distribution chart. - */ - private function coverageDistribution(array $classes) : array - { - $result = ['class' => ['0%' => 0, '0-10%' => 0, '10-20%' => 0, '20-30%' => 0, '30-40%' => 0, '40-50%' => 0, '50-60%' => 0, '60-70%' => 0, '70-80%' => 0, '80-90%' => 0, '90-100%' => 0, '100%' => 0], 'method' => ['0%' => 0, '0-10%' => 0, '10-20%' => 0, '20-30%' => 0, '30-40%' => 0, '40-50%' => 0, '50-60%' => 0, '60-70%' => 0, '70-80%' => 0, '80-90%' => 0, '90-100%' => 0, '100%' => 0]]; - foreach ($classes as $class) { - foreach ($class['methods'] as $methodName => $method) { - if ($method['coverage'] === 0) { - $result['method']['0%']++; - } elseif ($method['coverage'] === 100) { - $result['method']['100%']++; + $git = $this->getGitInformation($this->path); + if ($git) { + if (\substr_count($this->release, '.') + 1 === 3) { + $this->version = $git; } else { - $key = \floor($method['coverage'] / 10) * 10; - $key = $key . '-' . ($key + 10) . '%'; - $result['method'][$key]++; - } - } - if ($class['coverage'] === 0) { - $result['class']['0%']++; - } elseif ($class['coverage'] === 100) { - $result['class']['100%']++; - } else { - $key = \floor($class['coverage'] / 10) * 10; - $key = $key . '-' . ($key + 10) . '%'; - $result['class'][$key]++; - } - } - return ['class' => \json_encode(\array_values($result['class'])), 'method' => \json_encode(\array_values($result['method']))]; - } - /** - * Returns the classes / methods with insufficient coverage. - */ - private function insufficientCoverage(array $classes, string $baseLink) : array - { - $leastTestedClasses = []; - $leastTestedMethods = []; - $result = ['class' => '', 'method' => '']; - foreach ($classes as $className => $class) { - foreach ($class['methods'] as $methodName => $method) { - if ($method['coverage'] < $this->highLowerBound) { - $key = $methodName; - if ($className !== '*') { - $key = $className . '::' . $methodName; - } - $leastTestedMethods[$key] = $method['coverage']; + $git = \explode('-', $git); + $this->version = $this->release . '-' . \end($git); } } - if ($class['coverage'] < $this->highLowerBound) { - $leastTestedClasses[$className] = $class['coverage']; - } } - \asort($leastTestedClasses); - \asort($leastTestedMethods); - foreach ($leastTestedClasses as $className => $coverage) { - $result['class'] .= \sprintf(' %s%d%%' . "\n", \str_replace($baseLink, '', $classes[$className]['link']), $className, $coverage); - } - foreach ($leastTestedMethods as $methodName => $coverage) { - [$class, $method] = \explode('::', $methodName); - $result['method'] .= \sprintf(' %s%d%%' . "\n", \str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']), $methodName, $method, $coverage); - } - return $result; + return $this->version; } /** - * Returns the project risks according to the CRAP index. + * @return bool|string */ - private function projectRisks(array $classes, string $baseLink) : array + private function getGitInformation(string $path) { - $classRisks = []; - $methodRisks = []; - $result = ['class' => '', 'method' => '']; - foreach ($classes as $className => $class) { - foreach ($class['methods'] as $methodName => $method) { - if ($method['coverage'] < $this->highLowerBound && $method['ccn'] > 1) { - $key = $methodName; - if ($className !== '*') { - $key = $className . '::' . $methodName; - } - $methodRisks[$key] = $method['crap']; - } - } - if ($class['coverage'] < $this->highLowerBound && $class['ccn'] > \count($class['methods'])) { - $classRisks[$className] = $class['crap']; - } + if (!\is_dir($path . \DIRECTORY_SEPARATOR . '.git')) { + return \false; } - \arsort($classRisks); - \arsort($methodRisks); - foreach ($classRisks as $className => $crap) { - $result['class'] .= \sprintf(' %s%d' . "\n", \str_replace($baseLink, '', $classes[$className]['link']), $className, $crap); + $process = \proc_open('git describe --tags', [1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes, $path); + if (!\is_resource($process)) { + return \false; } - foreach ($methodRisks as $methodName => $crap) { - [$class, $method] = \explode('::', $methodName); - $result['method'] .= \sprintf(' %s%d' . "\n", \str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']), $methodName, $method, $crap); + $result = \trim(\stream_get_contents($pipes[1])); + \fclose($pipes[1]); + \fclose($pipes[2]); + $returnCode = \proc_close($process); + if ($returnCode !== 0) { + return \false; } return $result; } } - - {{name}} - {{classes_bar}} -
{{classes_tested_percent}}
-
{{classes_number}}
- {{methods_bar}} -
{{methods_tested_percent}}
-
{{methods_number}}
- {{crap}} - {{paths_bar}} -
{{paths_executed_percent}}
-
{{paths_number}}
- {{branches_bar}} -
{{branches_executed_percent}}
-
{{branches_number}}
- {{lines_bar}} -
{{lines_executed_percent}}
-
{{lines_number}}
- +Version - - - - - Code Coverage for {{full_path}} - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - -{{items}} - -
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
-
-{{lines}} -{{structure}} - -
- - - - - - - - {{icon}}{{name}} - {{lines_bar}} -
{{lines_executed_percent}}
-
{{lines_number}}
- {{methods_bar}} -
{{methods_tested_percent}}
-
{{methods_number}}
- {{classes_bar}} -
{{classes_tested_percent}}
-
{{classes_number}}
- +Copyright (c) 2013-2020, Sebastian Bergmann . +All rights reserved. - - - - - Code Coverage for {{full_path}} - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - -{{items}} - -
 
Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
-
-
-
-

Legend

-

- Low: 0% to {{low_upper_bound}}% - Medium: {{low_upper_bound}}% to {{high_lower_bound}}% - High: {{high_lower_bound}}% to 100% -

-

- Generated by php-code-coverage {{version}} using {{runtime}}{{generator}} at {{date}}. -

-
-
- - - - -{{lines}} - -
-
-

Branches

-

- Below are the source code lines that represent each code branch as identified by Xdebug. Please note a branch is not - necessarily coterminous with a line, a line may contain multiple branches and therefore show up more than once. - Please also be aware that some branches may be implicit rather than explicit, e.g. an if statement - always has an else as part of its logical flow even if you didn't write one. -

-{{branches}} - - - - - Code Coverage for {{full_path}} - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - -{{items}} - -
 
Code Coverage
 
Classes and Traits
Functions and Methods
Paths
Branches
Lines
-
-{{lines}} -{{structure}} - -
- - - - - - -
-

Paths

-

- Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not - necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once. - Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement - always has an else as part of its logical flow even if you didn't write one. -

-{{paths}} - - {{name}} - {{methods_bar}} -
{{methods_tested_percent}}
-
{{methods_number}}
- {{crap}} - {{lines_bar}} -
{{lines_executed_percent}}
-
{{lines_number}}
- - - - {{icon}}{{name}} - {{lines_bar}} -
{{lines_executed_percent}}
-
{{lines_number}}
- {{branches_bar}} -
{{branches_executed_percent}}
-
{{branches_number}}
- {{paths_bar}} -
{{paths_executed_percent}}
-
{{paths_number}}
- {{methods_bar}} -
{{methods_tested_percent}}
-
{{methods_number}}
- {{classes_bar}} -
{{classes_tested_percent}}
-
{{classes_number}}
- - - - - - - Dashboard for {{full_path}} - - - - - - - -
-
-
-
- -
-
-
-
-
-
-
-

Classes

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
-
-
-

Insufficient Coverage

-
- - - - - - - - -{{insufficient_coverage_classes}} - -
ClassCoverage
-
-
-
-

Project Risks

-
- - - - - - - - -{{project_risks_classes}} - -
ClassCRAP
-
-
-
-
-
-

Methods

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
-
-
-

Insufficient Coverage

-
- - - - - - - - -{{insufficient_coverage_methods}} - -
MethodCoverage
-
-
-
-

Project Risks

-
- - - - - - - - -{{project_risks_methods}} - -
MethodCRAP
-
-
-
- -
- - - - - - - {{lineNumber}}{{lineContent}} -
-
- {{percent}}% covered ({{level}}) -
-
- - {{name}} - {{methods_bar}} -
{{methods_tested_percent}}
-
{{methods_number}}
- {{crap}} - {{paths_bar}} -
{{paths_executed_percent}}
-
{{paths_number}}
- {{branches_bar}} -
{{branches_executed_percent}}
-
{{branches_number}}
- {{lines_bar}} -
{{lines_executed_percent}}
-
{{lines_number}}
- +use InvalidArgumentException; +use PHPUnit\phpDocumentor\Reflection\DocBlock\Description; +use PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnit\phpDocumentor\Reflection\TypeResolver; +use PHPUnit\phpDocumentor\Reflection\Types\Context as TypeContext; +use PHPUnit\phpDocumentor\Reflection\Types\Mixed_; +use PHPUnit\phpDocumentor\Reflection\Types\Void_; +use PHPUnit\Webmozart\Assert\Assert; +use function array_keys; +use function explode; +use function implode; +use function is_string; +use function preg_match; +use function sort; +use function strpos; +use function substr; +use function trim; +use function var_export; +/** + * Reflection class for an {@}method in a Docblock. + */ +final class Method extends BaseTag implements Factory\StaticMethod +{ + /** @var string */ + protected $name = 'method'; + /** @var string */ + private $methodName; + /** + * @phpstan-var array + * @var array> + */ + private $arguments; + /** @var bool */ + private $isStatic; + /** @var Type */ + private $returnType; + /** + * @param array> $arguments + * + * @phpstan-param array $arguments + */ + public function __construct(string $methodName, array $arguments = [], ?Type $returnType = null, bool $static = \false, ?Description $description = null) + { + Assert::stringNotEmpty($methodName); + if ($returnType === null) { + $returnType = new Void_(); + } + $this->methodName = $methodName; + $this->arguments = $this->filterArguments($arguments); + $this->returnType = $returnType; + $this->isStatic = $static; + $this->description = $description; + } + public static function create(string $body, ?TypeResolver $typeResolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : ?self + { + Assert::stringNotEmpty($body); + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + // 1. none or more whitespace + // 2. optionally the keyword "static" followed by whitespace + // 3. optionally a word with underscores followed by whitespace : as + // type for the return value + // 4. then optionally a word with underscores followed by () and + // whitespace : as method name as used by phpDocumentor + // 5. then a word with underscores, followed by ( and any character + // until a ) and whitespace : as method name with signature + // 6. any remaining text : as description + if (!preg_match('/^ + # Static keyword + # Declares a static method ONLY if type is also present + (?: + (static) + \\s+ + )? + # Return type + (?: + ( + (?:[\\w\\|_\\\\]*\\$this[\\w\\|_\\\\]*) + | + (?: + (?:[\\w\\|_\\\\]+) + # array notation + (?:\\[\\])* + )*+ + ) + \\s+ + )? + # Method name + ([\\w_]+) + # Arguments + (?: + \\(([^\\)]*)\\) + )? + \\s* + # Description + (.*) + $/sux', $body, $matches)) { + return null; + } + [, $static, $returnType, $methodName, $argumentLines, $description] = $matches; + $static = $static === 'static'; + if ($returnType === '') { + $returnType = 'void'; + } + $returnType = $typeResolver->resolve($returnType, $context); + $description = $descriptionFactory->create($description, $context); + /** @phpstan-var array $arguments */ + $arguments = []; + if ($argumentLines !== '') { + $argumentsExploded = explode(',', $argumentLines); + foreach ($argumentsExploded as $argument) { + $argument = explode(' ', self::stripRestArg(trim($argument)), 2); + if (strpos($argument[0], '$') === 0) { + $argumentName = substr($argument[0], 1); + $argumentType = new Mixed_(); + } else { + $argumentType = $typeResolver->resolve($argument[0], $context); + $argumentName = ''; + if (isset($argument[1])) { + $argument[1] = self::stripRestArg($argument[1]); + $argumentName = substr($argument[1], 1); + } + } + $arguments[] = ['name' => $argumentName, 'type' => $argumentType]; + } + } + return new static($methodName, $arguments, $returnType, $static, $description); + } + /** + * Retrieves the method name. + */ + public function getMethodName() : string + { + return $this->methodName; + } + /** + * @return array> + * + * @phpstan-return array + */ + public function getArguments() : array + { + return $this->arguments; + } + /** + * Checks whether the method tag describes a static method or not. + * + * @return bool TRUE if the method declaration is for a static method, FALSE otherwise. + */ + public function isStatic() : bool + { + return $this->isStatic; + } + public function getReturnType() : Type + { + return $this->returnType; + } + public function __toString() : string + { + $arguments = []; + foreach ($this->arguments as $argument) { + $arguments[] = $argument['type'] . ' $' . $argument['name']; + } + $argumentStr = '(' . implode(', ', $arguments) . ')'; + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + $static = $this->isStatic ? 'static' : ''; + $returnType = (string) $this->returnType; + $methodName = (string) $this->methodName; + return $static . ($returnType !== '' ? ($static !== '' ? ' ' : '') . $returnType : '') . ($methodName !== '' ? ($static !== '' || $returnType !== '' ? ' ' : '') . $methodName : '') . $argumentStr . ($description !== '' ? ' ' . $description : ''); + } + /** + * @param mixed[][]|string[] $arguments + * + * @return mixed[][] + * + * @phpstan-param array $arguments + * @phpstan-return array + */ + private function filterArguments(array $arguments = []) : array + { + $result = []; + foreach ($arguments as $argument) { + if (is_string($argument)) { + $argument = ['name' => $argument]; + } + if (!isset($argument['type'])) { + $argument['type'] = new Mixed_(); + } + $keys = array_keys($argument); + sort($keys); + if ($keys !== ['name', 'type']) { + throw new InvalidArgumentException('Arguments can only have the "name" and "type" fields, found: ' . var_export($keys, \true)); + } + $result[] = $argument; + } + return $result; + } + private static function stripRestArg(string $argument) : string + { + if (strpos($argument, '...') === 0) { + $argument = trim(substr($argument, 3)); + } + return $argument; + } +} + - - - - Dashboard for {{full_path}} - - - - - - - -
-
-
-
- -
-
-
-
-
-
-
-

Classes

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
-
-
-

Insufficient Coverage

-
- - - - - - - - -{{insufficient_coverage_classes}} - -
ClassCoverage
-
-
-
-

Project Risks

-
- - - - - - - - -{{project_risks_classes}} - -
ClassCRAP
-
-
-
-
-
-

Methods

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
-
-
-

Insufficient Coverage

-
- - - - - - - - -{{insufficient_coverage_methods}} - -
MethodCoverage
-
-
-
-

Project Risks

-
- - - - - - - - -{{project_risks_methods}} - -
MethodCRAP
-
-
-
- -
- - - - - - -
-
- {{percent}}% covered ({{level}}) -
-
- - {{name}} - {{classes_bar}} -
{{classes_tested_percent}}
-
{{classes_number}}
- {{methods_bar}} -
{{methods_tested_percent}}
-
{{methods_number}}
- {{crap}} - {{lines_bar}} -
{{lines_executed_percent}}
-
{{lines_number}}
- +declare (strict_types=1); +/** + * This file is part of phpDocumentor. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @link http://phpdoc.org + */ +namespace PHPUnit\phpDocumentor\Reflection\DocBlock\Tags; -.octicon { - display: inline-block; - vertical-align: text-top; - fill: currentColor; -} -.nvd3 .nv-axis{pointer-events:none;opacity:1}.nvd3 .nv-axis path{fill:none;stroke:#000;stroke-opacity:.75;shape-rendering:crispEdges}.nvd3 .nv-axis path.domain{stroke-opacity:.75}.nvd3 .nv-axis.nv-x path.domain{stroke-opacity:0}.nvd3 .nv-axis line{fill:none;stroke:#e5e5e5;shape-rendering:crispEdges}.nvd3 .nv-axis .zero line,.nvd3 .nv-axis line.zero{stroke-opacity:.75}.nvd3 .nv-axis .nv-axisMaxMin text{font-weight:700}.nvd3 .x .nv-axis .nv-axisMaxMin text,.nvd3 .x2 .nv-axis .nv-axisMaxMin text,.nvd3 .x3 .nv-axis .nv-axisMaxMin text{text-anchor:middle}.nvd3 .nv-axis.nv-disabled{opacity:0}.nvd3 .nv-bars rect{fill-opacity:.75;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-bars rect.hover{fill-opacity:1}.nvd3 .nv-bars .hover rect{fill:#add8e6}.nvd3 .nv-bars text{fill:rgba(0,0,0,0)}.nvd3 .nv-bars .hover text{fill:rgba(0,0,0,1)}.nvd3 .nv-multibar .nv-groups rect,.nvd3 .nv-multibarHorizontal .nv-groups rect,.nvd3 .nv-discretebar .nv-groups rect{stroke-opacity:0;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-multibar .nv-groups rect:hover,.nvd3 .nv-multibarHorizontal .nv-groups rect:hover,.nvd3 .nv-candlestickBar .nv-ticks rect:hover,.nvd3 .nv-discretebar .nv-groups rect:hover{fill-opacity:1}.nvd3 .nv-discretebar .nv-groups text,.nvd3 .nv-multibarHorizontal .nv-groups text{font-weight:700;fill:rgba(0,0,0,1);stroke:rgba(0,0,0,0)}.nvd3 .nv-boxplot circle{fill-opacity:.5}.nvd3 .nv-boxplot circle:hover{fill-opacity:1}.nvd3 .nv-boxplot rect:hover{fill-opacity:1}.nvd3 line.nv-boxplot-median{stroke:#000}.nv-boxplot-tick:hover{stroke-width:2.5px}.nvd3.nv-bullet{font:10px sans-serif}.nvd3.nv-bullet .nv-measure{fill-opacity:.8}.nvd3.nv-bullet .nv-measure:hover{fill-opacity:1}.nvd3.nv-bullet .nv-marker{stroke:#000;stroke-width:2px}.nvd3.nv-bullet .nv-markerTriangle{stroke:#000;fill:#fff;stroke-width:1.5px}.nvd3.nv-bullet .nv-tick line{stroke:#666;stroke-width:.5px}.nvd3.nv-bullet .nv-range.nv-s0{fill:#eee}.nvd3.nv-bullet .nv-range.nv-s1{fill:#ddd}.nvd3.nv-bullet .nv-range.nv-s2{fill:#ccc}.nvd3.nv-bullet .nv-title{font-size:14px;font-weight:700}.nvd3.nv-bullet .nv-subtitle{fill:#999}.nvd3.nv-bullet .nv-range{fill:#bababa;fill-opacity:.4}.nvd3.nv-bullet .nv-range:hover{fill-opacity:.7}.nvd3.nv-candlestickBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.positive rect{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.negative rect{stroke:#d62728;fill:#d62728}.with-transitions .nv-candlestickBar .nv-ticks .nv-tick{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-candlestickBar .nv-ticks line{stroke:#333}.nvd3 .nv-legend .nv-disabled rect{}.nvd3 .nv-check-box .nv-box{fill-opacity:0;stroke-width:2}.nvd3 .nv-check-box .nv-check{fill-opacity:0;stroke-width:4}.nvd3 .nv-series.nv-disabled .nv-check-box .nv-check{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-controlsWrap .nv-legend .nv-check-box .nv-check{opacity:0}.nvd3.nv-linePlusBar .nv-bar rect{fill-opacity:.75}.nvd3.nv-linePlusBar .nv-bar rect:hover{fill-opacity:1}.nvd3 .nv-groups path.nv-line{fill:none}.nvd3 .nv-groups path.nv-area{stroke:none}.nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point{fill-opacity:0;stroke-opacity:0}.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point{fill-opacity:.5!important;stroke-opacity:.5!important}.with-transitions .nvd3 .nv-groups .nv-point{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-scatter .nv-groups .nv-point.hover,.nvd3 .nv-groups .nv-point.hover{stroke-width:7px;fill-opacity:.95!important;stroke-opacity:.95!important}.nvd3 .nv-point-paths path{stroke:#aaa;stroke-opacity:0;fill:#eee;fill-opacity:0}.nvd3 .nv-indexLine{cursor:ew-resize}svg.nvd3-svg{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-ms-user-select:none;-moz-user-select:none;user-select:none;display:block;width:100%;height:100%}.nvtooltip.with-3d-shadow,.with-3d-shadow .nvtooltip{-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nvd3 text{font:400 12px Arial}.nvd3 .title{font:700 14px Arial}.nvd3 .nv-background{fill:#fff;fill-opacity:0}.nvd3.nv-noData{font-size:18px;font-weight:700}.nv-brush .extent{fill-opacity:.125;shape-rendering:crispEdges}.nv-brush .resize path{fill:#eee;stroke:#666}.nvd3 .nv-legend .nv-series{cursor:pointer}.nvd3 .nv-legend .nv-disabled circle{fill-opacity:0}.nvd3 .nv-brush .extent{fill-opacity:0!important}.nvd3 .nv-brushBackground rect{stroke:#000;stroke-width:.4;fill:#fff;fill-opacity:.7}.nvd3.nv-ohlcBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive{stroke:#2ca02c}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative{stroke:#d62728}.nvd3 .background path{fill:none;stroke:#EEE;stroke-opacity:.4;shape-rendering:crispEdges}.nvd3 .foreground path{fill:none;stroke-opacity:.7}.nvd3 .nv-parallelCoordinates-brush .extent{fill:#fff;fill-opacity:.6;stroke:gray;shape-rendering:crispEdges}.nvd3 .nv-parallelCoordinates .hover{fill-opacity:1;stroke-width:3px}.nvd3 .missingValuesline line{fill:none;stroke:#000;stroke-width:1;stroke-opacity:1;stroke-dasharray:5,5}.nvd3.nv-pie path{stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-pie .nv-pie-title{font-size:24px;fill:rgba(19,196,249,.59)}.nvd3.nv-pie .nv-slice text{stroke:#000;stroke-width:0}.nvd3.nv-pie path{stroke:#fff;stroke-width:1px;stroke-opacity:1}.nvd3.nv-pie .hover path{fill-opacity:.7}.nvd3.nv-pie .nv-label{pointer-events:none}.nvd3.nv-pie .nv-label rect{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-groups .nv-point.hover{stroke-width:20px;stroke-opacity:.5}.nvd3 .nv-scatter .nv-point.hover{fill-opacity:1}.nv-noninteractive{pointer-events:none}.nv-distx,.nv-disty{pointer-events:none}.nvd3.nv-sparkline path{fill:none}.nvd3.nv-sparklineplus g.nv-hoverValue{pointer-events:none}.nvd3.nv-sparklineplus .nv-hoverValue line{stroke:#333;stroke-width:1.5px}.nvd3.nv-sparklineplus,.nvd3.nv-sparklineplus g{pointer-events:all}.nvd3 .nv-hoverArea{fill-opacity:0;stroke-opacity:0}.nvd3.nv-sparklineplus .nv-xValue,.nvd3.nv-sparklineplus .nv-yValue{stroke-width:0;font-size:.9em;font-weight:400}.nvd3.nv-sparklineplus .nv-yValue{stroke:#f66}.nvd3.nv-sparklineplus .nv-maxValue{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-sparklineplus .nv-minValue{stroke:#d62728;fill:#d62728}.nvd3.nv-sparklineplus .nv-currentValue{font-weight:700;font-size:1.1em}.nvd3.nv-stackedarea path.nv-area{fill-opacity:.7;stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-stackedarea path.nv-area.hover{fill-opacity:.9}.nvd3.nv-stackedarea .nv-groups .nv-point{stroke-opacity:0;fill-opacity:0}.nvtooltip{position:absolute;background-color:rgba(255,255,255,1);color:rgba(0,0,0,1);padding:1px;border:1px solid rgba(0,0,0,.2);z-index:10000;display:block;font-family:Arial;font-size:13px;text-align:left;pointer-events:none;white-space:nowrap;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.nvtooltip{background:rgba(255,255,255,.8);border:1px solid rgba(0,0,0,.5);border-radius:4px}.nvtooltip.with-transitions,.with-transitions .nvtooltip{transition:opacity 50ms linear;-moz-transition:opacity 50ms linear;-webkit-transition:opacity 50ms linear;transition-delay:200ms;-moz-transition-delay:200ms;-webkit-transition-delay:200ms}.nvtooltip.x-nvtooltip,.nvtooltip.y-nvtooltip{padding:8px}.nvtooltip h3{margin:0;padding:4px 14px;line-height:18px;font-weight:400;background-color:rgba(247,247,247,.75);color:rgba(0,0,0,1);text-align:center;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.nvtooltip p{margin:0;padding:5px 14px;text-align:center}.nvtooltip span{display:inline-block;margin:2px 0}.nvtooltip table{margin:6px;border-spacing:0}.nvtooltip table td{padding:2px 9px 2px 0;vertical-align:middle}.nvtooltip table td.key{font-weight:400}.nvtooltip table td.value{text-align:right;font-weight:700}.nvtooltip table tr.highlight td{padding:1px 9px 1px 0;border-bottom-style:solid;border-bottom-width:1px;border-top-style:solid;border-top-width:1px}.nvtooltip table td.legend-color-guide div{width:8px;height:8px;vertical-align:middle}.nvtooltip table td.legend-color-guide div{width:12px;height:12px;border:1px solid #999}.nvtooltip .footer{padding:3px;text-align:center}.nvtooltip-pending-removal{pointer-events:none;display:none}.nvd3 .nv-interactiveGuideLine{pointer-events:none}.nvd3 line.nv-guideline{stroke:#ccc}body { - padding-top: 10px; +use PHPUnit\phpDocumentor\Reflection\Type; +use function in_array; +use function strlen; +use function substr; +use function trim; +abstract class TagWithType extends BaseTag +{ + /** @var ?Type */ + protected $type; + /** + * Returns the type section of the variable. + */ + public function getType() : ?Type + { + return $this->type; + } + /** + * @return string[] + */ + protected static function extractTypeFromBody(string $body) : array + { + $type = ''; + $nestingLevel = 0; + for ($i = 0, $iMax = strlen($body); $i < $iMax; $i++) { + $character = $body[$i]; + if ($nestingLevel === 0 && trim($character) === '') { + break; + } + $type .= $character; + if (in_array($character, ['<', '(', '[', '{'])) { + $nestingLevel++; + continue; + } + if (in_array($character, ['>', ')', ']', '}'])) { + $nestingLevel--; + continue; + } + } + $description = trim(substr($body, strlen($type))); + return [$type, $description]; + } } +thead>tr>td { - border-bottom-width: 1px; -} +declare (strict_types=1); +/** + * This file is part of phpDocumentor. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @link http://phpdoc.org + */ +namespace PHPUnit\phpDocumentor\Reflection\DocBlock\Tags; -.table tbody>tr>td, .table thead>tr>td { - padding-top: 3px; - padding-bottom: 3px; +use PHPUnit\phpDocumentor\Reflection\DocBlock\Description; +use PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnit\phpDocumentor\Reflection\TypeResolver; +use PHPUnit\phpDocumentor\Reflection\Types\Context as TypeContext; +use PHPUnit\phpDocumentor\Reflection\Utils; +use PHPUnit\Webmozart\Assert\Assert; +use function array_shift; +use function array_unshift; +use function implode; +use function strpos; +use function substr; +use const PREG_SPLIT_DELIM_CAPTURE; +/** + * Reflection class for a {@}property-write tag in a Docblock. + */ +final class PropertyWrite extends TagWithType implements Factory\StaticMethod +{ + /** @var string */ + protected $variableName; + public function __construct(?string $variableName, ?Type $type = null, ?Description $description = null) + { + Assert::string($variableName); + $this->name = 'property-write'; + $this->variableName = $variableName; + $this->type = $type; + $this->description = $description; + } + public static function create(string $body, ?TypeResolver $typeResolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self + { + Assert::stringNotEmpty($body); + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + [$firstPart, $body] = self::extractTypeFromBody($body); + $type = null; + $parts = Utils::pregSplit('/(\\s+)/Su', $body, 2, \PREG_SPLIT_DELIM_CAPTURE); + $variableName = ''; + // if the first item that is encountered is not a variable; it is a type + if ($firstPart && $firstPart[0] !== '$') { + $type = $typeResolver->resolve($firstPart, $context); + } else { + // first part is not a type; we should prepend it to the parts array for further processing + array_unshift($parts, $firstPart); + } + // if the next item starts with a $ it must be the variable name + if (isset($parts[0]) && strpos($parts[0], '$') === 0) { + $variableName = array_shift($parts); + if ($type) { + array_shift($parts); + } + Assert::notNull($variableName); + $variableName = substr($variableName, 1); + } + $description = $descriptionFactory->create(implode('', $parts), $context); + return new static($variableName, $type, $description); + } + /** + * Returns the variable's name. + */ + public function getVariableName() : ?string + { + return $this->variableName; + } + /** + * Returns a string representation for this tag. + */ + public function __toString() : string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + if ($this->variableName) { + $variableName = '$' . $this->variableName; + } else { + $variableName = ''; + } + $type = (string) $this->type; + return $type . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); + } } +tr>td { - padding-top: 0; - padding-bottom: 0; -} +declare (strict_types=1); +/** + * This file is part of phpDocumentor. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @link http://phpdoc.org + */ +namespace PHPUnit\phpDocumentor\Reflection\DocBlock\Tags; -.table .progress { - margin-bottom: inherit; +use PHPUnit\phpDocumentor\Reflection\DocBlock\Description; +use PHPUnit\phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use PHPUnit\phpDocumentor\Reflection\Types\Context as TypeContext; +use PHPUnit\Webmozart\Assert\Assert; +use function preg_match; +/** + * Reflection class for a {@}version tag in a Docblock. + */ +final class Version extends BaseTag implements Factory\StaticMethod +{ + /** @var string */ + protected $name = 'version'; + /** + * PCRE regular expression matching a version vector. + * Assumes the "x" modifier. + */ + public const REGEX_VECTOR = '(?: + # Normal release vectors. + \\d\\S* + | + # VCS version vectors. Per PHPCS, they are expected to + # follow the form of the VCS name, followed by ":", followed + # by the version vector itself. + # By convention, popular VCSes like CVS, SVN and GIT use "$" + # around the actual version vector. + [^\\s\\:]+\\:\\s*\\$[^\\$]+\\$ + )'; + /** @var string|null The version vector. */ + private $version; + public function __construct(?string $version = null, ?Description $description = null) + { + Assert::nullOrStringNotEmpty($version); + $this->version = $version; + $this->description = $description; + } + public static function create(?string $body, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : ?self + { + if (empty($body)) { + return new static(); + } + $matches = []; + if (!preg_match('/^(' . self::REGEX_VECTOR . ')\\s*(.+)?$/sux', $body, $matches)) { + return null; + } + $description = null; + if ($descriptionFactory !== null) { + $description = $descriptionFactory->create($matches[2] ?? '', $context); + } + return new static($matches[1], $description); + } + /** + * Gets the version section of the tag. + */ + public function getVersion() : ?string + { + return $this->version; + } + /** + * Returns a string representation for this tag. + */ + public function __toString() : string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + $version = (string) $this->version; + return $version . ($description !== '' ? ($version !== '' ? ' ' : '') . $description : ''); + } } +version = $version; + $this->description = $description; + } + /** + * @return static + */ + public static function create(?string $body, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self + { + if (empty($body)) { + return new static(); + } + $matches = []; + if (!preg_match('/^(' . self::REGEX_VECTOR . ')\\s*(.+)?$/sux', $body, $matches)) { + return new static(null, $descriptionFactory !== null ? $descriptionFactory->create($body, $context) : null); + } + Assert::notNull($descriptionFactory); + return new static($matches[1], $descriptionFactory->create($matches[2] ?? '', $context)); + } + /** + * Gets the version section of the tag. + */ + public function getVersion() : ?string + { + return $this->version; + } + /** + * Returns a string representation for this tag. + */ + public function __toString() : string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + $version = (string) $this->version; + return $version . ($description !== '' ? ($version !== '' ? ' ' : '') . $description : ''); + } } +link = $link; + $this->description = $description; + } + public static function create(string $body, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self + { + Assert::notNull($descriptionFactory); + $parts = Utils::pregSplit('/\\s+/Su', $body, 2); + $description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null; + return new static($parts[0], $description); + } + /** + * Gets the link + */ + public function getLink() : string + { + return $this->link; + } + /** + * Returns a string representation for this tag. + */ + public function __toString() : string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + $link = (string) $this->link; + return $link . ($description !== '' ? ($link !== '' ? ' ' : '') . $description : ''); + } } +name = 'var'; + $this->variableName = $variableName; + $this->type = $type; + $this->description = $description; + } + public static function create(string $body, ?TypeResolver $typeResolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self + { + Assert::stringNotEmpty($body); + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + [$firstPart, $body] = self::extractTypeFromBody($body); + $parts = Utils::pregSplit('/(\\s+)/Su', $body, 2, \PREG_SPLIT_DELIM_CAPTURE); + $type = null; + $variableName = ''; + // if the first item that is encountered is not a variable; it is a type + if ($firstPart && $firstPart[0] !== '$') { + $type = $typeResolver->resolve($firstPart, $context); + } else { + // first part is not a type; we should prepend it to the parts array for further processing + array_unshift($parts, $firstPart); + } + // if the next item starts with a $ it must be the variable name + if (isset($parts[0]) && strpos($parts[0], '$') === 0) { + $variableName = array_shift($parts); + if ($type) { + array_shift($parts); + } + Assert::notNull($variableName); + $variableName = substr($variableName, 1); + } + $description = $descriptionFactory->create(implode('', $parts), $context); + return new static($variableName, $type, $description); + } + /** + * Returns the variable's name. + */ + public function getVariableName() : ?string + { + return $this->variableName; + } + /** + * Returns a string representation for this tag. + */ + public function __toString() : string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + if ($this->variableName) { + $variableName = '$' . $this->variableName; + } else { + $variableName = ''; + } + $type = (string) $this->type; + return $type . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); + } } +name = 'return'; + $this->type = $type; + $this->description = $description; + } + public static function create(string $body, ?TypeResolver $typeResolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self + { + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + [$type, $description] = self::extractTypeFromBody($body); + $type = $typeResolver->resolve($type, $context); + $description = $descriptionFactory->create($description, $context); + return new static($type, $description); + } + public function __toString() : string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + $type = $this->type ? '' . $this->type : 'mixed'; + return $type . ($description !== '' ? ($type !== '' ? ' ' : '') . $description : ''); + } } +refers = $refers; + $this->description = $description; + } + public static function create(string $body, ?DescriptionFactory $descriptionFactory = null, ?FqsenResolver $resolver = null, ?TypeContext $context = null) : self + { + Assert::stringNotEmpty($body); + Assert::notNull($descriptionFactory); + Assert::notNull($resolver); + $parts = Utils::pregSplit('/\\s+/Su', $body, 2); + return new static(self::resolveFqsen($parts[0], $resolver, $context), $descriptionFactory->create($parts[1] ?? '', $context)); + } + private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context) : Fqsen + { + Assert::notNull($fqsenResolver); + $fqsenParts = explode('::', $parts); + $resolved = $fqsenResolver->resolve($fqsenParts[0], $context); + if (!array_key_exists(1, $fqsenParts)) { + return $resolved; + } + return new Fqsen($resolved . '::' . $fqsenParts[1]); + } + /** + * Returns the structural element this tag refers to. + */ + public function getReference() : Fqsen + { + return $this->refers; + } + /** + * Returns a string representation of this tag. + */ + public function __toString() : string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + $refers = (string) $this->refers; + return $refers . ($description !== '' ? ($refers !== '' ? ' ' : '') . $description : ''); + } } +filePath = $filePath; + $this->startingLine = $startingLine; + $this->lineCount = $lineCount; + if ($content !== null) { + $this->content = trim($content); + } + $this->isURI = $isURI; + } + public function getContent() : string + { + if ($this->content === null || $this->content === '') { + $filePath = $this->filePath; + if ($this->isURI) { + $filePath = $this->isUriRelative($this->filePath) ? str_replace('%2F', '/', rawurlencode($this->filePath)) : $this->filePath; + } + return trim($filePath); + } + return $this->content; + } + public function getDescription() : ?string + { + return $this->content; + } + public static function create(string $body) : ?Tag + { + // File component: File path in quotes or File URI / Source information + if (!preg_match('/^\\s*(?:(\\"[^\\"]+\\")|(\\S+))(?:\\s+(.*))?$/sux', $body, $matches)) { + return null; + } + $filePath = null; + $fileUri = null; + if ($matches[1] !== '') { + $filePath = $matches[1]; + } else { + $fileUri = $matches[2]; + } + $startingLine = 1; + $lineCount = 0; + $description = null; + if (array_key_exists(3, $matches)) { + $description = $matches[3]; + // Starting line / Number of lines / Description + if (preg_match('/^([1-9]\\d*)(?:\\s+((?1))\\s*)?(.*)$/sux', $matches[3], $contentMatches)) { + $startingLine = (int) $contentMatches[1]; + if (isset($contentMatches[2])) { + $lineCount = (int) $contentMatches[2]; + } + if (array_key_exists(3, $contentMatches)) { + $description = $contentMatches[3]; + } + } + } + return new static($filePath ?? $fileUri ?? '', $fileUri !== null, $startingLine, $lineCount, $description); + } + /** + * Returns the file path. + * + * @return string Path to a file to use as an example. + * May also be an absolute URI. + */ + public function getFilePath() : string + { + return trim($this->filePath, '"'); + } + /** + * Returns a string representation for this tag. + */ + public function __toString() : string + { + $filePath = (string) $this->filePath; + $isDefaultLine = $this->startingLine === 1 && $this->lineCount === 0; + $startingLine = !$isDefaultLine ? (string) $this->startingLine : ''; + $lineCount = !$isDefaultLine ? (string) $this->lineCount : ''; + $content = (string) $this->content; + return $filePath . ($startingLine !== '' ? ($filePath !== '' ? ' ' : '') . $startingLine : '') . ($lineCount !== '' ? ($filePath !== '' || $startingLine !== '' ? ' ' : '') . $lineCount : '') . ($content !== '' ? ($filePath !== '' || $startingLine !== '' || $lineCount !== '' ? ' ' : '') . $content : ''); + } + /** + * Returns true if the provided URI is relative or contains a complete scheme (and thus is absolute). + */ + private function isUriRelative(string $uri) : bool + { + return strpos($uri, ':') === \false; + } + public function getStartingLine() : int + { + return $this->startingLine; + } + public function getLineCount() : int + { + return $this->lineCount; + } + public function getName() : string + { + return 'example'; + } + public function render(?Formatter $formatter = null) : string + { + if ($formatter === null) { + $formatter = new Formatter\PassthroughFormatter(); + } + return $formatter->format($this); + } } +name = 'param'; + $this->variableName = $variableName; + $this->type = $type; + $this->isVariadic = $isVariadic; + $this->description = $description; + $this->isReference = $isReference; + } + public static function create(string $body, ?TypeResolver $typeResolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self + { + Assert::stringNotEmpty($body); + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + [$firstPart, $body] = self::extractTypeFromBody($body); + $type = null; + $parts = Utils::pregSplit('/(\\s+)/Su', $body, 2, \PREG_SPLIT_DELIM_CAPTURE); + $variableName = ''; + $isVariadic = \false; + $isReference = \false; + // if the first item that is encountered is not a variable; it is a type + if ($firstPart && !self::strStartsWithVariable($firstPart)) { + $type = $typeResolver->resolve($firstPart, $context); + } else { + // first part is not a type; we should prepend it to the parts array for further processing + array_unshift($parts, $firstPart); + } + // if the next item starts with a $ or ...$ or &$ or &...$ it must be the variable name + if (isset($parts[0]) && self::strStartsWithVariable($parts[0])) { + $variableName = array_shift($parts); + if ($type) { + array_shift($parts); + } + Assert::notNull($variableName); + if (strpos($variableName, '$') === 0) { + $variableName = substr($variableName, 1); + } elseif (strpos($variableName, '&$') === 0) { + $isReference = \true; + $variableName = substr($variableName, 2); + } elseif (strpos($variableName, '...$') === 0) { + $isVariadic = \true; + $variableName = substr($variableName, 4); + } elseif (strpos($variableName, '&...$') === 0) { + $isVariadic = \true; + $isReference = \true; + $variableName = substr($variableName, 5); + } + } + $description = $descriptionFactory->create(implode('', $parts), $context); + return new static($variableName, $type, $isVariadic, $description, $isReference); + } + /** + * Returns the variable's name. + */ + public function getVariableName() : ?string + { + return $this->variableName; + } + /** + * Returns whether this tag is variadic. + */ + public function isVariadic() : bool + { + return $this->isVariadic; + } + /** + * Returns whether this tag is passed by reference. + */ + public function isReference() : bool + { + return $this->isReference; + } + /** + * Returns a string representation for this tag. + */ + public function __toString() : string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + $variableName = ''; + if ($this->variableName) { + $variableName .= ($this->isReference ? '&' : '') . ($this->isVariadic ? '...' : ''); + $variableName .= '$' . $this->variableName; + } + $type = (string) $this->type; + return $type . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); + } + private static function strStartsWithVariable(string $str) : bool + { + return strpos($str, '$') === 0 || strpos($str, '...$') === 0 || strpos($str, '&$') === 0 || strpos($str, '&...$') === 0; + } } +refers = $refers; + $this->description = $description; + } + public static function create(string $body, ?FqsenResolver $resolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self + { + Assert::notNull($resolver); + Assert::notNull($descriptionFactory); + $parts = Utils::pregSplit('/\\s+/Su', $body, 2); + return new static(self::resolveFqsen($parts[0], $resolver, $context), $descriptionFactory->create($parts[1] ?? '', $context)); + } + private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context) : Fqsen + { + Assert::notNull($fqsenResolver); + $fqsenParts = explode('::', $parts); + $resolved = $fqsenResolver->resolve($fqsenParts[0], $context); + if (!array_key_exists(1, $fqsenParts)) { + return $resolved; + } + return new Fqsen($resolved . '::' . $fqsenParts[1]); + } + /** + * Returns the structural element this tag refers to. + */ + public function getReference() : Fqsen + { + return $this->refers; + } + /** + * Returns a string representation of this tag. + */ + public function __toString() : string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + $refers = (string) $this->refers; + return $refers . ($description !== '' ? ($refers !== '' ? ' ' : '') . $description : ''); + } } +name = 'throws'; + $this->type = $type; + $this->description = $description; + } + public static function create(string $body, ?TypeResolver $typeResolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self + { + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + [$type, $description] = self::extractTypeFromBody($body); + $type = $typeResolver->resolve($type, $context); + $description = $descriptionFactory->create($description, $context); + return new static($type, $description); + } + public function __toString() : string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + $type = (string) $this->type; + return $type . ($description !== '' ? ($type !== '' ? ' ' : '') . $description : ''); + } } +name = 'property'; + $this->variableName = $variableName; + $this->type = $type; + $this->description = $description; + } + public static function create(string $body, ?TypeResolver $typeResolver = null, ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null) : self + { + Assert::stringNotEmpty($body); + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + [$firstPart, $body] = self::extractTypeFromBody($body); + $type = null; + $parts = Utils::pregSplit('/(\\s+)/Su', $body, 2, \PREG_SPLIT_DELIM_CAPTURE); + $variableName = ''; + // if the first item that is encountered is not a variable; it is a type + if ($firstPart && $firstPart[0] !== '$') { + $type = $typeResolver->resolve($firstPart, $context); + } else { + // first part is not a type; we should prepend it to the parts array for further processing + array_unshift($parts, $firstPart); + } + // if the next item starts with a $ it must be the variable name + if (isset($parts[0]) && strpos($parts[0], '$') === 0) { + $variableName = array_shift($parts); + if ($type) { + array_shift($parts); + } + Assert::notNull($variableName); + $variableName = substr($variableName, 1); + } + $description = $descriptionFactory->create(implode('', $parts), $context); + return new static($variableName, $type, $description); + } + /** + * Returns the variable's name. + */ + public function getVariableName() : ?string + { + return $this->variableName; + } + /** + * Returns a string representation for this tag. + */ + public function __toString() : string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + if ($this->variableName) { + $variableName = '$' . $this->variableName; + } else { + $variableName = ''; + } + $type = (string) $this->type; + return $type . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); + } } +code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{-webkit-appearance:none;-moz-appearance:none;appearance:none}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item{display:-ms-flexbox;display:flex}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;line-height:0;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);height:-webkit-min-content;height:-moz-min-content;height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem);height:-webkit-min-content;height:-moz-min-content;height:min-content}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;-ms-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} -/*# sourceMappingURL=bootstrap.min.css.map */ - - - - Code Coverage for {{full_path}} - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - -{{items}} - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
-
-
-
-

Legend

-

- Low: 0% to {{low_upper_bound}}% - Medium: {{low_upper_bound}}% to {{high_lower_bound}}% - High: {{high_lower_bound}}% to 100% -

-

- Generated by php-code-coverage {{version}} using {{runtime}}{{generator}} at {{date}}. -

-
-
- - -/*! - * Bootstrap v4.5.0 (https://getbootstrap.com/) - * Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e((t=t||self).bootstrap={},t.jQuery,t.Popper)}(this,(function(t,e,n){"use strict";function i(t,e){for(var n=0;n=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}};c.jQueryDetection(),e.fn.emulateTransitionEnd=l,e.event.special[c.TRANSITION_END]={bindType:"transitionend",delegateType:"transitionend",handle:function(t){if(e(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}};var h="alert",u=e.fn[h],d=function(){function t(t){this._element=t}var n=t.prototype;return n.close=function(t){var e=this._element;t&&(e=this._getRootElement(t)),this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},n.dispose=function(){e.removeData(this._element,"bs.alert"),this._element=null},n._getRootElement=function(t){var n=c.getSelectorFromElement(t),i=!1;return n&&(i=document.querySelector(n)),i||(i=e(t).closest(".alert")[0]),i},n._triggerCloseEvent=function(t){var n=e.Event("close.bs.alert");return e(t).trigger(n),n},n._removeElement=function(t){var n=this;if(e(t).removeClass("show"),e(t).hasClass("fade")){var i=c.getTransitionDurationFromElement(t);e(t).one(c.TRANSITION_END,(function(e){return n._destroyElement(t,e)})).emulateTransitionEnd(i)}else this._destroyElement(t)},n._destroyElement=function(t){e(t).detach().trigger("closed.bs.alert").remove()},t._jQueryInterface=function(n){return this.each((function(){var i=e(this),o=i.data("bs.alert");o||(o=new t(this),i.data("bs.alert",o)),"close"===n&&o[n](this)}))},t._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}}]),t}();e(document).on("click.bs.alert.data-api",'[data-dismiss="alert"]',d._handleDismiss(new d)),e.fn[h]=d._jQueryInterface,e.fn[h].Constructor=d,e.fn[h].noConflict=function(){return e.fn[h]=u,d._jQueryInterface};var f=e.fn.button,g=function(){function t(t){this._element=t}var n=t.prototype;return n.toggle=function(){var t=!0,n=!0,i=e(this._element).closest('[data-toggle="buttons"]')[0];if(i){var o=this._element.querySelector('input:not([type="hidden"])');if(o){if("radio"===o.type)if(o.checked&&this._element.classList.contains("active"))t=!1;else{var s=i.querySelector(".active");s&&e(s).removeClass("active")}t&&("checkbox"!==o.type&&"radio"!==o.type||(o.checked=!this._element.classList.contains("active")),e(o).trigger("change")),o.focus(),n=!1}}this._element.hasAttribute("disabled")||this._element.classList.contains("disabled")||(n&&this._element.setAttribute("aria-pressed",!this._element.classList.contains("active")),t&&e(this._element).toggleClass("active"))},n.dispose=function(){e.removeData(this._element,"bs.button"),this._element=null},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.button");i||(i=new t(this),e(this).data("bs.button",i)),"toggle"===n&&i[n]()}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}}]),t}();e(document).on("click.bs.button.data-api",'[data-toggle^="button"]',(function(t){var n=t.target,i=n;if(e(n).hasClass("btn")||(n=e(n).closest(".btn")[0]),!n||n.hasAttribute("disabled")||n.classList.contains("disabled"))t.preventDefault();else{var o=n.querySelector('input:not([type="hidden"])');if(o&&(o.hasAttribute("disabled")||o.classList.contains("disabled")))return void t.preventDefault();"LABEL"===i.tagName&&o&&"checkbox"===o.type&&t.preventDefault(),g._jQueryInterface.call(e(n),"toggle")}})).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',(function(t){var n=e(t.target).closest(".btn")[0];e(n).toggleClass("focus",/^focus(in)?$/.test(t.type))})),e(window).on("load.bs.button.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-toggle="buttons"] .btn')),e=0,n=t.length;e0,this._pointerEvent=Boolean(window.PointerEvent||window.MSPointerEvent),this._addEventListeners()}var n=t.prototype;return n.next=function(){this._isSliding||this._slide("next")},n.nextWhenVisible=function(){!document.hidden&&e(this._element).is(":visible")&&"hidden"!==e(this._element).css("visibility")&&this.next()},n.prev=function(){this._isSliding||this._slide("prev")},n.pause=function(t){t||(this._isPaused=!0),this._element.querySelector(".carousel-item-next, .carousel-item-prev")&&(c.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},n.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},n.to=function(t){var n=this;this._activeElement=this._element.querySelector(".active.carousel-item");var i=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)e(this._element).one("slid.bs.carousel",(function(){return n.to(t)}));else{if(i===t)return this.pause(),void this.cycle();var o=t>i?"next":"prev";this._slide(o,this._items[t])}},n.dispose=function(){e(this._element).off(p),e.removeData(this._element,"bs.carousel"),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},n._getConfig=function(t){return t=a(a({},v),t),c.typeCheckConfig(m,t,b),t},n._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&this.prev(),e<0&&this.next()}},n._addEventListeners=function(){var t=this;this._config.keyboard&&e(this._element).on("keydown.bs.carousel",(function(e){return t._keydown(e)})),"hover"===this._config.pause&&e(this._element).on("mouseenter.bs.carousel",(function(e){return t.pause(e)})).on("mouseleave.bs.carousel",(function(e){return t.cycle(e)})),this._config.touch&&this._addTouchEventListeners()},n._addTouchEventListeners=function(){var t=this;if(this._touchSupported){var n=function(e){t._pointerEvent&&y[e.originalEvent.pointerType.toUpperCase()]?t.touchStartX=e.originalEvent.clientX:t._pointerEvent||(t.touchStartX=e.originalEvent.touches[0].clientX)},i=function(e){t._pointerEvent&&y[e.originalEvent.pointerType.toUpperCase()]&&(t.touchDeltaX=e.originalEvent.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),500+t._config.interval))};e(this._element.querySelectorAll(".carousel-item img")).on("dragstart.bs.carousel",(function(t){return t.preventDefault()})),this._pointerEvent?(e(this._element).on("pointerdown.bs.carousel",(function(t){return n(t)})),e(this._element).on("pointerup.bs.carousel",(function(t){return i(t)})),this._element.classList.add("pointer-event")):(e(this._element).on("touchstart.bs.carousel",(function(t){return n(t)})),e(this._element).on("touchmove.bs.carousel",(function(e){return function(e){e.originalEvent.touches&&e.originalEvent.touches.length>1?t.touchDeltaX=0:t.touchDeltaX=e.originalEvent.touches[0].clientX-t.touchStartX}(e)})),e(this._element).on("touchend.bs.carousel",(function(t){return i(t)})))}},n._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case 37:t.preventDefault(),this.prev();break;case 39:t.preventDefault(),this.next()}},n._getItemIndex=function(t){return this._items=t&&t.parentNode?[].slice.call(t.parentNode.querySelectorAll(".carousel-item")):[],this._items.indexOf(t)},n._getItemByDirection=function(t,e){var n="next"===t,i="prev"===t,o=this._getItemIndex(e),s=this._items.length-1;if((i&&0===o||n&&o===s)&&!this._config.wrap)return e;var r=(o+("prev"===t?-1:1))%this._items.length;return-1===r?this._items[this._items.length-1]:this._items[r]},n._triggerSlideEvent=function(t,n){var i=this._getItemIndex(t),o=this._getItemIndex(this._element.querySelector(".active.carousel-item")),s=e.Event("slide.bs.carousel",{relatedTarget:t,direction:n,from:o,to:i});return e(this._element).trigger(s),s},n._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var n=[].slice.call(this._indicatorsElement.querySelectorAll(".active"));e(n).removeClass("active");var i=this._indicatorsElement.children[this._getItemIndex(t)];i&&e(i).addClass("active")}},n._slide=function(t,n){var i,o,s,r=this,a=this._element.querySelector(".active.carousel-item"),l=this._getItemIndex(a),h=n||a&&this._getItemByDirection(t,a),u=this._getItemIndex(h),d=Boolean(this._interval);if("next"===t?(i="carousel-item-left",o="carousel-item-next",s="left"):(i="carousel-item-right",o="carousel-item-prev",s="right"),h&&e(h).hasClass("active"))this._isSliding=!1;else if(!this._triggerSlideEvent(h,s).isDefaultPrevented()&&a&&h){this._isSliding=!0,d&&this.pause(),this._setActiveIndicatorElement(h);var f=e.Event("slid.bs.carousel",{relatedTarget:h,direction:s,from:l,to:u});if(e(this._element).hasClass("slide")){e(h).addClass(o),c.reflow(h),e(a).addClass(i),e(h).addClass(i);var g=parseInt(h.getAttribute("data-interval"),10);g?(this._config.defaultInterval=this._config.defaultInterval||this._config.interval,this._config.interval=g):this._config.interval=this._config.defaultInterval||this._config.interval;var m=c.getTransitionDurationFromElement(a);e(a).one(c.TRANSITION_END,(function(){e(h).removeClass(i+" "+o).addClass("active"),e(a).removeClass("active "+o+" "+i),r._isSliding=!1,setTimeout((function(){return e(r._element).trigger(f)}),0)})).emulateTransitionEnd(m)}else e(a).removeClass("active"),e(h).addClass("active"),this._isSliding=!1,e(this._element).trigger(f);d&&this.cycle()}},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.carousel"),o=a(a({},v),e(this).data());"object"==typeof n&&(o=a(a({},o),n));var s="string"==typeof n?n:o.slide;if(i||(i=new t(this,o),e(this).data("bs.carousel",i)),"number"==typeof n)i.to(n);else if("string"==typeof s){if("undefined"==typeof i[s])throw new TypeError('No method named "'+s+'"');i[s]()}else o.interval&&o.ride&&(i.pause(),i.cycle())}))},t._dataApiClickHandler=function(n){var i=c.getSelectorFromElement(this);if(i){var o=e(i)[0];if(o&&e(o).hasClass("carousel")){var s=a(a({},e(o).data()),e(this).data()),r=this.getAttribute("data-slide-to");r&&(s.interval=!1),t._jQueryInterface.call(e(o),s),r&&e(o).data("bs.carousel").to(r),n.preventDefault()}}},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}},{key:"Default",get:function(){return v}}]),t}();e(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",E._dataApiClickHandler),e(window).on("load.bs.carousel.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-ride="carousel"]')),n=0,i=t.length;n0&&(this._selector=r,this._triggerArray.push(s))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var n=t.prototype;return n.toggle=function(){e(this._element).hasClass("show")?this.hide():this.show()},n.show=function(){var n,i,o=this;if(!this._isTransitioning&&!e(this._element).hasClass("show")&&(this._parent&&0===(n=[].slice.call(this._parent.querySelectorAll(".show, .collapsing")).filter((function(t){return"string"==typeof o._config.parent?t.getAttribute("data-parent")===o._config.parent:t.classList.contains("collapse")}))).length&&(n=null),!(n&&(i=e(n).not(this._selector).data("bs.collapse"))&&i._isTransitioning))){var s=e.Event("show.bs.collapse");if(e(this._element).trigger(s),!s.isDefaultPrevented()){n&&(t._jQueryInterface.call(e(n).not(this._selector),"hide"),i||e(n).data("bs.collapse",null));var r=this._getDimension();e(this._element).removeClass("collapse").addClass("collapsing"),this._element.style[r]=0,this._triggerArray.length&&e(this._triggerArray).removeClass("collapsed").attr("aria-expanded",!0),this.setTransitioning(!0);var a="scroll"+(r[0].toUpperCase()+r.slice(1)),l=c.getTransitionDurationFromElement(this._element);e(this._element).one(c.TRANSITION_END,(function(){e(o._element).removeClass("collapsing").addClass("collapse show"),o._element.style[r]="",o.setTransitioning(!1),e(o._element).trigger("shown.bs.collapse")})).emulateTransitionEnd(l),this._element.style[r]=this._element[a]+"px"}}},n.hide=function(){var t=this;if(!this._isTransitioning&&e(this._element).hasClass("show")){var n=e.Event("hide.bs.collapse");if(e(this._element).trigger(n),!n.isDefaultPrevented()){var i=this._getDimension();this._element.style[i]=this._element.getBoundingClientRect()[i]+"px",c.reflow(this._element),e(this._element).addClass("collapsing").removeClass("collapse show");var o=this._triggerArray.length;if(o>0)for(var s=0;s0},i._getOffset=function(){var t=this,e={};return"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=a(a({},e.offsets),t._config.offset(e.offsets,t._element)||{}),e}:e.offset=this._config.offset,e},i._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:this._getOffset(),flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(t.modifiers.applyStyle={enabled:!1}),a(a({},t),this._config.popperConfig)},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.dropdown");if(i||(i=new t(this,"object"==typeof n?n:null),e(this).data("bs.dropdown",i)),"string"==typeof n){if("undefined"==typeof i[n])throw new TypeError('No method named "'+n+'"');i[n]()}}))},t._clearMenus=function(n){if(!n||3!==n.which&&("keyup"!==n.type||9===n.which))for(var i=[].slice.call(document.querySelectorAll('[data-toggle="dropdown"]')),o=0,s=i.length;o0&&r--,40===n.which&&rdocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},n._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},n._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:F,popperConfig:null},Y={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},$=function(){function t(t,e){if("undefined"==typeof n)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var i=t.prototype;return i.enable=function(){this._isEnabled=!0},i.disable=function(){this._isEnabled=!1},i.toggleEnabled=function(){this._isEnabled=!this._isEnabled},i.toggle=function(t){if(this._isEnabled)if(t){var n=this.constructor.DATA_KEY,i=e(t.currentTarget).data(n);i||(i=new this.constructor(t.currentTarget,this._getDelegateConfig()),e(t.currentTarget).data(n,i)),i._activeTrigger.click=!i._activeTrigger.click,i._isWithActiveTrigger()?i._enter(null,i):i._leave(null,i)}else{if(e(this.getTipElement()).hasClass("show"))return void this._leave(null,this);this._enter(null,this)}},i.dispose=function(){clearTimeout(this._timeout),e.removeData(this.element,this.constructor.DATA_KEY),e(this.element).off(this.constructor.EVENT_KEY),e(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&e(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},i.show=function(){var t=this;if("none"===e(this.element).css("display"))throw new Error("Please use show on visible elements");var i=e.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){e(this.element).trigger(i);var o=c.findShadowRoot(this.element),s=e.contains(null!==o?o:this.element.ownerDocument.documentElement,this.element);if(i.isDefaultPrevented()||!s)return;var r=this.getTipElement(),a=c.getUID(this.constructor.NAME);r.setAttribute("id",a),this.element.setAttribute("aria-describedby",a),this.setContent(),this.config.animation&&e(r).addClass("fade");var l="function"==typeof this.config.placement?this.config.placement.call(this,r,this.element):this.config.placement,h=this._getAttachment(l);this.addAttachmentClass(h);var u=this._getContainer();e(r).data(this.constructor.DATA_KEY,this),e.contains(this.element.ownerDocument.documentElement,this.tip)||e(r).appendTo(u),e(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new n(this.element,r,this._getPopperConfig(h)),e(r).addClass("show"),"ontouchstart"in document.documentElement&&e(document.body).children().on("mouseover",null,e.noop);var d=function(){t.config.animation&&t._fixTransition();var n=t._hoverState;t._hoverState=null,e(t.element).trigger(t.constructor.Event.SHOWN),"out"===n&&t._leave(null,t)};if(e(this.tip).hasClass("fade")){var f=c.getTransitionDurationFromElement(this.tip);e(this.tip).one(c.TRANSITION_END,d).emulateTransitionEnd(f)}else d()}},i.hide=function(t){var n=this,i=this.getTipElement(),o=e.Event(this.constructor.Event.HIDE),s=function(){"show"!==n._hoverState&&i.parentNode&&i.parentNode.removeChild(i),n._cleanTipClass(),n.element.removeAttribute("aria-describedby"),e(n.element).trigger(n.constructor.Event.HIDDEN),null!==n._popper&&n._popper.destroy(),t&&t()};if(e(this.element).trigger(o),!o.isDefaultPrevented()){if(e(i).removeClass("show"),"ontouchstart"in document.documentElement&&e(document.body).children().off("mouseover",null,e.noop),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,e(this.tip).hasClass("fade")){var r=c.getTransitionDurationFromElement(i);e(i).one(c.TRANSITION_END,s).emulateTransitionEnd(r)}else s();this._hoverState=""}},i.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},i.isWithContent=function(){return Boolean(this.getTitle())},i.addAttachmentClass=function(t){e(this.getTipElement()).addClass("bs-tooltip-"+t)},i.getTipElement=function(){return this.tip=this.tip||e(this.config.template)[0],this.tip},i.setContent=function(){var t=this.getTipElement();this.setElementContent(e(t.querySelectorAll(".tooltip-inner")),this.getTitle()),e(t).removeClass("fade show")},i.setElementContent=function(t,n){"object"!=typeof n||!n.nodeType&&!n.jquery?this.config.html?(this.config.sanitize&&(n=H(n,this.config.whiteList,this.config.sanitizeFn)),t.html(n)):t.text(n):this.config.html?e(n).parent().is(t)||t.empty().append(n):t.text(e(n).text())},i.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},i._getPopperConfig=function(t){var e=this;return a(a({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:".arrow"},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}}),this.config.popperConfig)},i._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=a(a({},e.offsets),t.config.offset(e.offsets,t.element)||{}),e}:e.offset=this.config.offset,e},i._getContainer=function(){return!1===this.config.container?document.body:c.isElement(this.config.container)?e(this.config.container):e(document).find(this.config.container)},i._getAttachment=function(t){return K[t.toUpperCase()]},i._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(n){if("click"===n)e(t.element).on(t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if("manual"!==n){var i="hover"===n?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,o="hover"===n?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;e(t.element).on(i,t.config.selector,(function(e){return t._enter(e)})).on(o,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t.element&&t.hide()},e(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=a(a({},this.config),{},{trigger:"manual",selector:""}):this._fixTitle()},i._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},i._enter=function(t,n){var i=this.constructor.DATA_KEY;(n=n||e(t.currentTarget).data(i))||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),e(t.currentTarget).data(i,n)),t&&(n._activeTrigger["focusin"===t.type?"focus":"hover"]=!0),e(n.getTipElement()).hasClass("show")||"show"===n._hoverState?n._hoverState="show":(clearTimeout(n._timeout),n._hoverState="show",n.config.delay&&n.config.delay.show?n._timeout=setTimeout((function(){"show"===n._hoverState&&n.show()}),n.config.delay.show):n.show())},i._leave=function(t,n){var i=this.constructor.DATA_KEY;(n=n||e(t.currentTarget).data(i))||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),e(t.currentTarget).data(i,n)),t&&(n._activeTrigger["focusout"===t.type?"focus":"hover"]=!1),n._isWithActiveTrigger()||(clearTimeout(n._timeout),n._hoverState="out",n.config.delay&&n.config.delay.hide?n._timeout=setTimeout((function(){"out"===n._hoverState&&n.hide()}),n.config.delay.hide):n.hide())},i._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},i._getConfig=function(t){var n=e(this.element).data();return Object.keys(n).forEach((function(t){-1!==V.indexOf(t)&&delete n[t]})),"number"==typeof(t=a(a(a({},this.constructor.Default),n),"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),c.typeCheckConfig(U,t,this.constructor.DefaultType),t.sanitize&&(t.template=H(t.template,t.whiteList,t.sanitizeFn)),t},i._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},i._cleanTipClass=function(){var t=e(this.getTipElement()),n=t.attr("class").match(W);null!==n&&n.length&&t.removeClass(n.join(""))},i._handlePopperPlacementChange=function(t){this.tip=t.instance.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},i._fixTransition=function(){var t=this.getTipElement(),n=this.config.animation;null===t.getAttribute("x-placement")&&(e(t).removeClass("fade"),this.config.animation=!1,this.hide(),this.show(),this.config.animation=n)},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.tooltip"),o="object"==typeof n&&n;if((i||!/dispose|hide/.test(n))&&(i||(i=new t(this,o),e(this).data("bs.tooltip",i)),"string"==typeof n)){if("undefined"==typeof i[n])throw new TypeError('No method named "'+n+'"');i[n]()}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}},{key:"Default",get:function(){return X}},{key:"NAME",get:function(){return U}},{key:"DATA_KEY",get:function(){return"bs.tooltip"}},{key:"Event",get:function(){return Y}},{key:"EVENT_KEY",get:function(){return".bs.tooltip"}},{key:"DefaultType",get:function(){return z}}]),t}();e.fn[U]=$._jQueryInterface,e.fn[U].Constructor=$,e.fn[U].noConflict=function(){return e.fn[U]=M,$._jQueryInterface};var J="popover",G=e.fn[J],Z=new RegExp("(^|\\s)bs-popover\\S+","g"),tt=a(a({},$.Default),{},{placement:"right",trigger:"click",content:"",template:''}),et=a(a({},$.DefaultType),{},{content:"(string|element|function)"}),nt={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"},it=function(t){var n,i;function s(){return t.apply(this,arguments)||this}i=t,(n=s).prototype=Object.create(i.prototype),n.prototype.constructor=n,n.__proto__=i;var r=s.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(t){e(this.getTipElement()).addClass("bs-popover-"+t)},r.getTipElement=function(){return this.tip=this.tip||e(this.config.template)[0],this.tip},r.setContent=function(){var t=e(this.getTipElement());this.setElementContent(t.find(".popover-header"),this.getTitle());var n=this._getContent();"function"==typeof n&&(n=n.call(this.element)),this.setElementContent(t.find(".popover-body"),n),t.removeClass("fade show")},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var t=e(this.getTipElement()),n=t.attr("class").match(Z);null!==n&&n.length>0&&t.removeClass(n.join(""))},s._jQueryInterface=function(t){return this.each((function(){var n=e(this).data("bs.popover"),i="object"==typeof t?t:null;if((n||!/dispose|hide/.test(t))&&(n||(n=new s(this,i),e(this).data("bs.popover",n)),"string"==typeof t)){if("undefined"==typeof n[t])throw new TypeError('No method named "'+t+'"');n[t]()}}))},o(s,null,[{key:"VERSION",get:function(){return"4.5.0"}},{key:"Default",get:function(){return tt}},{key:"NAME",get:function(){return J}},{key:"DATA_KEY",get:function(){return"bs.popover"}},{key:"Event",get:function(){return nt}},{key:"EVENT_KEY",get:function(){return".bs.popover"}},{key:"DefaultType",get:function(){return et}}]),s}($);e.fn[J]=it._jQueryInterface,e.fn[J].Constructor=it,e.fn[J].noConflict=function(){return e.fn[J]=G,it._jQueryInterface};var ot="scrollspy",st=e.fn[ot],rt={offset:10,method:"auto",target:""},at={offset:"number",method:"string",target:"(string|element)"},lt=function(){function t(t,n){var i=this;this._element=t,this._scrollElement="BODY"===t.tagName?window:t,this._config=this._getConfig(n),this._selector=this._config.target+" .nav-link,"+this._config.target+" .list-group-item,"+this._config.target+" .dropdown-item",this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,e(this._scrollElement).on("scroll.bs.scrollspy",(function(t){return i._process(t)})),this.refresh(),this._process()}var n=t.prototype;return n.refresh=function(){var t=this,n=this._scrollElement===this._scrollElement.window?"offset":"position",i="auto"===this._config.method?n:this._config.method,o="position"===i?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map((function(t){var n,s=c.getSelectorFromElement(t);if(s&&(n=document.querySelector(s)),n){var r=n.getBoundingClientRect();if(r.width||r.height)return[e(n)[i]().top+o,s]}return null})).filter((function(t){return t})).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},n.dispose=function(){e.removeData(this._element,"bs.scrollspy"),e(this._scrollElement).off(".bs.scrollspy"),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},n._getConfig=function(t){if("string"!=typeof(t=a(a({},rt),"object"==typeof t&&t?t:{})).target&&c.isElement(t.target)){var n=e(t.target).attr("id");n||(n=c.getUID(ot),e(t.target).attr("id",n)),t.target="#"+n}return c.typeCheckConfig(ot,t,at),t},n._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},n._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},n._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},n._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;){this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t li > .active":".active";i=(i=e.makeArray(e(o).find(r)))[i.length-1]}var a=e.Event("hide.bs.tab",{relatedTarget:this._element}),l=e.Event("show.bs.tab",{relatedTarget:i});if(i&&e(i).trigger(a),e(this._element).trigger(l),!l.isDefaultPrevented()&&!a.isDefaultPrevented()){s&&(n=document.querySelector(s)),this._activate(this._element,o);var h=function(){var n=e.Event("hidden.bs.tab",{relatedTarget:t._element}),o=e.Event("shown.bs.tab",{relatedTarget:i});e(i).trigger(n),e(t._element).trigger(o)};n?this._activate(n,n.parentNode,h):h()}}},n.dispose=function(){e.removeData(this._element,"bs.tab"),this._element=null},n._activate=function(t,n,i){var o=this,s=(!n||"UL"!==n.nodeName&&"OL"!==n.nodeName?e(n).children(".active"):e(n).find("> li > .active"))[0],r=i&&s&&e(s).hasClass("fade"),a=function(){return o._transitionComplete(t,s,i)};if(s&&r){var l=c.getTransitionDurationFromElement(s);e(s).removeClass("show").one(c.TRANSITION_END,a).emulateTransitionEnd(l)}else a()},n._transitionComplete=function(t,n,i){if(n){e(n).removeClass("active");var o=e(n.parentNode).find("> .dropdown-menu .active")[0];o&&e(o).removeClass("active"),"tab"===n.getAttribute("role")&&n.setAttribute("aria-selected",!1)}if(e(t).addClass("active"),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),c.reflow(t),t.classList.contains("fade")&&t.classList.add("show"),t.parentNode&&e(t.parentNode).hasClass("dropdown-menu")){var s=e(t).closest(".dropdown")[0];if(s){var r=[].slice.call(s.querySelectorAll(".dropdown-toggle"));e(r).addClass("active")}t.setAttribute("aria-expanded",!0)}i&&i()},t._jQueryInterface=function(n){return this.each((function(){var i=e(this),o=i.data("bs.tab");if(o||(o=new t(this),i.data("bs.tab",o)),"string"==typeof n){if("undefined"==typeof o[n])throw new TypeError('No method named "'+n+'"');o[n]()}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}}]),t}();e(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',(function(t){t.preventDefault(),ht._jQueryInterface.call(e(this),"show")})),e.fn.tab=ht._jQueryInterface,e.fn.tab.Constructor=ht,e.fn.tab.noConflict=function(){return e.fn.tab=ct,ht._jQueryInterface};var ut=e.fn.toast,dt={animation:"boolean",autohide:"boolean",delay:"number"},ft={animation:!0,autohide:!0,delay:500},gt=function(){function t(t,e){this._element=t,this._config=this._getConfig(e),this._timeout=null,this._setListeners()}var n=t.prototype;return n.show=function(){var t=this,n=e.Event("show.bs.toast");if(e(this._element).trigger(n),!n.isDefaultPrevented()){this._config.animation&&this._element.classList.add("fade");var i=function(){t._element.classList.remove("showing"),t._element.classList.add("show"),e(t._element).trigger("shown.bs.toast"),t._config.autohide&&(t._timeout=setTimeout((function(){t.hide()}),t._config.delay))};if(this._element.classList.remove("hide"),c.reflow(this._element),this._element.classList.add("showing"),this._config.animation){var o=c.getTransitionDurationFromElement(this._element);e(this._element).one(c.TRANSITION_END,i).emulateTransitionEnd(o)}else i()}},n.hide=function(){if(this._element.classList.contains("show")){var t=e.Event("hide.bs.toast");e(this._element).trigger(t),t.isDefaultPrevented()||this._close()}},n.dispose=function(){clearTimeout(this._timeout),this._timeout=null,this._element.classList.contains("show")&&this._element.classList.remove("show"),e(this._element).off("click.dismiss.bs.toast"),e.removeData(this._element,"bs.toast"),this._element=null,this._config=null},n._getConfig=function(t){return t=a(a(a({},ft),e(this._element).data()),"object"==typeof t&&t?t:{}),c.typeCheckConfig("toast",t,this.constructor.DefaultType),t},n._setListeners=function(){var t=this;e(this._element).on("click.dismiss.bs.toast",'[data-dismiss="toast"]',(function(){return t.hide()}))},n._close=function(){var t=this,n=function(){t._element.classList.add("hide"),e(t._element).trigger("hidden.bs.toast")};if(this._element.classList.remove("show"),this._config.animation){var i=c.getTransitionDurationFromElement(this._element);e(this._element).one(c.TRANSITION_END,n).emulateTransitionEnd(i)}else n()},t._jQueryInterface=function(n){return this.each((function(){var i=e(this),o=i.data("bs.toast");if(o||(o=new t(this,"object"==typeof n&&n),i.data("bs.toast",o)),"string"==typeof n){if("undefined"==typeof o[n])throw new TypeError('No method named "'+n+'"');o[n](this)}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}},{key:"DefaultType",get:function(){return dt}},{key:"Default",get:function(){return ft}}]),t}();e.fn.toast=gt._jQueryInterface,e.fn.toast.Constructor=gt,e.fn.toast.noConflict=function(){return e.fn.toast=ut,gt._jQueryInterface},t.Alert=d,t.Button=g,t.Carousel=E,t.Collapse=D,t.Dropdown=j,t.Modal=R,t.Popover=it,t.Scrollspy=lt,t.Tab=ht,t.Toast=gt,t.Tooltip=$,t.Util=c,Object.defineProperty(t,"__esModule",{value:!0})})); -//# sourceMappingURL=bootstrap.min.js.map/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0f&&(e=a.render.queue[f]);f++)d=e.generate(),typeof e.callback==typeof Function&&e.callback(d);a.render.queue.splice(0,f),a.render.queue.length?setTimeout(c):(a.dispatch.render_end(),a.render.active=!1)};setTimeout(c)},a.render.active=!1,a.render.queue=[],a.addGraph=function(b){typeof arguments[0]==typeof Function&&(b={generate:arguments[0],callback:arguments[1]}),a.render.queue.push(b),a.render.active||a.render()},"undefined"!=typeof module&&"undefined"!=typeof exports&&(module.exports=a),"undefined"!=typeof window&&(window.nv=a),a.dom.write=function(a){return void 0!==window.fastdom?fastdom.write(a):a()},a.dom.read=function(a){return void 0!==window.fastdom?fastdom.read(a):a()},a.interactiveGuideline=function(){"use strict";function b(l){l.each(function(l){function m(){var a=d3.mouse(this),d=a[0],e=a[1],i=!0,j=!1;if(k&&(d=d3.event.offsetX,e=d3.event.offsetY,"svg"!==d3.event.target.tagName&&(i=!1),d3.event.target.className.baseVal.match("nv-legend")&&(j=!0)),i&&(d-=f.left,e-=f.top),0>d||0>e||d>o||e>p||d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement||j){if(k&&d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement&&(void 0===d3.event.relatedTarget.className||d3.event.relatedTarget.className.match(c.nvPointerEventsClass)))return;return h.elementMouseout({mouseX:d,mouseY:e}),b.renderGuideLine(null),void c.hidden(!0)}c.hidden(!1);var l=g.invert(d);h.elementMousemove({mouseX:d,mouseY:e,pointXValue:l}),"dblclick"===d3.event.type&&h.elementDblclick({mouseX:d,mouseY:e,pointXValue:l}),"click"===d3.event.type&&h.elementClick({mouseX:d,mouseY:e,pointXValue:l})}var n=d3.select(this),o=d||960,p=e||400,q=n.selectAll("g.nv-wrap.nv-interactiveLineLayer").data([l]),r=q.enter().append("g").attr("class"," nv-wrap nv-interactiveLineLayer");r.append("g").attr("class","nv-interactiveGuideLine"),j&&(j.on("touchmove",m).on("mousemove",m,!0).on("mouseout",m,!0).on("dblclick",m).on("click",m),b.guideLine=null,b.renderGuideLine=function(c){i&&(b.guideLine&&b.guideLine.attr("x1")===c||a.dom.write(function(){var b=q.select(".nv-interactiveGuideLine").selectAll("line").data(null!=c?[a.utils.NaNtoZero(c)]:[],String);b.enter().append("line").attr("class","nv-guideline").attr("x1",function(a){return a}).attr("x2",function(a){return a}).attr("y1",p).attr("y2",0),b.exit().remove()}))})})}var c=a.models.tooltip();c.duration(0).hideDelay(0)._isInteractiveLayer(!0).hidden(!1);var d=null,e=null,f={left:0,top:0},g=d3.scale.linear(),h=d3.dispatch("elementMousemove","elementMouseout","elementClick","elementDblclick"),i=!0,j=null,k="ActiveXObject"in window;return b.dispatch=h,b.tooltip=c,b.margin=function(a){return arguments.length?(f.top="undefined"!=typeof a.top?a.top:f.top,f.left="undefined"!=typeof a.left?a.left:f.left,b):f},b.width=function(a){return arguments.length?(d=a,b):d},b.height=function(a){return arguments.length?(e=a,b):e},b.xScale=function(a){return arguments.length?(g=a,b):g},b.showGuideLine=function(a){return arguments.length?(i=a,b):i},b.svgContainer=function(a){return arguments.length?(j=a,b):j},b},a.interactiveBisect=function(a,b,c){"use strict";if(!(a instanceof Array))return null;var d;d="function"!=typeof c?function(a){return a.x}:c;var e=function(a,b){return d(a)-b},f=d3.bisector(e).left,g=d3.max([0,f(a,b)-1]),h=d(a[g]);if("undefined"==typeof h&&(h=g),h===b)return g;var i=d3.min([g+1,a.length-1]),j=d(a[i]);return"undefined"==typeof j&&(j=i),Math.abs(j-b)>=Math.abs(h-b)?g:i},a.nearestValueIndex=function(a,b,c){"use strict";var d=1/0,e=null;return a.forEach(function(a,f){var g=Math.abs(b-a);null!=a&&d>=g&&c>g&&(d=g,e=f)}),e},function(){"use strict";a.models.tooltip=function(){function b(){if(k){var a=d3.select(k);"svg"!==a.node().tagName&&(a=a.select("svg"));var b=a.node()?a.attr("viewBox"):null;if(b){b=b.split(" ");var c=parseInt(a.style("width"),10)/b[2];p.left=p.left*c,p.top=p.top*c}}}function c(){if(!n){var a;a=k?k:document.body,n=d3.select(a).append("div").attr("class","nvtooltip "+(j?j:"xy-tooltip")).attr("id",v),n.style("top",0).style("left",0),n.style("opacity",0),n.selectAll("div, table, td, tr").classed(w,!0),n.classed(w,!0),o=n.node()}}function d(){if(r&&B(e)){b();var f=p.left,g=null!==i?i:p.top;return a.dom.write(function(){c();var b=A(e);b&&(o.innerHTML=b),k&&u?a.dom.read(function(){var a=k.getElementsByTagName("svg")[0],b={left:0,top:0};if(a){var c=a.getBoundingClientRect(),d=k.getBoundingClientRect(),e=c.top;if(0>e){var i=k.getBoundingClientRect();e=Math.abs(e)>i.height?0:e}b.top=Math.abs(e-d.top),b.left=Math.abs(c.left-d.left)}f+=k.offsetLeft+b.left-2*k.scrollLeft,g+=k.offsetTop+b.top-2*k.scrollTop,h&&h>0&&(g=Math.floor(g/h)*h),C([f,g])}):C([f,g])}),d}}var e=null,f="w",g=25,h=0,i=null,j=null,k=null,l=!0,m=400,n=null,o=null,p={left:null,top:null},q={left:0,top:0},r=!0,s=100,t=!0,u=!1,v="nvtooltip-"+Math.floor(1e5*Math.random()),w="nv-pointer-events-none",x=function(a){return a},y=function(a){return a},z=function(a){return a},A=function(a){if(null===a)return"";var b=d3.select(document.createElement("table"));if(t){var c=b.selectAll("thead").data([a]).enter().append("thead");c.append("tr").append("td").attr("colspan",3).append("strong").classed("x-value",!0).html(y(a.value))}var d=b.selectAll("tbody").data([a]).enter().append("tbody"),e=d.selectAll("tr").data(function(a){return a.series}).enter().append("tr").classed("highlight",function(a){return a.highlight});e.append("td").classed("legend-color-guide",!0).append("div").style("background-color",function(a){return a.color}),e.append("td").classed("key",!0).html(function(a,b){return z(a.key,b)}),e.append("td").classed("value",!0).html(function(a,b){return x(a.value,b)}),e.selectAll("td").each(function(a){if(a.highlight){var b=d3.scale.linear().domain([0,1]).range(["#fff",a.color]),c=.6;d3.select(this).style("border-bottom-color",b(c)).style("border-top-color",b(c))}});var f=b.node().outerHTML;return void 0!==a.footer&&(f+=""),f},B=function(a){if(a&&a.series){if(a.series instanceof Array)return!!a.series.length;if(a.series instanceof Object)return a.series=[a.series],!0}return!1},C=function(b){o&&a.dom.read(function(){var c,d,e=parseInt(o.offsetHeight,10),h=parseInt(o.offsetWidth,10),i=a.utils.windowSize().width,j=a.utils.windowSize().height,k=window.pageYOffset,p=window.pageXOffset;j=window.innerWidth>=document.body.scrollWidth?j:j-16,i=window.innerHeight>=document.body.scrollHeight?i:i-16;var r,t,u=function(a){var b=d;do isNaN(a.offsetTop)||(b+=a.offsetTop),a=a.offsetParent;while(a);return b},v=function(a){var b=c;do isNaN(a.offsetLeft)||(b+=a.offsetLeft),a=a.offsetParent;while(a);return b};switch(f){case"e":c=b[0]-h-g,d=b[1]-e/2,r=v(o),t=u(o),p>r&&(c=b[0]+g>p?b[0]+g:p-r+c),k>t&&(d=k-t+d),t+e>k+j&&(d=k+j-t+d-e);break;case"w":c=b[0]+g,d=b[1]-e/2,r=v(o),t=u(o),r+h>i&&(c=b[0]-h-g),k>t&&(d=k+5),t+e>k+j&&(d=k+j-t+d-e);break;case"n":c=b[0]-h/2-5,d=b[1]+g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),t+e>k+j&&(d=k+j-t+d-e);break;case"s":c=b[0]-h/2,d=b[1]-e-g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),k>t&&(d=k);break;case"none":c=b[0],d=b[1]-g,r=v(o),t=u(o)}c-=q.left,d-=q.top;var w=o.getBoundingClientRect(),k=window.pageYOffset||document.documentElement.scrollTop,p=window.pageXOffset||document.documentElement.scrollLeft,x="translate("+(w.left+p)+"px, "+(w.top+k)+"px)",y="translate("+c+"px, "+d+"px)",z=d3.interpolateString(x,y),A=n.style("opacity")<.1;l?n.transition().delay(m).duration(0).style("opacity",0):n.interrupt().transition().duration(A?0:s).styleTween("transform",function(){return z},"important").style("-webkit-transform",y).style("opacity",1)})};return d.nvPointerEventsClass=w,d.options=a.utils.optionsFunc.bind(d),d._options=Object.create({},{duration:{get:function(){return s},set:function(a){s=a}},gravity:{get:function(){return f},set:function(a){f=a}},distance:{get:function(){return g},set:function(a){g=a}},snapDistance:{get:function(){return h},set:function(a){h=a}},classes:{get:function(){return j},set:function(a){j=a}},chartContainer:{get:function(){return k},set:function(a){k=a}},fixedTop:{get:function(){return i},set:function(a){i=a}},enabled:{get:function(){return r},set:function(a){r=a}},hideDelay:{get:function(){return m},set:function(a){m=a}},contentGenerator:{get:function(){return A},set:function(a){A=a}},valueFormatter:{get:function(){return x},set:function(a){x=a}},headerFormatter:{get:function(){return y},set:function(a){y=a}},keyFormatter:{get:function(){return z},set:function(a){z=a}},headerEnabled:{get:function(){return t},set:function(a){t=a}},_isInteractiveLayer:{get:function(){return u},set:function(a){u=!!a}},position:{get:function(){return p},set:function(a){p.left=void 0!==a.left?a.left:p.left,p.top=void 0!==a.top?a.top:p.top}},offset:{get:function(){return q},set:function(a){q.left=void 0!==a.left?a.left:q.left,q.top=void 0!==a.top?a.top:q.top}},hidden:{get:function(){return l},set:function(a){l!=a&&(l=!!a,d())}},data:{get:function(){return e},set:function(a){a.point&&(a.value=a.point.x,a.series=a.series||{},a.series.value=a.point.y,a.series.color=a.point.color||a.series.color),e=a}},tooltipElem:{get:function(){return o},set:function(){}},id:{get:function(){return v},set:function(){}}}),a.utils.initOptions(d),d}}(),a.utils.windowSize=function(){var a={width:640,height:480};return window.innerWidth&&window.innerHeight?(a.width=window.innerWidth,a.height=window.innerHeight,a):"CSS1Compat"==document.compatMode&&document.documentElement&&document.documentElement.offsetWidth?(a.width=document.documentElement.offsetWidth,a.height=document.documentElement.offsetHeight,a):document.body&&document.body.offsetWidth?(a.width=document.body.offsetWidth,a.height=document.body.offsetHeight,a):a},a.utils.windowResize=function(b){return window.addEventListener?window.addEventListener("resize",b):a.log("ERROR: Failed to bind to window.resize with: ",b),{callback:b,clear:function(){window.removeEventListener("resize",b)}}},a.utils.getColor=function(b){if(void 0===b)return a.utils.defaultColor();if(Array.isArray(b)){var c=d3.scale.ordinal().range(b);return function(a,b){var d=void 0===b?a:b;return a.color||c(d)}}return b},a.utils.defaultColor=function(){return a.utils.getColor(d3.scale.category20().range())},a.utils.customTheme=function(a,b,c){b=b||function(a){return a.key},c=c||d3.scale.category20().range();var d=c.length;return function(e){var f=b(e);return"function"==typeof a[f]?a[f]():void 0!==a[f]?a[f]:(d||(d=c.length),d-=1,c[d])}},a.utils.pjax=function(b,c){var d=function(d){d3.html(d,function(d){var e=d3.select(c).node();e.parentNode.replaceChild(d3.select(d).select(c).node(),e),a.utils.pjax(b,c)})};d3.selectAll(b).on("click",function(){history.pushState(this.href,this.textContent,this.href),d(this.href),d3.event.preventDefault()}),d3.select(window).on("popstate",function(){d3.event.state&&d(d3.event.state)})},a.utils.calcApproxTextWidth=function(a){if("function"==typeof a.style&&"function"==typeof a.text){var b=parseInt(a.style("font-size").replace("px",""),10),c=a.text().length;return c*b*.5}return 0},a.utils.NaNtoZero=function(a){return"number"!=typeof a||isNaN(a)||null===a||1/0===a||a===-1/0?0:a},d3.selection.prototype.watchTransition=function(a){var b=[this].concat([].slice.call(arguments,1));return a.transition.apply(a,b)},a.utils.renderWatch=function(b,c){if(!(this instanceof a.utils.renderWatch))return new a.utils.renderWatch(b,c);var d=void 0!==c?c:250,e=[],f=this;this.models=function(a){return a=[].slice.call(arguments,0),a.forEach(function(a){a.__rendered=!1,function(a){a.dispatch.on("renderEnd",function(){a.__rendered=!0,f.renderEnd("model")})}(a),e.indexOf(a)<0&&e.push(a)}),this},this.reset=function(a){void 0!==a&&(d=a),e=[]},this.transition=function(a,b,c){if(b=arguments.length>1?[].slice.call(arguments,1):[],c=b.length>1?b.pop():void 0!==d?d:250,a.__rendered=!1,e.indexOf(a)<0&&e.push(a),0===c)return a.__rendered=!0,a.delay=function(){return this},a.duration=function(){return this},a;a.__rendered=0===a.length?!0:a.every(function(a){return!a.length})?!0:!1;var g=0;return a.transition().duration(c).each(function(){++g}).each("end",function(){0===--g&&(a.__rendered=!0,f.renderEnd.apply(this,b))})},this.renderEnd=function(){e.every(function(a){return a.__rendered})&&(e.forEach(function(a){a.__rendered=!1}),b.renderEnd.apply(this,arguments))}},a.utils.deepExtend=function(b){var c=arguments.length>1?[].slice.call(arguments,1):[];c.forEach(function(c){for(var d in c){var e=b[d]instanceof Array,f="object"==typeof b[d],g="object"==typeof c[d];f&&!e&&g?a.utils.deepExtend(b[d],c[d]):b[d]=c[d]}})},a.utils.state=function(){if(!(this instanceof a.utils.state))return new a.utils.state;var b={},c=function(){},d=function(){return{}},e=null,f=null;this.dispatch=d3.dispatch("change","set"),this.dispatch.on("set",function(a){c(a,!0)}),this.getter=function(a){return d=a,this},this.setter=function(a,b){return b||(b=function(){}),c=function(c,d){a(c),d&&b()},this},this.init=function(b){e=e||{},a.utils.deepExtend(e,b)};var g=function(){var a=d();if(JSON.stringify(a)===JSON.stringify(b))return!1;for(var c in a)void 0===b[c]&&(b[c]={}),b[c]=a[c],f=!0;return!0};this.update=function(){e&&(c(e,!1),e=null),g.call(this)&&this.dispatch.change(b)}},a.utils.optionsFunc=function(a){return a&&d3.map(a).forEach(function(a,b){"function"==typeof this[a]&&this[a](b)}.bind(this)),this},a.utils.calcTicksX=function(b,c){var d=1,e=0;for(e;ed?f:d}return a.log("Requested number of ticks: ",b),a.log("Calculated max values to be: ",d),b=b>d?b=d-1:b,b=1>b?1:b,b=Math.floor(b),a.log("Calculating tick count as: ",b),b},a.utils.calcTicksY=function(b,c){return a.utils.calcTicksX(b,c)},a.utils.initOption=function(a,b){a._calls&&a._calls[b]?a[b]=a._calls[b]:(a[b]=function(c){return arguments.length?(a._overrides[b]=!0,a._options[b]=c,a):a._options[b]},a["_"+b]=function(c){return arguments.length?(a._overrides[b]||(a._options[b]=c),a):a._options[b]})},a.utils.initOptions=function(b){b._overrides=b._overrides||{};var c=Object.getOwnPropertyNames(b._options||{}),d=Object.getOwnPropertyNames(b._calls||{});c=c.concat(d);for(var e in c)a.utils.initOption(b,c[e])},a.utils.inheritOptionsD3=function(a,b,c){a._d3options=c.concat(a._d3options||[]),c.unshift(b),c.unshift(a),d3.rebind.apply(this,c)},a.utils.arrayUnique=function(a){return a.sort().filter(function(b,c){return!c||b!=a[c-1]})},a.utils.symbolMap=d3.map(),a.utils.symbol=function(){function b(b,e){var f=c.call(this,b,e),g=d.call(this,b,e);return-1!==d3.svg.symbolTypes.indexOf(f)?d3.svg.symbol().type(f).size(g)():a.utils.symbolMap.get(f)(g)}var c,d=64;return b.type=function(a){return arguments.length?(c=d3.functor(a),b):c},b.size=function(a){return arguments.length?(d=d3.functor(a),b):d},b},a.utils.inheritOptions=function(b,c){var d=Object.getOwnPropertyNames(c._options||{}),e=Object.getOwnPropertyNames(c._calls||{}),f=c._inherited||[],g=c._d3options||[],h=d.concat(e).concat(f).concat(g);h.unshift(c),h.unshift(b),d3.rebind.apply(this,h),b._inherited=a.utils.arrayUnique(d.concat(e).concat(f).concat(d).concat(b._inherited||[])),b._d3options=a.utils.arrayUnique(g.concat(b._d3options||[]))},a.utils.initSVG=function(a){a.classed({"nvd3-svg":!0})},a.utils.sanitizeHeight=function(a,b){return a||parseInt(b.style("height"),10)||400},a.utils.sanitizeWidth=function(a,b){return a||parseInt(b.style("width"),10)||960},a.utils.availableHeight=function(b,c,d){return a.utils.sanitizeHeight(b,c)-d.top-d.bottom},a.utils.availableWidth=function(b,c,d){return a.utils.sanitizeWidth(b,c)-d.left-d.right},a.utils.noData=function(b,c){var d=b.options(),e=d.margin(),f=d.noData(),g=null==f?["No Data Available."]:[f],h=a.utils.availableHeight(d.height(),c,e),i=a.utils.availableWidth(d.width(),c,e),j=e.left+i/2,k=e.top+h/2;c.selectAll("g").remove();var l=c.selectAll(".nv-noData").data(g);l.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),l.attr("x",j).attr("y",k).text(function(a){return a})},a.models.axis=function(){"use strict";function b(g){return s.reset(),g.each(function(b){var g=d3.select(this);a.utils.initSVG(g);var p=g.selectAll("g.nv-wrap.nv-axis").data([b]),q=p.enter().append("g").attr("class","nvd3 nv-wrap nv-axis"),t=(q.append("g"),p.select("g"));null!==n?c.ticks(n):("top"==c.orient()||"bottom"==c.orient())&&c.ticks(Math.abs(d.range()[1]-d.range()[0])/100),t.watchTransition(s,"axis").call(c),r=r||c.scale();var u=c.tickFormat();null==u&&(u=r.tickFormat());var v=t.selectAll("text.nv-axislabel").data([h||null]);v.exit().remove();var w,x,y;switch(c.orient()){case"top":v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",0).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b))+",0)"}).select("text").attr("dy","-0.5em").attr("y",-c.tickPadding()).attr("text-anchor","middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max top").attr("transform",function(b,c){return"translate("+a.utils.NaNtoZero(d.range()[c])+",0)"}));break;case"bottom":w=o+36;var z=30,A=0,B=t.selectAll("g").select("text"),C="";if(j%360){B.each(function(){var a=this.getBoundingClientRect(),b=a.width;A=a.height,b>z&&(z=b)}),C="rotate("+j+" 0,"+(A/2+c.tickPadding())+")";var D=Math.abs(Math.sin(j*Math.PI/180));w=(D?D*z:z)+30,B.attr("transform",C).style("text-anchor",j%360>0?"start":"end")}v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",w).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data([d.domain()[0],d.domain()[d.domain().length-1]]),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"}).select("text").attr("dy",".71em").attr("y",c.tickPadding()).attr("transform",C).style("text-anchor",j?j%360>0?"start":"end":"middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max bottom").attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"})),l&&B.attr("transform",function(a,b){return"translate(0,"+(b%2==0?"0":"12")+")"});break;case"right":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"begin").attr("transform",k?"rotate(90)":"").attr("y",k?-Math.max(e.right,f)+12:-10).attr("x",k?d3.max(d.range())/2:c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(d(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",c.tickPadding()).style("text-anchor","start").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1));break;case"left":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"end").attr("transform",k?"rotate(-90)":"").attr("y",k?-Math.max(e.left,f)+25-(o||0):-10).attr("x",k?-d3.max(d.range())/2:-c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(r(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",-c.tickPadding()).attr("text-anchor","end").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1))}if(v.text(function(a){return a}),!i||"left"!==c.orient()&&"right"!==c.orient()||(t.selectAll("g").each(function(a){d3.select(this).select("text").attr("opacity",1),(d(a)d.range()[0]-10)&&((a>1e-10||-1e-10>a)&&d3.select(this).attr("opacity",0),d3.select(this).select("text").attr("opacity",0))}),d.domain()[0]==d.domain()[1]&&0==d.domain()[0]&&p.selectAll("g.nv-axisMaxMin").style("opacity",function(a,b){return b?0:1})),i&&("top"===c.orient()||"bottom"===c.orient())){var E=[];p.selectAll("g.nv-axisMaxMin").each(function(a,b){try{E.push(b?d(a)-this.getBoundingClientRect().width-4:d(a)+this.getBoundingClientRect().width+4)}catch(c){E.push(b?d(a)-4:d(a)+4)}}),t.selectAll("g").each(function(a){(d(a)E[1])&&(a>1e-10||-1e-10>a?d3.select(this).remove():d3.select(this).select("text").remove())})}t.selectAll(".tick").filter(function(a){return!parseFloat(Math.round(1e5*a)/1e6)&&void 0!==a}).classed("zero",!0),r=d.copy()}),s.renderEnd("axis immediate"),b}var c=d3.svg.axis(),d=d3.scale.linear(),e={top:0,right:0,bottom:0,left:0},f=75,g=60,h=null,i=!0,j=0,k=!0,l=!1,m=!1,n=null,o=0,p=250,q=d3.dispatch("renderEnd");c.scale(d).orient("bottom").tickFormat(function(a){return a});var r,s=a.utils.renderWatch(q,p);return b.axis=c,b.dispatch=q,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{axisLabelDistance:{get:function(){return o},set:function(a){o=a}},staggerLabels:{get:function(){return l},set:function(a){l=a}},rotateLabels:{get:function(){return j},set:function(a){j=a}},rotateYLabel:{get:function(){return k},set:function(a){k=a}},showMaxMin:{get:function(){return i},set:function(a){i=a}},axisLabel:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return g},set:function(a){g=a}},ticks:{get:function(){return n},set:function(a){n=a}},width:{get:function(){return f},set:function(a){f=a}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},duration:{get:function(){return p},set:function(a){p=a,s.reset(p)}},scale:{get:function(){return d},set:function(e){d=e,c.scale(d),m="function"==typeof d.rangeBands,a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"])}}}),a.utils.initOptions(b),a.utils.inheritOptionsD3(b,c,["orient","tickValues","tickSubdivide","tickSize","tickPadding","tickFormat"]),a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"]),b},a.models.boxPlot=function(){"use strict";function b(l){return v.reset(),l.each(function(b){var l=j-i.left-i.right,p=k-i.top-i.bottom;r=d3.select(this),a.utils.initSVG(r),m.domain(c||b.map(function(a,b){return o(a,b)})).rangeBands(e||[0,l],.1);var w=[];if(!d){var x=d3.min(b.map(function(a){var b=[];return b.push(a.values.Q1),a.values.hasOwnProperty("whisker_low")&&null!==a.values.whisker_low&&b.push(a.values.whisker_low),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.min(b)})),y=d3.max(b.map(function(a){var b=[];return b.push(a.values.Q3),a.values.hasOwnProperty("whisker_high")&&null!==a.values.whisker_high&&b.push(a.values.whisker_high),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.max(b)}));w=[x,y]}n.domain(d||w),n.range(f||[p,0]),g=g||m,h=h||n.copy().range([n(0),n(0)]);{var z=r.selectAll("g.nv-wrap").data([b]);z.enter().append("g").attr("class","nvd3 nv-wrap")}z.attr("transform","translate("+i.left+","+i.top+")");var A=z.selectAll(".nv-boxplot").data(function(a){return a}),B=A.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);A.attr("class","nv-boxplot").attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}).classed("hover",function(a){return a.hover}),A.watchTransition(v,"nv-boxplot: boxplots").style("stroke-opacity",1).style("fill-opacity",.75).delay(function(a,c){return c*t/b.length}).attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}),A.exit().remove(),B.each(function(a,b){var c=d3.select(this);["low","high"].forEach(function(d){a.values.hasOwnProperty("whisker_"+d)&&null!==a.values["whisker_"+d]&&(c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-whisker nv-boxplot-"+d),c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-tick nv-boxplot-"+d))})});var C=A.selectAll(".nv-boxplot-outlier").data(function(a){return a.values.hasOwnProperty("outliers")&&null!==a.values.outliers?a.values.outliers:[]});C.enter().append("circle").style("fill",function(a,b,c){return q(a,c)}).style("stroke",function(a,b,c){return q(a,c)}).on("mouseover",function(a,b,c){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:a,color:q(a,c)},e:d3.event})}).on("mouseout",function(a,b,c){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:a,color:q(a,c)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),C.attr("class","nv-boxplot-outlier"),C.watchTransition(v,"nv-boxplot: nv-boxplot-outlier").attr("cx",.45*m.rangeBand()).attr("cy",function(a){return n(a)}).attr("r","3"),C.exit().remove();var D=function(){return null===u?.9*m.rangeBand():Math.min(75,.9*m.rangeBand())},E=function(){return.45*m.rangeBand()-D()/2},F=function(){return.45*m.rangeBand()+D()/2};["low","high"].forEach(function(a){var b="low"===a?"Q1":"Q3";A.select("line.nv-boxplot-whisker.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",.45*m.rangeBand()).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",.45*m.rangeBand()).attr("y2",function(a){return n(a.values[b])}),A.select("line.nv-boxplot-tick.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",E).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",F).attr("y2",function(b){return n(b.values["whisker_"+a])})}),["low","high"].forEach(function(a){B.selectAll(".nv-boxplot-"+a).on("mouseover",function(b,c,d){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mouseout",function(b,c,d){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})})}),B.append("rect").attr("class","nv-boxplot-box").on("mouseover",function(a,b){d3.select(this).classed("hover",!0),s.elementMouseover({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),s.elementMouseout({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),A.select("rect.nv-boxplot-box").watchTransition(v,"nv-boxplot: boxes").attr("y",function(a){return n(a.values.Q3)}).attr("width",D).attr("x",E).attr("height",function(a){return Math.abs(n(a.values.Q3)-n(a.values.Q1))||1}).style("fill",function(a,b){return a.color||q(a,b)}).style("stroke",function(a,b){return a.color||q(a,b)}),B.append("line").attr("class","nv-boxplot-median"),A.select("line.nv-boxplot-median").watchTransition(v,"nv-boxplot: boxplots line").attr("x1",E).attr("y1",function(a){return n(a.values.Q2)}).attr("x2",F).attr("y2",function(a){return n(a.values.Q2)}),g=m.copy(),h=n.copy()}),v.renderEnd("nv-boxplot immediate"),b}var c,d,e,f,g,h,i={top:0,right:0,bottom:0,left:0},j=960,k=500,l=Math.floor(1e4*Math.random()),m=d3.scale.ordinal(),n=d3.scale.linear(),o=function(a){return a.x},p=function(a){return a.y},q=a.utils.defaultColor(),r=null,s=d3.dispatch("elementMouseover","elementMouseout","elementMousemove","renderEnd"),t=250,u=null,v=a.utils.renderWatch(s,t);return b.dispatch=s,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},maxBoxWidth:{get:function(){return u},set:function(a){u=a}},x:{get:function(){return o},set:function(a){o=a}},y:{get:function(){return p},set:function(a){p=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return l},set:function(a){l=a}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}},duration:{get:function(){return t},set:function(a){t=a,v.reset(t)}}}),a.utils.initOptions(b),b},a.models.boxPlotChart=function(){"use strict";function b(k){return t.reset(),t.models(e),l&&t.models(f),m&&t.models(g),k.each(function(k){var p=d3.select(this);a.utils.initSVG(p);var t=(i||parseInt(p.style("width"))||960)-h.left-h.right,u=(j||parseInt(p.style("height"))||400)-h.top-h.bottom;if(b.update=function(){r.beforeUpdate(),p.transition().duration(s).call(b)},b.container=this,!(k&&k.length&&k.filter(function(a){return a.values.hasOwnProperty("Q1")&&a.values.hasOwnProperty("Q2")&&a.values.hasOwnProperty("Q3")}).length)){var v=p.selectAll(".nv-noData").data([q]);return v.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),v.attr("x",h.left+t/2).attr("y",h.top+u/2).text(function(a){return a}),b}p.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var w=p.selectAll("g.nv-wrap.nv-boxPlotWithAxes").data([k]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-boxPlotWithAxes").append("g"),y=x.append("defs"),z=w.select("g"); -x.append("g").attr("class","nv-x nv-axis"),x.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),x.append("g").attr("class","nv-barsWrap"),z.attr("transform","translate("+h.left+","+h.top+")"),n&&z.select(".nv-y.nv-axis").attr("transform","translate("+t+",0)"),e.width(t).height(u);var A=z.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));if(A.transition().call(e),y.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),z.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(o?2:1)).attr("height",16).attr("x",-c.rangeBand()/(o?1:2)),l){f.scale(c).ticks(a.utils.calcTicksX(t/100,k)).tickSize(-u,0),z.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),z.select(".nv-x.nv-axis").call(f);var B=z.select(".nv-x.nv-axis").selectAll("g");o&&B.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}m&&(g.scale(d).ticks(Math.floor(u/36)).tickSize(-t,0),z.select(".nv-y.nv-axis").call(g)),z.select(".nv-zeroLine line").attr("x1",0).attr("x2",t).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("nv-boxplot chart immediate"),b}var c,d,e=a.models.boxPlot(),f=a.models.axis(),g=a.models.axis(),h={top:15,right:10,bottom:50,left:60},i=null,j=null,k=a.utils.getColor(),l=!0,m=!0,n=!1,o=!1,p=a.models.tooltip(),q="No Data Available.",r=d3.dispatch("tooltipShow","tooltipHide","beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(n?"right":"left").tickFormat(d3.format(",.1f")),p.duration(0);var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){p.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(a){p.data(a).hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){p.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.boxplot=e,b.xAxis=f,b.yAxis=g,b.tooltip=p,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},staggerLabels:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return l},set:function(a){l=a}},showYAxis:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return tooltips},set:function(a){tooltips=a}},tooltipContent:{get:function(){return p},set:function(a){p=a}},noData:{get:function(){return q},set:function(a){q=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!==a.top?a.top:h.top,h.right=void 0!==a.right?a.right:h.right,h.bottom=void 0!==a.bottom?a.bottom:h.bottom,h.left=void 0!==a.left?a.left:h.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}},rightAlignYAxis:{get:function(){return n},set:function(a){n=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.bullet=function(){"use strict";function b(d){return d.each(function(b,d){var p=m-c.left-c.right,s=n-c.top-c.bottom;o=d3.select(this),a.utils.initSVG(o);{var t=f.call(this,b,d).slice().sort(d3.descending),u=g.call(this,b,d).slice().sort(d3.descending),v=h.call(this,b,d).slice().sort(d3.descending),w=i.call(this,b,d).slice(),x=j.call(this,b,d).slice(),y=k.call(this,b,d).slice(),z=d3.scale.linear().domain(d3.extent(d3.merge([l,t]))).range(e?[p,0]:[0,p]);this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range())}this.__chart__=z;var A=d3.min(t),B=d3.max(t),C=t[1],D=o.selectAll("g.nv-wrap.nv-bullet").data([b]),E=D.enter().append("g").attr("class","nvd3 nv-wrap nv-bullet"),F=E.append("g"),G=D.select("g");F.append("rect").attr("class","nv-range nv-rangeMax"),F.append("rect").attr("class","nv-range nv-rangeAvg"),F.append("rect").attr("class","nv-range nv-rangeMin"),F.append("rect").attr("class","nv-measure"),D.attr("transform","translate("+c.left+","+c.top+")");var H=function(a){return Math.abs(z(a)-z(0))},I=function(a){return z(0>a?a:0)};G.select("rect.nv-rangeMax").attr("height",s).attr("width",H(B>0?B:A)).attr("x",I(B>0?B:A)).datum(B>0?B:A),G.select("rect.nv-rangeAvg").attr("height",s).attr("width",H(C)).attr("x",I(C)).datum(C),G.select("rect.nv-rangeMin").attr("height",s).attr("width",H(B)).attr("x",I(B)).attr("width",H(B>0?A:B)).attr("x",I(B>0?A:B)).datum(B>0?A:B),G.select("rect.nv-measure").style("fill",q).attr("height",s/3).attr("y",s/3).attr("width",0>v?z(0)-z(v[0]):z(v[0])-z(0)).attr("x",I(v)).on("mouseover",function(){r.elementMouseover({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mouseout",function(){r.elementMouseout({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})});var J=s/6,K=u.map(function(a,b){return{value:a,label:x[b]}});F.selectAll("path.nv-markerTriangle").data(K).enter().append("path").attr("class","nv-markerTriangle").attr("transform",function(a){return"translate("+z(a.value)+","+s/2+")"}).attr("d","M0,"+J+"L"+J+","+-J+" "+-J+","+-J+"Z").on("mouseover",function(a){r.elementMouseover({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill"),pos:[z(a.value),s/2]})}).on("mousemove",function(a){r.elementMousemove({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a){r.elementMouseout({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}),D.selectAll(".nv-range").on("mouseover",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseover({value:a,label:c,color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseout({value:a,label:c,color:d3.select(this).style("fill")})})}),b}var c={top:0,right:0,bottom:0,left:0},d="left",e=!1,f=function(a){return a.ranges},g=function(a){return a.markers?a.markers:[0]},h=function(a){return a.measures},i=function(a){return a.rangeLabels?a.rangeLabels:[]},j=function(a){return a.markerLabels?a.markerLabels:[]},k=function(a){return a.measureLabels?a.measureLabels:[]},l=[0],m=380,n=30,o=null,p=null,q=a.utils.getColor(["#1f77b4"]),r=d3.dispatch("elementMouseover","elementMouseout","elementMousemove");return b.dispatch=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return f},set:function(a){f=a}},markers:{get:function(){return g},set:function(a){g=a}},measures:{get:function(){return h},set:function(a){h=a}},forceX:{get:function(){return l},set:function(a){l=a}},width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},tickFormat:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},orient:{get:function(){return d},set:function(a){d=a,e="right"==d||"bottom"==d}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.bulletChart=function(){"use strict";function b(d){return d.each(function(e,o){var p=d3.select(this);a.utils.initSVG(p);var q=a.utils.availableWidth(k,p,g),r=l-g.top-g.bottom;if(b.update=function(){b(d)},b.container=this,!e||!h.call(this,e,o))return a.utils.noData(b,p),b;p.selectAll(".nv-noData").remove();var s=h.call(this,e,o).slice().sort(d3.descending),t=i.call(this,e,o).slice().sort(d3.descending),u=j.call(this,e,o).slice().sort(d3.descending),v=p.selectAll("g.nv-wrap.nv-bulletChart").data([e]),w=v.enter().append("g").attr("class","nvd3 nv-wrap nv-bulletChart"),x=w.append("g"),y=v.select("g");x.append("g").attr("class","nv-bulletWrap"),x.append("g").attr("class","nv-titles"),v.attr("transform","translate("+g.left+","+g.top+")");var z=d3.scale.linear().domain([0,Math.max(s[0],t[0],u[0])]).range(f?[q,0]:[0,q]),A=this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range());this.__chart__=z;var B=x.select(".nv-titles").append("g").attr("text-anchor","end").attr("transform","translate(-6,"+(l-g.top-g.bottom)/2+")");B.append("text").attr("class","nv-title").text(function(a){return a.title}),B.append("text").attr("class","nv-subtitle").attr("dy","1em").text(function(a){return a.subtitle}),c.width(q).height(r);var C=y.select(".nv-bulletWrap");d3.transition(C).call(c);var D=m||z.tickFormat(q/100),E=y.selectAll("g.nv-tick").data(z.ticks(n?n:q/50),function(a){return this.textContent||D(a)}),F=E.enter().append("g").attr("class","nv-tick").attr("transform",function(a){return"translate("+A(a)+",0)"}).style("opacity",1e-6);F.append("line").attr("y1",r).attr("y2",7*r/6),F.append("text").attr("text-anchor","middle").attr("dy","1em").attr("y",7*r/6).text(D);var G=d3.transition(E).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1);G.select("line").attr("y1",r).attr("y2",7*r/6),G.select("text").attr("y",7*r/6),d3.transition(E.exit()).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1e-6).remove()}),d3.timer.flush(),b}var c=a.models.bullet(),d=a.models.tooltip(),e="left",f=!1,g={top:5,right:40,bottom:20,left:120},h=function(a){return a.ranges},i=function(a){return a.markers?a.markers:[0]},j=function(a){return a.measures},k=null,l=55,m=null,n=null,o=null,p=d3.dispatch("tooltipShow","tooltipHide");return d.duration(0).headerEnabled(!1),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.label,value:a.value,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.bullet=c,b.dispatch=p,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return h},set:function(a){h=a}},markers:{get:function(){return i},set:function(a){i=a}},measures:{get:function(){return j},set:function(a){j=a}},width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},tickFormat:{get:function(){return m},set:function(a){m=a}},ticks:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return o},set:function(a){o=a}},tooltips:{get:function(){return d.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),d.enabled(!!b)}},tooltipContent:{get:function(){return d.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),d.contentGenerator(b)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},orient:{get:function(){return e},set:function(a){e=a,f="right"==e||"bottom"==e}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.candlestickBar=function(){"use strict";function b(x){return x.each(function(b){c=d3.select(this);var x=a.utils.availableWidth(i,c,h),y=a.utils.availableHeight(j,c,h);a.utils.initSVG(c);var A=x/b[0].values.length*.45;l.domain(d||d3.extent(b[0].values.map(n).concat(t))),l.range(v?f||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:f||[5+A/2,x-A/2-5]),m.domain(e||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(g||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var B=d3.select(this).selectAll("g.nv-wrap.nv-candlestickBar").data([b[0].values]),C=B.enter().append("g").attr("class","nvd3 nv-wrap nv-candlestickBar"),D=C.append("defs"),E=C.append("g"),F=B.select("g");E.append("g").attr("class","nv-ticks"),B.attr("transform","translate("+h.left+","+h.top+")"),c.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:k})}),D.append("clipPath").attr("id","nv-chart-clip-path-"+k).append("rect"),B.select("#nv-chart-clip-path-"+k+" rect").attr("width",x).attr("height",y),F.attr("clip-path",w?"url(#nv-chart-clip-path-"+k+")":"");var G=B.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});G.exit().remove();{var H=G.enter().append("g").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b});H.append("line").attr("class","nv-candlestick-lines").attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),H.append("rect").attr("class","nv-candlestick-rects nv-bars").attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}c.selectAll(".nv-candlestick-lines").transition().attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),c.selectAll(".nv-candlestick-rects").transition().attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}),b}var c,d,e,f,g,h={top:0,right:0,bottom:0,left:0},i=null,j=null,k=Math.floor(1e4*Math.random()),l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,d){b.clearHighlights(),c.select(".nv-candlestickBar .nv-tick-0-"+a).classed("hover",d)},b.clearHighlights=function(){c.select(".nv-candlestickBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return k},set:function(a){k=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!=a.top?a.top:h.top,h.right=void 0!=a.right?a.right:h.right,h.bottom=void 0!=a.bottom?a.bottom:h.bottom,h.left=void 0!=a.left?a.left:h.left}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.cumulativeLineChart=function(){"use strict";function b(l){return H.reset(),H.models(f),r&&H.models(g),s&&H.models(h),l.each(function(l){function A(){d3.select(b.container).style("cursor","ew-resize")}function E(){G.x=d3.event.x,G.i=Math.round(F.invert(G.x)),K()}function H(){d3.select(b.container).style("cursor","auto"),y.index=G.i,C.stateChange(y)}function K(){bb.data([G]);var a=b.duration();b.duration(0),b.update(),b.duration(a)}var L=d3.select(this);a.utils.initSVG(L),L.classed("nv-chart-"+x,!0);var M=this,N=a.utils.availableWidth(o,L,m),O=a.utils.availableHeight(p,L,m);if(b.update=function(){0===D?L.call(b):L.transition().duration(D).call(b)},b.container=this,y.setter(J(l),b.update).getter(I(l)).update(),y.disabled=l.map(function(a){return!!a.disabled}),!z){var P;z={};for(P in y)z[P]=y[P]instanceof Array?y[P].slice(0):y[P]}var Q=d3.behavior.drag().on("dragstart",A).on("drag",E).on("dragend",H);if(!(l&&l.length&&l.filter(function(a){return a.values.length}).length))return a.utils.noData(b,L),b;if(L.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale(),w)f.yDomain(null);else{var R=l.filter(function(a){return!a.disabled}).map(function(a){var b=d3.extent(a.values,f.y());return b[0]<-.95&&(b[0]=-.95),[(b[0]-b[1])/(1+b[1]),(b[1]-b[0])/(1+b[0])]}),S=[d3.min(R,function(a){return a[0]}),d3.max(R,function(a){return a[1]})];f.yDomain(S)}F.domain([0,l[0].values.length-1]).range([0,N]).clamp(!0);var l=c(G.i,l),T=v?"none":"all",U=L.selectAll("g.nv-wrap.nv-cumulativeLine").data([l]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-cumulativeLine").append("g"),W=U.select("g");if(V.append("g").attr("class","nv-interactive"),V.append("g").attr("class","nv-x nv-axis").style("pointer-events","none"),V.append("g").attr("class","nv-y nv-axis"),V.append("g").attr("class","nv-background"),V.append("g").attr("class","nv-linesWrap").style("pointer-events",T),V.append("g").attr("class","nv-avgLinesWrap").style("pointer-events","none"),V.append("g").attr("class","nv-legendWrap"),V.append("g").attr("class","nv-controlsWrap"),q&&(i.width(N),W.select(".nv-legendWrap").datum(l).call(i),m.top!=i.height()&&(m.top=i.height(),O=a.utils.availableHeight(p,L,m)),W.select(".nv-legendWrap").attr("transform","translate(0,"+-m.top+")")),u){var X=[{key:"Re-scale y-axis",disabled:!w}];j.width(140).color(["#444","#444","#444"]).rightAlign(!1).margin({top:5,right:0,bottom:5,left:20}),W.select(".nv-controlsWrap").datum(X).attr("transform","translate(0,"+-m.top+")").call(j)}U.attr("transform","translate("+m.left+","+m.top+")"),t&&W.select(".nv-y.nv-axis").attr("transform","translate("+N+",0)");var Y=l.filter(function(a){return a.tempDisabled});U.select(".tempDisabled").remove(),Y.length&&U.append("text").attr("class","tempDisabled").attr("x",N/2).attr("y","-.71em").style("text-anchor","end").text(Y.map(function(a){return a.key}).join(", ")+" values cannot be calculated for this time period."),v&&(k.width(N).height(O).margin({left:m.left,top:m.top}).svgContainer(L).xScale(d),U.select(".nv-interactive").call(k)),V.select(".nv-background").append("rect"),W.select(".nv-background rect").attr("width",N).attr("height",O),f.y(function(a){return a.display.y}).width(N).height(O).color(l.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!l[b].disabled&&!l[b].tempDisabled}));var Z=W.select(".nv-linesWrap").datum(l.filter(function(a){return!a.disabled&&!a.tempDisabled}));Z.call(f),l.forEach(function(a,b){a.seriesIndex=b});var $=l.filter(function(a){return!a.disabled&&!!B(a)}),_=W.select(".nv-avgLinesWrap").selectAll("line").data($,function(a){return a.key}),ab=function(a){var b=e(B(a));return 0>b?0:b>O?O:b};_.enter().append("line").style("stroke-width",2).style("stroke-dasharray","10,10").style("stroke",function(a){return f.color()(a,a.seriesIndex)}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.style("stroke-opacity",function(a){var b=e(B(a));return 0>b||b>O?0:1}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.exit().remove();var bb=Z.selectAll(".nv-indexLine").data([G]);bb.enter().append("rect").attr("class","nv-indexLine").attr("width",3).attr("x",-2).attr("fill","red").attr("fill-opacity",.5).style("pointer-events","all").call(Q),bb.attr("transform",function(a){return"translate("+F(a.i)+",0)"}).attr("height",O),r&&(g.scale(d)._ticks(a.utils.calcTicksX(N/70,l)).tickSize(-O,0),W.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),W.select(".nv-x.nv-axis").call(g)),s&&(h.scale(e)._ticks(a.utils.calcTicksY(O/36,l)).tickSize(-N,0),W.select(".nv-y.nv-axis").call(h)),W.select(".nv-background rect").on("click",function(){G.x=d3.mouse(this)[0],G.i=Math.round(F.invert(G.x)),y.index=G.i,C.stateChange(y),K()}),f.dispatch.on("elementClick",function(a){G.i=a.pointIndex,G.x=F(G.i),y.index=G.i,C.stateChange(y),K()}),j.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,w=!a.disabled,y.rescaleY=w,C.stateChange(y),b.update()}),i.dispatch.on("stateChange",function(a){for(var c in a)y[c]=a[c];C.stateChange(y),b.update()}),k.dispatch.on("elementMousemove",function(c){f.clearHighlights();var d,e,i,j=[];if(l.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g,h){e=a.interactiveBisect(g.values,c.pointXValue,b.x()),f.highlightPoint(h,e,!0);var k=g.values[e];"undefined"!=typeof k&&("undefined"==typeof d&&(d=k),"undefined"==typeof i&&(i=b.xScale()(b.x()(k,e))),j.push({key:g.key,value:b.y()(k,e),color:n(g,g.seriesIndex)}))}),j.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(j.map(function(a){return a.value}),o,q);null!==r&&(j[r].highlight=!0)}var s=g.tickFormat()(b.x()(d,e),e);k.tooltip.position({left:i+m.left,top:c.mouseY+m.top}).chartContainer(M.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:s,series:j})(),k.renderGuideLine(i)}),k.dispatch.on("elementMouseout",function(){f.clearHighlights()}),C.on("changeState",function(a){"undefined"!=typeof a.disabled&&(l.forEach(function(b,c){b.disabled=a.disabled[c]}),y.disabled=a.disabled),"undefined"!=typeof a.index&&(G.i=a.index,G.x=F(G.i),y.index=a.index,bb.data([G])),"undefined"!=typeof a.rescaleY&&(w=a.rescaleY),b.update()})}),H.renderEnd("cumulativeLineChart immediate"),b}function c(a,b){return K||(K=f.y()),b.map(function(b){if(!b.values)return b;var c=b.values[a];if(null==c)return b;var d=K(c,a);return-.95>d&&!E?(b.tempDisabled=!0,b):(b.tempDisabled=!1,b.values=b.values.map(function(a,b){return a.display={y:(K(a,b)-d)/(1+d)},a}),b)})}var d,e,f=a.models.line(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.models.legend(),k=a.interactiveGuideline(),l=a.models.tooltip(),m={top:30,right:30,bottom:50,left:60},n=a.utils.defaultColor(),o=null,p=null,q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=!0,x=f.id(),y=a.utils.state(),z=null,A=null,B=function(a){return a.average},C=d3.dispatch("stateChange","changeState","renderEnd"),D=250,E=!1;y.index=0,y.rescaleY=w,g.orient("bottom").tickPadding(7),h.orient(t?"right":"left"),l.valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)}),j.updateState(!1);var F=d3.scale.linear(),G={i:0,x:0},H=a.utils.renderWatch(C,D),I=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),index:G.i,rescaleY:w}}},J=function(a){return function(b){void 0!==b.index&&(G.i=b.index),void 0!==b.rescaleY&&(w=b.rescaleY),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};f.dispatch.on("elementMouseover.tooltip",function(a){var c={x:b.x()(a.point),y:b.y()(a.point),color:a.point.color};a.point=c,l.data(a).position(a.pos).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){l.hidden(!0)});var K=null;return b.dispatch=C,b.lines=f,b.legend=i,b.controls=j,b.xAxis=g,b.yAxis=h,b.interactiveLayer=k,b.state=y,b.tooltip=l,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return o},set:function(a){o=a}},height:{get:function(){return p},set:function(a){p=a}},rescaleY:{get:function(){return w},set:function(a){w=a}},showControls:{get:function(){return u},set:function(a){u=a}},showLegend:{get:function(){return q},set:function(a){q=a}},average:{get:function(){return B},set:function(a){B=a}},defaultState:{get:function(){return z},set:function(a){z=a}},noData:{get:function(){return A},set:function(a){A=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},noErrorCheck:{get:function(){return E},set:function(a){E=a}},tooltips:{get:function(){return l.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),l.enabled(!!b)}},tooltipContent:{get:function(){return l.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),l.contentGenerator(b)}},margin:{get:function(){return m},set:function(a){m.top=void 0!==a.top?a.top:m.top,m.right=void 0!==a.right?a.right:m.right,m.bottom=void 0!==a.bottom?a.bottom:m.bottom,m.left=void 0!==a.left?a.left:m.left}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),i.color(n)}},useInteractiveGuideline:{get:function(){return v},set:function(a){v=a,a===!0&&(b.interactive(!1),b.useVoronoi(!1))}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,h.orient(a?"right":"left")}},duration:{get:function(){return D},set:function(a){D=a,f.duration(D),g.duration(D),h.duration(D),H.reset(D)}}}),a.utils.inheritOptions(b,f),a.utils.initOptions(b),b},a.models.discreteBar=function(){"use strict";function b(m){return y.reset(),m.each(function(b){var m=k-j.left-j.right,x=l-j.top-j.bottom;c=d3.select(this),a.utils.initSVG(c),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var z=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),y0:a.y0}})});n.domain(d||d3.merge(z).map(function(a){return a.x})).rangeBands(f||[0,m],.1),o.domain(e||d3.extent(d3.merge(z).map(function(a){return a.y}).concat(r))),o.range(t?g||[x-(o.domain()[0]<0?12:0),o.domain()[1]>0?12:0]:g||[x,0]),h=h||n,i=i||o.copy().range([o(0),o(0)]);{var A=c.selectAll("g.nv-wrap.nv-discretebar").data([b]),B=A.enter().append("g").attr("class","nvd3 nv-wrap nv-discretebar"),C=B.append("g");A.select("g")}C.append("g").attr("class","nv-groups"),A.attr("transform","translate("+j.left+","+j.top+")");var D=A.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});D.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),D.exit().watchTransition(y,"discreteBar: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),D.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),D.watchTransition(y,"discreteBar: groups").style("stroke-opacity",1).style("fill-opacity",.75);var E=D.selectAll("g.nv-bar").data(function(a){return a.values});E.exit().remove();var F=E.enter().append("g").attr("transform",function(a,b){return"translate("+(n(p(a,b))+.05*n.rangeBand())+", "+o(0)+")"}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),v.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),v.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){v.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){v.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()});F.append("rect").attr("height",0).attr("width",.9*n.rangeBand()/b.length),t?(F.append("text").attr("text-anchor","middle"),E.select("text").text(function(a,b){return u(q(a,b))}).watchTransition(y,"discreteBar: bars text").attr("x",.9*n.rangeBand()/2).attr("y",function(a,b){return q(a,b)<0?o(q(a,b))-o(0)+12:-4})):E.selectAll("text").remove(),E.attr("class",function(a,b){return q(a,b)<0?"nv-bar negative":"nv-bar positive"}).style("fill",function(a,b){return a.color||s(a,b)}).style("stroke",function(a,b){return a.color||s(a,b)}).select("rect").attr("class",w).watchTransition(y,"discreteBar: bars rect").attr("width",.9*n.rangeBand()/b.length),E.watchTransition(y,"discreteBar: bars").attr("transform",function(a,b){var c=n(p(a,b))+.05*n.rangeBand(),d=q(a,b)<0?o(0):o(0)-o(q(a,b))<1?o(0)-1:o(q(a,b));return"translate("+c+", "+d+")"}).select("rect").attr("height",function(a,b){return Math.max(Math.abs(o(q(a,b))-o(e&&e[0]||0))||1)}),h=n.copy(),i=o.copy()}),y.renderEnd("discreteBar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=d3.scale.ordinal(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=[0],s=a.utils.defaultColor(),t=!1,u=d3.format(",.2f"),v=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),w="discreteBar",x=250,y=a.utils.renderWatch(v,x);return b.dispatch=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},forceY:{get:function(){return r},set:function(a){r=a}},showValues:{get:function(){return t},set:function(a){t=a}},x:{get:function(){return p},set:function(a){p=a}},y:{get:function(){return q},set:function(a){q=a}},xScale:{get:function(){return n},set:function(a){n=a}},yScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},valueFormat:{get:function(){return u},set:function(a){u=a}},id:{get:function(){return m},set:function(a){m=a}},rectClass:{get:function(){return w},set:function(a){w=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b)}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x)}}}),a.utils.initOptions(b),b},a.models.discreteBarChart=function(){"use strict";function b(h){return t.reset(),t.models(e),m&&t.models(f),n&&t.models(g),h.each(function(h){var l=d3.select(this);a.utils.initSVG(l);var q=a.utils.availableWidth(j,l,i),t=a.utils.availableHeight(k,l,i);if(b.update=function(){r.beforeUpdate(),l.transition().duration(s).call(b)},b.container=this,!(h&&h.length&&h.filter(function(a){return a.values.length}).length))return a.utils.noData(b,l),b;l.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var u=l.selectAll("g.nv-wrap.nv-discreteBarWithAxes").data([h]),v=u.enter().append("g").attr("class","nvd3 nv-wrap nv-discreteBarWithAxes").append("g"),w=v.append("defs"),x=u.select("g");v.append("g").attr("class","nv-x nv-axis"),v.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),v.append("g").attr("class","nv-barsWrap"),x.attr("transform","translate("+i.left+","+i.top+")"),o&&x.select(".nv-y.nv-axis").attr("transform","translate("+q+",0)"),e.width(q).height(t);var y=x.select(".nv-barsWrap").datum(h.filter(function(a){return!a.disabled}));if(y.transition().call(e),w.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),x.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(p?2:1)).attr("height",16).attr("x",-c.rangeBand()/(p?1:2)),m){f.scale(c)._ticks(a.utils.calcTicksX(q/100,h)).tickSize(-t,0),x.select(".nv-x.nv-axis").attr("transform","translate(0,"+(d.range()[0]+(e.showValues()&&d.domain()[0]<0?16:0))+")"),x.select(".nv-x.nv-axis").call(f); -var z=x.select(".nv-x.nv-axis").selectAll("g");p&&z.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}n&&(g.scale(d)._ticks(a.utils.calcTicksY(t/36,h)).tickSize(-q,0),x.select(".nv-y.nv-axis").call(g)),x.select(".nv-zeroLine line").attr("x1",0).attr("x2",q).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("discreteBar chart immediate"),b}var c,d,e=a.models.discreteBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.tooltip(),i={top:15,right:10,bottom:50,left:60},j=null,k=null,l=a.utils.getColor(),m=!0,n=!0,o=!1,p=!1,q=null,r=d3.dispatch("beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(o?"right":"left").tickFormat(d3.format(",.1f")),h.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).keyFormatter(function(a,b){return f.tickFormat()(a,b)});var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},h.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){h.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){h.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.discretebar=e,b.xAxis=f,b.yAxis=g,b.tooltip=h,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},staggerLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return m},set:function(a){m=a}},showYAxis:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return q},set:function(a){q=a}},tooltips:{get:function(){return h.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),h.enabled(!!b)}},tooltipContent:{get:function(){return h.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),h.contentGenerator(b)}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),e.color(l)}},rightAlignYAxis:{get:function(){return o},set:function(a){o=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.distribution=function(){"use strict";function b(k){return m.reset(),k.each(function(b){var k=(e-("x"===g?d.left+d.right:d.top+d.bottom),"x"==g?"y":"x"),l=d3.select(this);a.utils.initSVG(l),c=c||j;var n=l.selectAll("g.nv-distribution").data([b]),o=n.enter().append("g").attr("class","nvd3 nv-distribution"),p=(o.append("g"),n.select("g"));n.attr("transform","translate("+d.left+","+d.top+")");var q=p.selectAll("g.nv-dist").data(function(a){return a},function(a){return a.key});q.enter().append("g"),q.attr("class",function(a,b){return"nv-dist nv-series-"+b}).style("stroke",function(a,b){return i(a,b)});var r=q.selectAll("line.nv-dist"+g).data(function(a){return a.values});r.enter().append("line").attr(g+"1",function(a,b){return c(h(a,b))}).attr(g+"2",function(a,b){return c(h(a,b))}),m.transition(q.exit().selectAll("line.nv-dist"+g),"dist exit").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}).style("stroke-opacity",0).remove(),r.attr("class",function(a,b){return"nv-dist"+g+" nv-dist"+g+"-"+b}).attr(k+"1",0).attr(k+"2",f),m.transition(r,"dist").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}),c=j.copy()}),m.renderEnd("distribution immediate"),b}var c,d={top:0,right:0,bottom:0,left:0},e=400,f=8,g="x",h=function(a){return a[g]},i=a.utils.defaultColor(),j=d3.scale.linear(),k=250,l=d3.dispatch("renderEnd"),m=a.utils.renderWatch(l,k);return b.options=a.utils.optionsFunc.bind(b),b.dispatch=l,b.margin=function(a){return arguments.length?(d.top="undefined"!=typeof a.top?a.top:d.top,d.right="undefined"!=typeof a.right?a.right:d.right,d.bottom="undefined"!=typeof a.bottom?a.bottom:d.bottom,d.left="undefined"!=typeof a.left?a.left:d.left,b):d},b.width=function(a){return arguments.length?(e=a,b):e},b.axis=function(a){return arguments.length?(g=a,b):g},b.size=function(a){return arguments.length?(f=a,b):f},b.getData=function(a){return arguments.length?(h=d3.functor(a),b):h},b.scale=function(a){return arguments.length?(j=a,b):j},b.color=function(c){return arguments.length?(i=a.utils.getColor(c),b):i},b.duration=function(a){return arguments.length?(k=a,m.reset(k),b):k},b},a.models.furiousLegend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?g(a,b):"#fff":m?void 0:a.disabled?g(a,b):"#fff"}function r(a,b){return m&&"furious"==o?a.disengaged?"#fff":g(a,b):a.disabled?"#fff":g(a,b)}return p.each(function(b){var p=d-c.left-c.right,s=d3.select(this);a.utils.initSVG(s);var t=s.selectAll("g.nv-legend").data([b]),u=(t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),t.select("g"));t.attr("transform","translate("+c.left+","+c.top+")");var v,w=u.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),x=w.enter().append("g").attr("class","nv-series");if("classic"==o)x.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),v=w.select("circle");else if("furious"==o){x.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),v=w.select("rect"),x.append("g").attr("class","nv-check-box").property("innerHTML",'').attr("transform","translate(-10,-8)scale(0.5)");var y=w.select(".nv-check-box");y.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}x.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var z=w.select("text.nv-legend-text");w.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=w.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=w.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),w.classed("nv-disabled",function(a){return a.userDisabled}),w.exit().remove(),z.attr("fill",q).text(f);var A;switch(o){case"furious":A=23;break;case"classic":A=20}if(h){var B=[];w.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}B.push(b+i)});for(var C=0,D=0,E=[];p>D&&Cp&&C>1;){E=[],C--;for(var F=0;F(E[F%C]||0)&&(E[F%C]=B[F]);D=E.reduce(function(a,b){return a+b})}for(var G=[],H=0,I=0;C>H;H++)G[H]=I,I+=E[H];w.attr("transform",function(a,b){return"translate("+G[b%C]+","+(5+Math.floor(b/C)*A)+")"}),j?u.attr("transform","translate("+(d-c.right-D)+","+c.top+")"):u.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(B.length/C)*A}else{var J,K=5,L=5,M=0;w.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return J=L,dM&&(M=L),"translate("+J+","+K+")"}),u.attr("transform","translate("+(d-c.right-M)+","+c.top+")"),e=c.top+c.bottom+K+15}"furious"==o&&v.attr("width",function(a,b){return z[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),v.style("fill",r).style("stroke",function(a,b){return a.color||g(a,b)})}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=28,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBar=function(){"use strict";function b(x){return x.each(function(b){w.reset(),k=d3.select(this);var x=a.utils.availableWidth(h,k,g),y=a.utils.availableHeight(i,k,g);a.utils.initSVG(k),l.domain(c||d3.extent(b[0].values.map(n).concat(p))),l.range(r?e||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:e||[0,x]),m.domain(d||d3.extent(b[0].values.map(o).concat(q))).range(f||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var z=k.selectAll("g.nv-wrap.nv-historicalBar-"+j).data([b[0].values]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBar-"+j),B=A.append("defs"),C=A.append("g"),D=z.select("g");C.append("g").attr("class","nv-bars"),z.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){u.chartClick({data:a,index:b,pos:d3.event,id:j})}),B.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),z.select("#nv-chart-clip-path-"+j+" rect").attr("width",x).attr("height",y),D.attr("clip-path",s?"url(#nv-chart-clip-path-"+j+")":"");var E=z.select(".nv-bars").selectAll(".nv-bar").data(function(a){return a},function(a,b){return n(a,b)});E.exit().remove(),E.enter().append("rect").attr("x",0).attr("y",function(b,c){return a.utils.NaNtoZero(m(Math.max(0,o(b,c))))}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.abs(m(o(b,c))-m(0)))}).attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).on("mouseover",function(a,b){v&&(d3.select(this).classed("hover",!0),u.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mouseout",function(a,b){v&&(d3.select(this).classed("hover",!1),u.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mousemove",function(a,b){v&&u.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v&&(u.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}).on("dblclick",function(a,b){v&&(u.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}),E.attr("fill",function(a,b){return t(a,b)}).attr("class",function(a,b,c){return(o(a,b)<0?"nv-bar negative":"nv-bar positive")+" nv-bar-"+c+"-"+b}).watchTransition(w,"bars").attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).attr("width",x/b[0].values.length*.9),E.watchTransition(w,"bars").attr("y",function(b,c){var d=o(b,c)<0?m(0):m(0)-m(o(b,c))<1?m(0)-1:m(o(b,c));return a.utils.NaNtoZero(d)}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.max(Math.abs(m(o(b,c))-m(0)),1))})}),w.renderEnd("historicalBar immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=[],q=[0],r=!1,s=!0,t=a.utils.defaultColor(),u=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),v=!0,w=a.utils.renderWatch(u,0);return b.highlightPoint=function(a,b){k.select(".nv-bars .nv-bar-0-"+a).classed("hover",b)},b.clearHighlights=function(){k.select(".nv-bars .nv-bar.hover").classed("hover",!1)},b.dispatch=u,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},forceX:{get:function(){return p},set:function(a){p=a}},forceY:{get:function(){return q},set:function(a){q=a}},padData:{get:function(){return r},set:function(a){r=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},clipEdge:{get:function(){return s},set:function(a){s=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return v},set:function(a){v=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return t},set:function(b){t=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBarChart=function(b){"use strict";function c(b){return b.each(function(k){z.reset(),z.models(f),q&&z.models(g),r&&z.models(h);var w=d3.select(this),A=this;a.utils.initSVG(w);var B=a.utils.availableWidth(n,w,l),C=a.utils.availableHeight(o,w,l);if(c.update=function(){w.transition().duration(y).call(c)},c.container=this,u.disabled=k.map(function(a){return!!a.disabled}),!v){var D;v={};for(D in u)v[D]=u[D]instanceof Array?u[D].slice(0):u[D]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(c,w),c;w.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale();var E=w.selectAll("g.nv-wrap.nv-historicalBarChart").data([k]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBarChart").append("g"),G=E.select("g");F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-barsWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),p&&(i.width(B),G.select(".nv-legendWrap").datum(k).call(i),l.top!=i.height()&&(l.top=i.height(),C=a.utils.availableHeight(o,w,l)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-l.top+")")),E.attr("transform","translate("+l.left+","+l.top+")"),s&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),t&&(j.width(B).height(C).margin({left:l.left,top:l.top}).svgContainer(w).xScale(d),E.select(".nv-interactive").call(j)),f.width(B).height(C).color(k.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!k[b].disabled}));var H=G.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));H.transition().call(f),q&&(g.scale(d)._ticks(a.utils.calcTicksX(B/100,k)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),G.select(".nv-x.nv-axis").transition().call(g)),r&&(h.scale(e)._ticks(a.utils.calcTicksY(C/36,k)).tickSize(-B,0),G.select(".nv-y.nv-axis").transition().call(h)),j.dispatch.on("elementMousemove",function(b){f.clearHighlights();var d,e,i,n=[];k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g){e=a.interactiveBisect(g.values,b.pointXValue,c.x()),f.highlightPoint(e,!0);var h=g.values[e];void 0!==h&&(void 0===d&&(d=h),void 0===i&&(i=c.xScale()(c.x()(h,e))),n.push({key:g.key,value:c.y()(h,e),color:m(g,g.seriesIndex),data:g.values[e]}))});var o=g.tickFormat()(c.x()(d,e));j.tooltip.position({left:i+l.left,top:b.mouseY+l.top}).chartContainer(A.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:o,index:e,series:n})(),j.renderGuideLine(i)}),j.dispatch.on("elementMouseout",function(){x.tooltipHide(),f.clearHighlights()}),i.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,k.filter(function(a){return!a.disabled}).length||k.map(function(a){return a.disabled=!1,E.selectAll(".nv-series").classed("disabled",!1),a}),u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),b.transition().call(c)}),i.dispatch.on("legendDblclick",function(a){k.forEach(function(a){a.disabled=!0}),a.disabled=!1,u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),c.update()}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),c.update()})}),z.renderEnd("historicalBarChart immediate"),c}var d,e,f=b||a.models.historicalBar(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:90,bottom:50,left:90},m=a.utils.defaultColor(),n=null,o=null,p=!1,q=!0,r=!0,s=!1,t=!1,u={},v=null,w=null,x=d3.dispatch("tooltipHide","stateChange","changeState","renderEnd"),y=250;g.orient("bottom").tickPadding(7),h.orient(s?"right":"left"),k.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)});var z=a.utils.renderWatch(x,0);return f.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:c.x()(a.data),value:c.y()(a.data),color:a.color},k.data(a).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),f.dispatch.on("elementMousemove.tooltip",function(){k.position({top:d3.event.pageY,left:d3.event.pageX})()}),c.dispatch=x,c.bars=f,c.legend=i,c.xAxis=g,c.yAxis=h,c.interactiveLayer=j,c.tooltip=k,c.options=a.utils.optionsFunc.bind(c),c._options=Object.create({},{width:{get:function(){return n},set:function(a){n=a}},height:{get:function(){return o},set:function(a){o=a}},showLegend:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return q},set:function(a){q=a}},showYAxis:{get:function(){return r},set:function(a){r=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b),i.color(m),f.color(m)}},duration:{get:function(){return y},set:function(a){y=a,z.reset(y),h.duration(y),g.duration(y)}},rightAlignYAxis:{get:function(){return s},set:function(a){s=a,h.orient(a?"right":"left")}},useInteractiveGuideline:{get:function(){return t},set:function(a){t=a,a===!0&&c.interactive(!1)}}}),a.utils.inheritOptions(c,f),a.utils.initOptions(c),c},a.models.ohlcBarChart=function(){var b=a.models.historicalBarChart(a.models.ohlcBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open'+a.value+"
open:"+b.yAxis.tickFormat()(c.open)+"
close:"+b.yAxis.tickFormat()(c.close)+"
high"+b.yAxis.tickFormat()(c.high)+"
low:"+b.yAxis.tickFormat()(c.low)+"
"}),b},a.models.candlestickBarChart=function(){var b=a.models.historicalBarChart(a.models.candlestickBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open'+a.value+"
open:"+b.yAxis.tickFormat()(c.open)+"
close:"+b.yAxis.tickFormat()(c.close)+"
high"+b.yAxis.tickFormat()(c.high)+"
low:"+b.yAxis.tickFormat()(c.low)+"
"}),b},a.models.legend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?"#000":"#fff":m?void 0:(a.color||(a.color=g(a,b)),a.disabled?a.color:"#fff")}function r(a,b){return m&&"furious"==o&&a.disengaged?"#eee":a.color||g(a,b)}function s(a){return m&&"furious"==o?1:a.disabled?0:1}return p.each(function(b){var g=d-c.left-c.right,p=d3.select(this);a.utils.initSVG(p);var t=p.selectAll("g.nv-legend").data([b]),u=t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),v=t.select("g");t.attr("transform","translate("+c.left+","+c.top+")");var w,x,y=v.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),z=y.enter().append("g").attr("class","nv-series");switch(o){case"furious":x=23;break;case"classic":x=20}if("classic"==o)z.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),w=y.select("circle");else if("furious"==o){z.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),w=y.select(".nv-legend-symbol"),z.append("g").attr("class","nv-check-box").property("innerHTML",'').attr("transform","translate(-10,-8)scale(0.5)");var A=y.select(".nv-check-box");A.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}z.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var B=y.select("text.nv-legend-text");y.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=y.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=y.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),y.classed("nv-disabled",function(a){return a.userDisabled}),y.exit().remove(),B.attr("fill",q).text(f);var C=0;if(h){var D=[];y.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}D.push(b+i)});var E=0,F=[];for(C=0;g>C&&Eg&&E>1;){F=[],E--;for(var G=0;G(F[G%E]||0)&&(F[G%E]=D[G]);C=F.reduce(function(a,b){return a+b})}for(var H=[],I=0,J=0;E>I;I++)H[I]=J,J+=F[I];y.attr("transform",function(a,b){return"translate("+H[b%E]+","+(5+Math.floor(b/E)*x)+")"}),j?v.attr("transform","translate("+(d-c.right-C)+","+c.top+")"):v.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(D.length/E)*x}else{var K,L=5,M=5,N=0;y.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return K=M,dN&&(N=M),K+N>C&&(C=K+N),"translate("+K+","+L+")"}),v.attr("transform","translate("+(d-c.right-N)+","+c.top+")"),e=c.top+c.bottom+L+15}if("furious"==o){w.attr("width",function(a,b){return B[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),u.insert("rect",":first-child").attr("class","nv-legend-bg").attr("fill","#eee").attr("opacity",0);var O=v.select(".nv-legend-bg");O.transition().duration(300).attr("x",-x).attr("width",C+x-12).attr("height",e+10).attr("y",-c.top-10).attr("opacity",m?1:0)}w.style("fill",r).style("fill-opacity",s).style("stroke",r)}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=32,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.line=function(){"use strict";function b(r){return v.reset(),v.models(e),r.each(function(b){i=d3.select(this);var r=a.utils.availableWidth(g,i,f),s=a.utils.availableHeight(h,i,f);a.utils.initSVG(i),c=e.xScale(),d=e.yScale(),t=t||c,u=u||d;var w=i.selectAll("g.nv-wrap.nv-line").data([b]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-line"),y=x.append("defs"),z=x.append("g"),A=w.select("g");z.append("g").attr("class","nv-groups"),z.append("g").attr("class","nv-scatterWrap"),w.attr("transform","translate("+f.left+","+f.top+")"),e.width(r).height(s);var B=w.select(".nv-scatterWrap");B.call(e),y.append("clipPath").attr("id","nv-edge-clip-"+e.id()).append("rect"),w.select("#nv-edge-clip-"+e.id()+" rect").attr("width",r).attr("height",s>0?s:0),A.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":""),B.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":"");var C=w.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});C.enter().append("g").style("stroke-opacity",1e-6).style("stroke-width",function(a){return a.strokeWidth||j}).style("fill-opacity",1e-6),C.exit().remove(),C.attr("class",function(a,b){return(a.classed||"")+" nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return k(a,b)}).style("stroke",function(a,b){return k(a,b)}),C.watchTransition(v,"line: groups").style("stroke-opacity",1).style("fill-opacity",function(a){return a.fillOpacity||.5});var D=C.selectAll("path.nv-area").data(function(a){return o(a)?[a]:[]});D.enter().append("path").attr("class","nv-area").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y0(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))}).y1(function(){return u(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])}),C.exit().selectAll("path.nv-area").remove(),D.watchTransition(v,"line: areaPaths").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y0(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))}).y1(function(){return d(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])});var E=C.selectAll("path.nv-line").data(function(a){return[a.values]});E.enter().append("path").attr("class","nv-line").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))})),E.watchTransition(v,"line: linePaths").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))})),t=c.copy(),u=d.copy()}),v.renderEnd("line immediate"),b}var c,d,e=a.models.scatter(),f={top:0,right:0,bottom:0,left:0},g=960,h=500,i=null,j=1.5,k=a.utils.defaultColor(),l=function(a){return a.x},m=function(a){return a.y},n=function(a,b){return!isNaN(m(a,b))&&null!==m(a,b)},o=function(a){return a.area},p=!1,q="linear",r=250,s=d3.dispatch("elementClick","elementMouseover","elementMouseout","renderEnd");e.pointSize(16).pointDomain([16,256]);var t,u,v=a.utils.renderWatch(s,r);return b.dispatch=s,b.scatter=e,e.dispatch.on("elementClick",function(){s.elementClick.apply(this,arguments)}),e.dispatch.on("elementMouseover",function(){s.elementMouseover.apply(this,arguments)}),e.dispatch.on("elementMouseout",function(){s.elementMouseout.apply(this,arguments)}),b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},defined:{get:function(){return n},set:function(a){n=a}},interpolate:{get:function(){return q},set:function(a){q=a}},clipEdge:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}},duration:{get:function(){return r},set:function(a){r=a,v.reset(r),e.duration(r)}},isArea:{get:function(){return o},set:function(a){o=d3.functor(a)}},x:{get:function(){return l},set:function(a){l=a,e.x(a)}},y:{get:function(){return m},set:function(a){m=a,e.y(a)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.lineChart=function(){"use strict";function b(j){return y.reset(),y.models(e),p&&y.models(f),q&&y.models(g),j.each(function(j){var v=d3.select(this),y=this;a.utils.initSVG(v);var B=a.utils.availableWidth(m,v,k),C=a.utils.availableHeight(n,v,k);if(b.update=function(){0===x?v.call(b):v.transition().duration(x).call(b)},b.container=this,t.setter(A(j),b.update).getter(z(j)).update(),t.disabled=j.map(function(a){return!!a.disabled}),!u){var D;u={};for(D in t)u[D]=t[D]instanceof Array?t[D].slice(0):t[D] -}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,v),b;v.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var E=v.selectAll("g.nv-wrap.nv-lineChart").data([j]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-lineChart").append("g"),G=E.select("g");F.append("rect").style("opacity",0),F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-linesWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),G.select("rect").attr("width",B).attr("height",C>0?C:0),o&&(h.width(B),G.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),C=a.utils.availableHeight(n,v,k)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-k.top+")")),E.attr("transform","translate("+k.left+","+k.top+")"),r&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),s&&(i.width(B).height(C).margin({left:k.left,top:k.top}).svgContainer(v).xScale(c),E.select(".nv-interactive").call(i)),e.width(B).height(C).color(j.map(function(a,b){return a.color||l(a,b)}).filter(function(a,b){return!j[b].disabled}));var H=G.select(".nv-linesWrap").datum(j.filter(function(a){return!a.disabled}));H.call(e),p&&(f.scale(c)._ticks(a.utils.calcTicksX(B/100,j)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),G.select(".nv-x.nv-axis").call(f)),q&&(g.scale(d)._ticks(a.utils.calcTicksY(C/36,j)).tickSize(-B,0),G.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)t[c]=a[c];w.stateChange(t),b.update()}),i.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,h,m,n=[];if(j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,g){h=a.interactiveBisect(f.values,c.pointXValue,b.x());var i=f.values[h],j=b.y()(i,h);null!=j&&e.highlightPoint(g,h,!0),void 0!==i&&(void 0===d&&(d=i),void 0===m&&(m=b.xScale()(b.x()(i,h))),n.push({key:f.key,value:j,color:l(f,f.seriesIndex)}))}),n.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(n.map(function(a){return a.value}),o,q);null!==r&&(n[r].highlight=!0)}var s=f.tickFormat()(b.x()(d,h));i.tooltip.position({left:c.mouseX+k.left,top:c.mouseY+k.top}).chartContainer(y.parentNode).valueFormatter(function(a){return null==a?"N/A":g.tickFormat()(a)}).data({value:s,index:h,series:n})(),i.renderGuideLine(m)}),i.dispatch.on("elementClick",function(c){var d,f=[];j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(e){var g=a.interactiveBisect(e.values,c.pointXValue,b.x()),h=e.values[g];if("undefined"!=typeof h){"undefined"==typeof d&&(d=b.xScale()(b.x()(h,g)));var i=b.yScale()(b.y()(h,g));f.push({point:h,pointIndex:g,pos:[d,i],seriesIndex:e.seriesIndex,series:e})}}),e.dispatch.elementClick(f)}),i.dispatch.on("elementMouseout",function(){e.clearHighlights()}),w.on("changeState",function(a){"undefined"!=typeof a.disabled&&j.length===a.disabled.length&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),t.disabled=a.disabled),b.update()})}),y.renderEnd("lineChart immediate"),b}var c,d,e=a.models.line(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.interactiveGuideline(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=a.utils.defaultColor(),m=null,n=null,o=!0,p=!0,q=!0,r=!1,s=!1,t=a.utils.state(),u=null,v=null,w=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),x=250;f.orient("bottom").tickPadding(7),g.orient(r?"right":"left"),j.valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)});var y=a.utils.renderWatch(w,x),z=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},A=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){j.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),b.dispatch=w,b.lines=e,b.legend=h,b.xAxis=f,b.yAxis=g,b.interactiveLayer=i,b.tooltip=j,b.dispatch=w,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return p},set:function(a){p=a}},showYAxis:{get:function(){return q},set:function(a){q=a}},defaultState:{get:function(){return u},set:function(a){u=a}},noData:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x),e.duration(x),f.duration(x),g.duration(x)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),h.color(l),e.color(l)}},rightAlignYAxis:{get:function(){return r},set:function(a){r=a,g.orient(r?"right":"left")}},useInteractiveGuideline:{get:function(){return s},set:function(a){s=a,s&&(e.interactive(!1),e.useVoronoi(!1))}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.linePlusBarChart=function(){"use strict";function b(v){return v.each(function(v){function J(a){var b=+("e"==a),c=b?1:-1,d=X/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function S(){u.empty()||u.extent(I),kb.data([u.empty()?e.domain():I]).each(function(a){var b=e(a[0])-e.range()[0],c=e.range()[1]-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>c?0:c)})}function T(){I=u.empty()?null:u.extent(),c=u.empty()?e.domain():u.extent(),K.brush({extent:c,brush:u}),S(),l.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),j.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var b=db.select(".nv-focus .nv-barsWrap").datum(Z.length?Z.map(function(a){return{key:a.key,values:a.values.filter(function(a,b){return l.x()(a,b)>=c[0]&&l.x()(a,b)<=c[1]})}}):[{values:[]}]),h=db.select(".nv-focus .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$.map(function(a){return{area:a.area,fillOpacity:a.fillOpacity,key:a.key,values:a.values.filter(function(a,b){return j.x()(a,b)>=c[0]&&j.x()(a,b)<=c[1]})}}));d=Z.length?l.xScale():j.xScale(),n.scale(d)._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-W,0),n.domain([Math.ceil(c[0]),Math.floor(c[1])]),db.select(".nv-x.nv-axis").transition().duration(L).call(n),b.transition().duration(L).call(l),h.transition().duration(L).call(j),db.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),p.scale(f)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(-V,0),q.scale(g)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(Z.length?0:-V,0),db.select(".nv-focus .nv-y1.nv-axis").style("opacity",Z.length?1:0),db.select(".nv-focus .nv-y2.nv-axis").style("opacity",$.length&&!$[0].disabled?1:0).attr("transform","translate("+d.range()[1]+",0)"),db.select(".nv-focus .nv-y1.nv-axis").transition().duration(L).call(p),db.select(".nv-focus .nv-y2.nv-axis").transition().duration(L).call(q)}var U=d3.select(this);a.utils.initSVG(U);var V=a.utils.availableWidth(y,U,w),W=a.utils.availableHeight(z,U,w)-(E?H:0),X=H-x.top-x.bottom;if(b.update=function(){U.transition().duration(L).call(b)},b.container=this,M.setter(R(v),b.update).getter(Q(v)).update(),M.disabled=v.map(function(a){return!!a.disabled}),!N){var Y;N={};for(Y in M)N[Y]=M[Y]instanceof Array?M[Y].slice(0):M[Y]}if(!(v&&v.length&&v.filter(function(a){return a.values.length}).length))return a.utils.noData(b,U),b;U.selectAll(".nv-noData").remove();var Z=v.filter(function(a){return!a.disabled&&a.bar}),$=v.filter(function(a){return!a.bar});d=l.xScale(),e=o.scale(),f=l.yScale(),g=j.yScale(),h=m.yScale(),i=k.yScale();var _=v.filter(function(a){return!a.disabled&&a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})}),ab=v.filter(function(a){return!a.disabled&&!a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})});d.range([0,V]),e.domain(d3.extent(d3.merge(_.concat(ab)),function(a){return a.x})).range([0,V]);var bb=U.selectAll("g.nv-wrap.nv-linePlusBar").data([v]),cb=bb.enter().append("g").attr("class","nvd3 nv-wrap nv-linePlusBar").append("g"),db=bb.select("g");cb.append("g").attr("class","nv-legendWrap");var eb=cb.append("g").attr("class","nv-focus");eb.append("g").attr("class","nv-x nv-axis"),eb.append("g").attr("class","nv-y1 nv-axis"),eb.append("g").attr("class","nv-y2 nv-axis"),eb.append("g").attr("class","nv-barsWrap"),eb.append("g").attr("class","nv-linesWrap");var fb=cb.append("g").attr("class","nv-context");if(fb.append("g").attr("class","nv-x nv-axis"),fb.append("g").attr("class","nv-y1 nv-axis"),fb.append("g").attr("class","nv-y2 nv-axis"),fb.append("g").attr("class","nv-barsWrap"),fb.append("g").attr("class","nv-linesWrap"),fb.append("g").attr("class","nv-brushBackground"),fb.append("g").attr("class","nv-x nv-brush"),D){var gb=t.align()?V/2:V,hb=t.align()?gb:0;t.width(gb),db.select(".nv-legendWrap").datum(v.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(a.bar?O:P),a})).call(t),w.top!=t.height()&&(w.top=t.height(),W=a.utils.availableHeight(z,U,w)-H),db.select(".nv-legendWrap").attr("transform","translate("+hb+","+-w.top+")")}bb.attr("transform","translate("+w.left+","+w.top+")"),db.select(".nv-context").style("display",E?"initial":"none"),m.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),k.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var ib=db.select(".nv-context .nv-barsWrap").datum(Z.length?Z:[{values:[]}]),jb=db.select(".nv-context .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$);db.select(".nv-context").attr("transform","translate(0,"+(W+w.bottom+x.top)+")"),ib.transition().call(m),jb.transition().call(k),G&&(o._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-X,0),db.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+h.range()[0]+")"),db.select(".nv-context .nv-x.nv-axis").transition().call(o)),F&&(r.scale(h)._ticks(X/36).tickSize(-V,0),s.scale(i)._ticks(X/36).tickSize(Z.length?0:-V,0),db.select(".nv-context .nv-y3.nv-axis").style("opacity",Z.length?1:0).attr("transform","translate(0,"+e.range()[0]+")"),db.select(".nv-context .nv-y2.nv-axis").style("opacity",$.length?1:0).attr("transform","translate("+e.range()[1]+",0)"),db.select(".nv-context .nv-y1.nv-axis").transition().call(r),db.select(".nv-context .nv-y2.nv-axis").transition().call(s)),u.x(e).on("brush",T),I&&u.extent(I);var kb=db.select(".nv-brushBackground").selectAll("g").data([I||u.extent()]),lb=kb.enter().append("g");lb.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",X),lb.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",X);var mb=db.select(".nv-x.nv-brush").call(u);mb.selectAll("rect").attr("height",X),mb.selectAll(".resize").append("path").attr("d",J),t.dispatch.on("stateChange",function(a){for(var c in a)M[c]=a[c];K.stateChange(M),b.update()}),K.on("changeState",function(a){"undefined"!=typeof a.disabled&&(v.forEach(function(b,c){b.disabled=a.disabled[c]}),M.disabled=a.disabled),b.update()}),T()}),b}var c,d,e,f,g,h,i,j=a.models.line(),k=a.models.line(),l=a.models.historicalBar(),m=a.models.historicalBar(),n=a.models.axis(),o=a.models.axis(),p=a.models.axis(),q=a.models.axis(),r=a.models.axis(),s=a.models.axis(),t=a.models.legend(),u=d3.svg.brush(),v=a.models.tooltip(),w={top:30,right:30,bottom:30,left:60},x={top:0,right:30,bottom:20,left:60},y=null,z=null,A=function(a){return a.x},B=function(a){return a.y},C=a.utils.defaultColor(),D=!0,E=!0,F=!1,G=!0,H=50,I=null,J=null,K=d3.dispatch("brush","stateChange","changeState"),L=0,M=a.utils.state(),N=null,O=" (left axis)",P=" (right axis)";j.clipEdge(!0),k.interactive(!1),n.orient("bottom").tickPadding(5),p.orient("left"),q.orient("right"),o.orient("bottom").tickPadding(5),r.orient("left"),s.orient("right"),v.headerEnabled(!0).headerFormatter(function(a,b){return n.tickFormat()(a,b)});var Q=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},R=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return j.dispatch.on("elementMouseover.tooltip",function(a){v.duration(100).valueFormatter(function(a,b){return q.tickFormat()(a,b)}).data(a).position(a.pos).hidden(!1)}),j.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={value:b.y()(a.data),color:a.color},v.duration(0).valueFormatter(function(a,b){return p.tickFormat()(a,b)}).data(a).hidden(!1)}),l.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMousemove.tooltip",function(){v.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=K,b.legend=t,b.lines=j,b.lines2=k,b.bars=l,b.bars2=m,b.xAxis=n,b.x2Axis=o,b.y1Axis=p,b.y2Axis=q,b.y3Axis=r,b.y4Axis=s,b.tooltip=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return y},set:function(a){y=a}},height:{get:function(){return z},set:function(a){z=a}},showLegend:{get:function(){return D},set:function(a){D=a}},brushExtent:{get:function(){return I},set:function(a){I=a}},noData:{get:function(){return J},set:function(a){J=a}},focusEnable:{get:function(){return E},set:function(a){E=a}},focusHeight:{get:function(){return H},set:function(a){H=a}},focusShowAxisX:{get:function(){return G},set:function(a){G=a}},focusShowAxisY:{get:function(){return F},set:function(a){F=a}},legendLeftAxisHint:{get:function(){return O},set:function(a){O=a}},legendRightAxisHint:{get:function(){return P},set:function(a){P=a}},tooltips:{get:function(){return v.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),v.enabled(!!b)}},tooltipContent:{get:function(){return v.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),v.contentGenerator(b)}},margin:{get:function(){return w},set:function(a){w.top=void 0!==a.top?a.top:w.top,w.right=void 0!==a.right?a.right:w.right,w.bottom=void 0!==a.bottom?a.bottom:w.bottom,w.left=void 0!==a.left?a.left:w.left}},duration:{get:function(){return L},set:function(a){L=a}},color:{get:function(){return C},set:function(b){C=a.utils.getColor(b),t.color(C)}},x:{get:function(){return A},set:function(a){A=a,j.x(a),k.x(a),l.x(a),m.x(a)}},y:{get:function(){return B},set:function(a){B=a,j.y(a),k.y(a),l.y(a),m.y(a)}}}),a.utils.inheritOptions(b,j),a.utils.initOptions(b),b},a.models.lineWithFocusChart=function(){"use strict";function b(o){return o.each(function(o){function z(a){var b=+("e"==a),c=b?1:-1,d=M/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function G(){n.empty()||n.extent(y),U.data([n.empty()?e.domain():y]).each(function(a){var b=e(a[0])-c.range()[0],d=K-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>d?0:d)})}function H(){y=n.empty()?null:n.extent();var a=n.empty()?e.domain():n.extent();if(!(Math.abs(a[0]-a[1])<=1)){A.brush({extent:a,brush:n}),G();var b=Q.select(".nv-focus .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}).map(function(b){return{key:b.key,area:b.area,values:b.values.filter(function(b,c){return g.x()(b,c)>=a[0]&&g.x()(b,c)<=a[1]})}}));b.transition().duration(B).call(g),Q.select(".nv-focus .nv-x.nv-axis").transition().duration(B).call(i),Q.select(".nv-focus .nv-y.nv-axis").transition().duration(B).call(j)}}var I=d3.select(this),J=this;a.utils.initSVG(I);var K=a.utils.availableWidth(t,I,q),L=a.utils.availableHeight(u,I,q)-v,M=v-r.top-r.bottom;if(b.update=function(){I.transition().duration(B).call(b)},b.container=this,C.setter(F(o),b.update).getter(E(o)).update(),C.disabled=o.map(function(a){return!!a.disabled}),!D){var N;D={};for(N in C)D[N]=C[N]instanceof Array?C[N].slice(0):C[N]}if(!(o&&o.length&&o.filter(function(a){return a.values.length}).length))return a.utils.noData(b,I),b;I.selectAll(".nv-noData").remove(),c=g.xScale(),d=g.yScale(),e=h.xScale(),f=h.yScale();var O=I.selectAll("g.nv-wrap.nv-lineWithFocusChart").data([o]),P=O.enter().append("g").attr("class","nvd3 nv-wrap nv-lineWithFocusChart").append("g"),Q=O.select("g");P.append("g").attr("class","nv-legendWrap");var R=P.append("g").attr("class","nv-focus");R.append("g").attr("class","nv-x nv-axis"),R.append("g").attr("class","nv-y nv-axis"),R.append("g").attr("class","nv-linesWrap"),R.append("g").attr("class","nv-interactive");var S=P.append("g").attr("class","nv-context");S.append("g").attr("class","nv-x nv-axis"),S.append("g").attr("class","nv-y nv-axis"),S.append("g").attr("class","nv-linesWrap"),S.append("g").attr("class","nv-brushBackground"),S.append("g").attr("class","nv-x nv-brush"),x&&(m.width(K),Q.select(".nv-legendWrap").datum(o).call(m),q.top!=m.height()&&(q.top=m.height(),L=a.utils.availableHeight(u,I,q)-v),Q.select(".nv-legendWrap").attr("transform","translate(0,"+-q.top+")")),O.attr("transform","translate("+q.left+","+q.top+")"),w&&(p.width(K).height(L).margin({left:q.left,top:q.top}).svgContainer(I).xScale(c),O.select(".nv-interactive").call(p)),g.width(K).height(L).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),h.defined(g.defined()).width(K).height(M).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),Q.select(".nv-context").attr("transform","translate(0,"+(L+q.bottom+r.top)+")");var T=Q.select(".nv-context .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}));d3.transition(T).call(h),i.scale(c)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-L,0),j.scale(d)._ticks(a.utils.calcTicksY(L/36,o)).tickSize(-K,0),Q.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+L+")"),n.x(e).on("brush",function(){H()}),y&&n.extent(y);var U=Q.select(".nv-brushBackground").selectAll("g").data([y||n.extent()]),V=U.enter().append("g");V.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",M),V.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",M);var W=Q.select(".nv-x.nv-brush").call(n);W.selectAll("rect").attr("height",M),W.selectAll(".resize").append("path").attr("d",z),H(),k.scale(e)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-M,0),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),d3.transition(Q.select(".nv-context .nv-x.nv-axis")).call(k),l.scale(f)._ticks(a.utils.calcTicksY(M/36,o)).tickSize(-K,0),d3.transition(Q.select(".nv-context .nv-y.nv-axis")).call(l),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),m.dispatch.on("stateChange",function(a){for(var c in a)C[c]=a[c];A.stateChange(C),b.update()}),p.dispatch.on("elementMousemove",function(c){g.clearHighlights();var d,f,h,k=[];if(o.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(i,j){var l=n.empty()?e.domain():n.extent(),m=i.values.filter(function(a,b){return g.x()(a,b)>=l[0]&&g.x()(a,b)<=l[1]});f=a.interactiveBisect(m,c.pointXValue,g.x());var o=m[f],p=b.y()(o,f);null!=p&&g.highlightPoint(j,f,!0),void 0!==o&&(void 0===d&&(d=o),void 0===h&&(h=b.xScale()(b.x()(o,f))),k.push({key:i.key,value:b.y()(o,f),color:s(i,i.seriesIndex)}))}),k.length>2){var l=b.yScale().invert(c.mouseY),m=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),r=.03*m,t=a.nearestValueIndex(k.map(function(a){return a.value}),l,r);null!==t&&(k[t].highlight=!0)}var u=i.tickFormat()(b.x()(d,f));p.tooltip.position({left:c.mouseX+q.left,top:c.mouseY+q.top}).chartContainer(J.parentNode).valueFormatter(function(a){return null==a?"N/A":j.tickFormat()(a)}).data({value:u,index:f,series:k})(),p.renderGuideLine(h)}),p.dispatch.on("elementMouseout",function(){g.clearHighlights()}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&o.forEach(function(b,c){b.disabled=a.disabled[c]}),b.update()})}),b}var c,d,e,f,g=a.models.line(),h=a.models.line(),i=a.models.axis(),j=a.models.axis(),k=a.models.axis(),l=a.models.axis(),m=a.models.legend(),n=d3.svg.brush(),o=a.models.tooltip(),p=a.interactiveGuideline(),q={top:30,right:30,bottom:30,left:60},r={top:0,right:30,bottom:20,left:60},s=a.utils.defaultColor(),t=null,u=null,v=50,w=!1,x=!0,y=null,z=null,A=d3.dispatch("brush","stateChange","changeState"),B=250,C=a.utils.state(),D=null;g.clipEdge(!0).duration(0),h.interactive(!1),i.orient("bottom").tickPadding(5),j.orient("left"),k.orient("bottom").tickPadding(5),l.orient("left"),o.valueFormatter(function(a,b){return j.tickFormat()(a,b)}).headerFormatter(function(a,b){return i.tickFormat()(a,b)});var E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return g.dispatch.on("elementMouseover.tooltip",function(a){o.data(a).position(a.pos).hidden(!1)}),g.dispatch.on("elementMouseout.tooltip",function(){o.hidden(!0)}),b.dispatch=A,b.legend=m,b.lines=g,b.lines2=h,b.xAxis=i,b.yAxis=j,b.x2Axis=k,b.y2Axis=l,b.interactiveLayer=p,b.tooltip=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return t},set:function(a){t=a}},height:{get:function(){return u},set:function(a){u=a}},focusHeight:{get:function(){return v},set:function(a){v=a}},showLegend:{get:function(){return x},set:function(a){x=a}},brushExtent:{get:function(){return y},set:function(a){y=a}},defaultState:{get:function(){return D},set:function(a){D=a}},noData:{get:function(){return z},set:function(a){z=a}},tooltips:{get:function(){return o.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),o.enabled(!!b)}},tooltipContent:{get:function(){return o.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),o.contentGenerator(b)}},margin:{get:function(){return q},set:function(a){q.top=void 0!==a.top?a.top:q.top,q.right=void 0!==a.right?a.right:q.right,q.bottom=void 0!==a.bottom?a.bottom:q.bottom,q.left=void 0!==a.left?a.left:q.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b),m.color(s)}},interpolate:{get:function(){return g.interpolate()},set:function(a){g.interpolate(a),h.interpolate(a)}},xTickFormat:{get:function(){return i.tickFormat()},set:function(a){i.tickFormat(a),k.tickFormat(a)}},yTickFormat:{get:function(){return j.tickFormat()},set:function(a){j.tickFormat(a),l.tickFormat(a)}},duration:{get:function(){return B},set:function(a){B=a,j.duration(B),l.duration(B),i.duration(B),k.duration(B)}},x:{get:function(){return g.x()},set:function(a){g.x(a),h.x(a)}},y:{get:function(){return g.y()},set:function(a){g.y(a),h.y(a)}},useInteractiveGuideline:{get:function(){return w},set:function(a){w=a,w&&(g.interactive(!1),g.useVoronoi(!1))}}}),a.utils.inheritOptions(b,g),a.utils.initOptions(b),b},a.models.multiBar=function(){"use strict";function b(E){return C.reset(),E.each(function(b){var E=k-j.left-j.right,F=l-j.top-j.bottom;p=d3.select(this),a.utils.initSVG(p);var G=0;if(x&&b.length&&(x=[{values:b[0].values.map(function(a){return{x:a.x,y:0,series:a.series,size:.01}})}]),u){var H=d3.layout.stack().offset(v).values(function(a){return a.values}).y(r)(!b.length&&x?x:b);H.forEach(function(a,c){a.nonStackable?(b[c].nonStackableSeries=G++,H[c]=b[c]):c>0&&H[c-1].nonStackable&&H[c].values.map(function(a,b){a.y0-=H[c-1].values[b].y,a.y1=a.y0+a.y})}),b=H}b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),u&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a,f){if(!b[f].nonStackable){var g=a.values[c];g.size=Math.abs(g.y),g.y<0?(g.y1=e,e-=g.size):(g.y1=g.size+d,d+=g.size)}})});var I=d&&e?[]:b.map(function(a,b){return a.values.map(function(a,c){return{x:q(a,c),y:r(a,c),y0:a.y0,y1:a.y1,idx:b}})});m.domain(d||d3.merge(I).map(function(a){return a.x})).rangeBands(f||[0,E],A),n.domain(e||d3.extent(d3.merge(I).map(function(a){var c=a.y;return u&&!b[a.idx].nonStackable&&(c=a.y>0?a.y1:a.y1+a.y),c}).concat(s))).range(g||[F,0]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]+.01*n.domain()[0],n.domain()[1]-.01*n.domain()[1]]:[-1,1]),h=h||m,i=i||n;var J=p.selectAll("g.nv-wrap.nv-multibar").data([b]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multibar"),L=K.append("defs"),M=K.append("g"),N=J.select("g");M.append("g").attr("class","nv-groups"),J.attr("transform","translate("+j.left+","+j.top+")"),L.append("clipPath").attr("id","nv-edge-clip-"+o).append("rect"),J.select("#nv-edge-clip-"+o+" rect").attr("width",E).attr("height",F),N.attr("clip-path",t?"url(#nv-edge-clip-"+o+")":"");var O=J.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});O.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);var P=C.transition(O.exit().selectAll("rect.nv-bar"),"multibarExit",Math.min(100,z)).attr("y",function(a){var c=i(0)||0;return u&&b[a.series]&&!b[a.series].nonStackable&&(c=i(a.y0)),c}).attr("height",0).remove();P.delay&&P.delay(function(a,b){var c=b*(z/(D+1))-b;return c}),O.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return w(a,b)}).style("stroke",function(a,b){return w(a,b)}),O.style("stroke-opacity",1).style("fill-opacity",.75);var Q=O.selectAll("rect.nv-bar").data(function(a){return x&&!b.length?x.values:a.values});Q.exit().remove();Q.enter().append("rect").attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("x",function(a,c,d){return u&&!b[d].nonStackable?0:d*m.rangeBand()/b.length}).attr("y",function(a,c,d){return i(u&&!b[d].nonStackable?a.y0:0)||0}).attr("height",0).attr("width",function(a,c,d){return m.rangeBand()/(u&&!b[d].nonStackable?1:b.length)}).attr("transform",function(a,b){return"translate("+m(q(a,b))+",0)"});Q.style("fill",function(a,b,c){return w(a,c,b)}).style("stroke",function(a,b,c){return w(a,c,b)}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),B.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),B.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){B.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){B.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){B.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),Q.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("transform",function(a,b){return"translate("+m(q(a,b))+",0)"}),y&&(c||(c=b.map(function(){return!0})),Q.style("fill",function(a,b,d){return d3.rgb(y(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(y(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}));var R=Q.watchTransition(C,"multibar",Math.min(250,z)).delay(function(a,c){return c*z/b[0].values.length});u?R.attr("y",function(a,c,d){var e=0;return e=b[d].nonStackable?r(a,c)<0?n(0):n(0)-n(r(a,c))<-1?n(0)-1:n(r(a,c))||0:n(a.y1)}).attr("height",function(a,c,d){return b[d].nonStackable?Math.max(Math.abs(n(r(a,c))-n(0)),1)||0:Math.max(Math.abs(n(a.y+a.y0)-n(a.y0)),1)}).attr("x",function(a,c,d){var e=0;return b[d].nonStackable&&(e=a.series*m.rangeBand()/b.length,b.length!==G&&(e=b[d].nonStackableSeries*m.rangeBand()/(2*G))),e}).attr("width",function(a,c,d){if(b[d].nonStackable){var e=m.rangeBand()/G;return b.length!==G&&(e=m.rangeBand()/(2*G)),e}return m.rangeBand()}):R.attr("x",function(a){return a.series*m.rangeBand()/b.length}).attr("width",m.rangeBand()/b.length).attr("y",function(a,b){return r(a,b)<0?n(0):n(0)-n(r(a,b))<1?n(0)-1:n(r(a,b))||0}).attr("height",function(a,b){return Math.max(Math.abs(n(r(a,b))-n(0)),1)||0}),h=m.copy(),i=n.copy(),b[0]&&b[0].values&&(D=b[0].values.length)}),C.renderEnd("multibar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=d3.scale.ordinal(),n=d3.scale.linear(),o=Math.floor(1e4*Math.random()),p=null,q=function(a){return a.x},r=function(a){return a.y},s=[0],t=!0,u=!1,v="zero",w=a.utils.defaultColor(),x=!1,y=null,z=500,A=.1,B=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),C=a.utils.renderWatch(B,z),D=0;return b.dispatch=B,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return s},set:function(a){s=a}},stacked:{get:function(){return u},set:function(a){u=a}},stackOffset:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return t},set:function(a){t=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return o},set:function(a){o=a}},hideable:{get:function(){return x},set:function(a){x=a}},groupSpacing:{get:function(){return A},set:function(a){A=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z)}},color:{get:function(){return w},set:function(b){w=a.utils.getColor(b)}},barColor:{get:function(){return y},set:function(b){y=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarChart=function(){"use strict";function b(j){return D.reset(),D.models(e),r&&D.models(f),s&&D.models(g),j.each(function(j){var z=d3.select(this);a.utils.initSVG(z);var D=a.utils.availableWidth(l,z,k),H=a.utils.availableHeight(m,z,k);if(b.update=function(){0===C?z.call(b):z.transition().duration(C).call(b)},b.container=this,x.setter(G(j),b.update).getter(F(j)).update(),x.disabled=j.map(function(a){return!!a.disabled}),!y){var I;y={};for(I in x)y[I]=x[I]instanceof Array?x[I].slice(0):x[I]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,z),b;z.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale(); -var J=z.selectAll("g.nv-wrap.nv-multiBarWithLegend").data([j]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarWithLegend").append("g"),L=J.select("g");if(K.append("g").attr("class","nv-x nv-axis"),K.append("g").attr("class","nv-y nv-axis"),K.append("g").attr("class","nv-barsWrap"),K.append("g").attr("class","nv-legendWrap"),K.append("g").attr("class","nv-controlsWrap"),q&&(h.width(D-B()),L.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),H=a.utils.availableHeight(m,z,k)),L.select(".nv-legendWrap").attr("transform","translate("+B()+","+-k.top+")")),o){var M=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(B()).color(["#444","#444","#444"]),L.select(".nv-controlsWrap").datum(M).attr("transform","translate(0,"+-k.top+")").call(i)}J.attr("transform","translate("+k.left+","+k.top+")"),t&&L.select(".nv-y.nv-axis").attr("transform","translate("+D+",0)"),e.disabled(j.map(function(a){return a.disabled})).width(D).height(H).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var N=L.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(N.call(e),r){f.scale(c)._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-H,0),L.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),L.select(".nv-x.nv-axis").call(f);var O=L.select(".nv-x.nv-axis > g").selectAll("g");if(O.selectAll("line, text").style("opacity",1),v){var P=function(a,b){return"translate("+a+","+b+")"},Q=5,R=17;O.selectAll("text").attr("transform",function(a,b,c){return P(0,c%2==0?Q:R)});var S=d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;L.selectAll(".nv-x.nv-axis .nv-axisMaxMin text").attr("transform",function(a,b){return P(0,0===b||S%2!==0?R:Q)})}u&&O.filter(function(a,b){return b%Math.ceil(j[0].values.length/(D/100))!==0}).selectAll("text, line").style("opacity",0),w&&O.selectAll(".tick text").attr("transform","rotate("+w+" 0,0)").style("text-anchor",w>0?"start":"end"),L.select(".nv-x.nv-axis").selectAll("g.nv-axisMaxMin text").style("opacity",1)}s&&(g.scale(d)._ticks(a.utils.calcTicksY(H/36,j)).tickSize(-D,0),L.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)x[c]=a[c];A.stateChange(x),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(M=M.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":case p.grouped:e.stacked(!1);break;case"Stacked":case p.stacked:e.stacked(!0)}x.stacked=e.stacked(),A.stateChange(x),b.update()}}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),x.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),x.stacked=a.stacked,E=a.stacked),b.update()})}),D.renderEnd("multibarchart immediate"),b}var c,d,e=a.models.multiBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=0,x=a.utils.state(),y=null,z=null,A=d3.dispatch("stateChange","changeState","renderEnd"),B=function(){return o?180:0},C=250;x.stacked=!1,e.stacked(!1),f.orient("bottom").tickPadding(7).showMaxMin(!1).tickFormat(function(a){return a}),g.orient(t?"right":"left").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var D=a.utils.renderWatch(A),E=!1,F=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:E}}},G=function(a){return function(b){void 0!==b.stacked&&(E=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=A,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=x,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return y},set:function(a){y=a}},noData:{get:function(){return z},set:function(a){z=a}},reduceXTicks:{get:function(){return u},set:function(a){u=a}},rotateLabels:{get:function(){return w},set:function(a){w=a}},staggerLabels:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return C},set:function(a){C=a,e.duration(C),f.duration(C),g.duration(C),D.reset(C)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiBarHorizontal=function(){"use strict";function b(m){return E.reset(),m.each(function(b){var m=k-j.left-j.right,C=l-j.top-j.bottom;n=d3.select(this),a.utils.initSVG(n),w&&(b=d3.layout.stack().offset("zero").values(function(a){return a.values}).y(r)(b)),b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),w&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a){var b=a.values[c];b.size=Math.abs(b.y),b.y<0?(b.y1=e-b.size,e-=b.size):(b.y1=d,d+=b.size)})});var F=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:q(a,b),y:r(a,b),y0:a.y0,y1:a.y1}})});o.domain(d||d3.merge(F).map(function(a){return a.x})).rangeBands(f||[0,C],A),p.domain(e||d3.extent(d3.merge(F).map(function(a){return w?a.y>0?a.y1+a.y:a.y1:a.y}).concat(t))),p.range(x&&!w?g||[p.domain()[0]<0?z:0,m-(p.domain()[1]>0?z:0)]:g||[0,m]),h=h||o,i=i||d3.scale.linear().domain(p.domain()).range([p(0),p(0)]);{var G=d3.select(this).selectAll("g.nv-wrap.nv-multibarHorizontal").data([b]),H=G.enter().append("g").attr("class","nvd3 nv-wrap nv-multibarHorizontal"),I=(H.append("defs"),H.append("g"));G.select("g")}I.append("g").attr("class","nv-groups"),G.attr("transform","translate("+j.left+","+j.top+")");var J=G.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});J.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),J.exit().watchTransition(E,"multibarhorizontal: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),J.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return u(a,b)}).style("stroke",function(a,b){return u(a,b)}),J.watchTransition(E,"multibarhorizontal: groups").style("stroke-opacity",1).style("fill-opacity",.75);var K=J.selectAll("g.nv-bar").data(function(a){return a.values});K.exit().remove();var L=K.enter().append("g").attr("transform",function(a,c,d){return"translate("+i(w?a.y0:0)+","+(w?0:d*o.rangeBand()/b.length+o(q(a,c)))+")"});L.append("rect").attr("width",0).attr("height",o.rangeBand()/(w?1:b.length)),K.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),D.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){D.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){D.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){D.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),s(b[0],0)&&(L.append("polyline"),K.select("polyline").attr("fill","none").attr("points",function(a,c){var d=s(a,c),e=.8*o.rangeBand()/(2*(w?1:b.length));d=d.length?d:[-Math.abs(d),Math.abs(d)],d=d.map(function(a){return p(a)-p(0)});var f=[[d[0],-e],[d[0],e],[d[0],0],[d[1],0],[d[1],-e],[d[1],e]];return f.map(function(a){return a.join(",")}).join(" ")}).attr("transform",function(a,c){var d=o.rangeBand()/(2*(w?1:b.length));return"translate("+(r(a,c)<0?0:p(r(a,c))-p(0))+", "+d+")"})),L.append("text"),x&&!w?(K.select("text").attr("text-anchor",function(a,b){return r(a,b)<0?"end":"start"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){var c=B(r(a,b)),d=s(a,b);return void 0===d?c:d.length?c+"+"+B(Math.abs(d[1]))+"-"+B(Math.abs(d[0])):c+"±"+B(Math.abs(d))}),K.watchTransition(E,"multibarhorizontal: bars").select("text").attr("x",function(a,b){return r(a,b)<0?-4:p(r(a,b))-p(0)+4})):K.selectAll("text").text(""),y&&!w?(L.append("text").classed("nv-bar-label",!0),K.select("text.nv-bar-label").attr("text-anchor",function(a,b){return r(a,b)<0?"start":"end"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){return q(a,b)}),K.watchTransition(E,"multibarhorizontal: bars").select("text.nv-bar-label").attr("x",function(a,b){return r(a,b)<0?p(0)-p(r(a,b))+4:-4})):K.selectAll("text.nv-bar-label").text(""),K.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}),v&&(c||(c=b.map(function(){return!0})),K.style("fill",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()})),w?K.watchTransition(E,"multibarhorizontal: bars").attr("transform",function(a,b){return"translate("+p(a.y1)+","+o(q(a,b))+")"}).select("rect").attr("width",function(a,b){return Math.abs(p(r(a,b)+a.y0)-p(a.y0))}).attr("height",o.rangeBand()):K.watchTransition(E,"multibarhorizontal: bars").attr("transform",function(a,c){return"translate("+p(r(a,c)<0?r(a,c):0)+","+(a.series*o.rangeBand()/b.length+o(q(a,c)))+")"}).select("rect").attr("height",o.rangeBand()/b.length).attr("width",function(a,b){return Math.max(Math.abs(p(r(a,b))-p(0)),1)}),h=o.copy(),i=p.copy()}),E.renderEnd("multibarHorizontal immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=null,o=d3.scale.ordinal(),p=d3.scale.linear(),q=function(a){return a.x},r=function(a){return a.y},s=function(a){return a.yErr},t=[0],u=a.utils.defaultColor(),v=null,w=!1,x=!1,y=!1,z=60,A=.1,B=d3.format(",.2f"),C=250,D=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),E=a.utils.renderWatch(D,C);return b.dispatch=D,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},yErr:{get:function(){return s},set:function(a){s=a}},xScale:{get:function(){return o},set:function(a){o=a}},yScale:{get:function(){return p},set:function(a){p=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return t},set:function(a){t=a}},stacked:{get:function(){return w},set:function(a){w=a}},showValues:{get:function(){return x},set:function(a){x=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return m},set:function(a){m=a}},valueFormat:{get:function(){return B},set:function(a){B=a}},valuePadding:{get:function(){return z},set:function(a){z=a}},groupSpacing:{get:function(){return A},set:function(a){A=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return C},set:function(a){C=a,E.reset(C)}},color:{get:function(){return u},set:function(b){u=a.utils.getColor(b)}},barColor:{get:function(){return v},set:function(b){v=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarHorizontalChart=function(){"use strict";function b(j){return C.reset(),C.models(e),r&&C.models(f),s&&C.models(g),j.each(function(j){var w=d3.select(this);a.utils.initSVG(w);var C=a.utils.availableWidth(l,w,k),D=a.utils.availableHeight(m,w,k);if(b.update=function(){w.transition().duration(z).call(b)},b.container=this,t=e.stacked(),u.setter(B(j),b.update).getter(A(j)).update(),u.disabled=j.map(function(a){return!!a.disabled}),!v){var E;v={};for(E in u)v[E]=u[E]instanceof Array?u[E].slice(0):u[E]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,w),b;w.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var F=w.selectAll("g.nv-wrap.nv-multiBarHorizontalChart").data([j]),G=F.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarHorizontalChart").append("g"),H=F.select("g");if(G.append("g").attr("class","nv-x nv-axis"),G.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),G.append("g").attr("class","nv-barsWrap"),G.append("g").attr("class","nv-legendWrap"),G.append("g").attr("class","nv-controlsWrap"),q&&(h.width(C-y()),H.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),D=a.utils.availableHeight(m,w,k)),H.select(".nv-legendWrap").attr("transform","translate("+y()+","+-k.top+")")),o){var I=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(y()).color(["#444","#444","#444"]),H.select(".nv-controlsWrap").datum(I).attr("transform","translate(0,"+-k.top+")").call(i)}F.attr("transform","translate("+k.left+","+k.top+")"),e.disabled(j.map(function(a){return a.disabled})).width(C).height(D).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var J=H.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(J.transition().call(e),r){f.scale(c)._ticks(a.utils.calcTicksY(D/24,j)).tickSize(-C,0),H.select(".nv-x.nv-axis").call(f);var K=H.select(".nv-x.nv-axis").selectAll("g");K.selectAll("line, text")}s&&(g.scale(d)._ticks(a.utils.calcTicksX(C/100,j)).tickSize(-D,0),H.select(".nv-y.nv-axis").attr("transform","translate(0,"+D+")"),H.select(".nv-y.nv-axis").call(g)),H.select(".nv-zeroLine line").attr("x1",d(0)).attr("x2",d(0)).attr("y1",0).attr("y2",-D),h.dispatch.on("stateChange",function(a){for(var c in a)u[c]=a[c];x.stateChange(u),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(I=I.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":e.stacked(!1);break;case"Stacked":e.stacked(!0)}u.stacked=e.stacked(),x.stateChange(u),t=e.stacked(),b.update()}}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),u.stacked=a.stacked,t=a.stacked),b.update()})}),C.renderEnd("multibar horizontal chart immediate"),b}var c,d,e=a.models.multiBarHorizontal(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend().height(30),i=a.models.legend().height(30),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=a.utils.state(),v=null,w=null,x=d3.dispatch("stateChange","changeState","renderEnd"),y=function(){return o?180:0},z=250;u.stacked=!1,e.stacked(t),f.orient("left").tickPadding(5).showMaxMin(!1).tickFormat(function(a){return a}),g.orient("bottom").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var A=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:t}}},B=function(a){return function(b){void 0!==b.stacked&&(t=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},C=a.utils.renderWatch(x,z);return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=x,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=u,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z),e.duration(z),f.duration(z),g.duration(z)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiChart=function(){"use strict";function b(j){return j.each(function(j){function k(a){var b=2===j[a.seriesIndex].yAxis?z:y;a.value=a.point.x,a.series={value:a.point.y,color:a.point.color},B.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function l(a){var b=2===j[a.seriesIndex].yAxis?z:y;a.point.x=v.x()(a.point),a.point.y=v.y()(a.point),B.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function n(a){var b=2===j[a.data.series].yAxis?z:y;a.value=t.x()(a.data),a.series={value:t.y()(a.data),color:a.color},B.duration(0).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).hidden(!1)}var C=d3.select(this);a.utils.initSVG(C),b.update=function(){C.transition().call(b)},b.container=this;var D=a.utils.availableWidth(g,C,e),E=a.utils.availableHeight(h,C,e),F=j.filter(function(a){return"line"==a.type&&1==a.yAxis}),G=j.filter(function(a){return"line"==a.type&&2==a.yAxis}),H=j.filter(function(a){return"bar"==a.type&&1==a.yAxis}),I=j.filter(function(a){return"bar"==a.type&&2==a.yAxis}),J=j.filter(function(a){return"area"==a.type&&1==a.yAxis}),K=j.filter(function(a){return"area"==a.type&&2==a.yAxis});if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,C),b;C.selectAll(".nv-noData").remove();var L=j.filter(function(a){return!a.disabled&&1==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})}),M=j.filter(function(a){return!a.disabled&&2==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})});o.domain(d3.extent(d3.merge(L.concat(M)),function(a){return a.x})).range([0,D]);var N=C.selectAll("g.wrap.multiChart").data([j]),O=N.enter().append("g").attr("class","wrap nvd3 multiChart").append("g");O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y1 nv-axis"),O.append("g").attr("class","nv-y2 nv-axis"),O.append("g").attr("class","lines1Wrap"),O.append("g").attr("class","lines2Wrap"),O.append("g").attr("class","bars1Wrap"),O.append("g").attr("class","bars2Wrap"),O.append("g").attr("class","stack1Wrap"),O.append("g").attr("class","stack2Wrap"),O.append("g").attr("class","legendWrap");var P=N.select("g"),Q=j.map(function(a,b){return j[b].color||f(a,b)});if(i){var R=A.align()?D/2:D,S=A.align()?R:0;A.width(R),A.color(Q),P.select(".legendWrap").datum(j.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(1==a.yAxis?"":" (right axis)"),a})).call(A),e.top!=A.height()&&(e.top=A.height(),E=a.utils.availableHeight(h,C,e)),P.select(".legendWrap").attr("transform","translate("+S+","+-e.top+")")}r.width(D).height(E).interpolate(m).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"line"==j[b].type})),s.width(D).height(E).interpolate(m).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"line"==j[b].type})),t.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"bar"==j[b].type})),u.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"bar"==j[b].type})),v.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"area"==j[b].type})),w.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"area"==j[b].type})),P.attr("transform","translate("+e.left+","+e.top+")");var T=P.select(".lines1Wrap").datum(F.filter(function(a){return!a.disabled})),U=P.select(".bars1Wrap").datum(H.filter(function(a){return!a.disabled})),V=P.select(".stack1Wrap").datum(J.filter(function(a){return!a.disabled})),W=P.select(".lines2Wrap").datum(G.filter(function(a){return!a.disabled})),X=P.select(".bars2Wrap").datum(I.filter(function(a){return!a.disabled})),Y=P.select(".stack2Wrap").datum(K.filter(function(a){return!a.disabled})),Z=J.length?J.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[],$=K.length?K.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[];p.domain(c||d3.extent(d3.merge(L).concat(Z),function(a){return a.y})).range([0,E]),q.domain(d||d3.extent(d3.merge(M).concat($),function(a){return a.y})).range([0,E]),r.yDomain(p.domain()),t.yDomain(p.domain()),v.yDomain(p.domain()),s.yDomain(q.domain()),u.yDomain(q.domain()),w.yDomain(q.domain()),J.length&&d3.transition(V).call(v),K.length&&d3.transition(Y).call(w),H.length&&d3.transition(U).call(t),I.length&&d3.transition(X).call(u),F.length&&d3.transition(T).call(r),G.length&&d3.transition(W).call(s),x._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-E,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+E+")"),d3.transition(P.select(".nv-x.nv-axis")).call(x),y._ticks(a.utils.calcTicksY(E/36,j)).tickSize(-D,0),d3.transition(P.select(".nv-y1.nv-axis")).call(y),z._ticks(a.utils.calcTicksY(E/36,j)).tickSize(-D,0),d3.transition(P.select(".nv-y2.nv-axis")).call(z),P.select(".nv-y1.nv-axis").classed("nv-disabled",L.length?!1:!0).attr("transform","translate("+o.range()[0]+",0)"),P.select(".nv-y2.nv-axis").classed("nv-disabled",M.length?!1:!0).attr("transform","translate("+o.range()[1]+",0)"),A.dispatch.on("stateChange",function(){b.update()}),r.dispatch.on("elementMouseover.tooltip",k),s.dispatch.on("elementMouseover.tooltip",k),r.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),s.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),v.dispatch.on("elementMouseover.tooltip",l),w.dispatch.on("elementMouseover.tooltip",l),v.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),w.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),t.dispatch.on("elementMouseover.tooltip",n),u.dispatch.on("elementMouseover.tooltip",n),t.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),u.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),t.dispatch.on("elementMousemove.tooltip",function(){B.position({top:d3.event.pageY,left:d3.event.pageX})()}),u.dispatch.on("elementMousemove.tooltip",function(){B.position({top:d3.event.pageY,left:d3.event.pageX})()})}),b}var c,d,e={top:30,right:20,bottom:50,left:60},f=a.utils.defaultColor(),g=null,h=null,i=!0,j=null,k=function(a){return a.x},l=function(a){return a.y},m="monotone",n=!0,o=d3.scale.linear(),p=d3.scale.linear(),q=d3.scale.linear(),r=a.models.line().yScale(p),s=a.models.line().yScale(q),t=a.models.multiBar().stacked(!1).yScale(p),u=a.models.multiBar().stacked(!1).yScale(q),v=a.models.stackedArea().yScale(p),w=a.models.stackedArea().yScale(q),x=a.models.axis().scale(o).orient("bottom").tickPadding(5),y=a.models.axis().scale(p).orient("left"),z=a.models.axis().scale(q).orient("right"),A=a.models.legend().height(30),B=a.models.tooltip(),C=d3.dispatch();return b.dispatch=C,b.lines1=r,b.lines2=s,b.bars1=t,b.bars2=u,b.stack1=v,b.stack2=w,b.xAxis=x,b.yAxis1=y,b.yAxis2=z,b.tooltip=B,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},showLegend:{get:function(){return i},set:function(a){i=a}},yDomain1:{get:function(){return c},set:function(a){c=a}},yDomain2:{get:function(){return d},set:function(a){d=a}},noData:{get:function(){return j},set:function(a){j=a}},interpolate:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return B.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),B.enabled(!!b)}},tooltipContent:{get:function(){return B.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),B.contentGenerator(b)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return f},set:function(b){f=a.utils.getColor(b)}},x:{get:function(){return k},set:function(a){k=a,r.x(a),s.x(a),t.x(a),u.x(a),v.x(a),w.x(a)}},y:{get:function(){return l},set:function(a){l=a,r.y(a),s.y(a),v.y(a),w.y(a),t.y(a),u.y(a)}},useVoronoi:{get:function(){return n},set:function(a){n=a,r.useVoronoi(a),s.useVoronoi(a),v.useVoronoi(a),w.useVoronoi(a)}}}),a.utils.initOptions(b),b},a.models.ohlcBar=function(){"use strict";function b(y){return y.each(function(b){k=d3.select(this);var y=a.utils.availableWidth(h,k,g),A=a.utils.availableHeight(i,k,g);a.utils.initSVG(k);var B=y/b[0].values.length*.9;l.domain(c||d3.extent(b[0].values.map(n).concat(t))),l.range(v?e||[.5*y/b[0].values.length,y*(b[0].values.length-.5)/b[0].values.length]:e||[5+B/2,y-B/2-5]),m.domain(d||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(f||[A,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var C=d3.select(this).selectAll("g.nv-wrap.nv-ohlcBar").data([b[0].values]),D=C.enter().append("g").attr("class","nvd3 nv-wrap nv-ohlcBar"),E=D.append("defs"),F=D.append("g"),G=C.select("g");F.append("g").attr("class","nv-ticks"),C.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:j})}),E.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),C.select("#nv-chart-clip-path-"+j+" rect").attr("width",y).attr("height",A),G.attr("clip-path",w?"url(#nv-chart-clip-path-"+j+")":"");var H=C.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});H.exit().remove(),H.enter().append("path").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}).attr("d",function(a,b){return"m0,0l0,"+(m(p(a,b))-m(r(a,b)))+"l"+-B/2+",0l"+B/2+",0l0,"+(m(s(a,b))-m(p(a,b)))+"l0,"+(m(q(a,b))-m(s(a,b)))+"l"+B/2+",0l"+-B/2+",0z"}).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("fill",function(){return x[0]}).attr("stroke",function(){return x[0]}).attr("x",0).attr("y",function(a,b){return m(Math.max(0,o(a,b)))}).attr("height",function(a,b){return Math.abs(m(o(a,b))-m(0))}),H.attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}),d3.transition(H).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("d",function(a,c){var d=y/b[0].values.length*.9;return"m0,0l0,"+(m(p(a,c))-m(r(a,c)))+"l"+-d/2+",0l"+d/2+",0l0,"+(m(s(a,c))-m(p(a,c)))+"l0,"+(m(q(a,c))-m(s(a,c)))+"l"+d/2+",0l"+-d/2+",0z"})}),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,c){b.clearHighlights(),k.select(".nv-ohlcBar .nv-tick-0-"+a).classed("hover",c)},b.clearHighlights=function(){k.select(".nv-ohlcBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!=a.top?a.top:g.top,g.right=void 0!=a.right?a.right:g.right,g.bottom=void 0!=a.bottom?a.bottom:g.bottom,g.left=void 0!=a.left?a.left:g.left -}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.parallelCoordinates=function(){"use strict";function b(p){return p.each(function(b){function p(a){return F(h.map(function(b){if(isNaN(a[b])||isNaN(parseFloat(a[b]))){var c=g[b].domain(),d=g[b].range(),e=c[0]-(c[1]-c[0])/9;if(J.indexOf(b)<0){var h=d3.scale.linear().domain([e,c[1]]).range([x-12,d[1]]);g[b].brush.y(h),J.push(b)}return[f(b),g[b](e)]}return J.length>0?(D.style("display","inline"),E.style("display","inline")):(D.style("display","none"),E.style("display","none")),[f(b),g[b](a[b])]}))}function q(){var a=h.filter(function(a){return!g[a].brush.empty()}),b=a.map(function(a){return g[a].brush.extent()});k=[],a.forEach(function(a,c){k[c]={dimension:a,extent:b[c]}}),l=[],M.style("display",function(c){var d=a.every(function(a,d){return isNaN(c[a])&&b[d][0]==g[a].brush.y().domain()[0]?!0:b[d][0]<=c[a]&&c[a]<=b[d][1]});return d&&l.push(c),d?null:"none"}),o.brush({filters:k,active:l})}function r(a){m[a]=this.parentNode.__origin__=f(a),L.attr("visibility","hidden")}function s(a){m[a]=Math.min(w,Math.max(0,this.parentNode.__origin__+=d3.event.x)),M.attr("d",p),h.sort(function(a,b){return u(a)-u(b)}),f.domain(h),N.attr("transform",function(a){return"translate("+u(a)+")"})}function t(a){delete this.parentNode.__origin__,delete m[a],d3.select(this.parentNode).attr("transform","translate("+f(a)+")"),M.attr("d",p),L.attr("d",p).attr("visibility",null)}function u(a){var b=m[a];return null==b?f(a):b}var v=d3.select(this),w=a.utils.availableWidth(d,v,c),x=a.utils.availableHeight(e,v,c);a.utils.initSVG(v),l=b,f.rangePoints([0,w],1).domain(h);var y={};h.forEach(function(a){var c=d3.extent(b,function(b){return+b[a]});return y[a]=!1,void 0===c[0]&&(y[a]=!0,c[0]=0,c[1]=0),c[0]===c[1]&&(c[0]=c[0]-1,c[1]=c[1]+1),g[a]=d3.scale.linear().domain(c).range([.9*(x-12),0]),g[a].brush=d3.svg.brush().y(g[a]).on("brush",q),"name"!=a});var z=v.selectAll("g.nv-wrap.nv-parallelCoordinates").data([b]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-parallelCoordinates"),B=A.append("g"),C=z.select("g");B.append("g").attr("class","nv-parallelCoordinates background"),B.append("g").attr("class","nv-parallelCoordinates foreground"),B.append("g").attr("class","nv-parallelCoordinates missingValuesline"),z.attr("transform","translate("+c.left+","+c.top+")");var D,E,F=d3.svg.line().interpolate("cardinal").tension(n),G=d3.svg.axis().orient("left"),H=d3.behavior.drag().on("dragstart",r).on("drag",s).on("dragend",t),I=f.range()[1]-f.range()[0],J=[],K=[0+I/2,x-12,w-I/2,x-12];D=z.select(".missingValuesline").selectAll("line").data([K]),D.enter().append("line"),D.exit().remove(),D.attr("x1",function(a){return a[0]}).attr("y1",function(a){return a[1]}).attr("x2",function(a){return a[2]}).attr("y2",function(a){return a[3]}),E=z.select(".missingValuesline").selectAll("text").data(["undefined values"]),E.append("text").data(["undefined values"]),E.enter().append("text"),E.exit().remove(),E.attr("y",x).attr("x",w-92-I/2).text(function(a){return a});var L=z.select(".background").selectAll("path").data(b);L.enter().append("path"),L.exit().remove(),L.attr("d",p);var M=z.select(".foreground").selectAll("path").data(b);M.enter().append("path"),M.exit().remove(),M.attr("d",p).attr("stroke",j),M.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),o.elementMouseover({label:a.name,data:a.data,index:b,pos:[d3.mouse(this.parentNode)[0],d3.mouse(this.parentNode)[1]]})}),M.on("mouseout",function(a,b){d3.select(this).classed("hover",!1),o.elementMouseout({label:a.name,data:a.data,index:b})});var N=C.selectAll(".dimension").data(h),O=N.enter().append("g").attr("class","nv-parallelCoordinates dimension");O.append("g").attr("class","nv-parallelCoordinates nv-axis"),O.append("g").attr("class","nv-parallelCoordinates-brush"),O.append("text").attr("class","nv-parallelCoordinates nv-label"),N.attr("transform",function(a){return"translate("+f(a)+",0)"}),N.exit().remove(),N.select(".nv-label").style("cursor","move").attr("dy","-1em").attr("text-anchor","middle").text(String).on("mouseover",function(a){o.elementMouseover({dim:a,pos:[d3.mouse(this.parentNode.parentNode)[0],d3.mouse(this.parentNode.parentNode)[1]]})}).on("mouseout",function(a){o.elementMouseout({dim:a})}).call(H),N.select(".nv-axis").each(function(a,b){d3.select(this).call(G.scale(g[a]).tickFormat(d3.format(i[b])))}),N.select(".nv-parallelCoordinates-brush").each(function(a){d3.select(this).call(g[a].brush)}).selectAll("rect").attr("x",-8).attr("width",16)}),b}var c={top:30,right:0,bottom:10,left:0},d=null,e=null,f=d3.scale.ordinal(),g={},h=[],i=[],j=a.utils.defaultColor(),k=[],l=[],m=[],n=1,o=d3.dispatch("brush","elementMouseover","elementMouseout");return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},dimensionNames:{get:function(){return h},set:function(a){h=a}},dimensionFormats:{get:function(){return i},set:function(a){i=a}},lineTension:{get:function(){return n},set:function(a){n=a}},dimensions:{get:function(){return h},set:function(b){a.deprecated("dimensions","use dimensionNames instead"),h=b}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.pie=function(){"use strict";function b(E){return D.reset(),E.each(function(b){function E(a,b){a.endAngle=isNaN(a.endAngle)?0:a.endAngle,a.startAngle=isNaN(a.startAngle)?0:a.startAngle,p||(a.innerRadius=0);var c=d3.interpolate(this._current,a);return this._current=c(0),function(a){return B[b](c(a))}}var F=d-c.left-c.right,G=e-c.top-c.bottom,H=Math.min(F,G)/2,I=[],J=[];if(i=d3.select(this),0===z.length)for(var K=H-H/5,L=y*H,M=0;Mc)return"";if("function"==typeof n)d=n(a,b,{key:f(a.data),value:g(a.data),percent:k(c)});else switch(n){case"key":d=f(a.data);break;case"value":d=k(g(a.data));break;case"percent":d=d3.format("%")(c)}return d})}}),D.renderEnd("pie immediate"),b}var c={top:0,right:0,bottom:0,left:0},d=500,e=500,f=function(a){return a.x},g=function(a){return a.y},h=Math.floor(1e4*Math.random()),i=null,j=a.utils.defaultColor(),k=d3.format(",.2f"),l=!0,m=!1,n="key",o=.02,p=!1,q=!1,r=!0,s=0,t=!1,u=!1,v=!1,w=!1,x=0,y=.5,z=[],A=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),B=[],C=[],D=a.utils.renderWatch(A);return b.dispatch=A,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{arcsRadius:{get:function(){return z},set:function(a){z=a}},width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},showLabels:{get:function(){return l},set:function(a){l=a}},title:{get:function(){return q},set:function(a){q=a}},titleOffset:{get:function(){return s},set:function(a){s=a}},labelThreshold:{get:function(){return o},set:function(a){o=a}},valueFormat:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return h},set:function(a){h=a}},endAngle:{get:function(){return w},set:function(a){w=a}},startAngle:{get:function(){return u},set:function(a){u=a}},padAngle:{get:function(){return v},set:function(a){v=a}},cornerRadius:{get:function(){return x},set:function(a){x=a}},donutRatio:{get:function(){return y},set:function(a){y=a}},labelsOutside:{get:function(){return m},set:function(a){m=a}},labelSunbeamLayout:{get:function(){return t},set:function(a){t=a}},donut:{get:function(){return p},set:function(a){p=a}},growOnHover:{get:function(){return r},set:function(a){r=a}},pieLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("pieLabelsOutside","use labelsOutside instead")}},donutLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("donutLabelsOutside","use labelsOutside instead")}},labelFormat:{get:function(){return k},set:function(b){k=b,a.deprecated("labelFormat","use valueFormat instead")}},margin:{get:function(){return c},set:function(a){c.top="undefined"!=typeof a.top?a.top:c.top,c.right="undefined"!=typeof a.right?a.right:c.right,c.bottom="undefined"!=typeof a.bottom?a.bottom:c.bottom,c.left="undefined"!=typeof a.left?a.left:c.left}},y:{get:function(){return g},set:function(a){g=d3.functor(a)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},labelType:{get:function(){return n},set:function(a){n=a||"key"}}}),a.utils.initOptions(b),b},a.models.pieChart=function(){"use strict";function b(e){return q.reset(),q.models(c),e.each(function(e){var k=d3.select(this);a.utils.initSVG(k);var n=a.utils.availableWidth(g,k,f),o=a.utils.availableHeight(h,k,f);if(b.update=function(){k.transition().call(b)},b.container=this,l.setter(s(e),b.update).getter(r(e)).update(),l.disabled=e.map(function(a){return!!a.disabled}),!m){var q;m={};for(q in l)m[q]=l[q]instanceof Array?l[q].slice(0):l[q]}if(!e||!e.length)return a.utils.noData(b,k),b;k.selectAll(".nv-noData").remove();var t=k.selectAll("g.nv-wrap.nv-pieChart").data([e]),u=t.enter().append("g").attr("class","nvd3 nv-wrap nv-pieChart").append("g"),v=t.select("g");if(u.append("g").attr("class","nv-pieWrap"),u.append("g").attr("class","nv-legendWrap"),i)if("top"===j)d.width(n).key(c.x()),t.select(".nv-legendWrap").datum(e).call(d),f.top!=d.height()&&(f.top=d.height(),o=a.utils.availableHeight(h,k,f)),t.select(".nv-legendWrap").attr("transform","translate(0,"+-f.top+")");else if("right"===j){var w=a.models.legend().width();w>n/2&&(w=n/2),d.height(o).key(c.x()),d.width(w),n-=d.width(),t.select(".nv-legendWrap").datum(e).call(d).attr("transform","translate("+n+",0)")}t.attr("transform","translate("+f.left+","+f.top+")"),c.width(n).height(o);var x=v.select(".nv-pieWrap").datum([e]);d3.transition(x).call(c),d.dispatch.on("stateChange",function(a){for(var c in a)l[c]=a[c];p.stateChange(l),b.update()}),p.on("changeState",function(a){"undefined"!=typeof a.disabled&&(e.forEach(function(b,c){b.disabled=a.disabled[c]}),l.disabled=a.disabled),b.update()})}),q.renderEnd("pieChart immediate"),b}var c=a.models.pie(),d=a.models.legend(),e=a.models.tooltip(),f={top:30,right:20,bottom:20,left:20},g=null,h=null,i=!0,j="top",k=a.utils.defaultColor(),l=a.utils.state(),m=null,n=null,o=250,p=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd");e.headerEnabled(!1).duration(0).valueFormatter(function(a,b){return c.valueFormat()(a,b)});var q=a.utils.renderWatch(p),r=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},s=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},e.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){e.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){e.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.legend=d,b.dispatch=p,b.pie=c,b.tooltip=e,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return i},set:function(a){i=a}},legendPosition:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return e.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),e.enabled(!!b)}},tooltipContent:{get:function(){return e.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),e.contentGenerator(b)}},color:{get:function(){return k},set:function(a){k=a,d.color(k),c.color(k)}},duration:{get:function(){return o},set:function(a){o=a,q.reset(o)}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.scatter=function(){"use strict";function b(N){return P.reset(),N.each(function(b){function N(){if(O=!1,!w)return!1;if(M===!0){var a=d3.merge(b.map(function(a,b){return a.values.map(function(a,c){var d=p(a,c),e=q(a,c);return[m(d)+1e-4*Math.random(),n(e)+1e-4*Math.random(),b,c,a]}).filter(function(a,b){return x(a[4],b)})}));if(0==a.length)return!1;a.length<3&&(a.push([m.range()[0]-20,n.range()[0]-20,null,null]),a.push([m.range()[1]+20,n.range()[1]+20,null,null]),a.push([m.range()[0]-20,n.range()[0]+20,null,null]),a.push([m.range()[1]+20,n.range()[1]-20,null,null]));var c=d3.geom.polygon([[-10,-10],[-10,i+10],[h+10,i+10],[h+10,-10]]),d=d3.geom.voronoi(a).map(function(b,d){return{data:c.clip(b),series:a[d][2],point:a[d][3]}});U.select(".nv-point-paths").selectAll("path").remove();var e=U.select(".nv-point-paths").selectAll("path").data(d),f=e.enter().append("svg:path").attr("d",function(a){return a&&a.data&&0!==a.data.length?"M"+a.data.join(",")+"Z":"M 0 0"}).attr("id",function(a,b){return"nv-path-"+b}).attr("clip-path",function(a,b){return"url(#nv-clip-"+b+")"});C&&f.style("fill",d3.rgb(230,230,230)).style("fill-opacity",.4).style("stroke-opacity",1).style("stroke",d3.rgb(200,200,200)),B&&(U.select(".nv-point-clips").selectAll("clipPath").remove(),U.select(".nv-point-clips").selectAll("clipPath").data(a).enter().append("svg:clipPath").attr("id",function(a,b){return"nv-clip-"+b}).append("svg:circle").attr("cx",function(a){return a[0]}).attr("cy",function(a){return a[1]}).attr("r",D));var k=function(a,c){if(O)return 0;var d=b[a.series];if(void 0!==d){var e=d.values[a.point];e.color=j(d,a.series),e.x=p(e),e.y=q(e);var f=l.node().getBoundingClientRect(),h=window.pageYOffset||document.documentElement.scrollTop,i=window.pageXOffset||document.documentElement.scrollLeft,k={left:m(p(e,a.point))+f.left+i+g.left+10,top:n(q(e,a.point))+f.top+h+g.top+10};c({point:e,series:d,pos:k,seriesIndex:a.series,pointIndex:a.point})}};e.on("click",function(a){k(a,L.elementClick)}).on("dblclick",function(a){k(a,L.elementDblClick)}).on("mouseover",function(a){k(a,L.elementMouseover)}).on("mouseout",function(a){k(a,L.elementMouseout)})}else U.select(".nv-groups").selectAll(".nv-group").selectAll(".nv-point").on("click",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("dblclick",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementDblClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("mouseover",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseover({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c,color:j(a,c)})}).on("mouseout",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseout({point:e,series:d,seriesIndex:a.series,pointIndex:c,color:j(a,c)})})}l=d3.select(this);var R=a.utils.availableWidth(h,l,g),S=a.utils.availableHeight(i,l,g);a.utils.initSVG(l),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var T=E&&F&&I?[]:d3.merge(b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),size:r(a,b)}})}));m.domain(E||d3.extent(T.map(function(a){return a.x}).concat(t))),m.range(y&&b[0]?G||[(R*z+R)/(2*b[0].values.length),R-R*(1+z)/(2*b[0].values.length)]:G||[0,R]),n.domain(F||d3.extent(T.map(function(a){return a.y}).concat(u))).range(H||[S,0]),o.domain(I||d3.extent(T.map(function(a){return a.size}).concat(v))).range(J||Q),K=m.domain()[0]===m.domain()[1]||n.domain()[0]===n.domain()[1],m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]-.01*n.domain()[0],n.domain()[1]+.01*n.domain()[1]]:[-1,1]),isNaN(m.domain()[0])&&m.domain([-1,1]),isNaN(n.domain()[0])&&n.domain([-1,1]),c=c||m,d=d||n,e=e||o;var U=l.selectAll("g.nv-wrap.nv-scatter").data([b]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-scatter nv-chart-"+k),W=V.append("defs"),X=V.append("g"),Y=U.select("g");U.classed("nv-single-point",K),X.append("g").attr("class","nv-groups"),X.append("g").attr("class","nv-point-paths"),V.append("g").attr("class","nv-point-clips"),U.attr("transform","translate("+g.left+","+g.top+")"),W.append("clipPath").attr("id","nv-edge-clip-"+k).append("rect"),U.select("#nv-edge-clip-"+k+" rect").attr("width",R).attr("height",S>0?S:0),Y.attr("clip-path",A?"url(#nv-edge-clip-"+k+")":""),O=!0;var Z=U.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});Z.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),Z.exit().remove(),Z.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),Z.watchTransition(P,"scatter: groups").style("fill",function(a,b){return j(a,b)}).style("stroke",function(a,b){return j(a,b)}).style("stroke-opacity",1).style("fill-opacity",.5);var $=Z.selectAll("path.nv-point").data(function(a){return a.values.map(function(a,b){return[a,b]}).filter(function(a,b){return x(a[0],b)})});$.enter().append("path").style("fill",function(a){return a.color}).style("stroke",function(a){return a.color}).attr("transform",function(a){return"translate("+c(p(a[0],a[1]))+","+d(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),$.exit().remove(),Z.exit().selectAll("path.nv-point").watchTransition(P,"scatter exit").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).remove(),$.each(function(a){d3.select(this).classed("nv-point",!0).classed("nv-point-"+a[1],!0).classed("nv-noninteractive",!w).classed("hover",!1)}),$.watchTransition(P,"scatter points").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),clearTimeout(f),f=setTimeout(N,300),c=m.copy(),d=n.copy(),e=o.copy()}),P.renderEnd("scatter immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=a.utils.defaultColor(),k=Math.floor(1e5*Math.random()),l=null,m=d3.scale.linear(),n=d3.scale.linear(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=function(a){return a.size||1},s=function(a){return a.shape||"circle"},t=[],u=[],v=[],w=!0,x=function(a){return!a.notActive},y=!1,z=.1,A=!1,B=!0,C=!1,D=function(){return 25},E=null,F=null,G=null,H=null,I=null,J=null,K=!1,L=d3.dispatch("elementClick","elementDblClick","elementMouseover","elementMouseout","renderEnd"),M=!0,N=250,O=!1,P=a.utils.renderWatch(L,N),Q=[16,256];return b.dispatch=L,b.options=a.utils.optionsFunc.bind(b),b._calls=new function(){this.clearHighlights=function(){return a.dom.write(function(){l.selectAll(".nv-point.hover").classed("hover",!1)}),null},this.highlightPoint=function(b,c,d){a.dom.write(function(){l.select(" .nv-series-"+b+" .nv-point-"+c).classed("hover",d)})}},L.on("elementMouseover.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!0)}),L.on("elementMouseout.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!1)}),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},pointScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return E},set:function(a){E=a}},yDomain:{get:function(){return F},set:function(a){F=a}},pointDomain:{get:function(){return I},set:function(a){I=a}},xRange:{get:function(){return G},set:function(a){G=a}},yRange:{get:function(){return H},set:function(a){H=a}},pointRange:{get:function(){return J},set:function(a){J=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},forcePoint:{get:function(){return v},set:function(a){v=a}},interactive:{get:function(){return w},set:function(a){w=a}},pointActive:{get:function(){return x},set:function(a){x=a}},padDataOuter:{get:function(){return z},set:function(a){z=a}},padData:{get:function(){return y},set:function(a){y=a}},clipEdge:{get:function(){return A},set:function(a){A=a}},clipVoronoi:{get:function(){return B},set:function(a){B=a}},clipRadius:{get:function(){return D},set:function(a){D=a}},showVoronoi:{get:function(){return C},set:function(a){C=a}},id:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return p},set:function(a){p=d3.functor(a)}},y:{get:function(){return q},set:function(a){q=d3.functor(a)}},pointSize:{get:function(){return r},set:function(a){r=d3.functor(a)}},pointShape:{get:function(){return s},set:function(a){s=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},duration:{get:function(){return N},set:function(a){N=a,P.reset(N)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},useVoronoi:{get:function(){return M},set:function(a){M=a,M===!1&&(B=!1)}}}),a.utils.initOptions(b),b},a.models.scatterChart=function(){"use strict";function b(z){return D.reset(),D.models(c),t&&D.models(d),u&&D.models(e),q&&D.models(g),r&&D.models(h),z.each(function(z){m=d3.select(this),a.utils.initSVG(m);var G=a.utils.availableWidth(k,m,j),H=a.utils.availableHeight(l,m,j);if(b.update=function(){0===A?m.call(b):m.transition().duration(A).call(b)},b.container=this,w.setter(F(z),b.update).getter(E(z)).update(),w.disabled=z.map(function(a){return!!a.disabled}),!x){var I;x={};for(I in w)x[I]=w[I]instanceof Array?w[I].slice(0):w[I]}if(!(z&&z.length&&z.filter(function(a){return a.values.length}).length))return a.utils.noData(b,m),D.renderEnd("scatter immediate"),b;m.selectAll(".nv-noData").remove(),o=c.xScale(),p=c.yScale();var J=m.selectAll("g.nv-wrap.nv-scatterChart").data([z]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-scatterChart nv-chart-"+c.id()),L=K.append("g"),M=J.select("g");if(L.append("rect").attr("class","nvd3 nv-background").style("pointer-events","none"),L.append("g").attr("class","nv-x nv-axis"),L.append("g").attr("class","nv-y nv-axis"),L.append("g").attr("class","nv-scatterWrap"),L.append("g").attr("class","nv-regressionLinesWrap"),L.append("g").attr("class","nv-distWrap"),L.append("g").attr("class","nv-legendWrap"),v&&M.select(".nv-y.nv-axis").attr("transform","translate("+G+",0)"),s){var N=G;f.width(N),J.select(".nv-legendWrap").datum(z).call(f),j.top!=f.height()&&(j.top=f.height(),H=a.utils.availableHeight(l,m,j)),J.select(".nv-legendWrap").attr("transform","translate(0,"+-j.top+")")}J.attr("transform","translate("+j.left+","+j.top+")"),c.width(G).height(H).color(z.map(function(a,b){return a.color=a.color||n(a,b),a.color}).filter(function(a,b){return!z[b].disabled})),J.select(".nv-scatterWrap").datum(z.filter(function(a){return!a.disabled})).call(c),J.select(".nv-regressionLinesWrap").attr("clip-path","url(#nv-edge-clip-"+c.id()+")");var O=J.select(".nv-regressionLinesWrap").selectAll(".nv-regLines").data(function(a){return a});O.enter().append("g").attr("class","nv-regLines");var P=O.selectAll(".nv-regLine").data(function(a){return[a]});P.enter().append("line").attr("class","nv-regLine").style("stroke-opacity",0),P.filter(function(a){return a.intercept&&a.slope}).watchTransition(D,"scatterPlusLineChart: regline").attr("x1",o.range()[0]).attr("x2",o.range()[1]).attr("y1",function(a){return p(o.domain()[0]*a.slope+a.intercept)}).attr("y2",function(a){return p(o.domain()[1]*a.slope+a.intercept)}).style("stroke",function(a,b,c){return n(a,c)}).style("stroke-opacity",function(a){return a.disabled||"undefined"==typeof a.slope||"undefined"==typeof a.intercept?0:1}),t&&(d.scale(o)._ticks(a.utils.calcTicksX(G/100,z)).tickSize(-H,0),M.select(".nv-x.nv-axis").attr("transform","translate(0,"+p.range()[0]+")").call(d)),u&&(e.scale(p)._ticks(a.utils.calcTicksY(H/36,z)).tickSize(-G,0),M.select(".nv-y.nv-axis").call(e)),q&&(g.getData(c.x()).scale(o).width(G).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionX"),M.select(".nv-distributionX").attr("transform","translate(0,"+p.range()[0]+")").datum(z.filter(function(a){return!a.disabled})).call(g)),r&&(h.getData(c.y()).scale(p).width(H).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionY"),M.select(".nv-distributionY").attr("transform","translate("+(v?G:-h.size())+",0)").datum(z.filter(function(a){return!a.disabled})).call(h)),f.dispatch.on("stateChange",function(a){for(var c in a)w[c]=a[c];y.stateChange(w),b.update()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&(z.forEach(function(b,c){b.disabled=a.disabled[c]}),w.disabled=a.disabled),b.update()}),c.dispatch.on("elementMouseout.tooltip",function(a){i.hidden(!0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",h.size())}),c.dispatch.on("elementMouseover.tooltip",function(a){m.select(".nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",a.pos.top-H-j.top),m.select(".nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",a.pos.left+g.size()-j.left),i.position(a.pos).data(a).hidden(!1)}),B=o.copy(),C=p.copy()}),D.renderEnd("scatter with line immediate"),b}var c=a.models.scatter(),d=a.models.axis(),e=a.models.axis(),f=a.models.legend(),g=a.models.distribution(),h=a.models.distribution(),i=a.models.tooltip(),j={top:30,right:20,bottom:50,left:75},k=null,l=null,m=null,n=a.utils.defaultColor(),o=c.xScale(),p=c.yScale(),q=!1,r=!1,s=!0,t=!0,u=!0,v=!1,w=a.utils.state(),x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=null,A=250;c.xScale(o).yScale(p),d.orient("bottom").tickPadding(10),e.orient(v?"right":"left").tickPadding(10),g.axis("x"),h.axis("y"),i.headerFormatter(function(a,b){return d.tickFormat()(a,b)}).valueFormatter(function(a,b){return e.tickFormat()(a,b)});var B,C,D=a.utils.renderWatch(y,A),E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return b.dispatch=y,b.scatter=c,b.legend=f,b.xAxis=d,b.yAxis=e,b.distX=g,b.distY=h,b.tooltip=i,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},container:{get:function(){return m},set:function(a){m=a}},showDistX:{get:function(){return q},set:function(a){q=a}},showDistY:{get:function(){return r},set:function(a){r=a}},showLegend:{get:function(){return s},set:function(a){s=a}},showXAxis:{get:function(){return t},set:function(a){t=a}},showYAxis:{get:function(){return u},set:function(a){u=a}},defaultState:{get:function(){return x},set:function(a){x=a}},noData:{get:function(){return z},set:function(a){z=a}},duration:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return i.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),i.enabled(!!b) -}},tooltipContent:{get:function(){return i.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),i.contentGenerator(b)}},tooltipXContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},tooltipYContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},rightAlignYAxis:{get:function(){return v},set:function(a){v=a,e.orient(a?"right":"left")}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),f.color(n),g.color(n),h.color(n)}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.sparkline=function(){"use strict";function b(k){return k.each(function(b){var k=h-g.left-g.right,q=i-g.top-g.bottom;j=d3.select(this),a.utils.initSVG(j),l.domain(c||d3.extent(b,n)).range(e||[0,k]),m.domain(d||d3.extent(b,o)).range(f||[q,0]);{var r=j.selectAll("g.nv-wrap.nv-sparkline").data([b]),s=r.enter().append("g").attr("class","nvd3 nv-wrap nv-sparkline");s.append("g"),r.select("g")}r.attr("transform","translate("+g.left+","+g.top+")");var t=r.selectAll("path").data(function(a){return[a]});t.enter().append("path"),t.exit().remove(),t.style("stroke",function(a,b){return a.color||p(a,b)}).attr("d",d3.svg.line().x(function(a,b){return l(n(a,b))}).y(function(a,b){return m(o(a,b))}));var u=r.selectAll("circle.nv-point").data(function(a){function b(b){if(-1!=b){var c=a[b];return c.pointIndex=b,c}return null}var c=a.map(function(a,b){return o(a,b)}),d=b(c.lastIndexOf(m.domain()[1])),e=b(c.indexOf(m.domain()[0])),f=b(c.length-1);return[e,d,f].filter(function(a){return null!=a})});u.enter().append("circle"),u.exit().remove(),u.attr("cx",function(a){return l(n(a,a.pointIndex))}).attr("cy",function(a){return m(o(a,a.pointIndex))}).attr("r",2).attr("class",function(a){return n(a,a.pointIndex)==l.domain()[1]?"nv-point nv-currentValue":o(a,a.pointIndex)==m.domain()[0]?"nv-point nv-minValue":"nv-point nv-maxValue"})}),b}var c,d,e,f,g={top:2,right:0,bottom:2,left:0},h=400,i=32,j=null,k=!0,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=a.utils.getColor(["#000"]);return b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},animate:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return n},set:function(a){n=d3.functor(a)}},y:{get:function(){return o},set:function(a){o=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return p},set:function(b){p=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sparklinePlus=function(){"use strict";function b(p){return p.each(function(p){function q(){if(!j){var a=z.selectAll(".nv-hoverValue").data(i),b=a.enter().append("g").attr("class","nv-hoverValue").style("stroke-opacity",0).style("fill-opacity",0);a.exit().transition().duration(250).style("stroke-opacity",0).style("fill-opacity",0).remove(),a.attr("transform",function(a){return"translate("+c(e.x()(p[a],a))+",0)"}).transition().duration(250).style("stroke-opacity",1).style("fill-opacity",1),i.length&&(b.append("line").attr("x1",0).attr("y1",-f.top).attr("x2",0).attr("y2",u),b.append("text").attr("class","nv-xValue").attr("x",-6).attr("y",-f.top).attr("text-anchor","end").attr("dy",".9em"),z.select(".nv-hoverValue .nv-xValue").text(k(e.x()(p[i[0]],i[0]))),b.append("text").attr("class","nv-yValue").attr("x",6).attr("y",-f.top).attr("text-anchor","start").attr("dy",".9em"),z.select(".nv-hoverValue .nv-yValue").text(l(e.y()(p[i[0]],i[0]))))}}function r(){function a(a,b){for(var c=Math.abs(e.x()(a[0],0)-b),d=0,f=0;fc;++c){for(b=0,d=0;bb;b++)a[b][c][1]/=d;else for(b=0;e>b;b++)a[b][c][1]=0}for(c=0;f>c;++c)g[c]=0;return g}}),u.renderEnd("stackedArea immediate"),b}var c,d,e={top:0,right:0,bottom:0,left:0},f=960,g=500,h=a.utils.defaultColor(),i=Math.floor(1e5*Math.random()),j=null,k=function(a){return a.x},l=function(a){return a.y},m="stack",n="zero",o="default",p="linear",q=!1,r=a.models.scatter(),s=250,t=d3.dispatch("areaClick","areaMouseover","areaMouseout","renderEnd","elementClick","elementMouseover","elementMouseout");r.pointSize(2.2).pointDomain([2.2,2.2]);var u=a.utils.renderWatch(t,s);return b.dispatch=t,b.scatter=r,r.dispatch.on("elementClick",function(){t.elementClick.apply(this,arguments)}),r.dispatch.on("elementMouseover",function(){t.elementMouseover.apply(this,arguments)}),r.dispatch.on("elementMouseout",function(){t.elementMouseout.apply(this,arguments)}),b.interpolate=function(a){return arguments.length?(p=a,b):p},b.duration=function(a){return arguments.length?(s=a,u.reset(s),r.duration(s),b):s},b.dispatch=t,b.scatter=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return f},set:function(a){f=a}},height:{get:function(){return g},set:function(a){g=a}},clipEdge:{get:function(){return q},set:function(a){q=a}},offset:{get:function(){return n},set:function(a){n=a}},order:{get:function(){return o},set:function(a){o=a}},interpolate:{get:function(){return p},set:function(a){p=a}},x:{get:function(){return k},set:function(a){k=d3.functor(a)}},y:{get:function(){return l},set:function(a){l=d3.functor(a)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return h},set:function(b){h=a.utils.getColor(b)}},style:{get:function(){return m},set:function(a){switch(m=a){case"stack":b.offset("zero"),b.order("default");break;case"stream":b.offset("wiggle"),b.order("inside-out");break;case"stream-center":b.offset("silhouette"),b.order("inside-out");break;case"expand":b.offset("expand"),b.order("default");break;case"stack_percent":b.offset(b.d3_stackedOffset_stackPercent),b.order("default")}}},duration:{get:function(){return s},set:function(a){s=a,u.reset(s),r.duration(s)}}}),a.utils.inheritOptions(b,r),a.utils.initOptions(b),b},a.models.stackedAreaChart=function(){"use strict";function b(k){return F.reset(),F.models(e),r&&F.models(f),s&&F.models(g),k.each(function(k){var x=d3.select(this),F=this;a.utils.initSVG(x);var K=a.utils.availableWidth(m,x,l),L=a.utils.availableHeight(n,x,l);if(b.update=function(){x.transition().duration(C).call(b)},b.container=this,v.setter(I(k),b.update).getter(H(k)).update(),v.disabled=k.map(function(a){return!!a.disabled}),!w){var M;w={};for(M in v)w[M]=v[M]instanceof Array?v[M].slice(0):v[M]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(b,x),b;x.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var N=x.selectAll("g.nv-wrap.nv-stackedAreaChart").data([k]),O=N.enter().append("g").attr("class","nvd3 nv-wrap nv-stackedAreaChart").append("g"),P=N.select("g");if(O.append("rect").style("opacity",0),O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y nv-axis"),O.append("g").attr("class","nv-stackedWrap"),O.append("g").attr("class","nv-legendWrap"),O.append("g").attr("class","nv-controlsWrap"),O.append("g").attr("class","nv-interactive"),P.select("rect").attr("width",K).attr("height",L),q){var Q=p?K-z:K;h.width(Q),P.select(".nv-legendWrap").datum(k).call(h),l.top!=h.height()&&(l.top=h.height(),L=a.utils.availableHeight(n,x,l)),P.select(".nv-legendWrap").attr("transform","translate("+(K-Q)+","+-l.top+")")}if(p){var R=[{key:B.stacked||"Stacked",metaKey:"Stacked",disabled:"stack"!=e.style(),style:"stack"},{key:B.stream||"Stream",metaKey:"Stream",disabled:"stream"!=e.style(),style:"stream"},{key:B.expanded||"Expanded",metaKey:"Expanded",disabled:"expand"!=e.style(),style:"expand"},{key:B.stack_percent||"Stack %",metaKey:"Stack_Percent",disabled:"stack_percent"!=e.style(),style:"stack_percent"}];z=A.length/3*260,R=R.filter(function(a){return-1!==A.indexOf(a.metaKey)}),i.width(z).color(["#444","#444","#444"]),P.select(".nv-controlsWrap").datum(R).call(i),l.top!=Math.max(i.height(),h.height())&&(l.top=Math.max(i.height(),h.height()),L=a.utils.availableHeight(n,x,l)),P.select(".nv-controlsWrap").attr("transform","translate(0,"+-l.top+")")}N.attr("transform","translate("+l.left+","+l.top+")"),t&&P.select(".nv-y.nv-axis").attr("transform","translate("+K+",0)"),u&&(j.width(K).height(L).margin({left:l.left,top:l.top}).svgContainer(x).xScale(c),N.select(".nv-interactive").call(j)),e.width(K).height(L);var S=P.select(".nv-stackedWrap").datum(k);if(S.transition().call(e),r&&(f.scale(c)._ticks(a.utils.calcTicksX(K/100,k)).tickSize(-L,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+L+")"),P.select(".nv-x.nv-axis").transition().duration(0).call(f)),s){var T;if(T="wiggle"===e.offset()?0:a.utils.calcTicksY(L/36,k),g.scale(d)._ticks(T).tickSize(-K,0),"expand"===e.style()||"stack_percent"===e.style()){var U=g.tickFormat();D&&U===J||(D=U),g.tickFormat(J)}else D&&(g.tickFormat(D),D=null);P.select(".nv-y.nv-axis").transition().duration(0).call(g)}e.dispatch.on("areaClick.toggle",function(a){k.forEach(1===k.filter(function(a){return!a.disabled}).length?function(a){a.disabled=!1}:function(b,c){b.disabled=c!=a.seriesIndex}),v.disabled=k.map(function(a){return!!a.disabled}),y.stateChange(v),b.update()}),h.dispatch.on("stateChange",function(a){for(var c in a)v[c]=a[c];y.stateChange(v),b.update()}),i.dispatch.on("legendClick",function(a){a.disabled&&(R=R.map(function(a){return a.disabled=!0,a}),a.disabled=!1,e.style(a.style),v.style=e.style(),y.stateChange(v),b.update())}),j.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,g,h,i=[];if(k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,j){g=a.interactiveBisect(f.values,c.pointXValue,b.x());var k=f.values[g],l=b.y()(k,g);if(null!=l&&e.highlightPoint(j,g,!0),"undefined"!=typeof k){"undefined"==typeof d&&(d=k),"undefined"==typeof h&&(h=b.xScale()(b.x()(k,g)));var m="expand"==e.style()?k.display.y:b.y()(k,g);i.push({key:f.key,value:m,color:o(f,f.seriesIndex),stackedValue:k.display})}}),i.reverse(),i.length>2){var m=b.yScale().invert(c.mouseY),n=null;i.forEach(function(a,b){m=Math.abs(m);var c=Math.abs(a.stackedValue.y0),d=Math.abs(a.stackedValue.y);return m>=c&&d+c>=m?void(n=b):void 0}),null!=n&&(i[n].highlight=!0)}var p=f.tickFormat()(b.x()(d,g)),q=j.tooltip.valueFormatter();"expand"===e.style()||"stack_percent"===e.style()?(E||(E=q),q=d3.format(".1%")):E&&(q=E,E=null),j.tooltip.position({left:h+l.left,top:c.mouseY+l.top}).chartContainer(F.parentNode).valueFormatter(q).data({value:p,series:i})(),j.renderGuideLine(h)}),j.dispatch.on("elementMouseout",function(){e.clearHighlights()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&k.length===a.disabled.length&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),v.disabled=a.disabled),"undefined"!=typeof a.style&&(e.style(a.style),G=a.style),b.update()})}),F.renderEnd("stacked Area chart immediate"),b}var c,d,e=a.models.stackedArea(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:25,bottom:50,left:60},m=null,n=null,o=a.utils.defaultColor(),p=!0,q=!0,r=!0,s=!0,t=!1,u=!1,v=a.utils.state(),w=null,x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=250,A=["Stacked","Stream","Expanded"],B={},C=250;v.style=e.style(),f.orient("bottom").tickPadding(7),g.orient(t?"right":"left"),k.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)}),j.tooltip.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)});var D=null,E=null;i.updateState(!1);var F=a.utils.renderWatch(y),G=e.style(),H=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),style:e.style()}}},I=function(a){return function(b){void 0!==b.style&&(G=b.style),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},J=d3.format("%");return e.dispatch.on("elementMouseover.tooltip",function(a){a.point.x=e.x()(a.point),a.point.y=e.y()(a.point),k.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),b.dispatch=y,b.stacked=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.interactiveLayer=j,b.tooltip=k,b.dispatch=y,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return w},set:function(a){w=a}},noData:{get:function(){return x},set:function(a){x=a}},showControls:{get:function(){return p},set:function(a){p=a}},controlLabels:{get:function(){return B},set:function(a){B=a}},controlOptions:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},duration:{get:function(){return C},set:function(a){C=a,F.reset(C),e.duration(C),f.duration(C),g.duration(C)}},color:{get:function(){return o},set:function(b){o=a.utils.getColor(b),h.color(o),e.color(o)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},useInteractiveGuideline:{get:function(){return u},set:function(a){u=!!a,b.interactive(!a),b.useVoronoi(!a),e.scatter.interactive(!a)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.sunburst=function(){"use strict";function b(u){return t.reset(),u.each(function(b){function t(a){a.x0=a.x,a.dx0=a.dx}function u(a){var b=d3.interpolate(p.domain(),[a.x,a.x+a.dx]),c=d3.interpolate(q.domain(),[a.y,1]),d=d3.interpolate(q.range(),[a.y?20:0,y]);return function(a,e){return e?function(){return s(a)}:function(e){return p.domain(b(e)),q.domain(c(e)).range(d(e)),s(a)}}}l=d3.select(this);var v,w=a.utils.availableWidth(g,l,f),x=a.utils.availableHeight(h,l,f),y=Math.min(w,x)/2;a.utils.initSVG(l);var z=l.selectAll(".nv-wrap.nv-sunburst").data(b),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburst nv-chart-"+k),B=A.selectAll("nv-sunburst");z.attr("transform","translate("+w/2+","+x/2+")"),l.on("click",function(a,b){o.chartClick({data:a,index:b,pos:d3.event,id:k})}),q.range([0,y]),c=c||b,e=b[0],r.value(j[i]||j.count),v=B.data(r.nodes).enter().append("path").attr("d",s).style("fill",function(a){return m((a.children?a:a.parent).name)}).style("stroke","#FFF").on("click",function(a){d!==c&&c!==a&&(d=c),c=a,v.transition().duration(n).attrTween("d",u(a))}).each(t).on("dblclick",function(a){d.parent==a&&v.transition().duration(n).attrTween("d",u(e))}).each(t).on("mouseover",function(a){d3.select(this).classed("hover",!0).style("opacity",.8),o.elementMouseover({data:a,color:d3.select(this).style("fill")})}).on("mouseout",function(a){d3.select(this).classed("hover",!1).style("opacity",1),o.elementMouseout({data:a})}).on("mousemove",function(a){o.elementMousemove({data:a})})}),t.renderEnd("sunburst immediate"),b}var c,d,e,f={top:0,right:0,bottom:0,left:0},g=null,h=null,i="count",j={count:function(){return 1},size:function(a){return a.size}},k=Math.floor(1e4*Math.random()),l=null,m=a.utils.defaultColor(),n=500,o=d3.dispatch("chartClick","elementClick","elementDblClick","elementMousemove","elementMouseover","elementMouseout","renderEnd"),p=d3.scale.linear().range([0,2*Math.PI]),q=d3.scale.sqrt(),r=d3.layout.partition().sort(null).value(function(){return 1}),s=d3.svg.arc().startAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x)))}).endAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x+a.dx)))}).innerRadius(function(a){return Math.max(0,q(a.y))}).outerRadius(function(a){return Math.max(0,q(a.y+a.dy))}),t=a.utils.renderWatch(o);return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},mode:{get:function(){return i},set:function(a){i=a}},id:{get:function(){return k},set:function(a){k=a}},duration:{get:function(){return n},set:function(a){n=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!=a.top?a.top:f.top,f.right=void 0!=a.right?a.right:f.right,f.bottom=void 0!=a.bottom?a.bottom:f.bottom,f.left=void 0!=a.left?a.left:f.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sunburstChart=function(){"use strict";function b(d){return m.reset(),m.models(c),d.each(function(d){var h=d3.select(this);a.utils.initSVG(h);var i=a.utils.availableWidth(f,h,e),j=a.utils.availableHeight(g,h,e);if(b.update=function(){0===k?h.call(b):h.transition().duration(k).call(b)},b.container=this,!d||!d.length)return a.utils.noData(b,h),b;h.selectAll(".nv-noData").remove();var l=h.selectAll("g.nv-wrap.nv-sunburstChart").data(d),m=l.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburstChart").append("g"),n=l.select("g");m.append("g").attr("class","nv-sunburstWrap"),l.attr("transform","translate("+e.left+","+e.top+")"),c.width(i).height(j);var o=n.select(".nv-sunburstWrap").datum(d);d3.transition(o).call(c)}),m.renderEnd("sunburstChart immediate"),b}var c=a.models.sunburst(),d=a.models.tooltip(),e={top:30,right:20,bottom:20,left:20},f=null,g=null,h=a.utils.defaultColor(),i=(Math.round(1e5*Math.random()),null),j=null,k=250,l=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),m=a.utils.renderWatch(l);return d.headerEnabled(!1).duration(0).valueFormatter(function(a){return a}),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.data.name,value:a.data.size,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=l,b.sunburst=c,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return i},set:function(a){i=a}},color:{get:function(){return h},set:function(a){h=a,c.color(h)}},duration:{get:function(){return k},set:function(a){k=a,m.reset(k),c.duration(k)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.version="1.8.1"}();!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:NaN}function r(n){return null===n?NaN:+n}function i(n){return!isNaN(n)}function u(n){return{left:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)<0?r=u+1:i=u}return r},right:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)>0?i=u:r=u+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function l(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function c(){this._=Object.create(null)}function f(n){return(n+="")===bo||n[0]===_o?_o+n:n}function s(n){return(n+="")[0]===_o?n.slice(1):n}function h(n){return f(n)in this._}function p(n){return(n=f(n))in this._&&delete this._[n]}function g(){var n=[];for(var t in this._)n.push(s(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function y(){this._=Object.create(null)}function m(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=wo.length;r>e;++e){var i=wo[e]+t;if(i in n)return i}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,i=-1,u=r.length;++ie;e++)for(var i,u=n[e],o=0,a=u.length;a>o;o++)(i=u[o])&&t(i,o,e);return n}function Z(n){return ko(n,qo),n}function V(n){var t,e;return function(r,i,u){var o,a=n[u].update,l=a.length;for(u!=e&&(e=u,t=0),i>=t&&(t=i+1);!(o=a[t])&&++t0&&(n=n.slice(0,a));var c=To.get(n);return c&&(n=c,l=B),a?t?i:r:t?b:u}function $(n,t){return function(e){var r=ao.event;ao.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ao.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Do,i="click"+r,u=ao.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ro&&(Ro="onselectstart"in e?!1:x(e.style,"userSelect")),Ro){var o=n(e).style,a=o[Ro];o[Ro]="none"}return function(n){if(u.on(r,null),Ro&&(o[Ro]=a),n){var t=function(){u.on(i,null)};u.on(i,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var i=r.createSVGPoint();if(0>Po){var u=t(n);if(u.scrollX||u.scrollY){r=ao.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Po=!(o.f||o.e),r.remove()}}return Po?(i.x=e.pageX,i.y=e.pageY):(i.x=e.clientX,i.y=e.clientY),i=i.matrixTransform(n.getScreenCTM().inverse()),[i.x,i.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ao.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nn(n){return n>1?0:-1>n?Fo:Math.acos(n)}function tn(n){return n>1?Io:-1>n?-Io:Math.asin(n)}function en(n){return((n=Math.exp(n))-1/n)/2}function rn(n){return((n=Math.exp(n))+1/n)/2}function un(n){return((n=Math.exp(2*n))-1)/(n+1)}function on(n){return(n=Math.sin(n/2))*n}function an(){}function ln(n,t,e){return this instanceof ln?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof ln?new ln(n.h,n.s,n.l):_n(""+n,wn,ln):new ln(n,t,e)}function cn(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?u+(o-u)*n/60:180>n?o:240>n?u+(o-u)*(240-n)/60:u}function i(n){return Math.round(255*r(n))}var u,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,u=2*e-o,new mn(i(n+120),i(n),i(n-120))}function fn(n,t,e){return this instanceof fn?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof fn?new fn(n.h,n.c,n.l):n instanceof hn?gn(n.l,n.a,n.b):gn((n=Sn((n=ao.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new fn(n,t,e)}function sn(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new hn(e,Math.cos(n*=Yo)*t,Math.sin(n)*t)}function hn(n,t,e){return this instanceof hn?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof hn?new hn(n.l,n.a,n.b):n instanceof fn?sn(n.h,n.c,n.l):Sn((n=mn(n)).r,n.g,n.b):new hn(n,t,e)}function pn(n,t,e){var r=(n+16)/116,i=r+t/500,u=r-e/200;return i=vn(i)*na,r=vn(r)*ta,u=vn(u)*ea,new mn(yn(3.2404542*i-1.5371385*r-.4985314*u),yn(-.969266*i+1.8760108*r+.041556*u),yn(.0556434*i-.2040259*r+1.0572252*u))}function gn(n,t,e){return n>0?new fn(Math.atan2(e,t)*Zo,Math.sqrt(t*t+e*e),n):new fn(NaN,NaN,n)}function vn(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function dn(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function yn(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mn(n,t,e){return this instanceof mn?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mn?new mn(n.r,n.g,n.b):_n(""+n,mn,cn):new mn(n,t,e)}function Mn(n){return new mn(n>>16,n>>8&255,255&n)}function xn(n){return Mn(n)+""}function bn(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function _n(n,t,e){var r,i,u,o=0,a=0,l=0;if(r=/([a-z]+)\((.*)\)/.exec(n=n.toLowerCase()))switch(i=r[2].split(","),r[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Nn(i[0]),Nn(i[1]),Nn(i[2]))}return(u=ua.get(n))?t(u.r,u.g,u.b):(null==n||"#"!==n.charAt(0)||isNaN(u=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&u)>>4,o=o>>4|o,a=240&u,a=a>>4|a,l=15&u,l=l<<4|l):7===n.length&&(o=(16711680&u)>>16,a=(65280&u)>>8,l=255&u)),t(o,a,l))}function wn(n,t,e){var r,i,u=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-u,l=(o+u)/2;return a?(i=.5>l?a/(o+u):a/(2-o-u),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=NaN,i=l>0&&1>l?0:r),new ln(r,i,l)}function Sn(n,t,e){n=kn(n),t=kn(t),e=kn(e);var r=dn((.4124564*n+.3575761*t+.1804375*e)/na),i=dn((.2126729*n+.7151522*t+.072175*e)/ta),u=dn((.0193339*n+.119192*t+.9503041*e)/ea);return hn(116*i-16,500*(r-i),200*(i-u))}function kn(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Nn(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function En(n){return"function"==typeof n?n:function(){return n}}function An(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Cn(t,e,n,r)}}function Cn(n,t,e,r){function i(){var n,t=l.status;if(!t&&Ln(l)||t>=200&&300>t||304===t){try{n=e.call(u,l)}catch(r){return void o.error.call(u,r)}o.load.call(u,n)}else o.error.call(u,l)}var u={},o=ao.dispatch("beforesend","progress","load","error"),a={},l=new XMLHttpRequest,c=null;return!this.XDomainRequest||"withCredentials"in l||!/^(http(s)?:)?\/\//.test(n)||(l=new XDomainRequest),"onload"in l?l.onload=l.onerror=i:l.onreadystatechange=function(){l.readyState>3&&i()},l.onprogress=function(n){var t=ao.event;ao.event=n;try{o.progress.call(u,l)}finally{ao.event=t}},u.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",u)},u.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",u):t},u.responseType=function(n){return arguments.length?(c=n,u):c},u.response=function(n){return e=n,u},["get","post"].forEach(function(n){u[n]=function(){return u.send.apply(u,[n].concat(co(arguments)))}}),u.send=function(e,r,i){if(2===arguments.length&&"function"==typeof r&&(i=r,r=null),l.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),l.setRequestHeader)for(var f in a)l.setRequestHeader(f,a[f]);return null!=t&&l.overrideMimeType&&l.overrideMimeType(t),null!=c&&(l.responseType=c),null!=i&&u.on("error",i).on("load",function(n){i(null,n)}),o.beforesend.call(u,l),l.send(null==r?null:r),u},u.abort=function(){return l.abort(),u},ao.rebind(u,o,"on"),null==r?u:u.get(zn(r))}function zn(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Ln(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qn(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var i=e+t,u={c:n,t:i,n:null};return aa?aa.n=u:oa=u,aa=u,la||(ca=clearTimeout(ca),la=1,fa(Tn)),u}function Tn(){var n=Rn(),t=Dn()-n;t>24?(isFinite(t)&&(clearTimeout(ca),ca=setTimeout(Tn,t)),la=0):(la=1,fa(Tn))}function Rn(){for(var n=Date.now(),t=oa;t;)n>=t.t&&t.c(n-t.t)&&(t.c=null),t=t.n;return n}function Dn(){for(var n,t=oa,e=1/0;t;)t.c?(t.t8?function(n){return n/e}:function(n){return n*e},symbol:n}}function jn(n){var t=n.decimal,e=n.thousands,r=n.grouping,i=n.currency,u=r&&e?function(n,t){for(var i=n.length,u=[],o=0,a=r[0],l=0;i>0&&a>0&&(l+a+1>t&&(a=Math.max(1,t-l)),u.push(n.substring(i-=a,i+a)),!((l+=a+1)>t));)a=r[o=(o+1)%r.length];return u.reverse().join(e)}:m;return function(n){var e=ha.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",l=e[4]||"",c=e[5],f=+e[6],s=e[7],h=e[8],p=e[9],g=1,v="",d="",y=!1,m=!0;switch(h&&(h=+h.substring(1)),(c||"0"===r&&"="===o)&&(c=r="0",o="="),p){case"n":s=!0,p="g";break;case"%":g=100,d="%",p="f";break;case"p":g=100,d="%",p="r";break;case"b":case"o":case"x":case"X":"#"===l&&(v="0"+p.toLowerCase());case"c":m=!1;case"d":y=!0,h=0;break;case"s":g=-1,p="r"}"$"===l&&(v=i[0],d=i[1]),"r"!=p||h||(p="g"),null!=h&&("g"==p?h=Math.max(1,Math.min(21,h)):"e"!=p&&"f"!=p||(h=Math.max(0,Math.min(20,h)))),p=pa.get(p)||Fn;var M=c&&s;return function(n){var e=d;if(y&&n%1)return"";var i=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>g){var l=ao.formatPrefix(n,h);n=l.scale(n),e=l.symbol+d}else n*=g;n=p(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=m?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!c&&s&&(x=u(x,1/0));var S=v.length+x.length+b.length+(M?0:i.length),k=f>S?new Array(S=f-S+1).join(r):"";return M&&(x=u(k+x,k.length?f-b.length:1/0)),i+=v,n=x+b,("<"===o?i+n+k:">"===o?k+i+n:"^"===o?k.substring(0,S>>=1)+i+n+k.substring(S):i+(M?n:k+n))+e}}}function Fn(n){return n+""}function Hn(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function On(n,t,e){function r(t){var e=n(t),r=u(e,1);return r-t>t-e?e:r}function i(e){return t(e=n(new va(e-1)),1),e}function u(n,e){return t(n=new va(+n),e),n}function o(n,r,u){var o=i(n),a=[];if(u>1)for(;r>o;)e(o)%u||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{va=Hn;var r=new Hn;return r._=n,o(r,t,e)}finally{va=Date}}n.floor=n,n.round=r,n.ceil=i,n.offset=u,n.range=o;var l=n.utc=In(n);return l.floor=l,l.round=In(r),l.ceil=In(i),l.offset=In(u),l.range=a,n}function In(n){return function(t,e){try{va=Hn;var r=new Hn;return r._=t,n(r,e)._}finally{va=Date}}}function Yn(n){function t(n){function t(t){for(var e,i,u,o=[],a=-1,l=0;++aa;){if(r>=c)return-1;if(i=t.charCodeAt(a++),37===i){if(o=t.charAt(a++),u=C[o in ya?t.charAt(a++):o],!u||(r=u(n,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){N.lastIndex=0;var r=N.exec(t.slice(e));return r?(n.m=E.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,A.c.toString(),t,r)}function l(n,t,r){return e(n,A.x.toString(),t,r)}function c(n,t,r){return e(n,A.X.toString(),t,r)}function f(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var s=n.dateTime,h=n.date,p=n.time,g=n.periods,v=n.days,d=n.shortDays,y=n.months,m=n.shortMonths;t.utc=function(n){function e(n){try{va=Hn;var t=new va;return t._=n,r(t)}finally{va=Date}}var r=t(n);return e.parse=function(n){try{va=Hn;var t=r.parse(n);return t&&t._}finally{va=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ct;var M=ao.map(),x=Vn(v),b=Xn(v),_=Vn(d),w=Xn(d),S=Vn(y),k=Xn(y),N=Vn(m),E=Xn(m);g.forEach(function(n,t){M.set(n.toLowerCase(),t)});var A={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return m[n.getMonth()]},B:function(n){return y[n.getMonth()]},c:t(s),d:function(n,t){return Zn(n.getDate(),t,2)},e:function(n,t){return Zn(n.getDate(),t,2)},H:function(n,t){return Zn(n.getHours(),t,2)},I:function(n,t){return Zn(n.getHours()%12||12,t,2)},j:function(n,t){return Zn(1+ga.dayOfYear(n),t,3)},L:function(n,t){return Zn(n.getMilliseconds(),t,3)},m:function(n,t){return Zn(n.getMonth()+1,t,2)},M:function(n,t){return Zn(n.getMinutes(),t,2)},p:function(n){return g[+(n.getHours()>=12)]},S:function(n,t){return Zn(n.getSeconds(),t,2)},U:function(n,t){return Zn(ga.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Zn(ga.mondayOfYear(n),t,2)},x:t(h),X:t(p),y:function(n,t){return Zn(n.getFullYear()%100,t,2)},Y:function(n,t){return Zn(n.getFullYear()%1e4,t,4)},Z:at,"%":function(){return"%"}},C={a:r,A:i,b:u,B:o,c:a,d:tt,e:tt,H:rt,I:rt,j:et,L:ot,m:nt,M:it,p:f,S:ut,U:Bn,w:$n,W:Wn,x:l,X:c,y:Gn,Y:Jn,Z:Kn,"%":lt};return t}function Zn(n,t,e){var r=0>n?"-":"",i=(r?-n:n)+"",u=i.length;return r+(e>u?new Array(e-u+1).join(t)+i:i)}function Vn(n){return new RegExp("^(?:"+n.map(ao.requote).join("|")+")","i")}function Xn(n){for(var t=new c,e=-1,r=n.length;++e68?1900:2e3)}function nt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function tt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function et(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function rt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function it(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function ut(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ot(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function at(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=xo(t)/60|0,i=xo(t)%60;return e+Zn(r,"0",2)+Zn(i,"0",2)}function lt(n,t,e){Ma.lastIndex=0;var r=Ma.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ct(n){for(var t=n.length,e=-1;++e=0?1:-1,a=o*e,l=Math.cos(t),c=Math.sin(t),f=u*c,s=i*l+f*Math.cos(a),h=f*o*Math.sin(a);ka.add(Math.atan2(h,s)),r=n,i=l,u=c}var t,e,r,i,u;Na.point=function(o,a){Na.point=n,r=(t=o)*Yo,i=Math.cos(a=(e=a)*Yo/2+Fo/4),u=Math.sin(a)},Na.lineEnd=function(){n(t,e)}}function dt(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function yt(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function mt(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function Mt(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function xt(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function bt(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function _t(n){return[Math.atan2(n[1],n[0]),tn(n[2])]}function wt(n,t){return xo(n[0]-t[0])a;++a)i.point((e=n[a])[0],e[1]);return void i.lineEnd()}var l=new Tt(e,n,null,!0),c=new Tt(e,null,l,!1);l.o=c,u.push(l),o.push(c),l=new Tt(r,n,null,!1),c=new Tt(r,null,l,!0),l.o=c,u.push(l),o.push(c)}}),o.sort(t),qt(u),qt(o),u.length){for(var a=0,l=e,c=o.length;c>a;++a)o[a].e=l=!l;for(var f,s,h=u[0];;){for(var p=h,g=!0;p.v;)if((p=p.n)===h)return;f=p.z,i.lineStart();do{if(p.v=p.o.v=!0,p.e){if(g)for(var a=0,c=f.length;c>a;++a)i.point((s=f[a])[0],s[1]);else r(p.x,p.n.x,1,i);p=p.n}else{if(g){f=p.p.z;for(var a=f.length-1;a>=0;--a)i.point((s=f[a])[0],s[1])}else r(p.x,p.p.x,-1,i);p=p.p}p=p.o,f=p.z,g=!g}while(!p.v);i.lineEnd()}}}function qt(n){if(t=n.length){for(var t,e,r=0,i=n[0];++r0){for(b||(u.polygonStart(),b=!0),u.lineStart();++o1&&2&t&&e.push(e.pop().concat(e.shift())),p.push(e.filter(Dt))}var p,g,v,d=t(u),y=i.invert(r[0],r[1]),m={point:o,lineStart:l,lineEnd:c,polygonStart:function(){m.point=f,m.lineStart=s,m.lineEnd=h,p=[],g=[]},polygonEnd:function(){m.point=o,m.lineStart=l,m.lineEnd=c,p=ao.merge(p);var n=Ot(y,g);p.length?(b||(u.polygonStart(),b=!0),Lt(p,Ut,n,e,u)):n&&(b||(u.polygonStart(),b=!0),u.lineStart(),e(null,null,1,u),u.lineEnd()),b&&(u.polygonEnd(),b=!1),p=g=null},sphere:function(){u.polygonStart(),u.lineStart(),e(null,null,1,u),u.lineEnd(),u.polygonEnd()}},M=Pt(),x=t(M),b=!1;return m}}function Dt(n){return n.length>1}function Pt(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ut(n,t){return((n=n.x)[0]<0?n[1]-Io-Uo:Io-n[1])-((t=t.x)[0]<0?t[1]-Io-Uo:Io-t[1])}function jt(n){var t,e=NaN,r=NaN,i=NaN;return{lineStart:function(){n.lineStart(),t=1},point:function(u,o){var a=u>0?Fo:-Fo,l=xo(u-e);xo(l-Fo)0?Io:-Io),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(u,r),t=0):i!==a&&l>=Fo&&(xo(e-i)Uo?Math.atan((Math.sin(t)*(u=Math.cos(r))*Math.sin(e)-Math.sin(r)*(i=Math.cos(t))*Math.sin(n))/(i*u*o)):(t+r)/2}function Ht(n,t,e,r){var i;if(null==n)i=e*Io,r.point(-Fo,i),r.point(0,i),r.point(Fo,i),r.point(Fo,0),r.point(Fo,-i),r.point(0,-i),r.point(-Fo,-i),r.point(-Fo,0),r.point(-Fo,i);else if(xo(n[0]-t[0])>Uo){var u=n[0]a;++a){var c=t[a],f=c.length;if(f)for(var s=c[0],h=s[0],p=s[1]/2+Fo/4,g=Math.sin(p),v=Math.cos(p),d=1;;){d===f&&(d=0),n=c[d];var y=n[0],m=n[1]/2+Fo/4,M=Math.sin(m),x=Math.cos(m),b=y-h,_=b>=0?1:-1,w=_*b,S=w>Fo,k=g*M;if(ka.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),u+=S?b+_*Ho:b,S^h>=e^y>=e){var N=mt(dt(s),dt(n));bt(N);var E=mt(i,N);bt(E);var A=(S^b>=0?-1:1)*tn(E[2]);(r>A||r===A&&(N[0]||N[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=y,g=M,v=x,s=n}}return(-Uo>u||Uo>u&&-Uo>ka)^1&o}function It(n){function t(n,t){return Math.cos(n)*Math.cos(t)>u}function e(n){var e,u,l,c,f;return{lineStart:function(){c=l=!1,f=1},point:function(s,h){var p,g=[s,h],v=t(s,h),d=o?v?0:i(s,h):v?i(s+(0>s?Fo:-Fo),h):0;if(!e&&(c=l=v)&&n.lineStart(),v!==l&&(p=r(e,g),(wt(e,p)||wt(g,p))&&(g[0]+=Uo,g[1]+=Uo,v=t(g[0],g[1]))),v!==l)f=0,v?(n.lineStart(),p=r(g,e),n.point(p[0],p[1])):(p=r(e,g),n.point(p[0],p[1]),n.lineEnd()),e=p;else if(a&&e&&o^v){var y;d&u||!(y=r(g,e,!0))||(f=0,o?(n.lineStart(),n.point(y[0][0],y[0][1]),n.point(y[1][0],y[1][1]),n.lineEnd()):(n.point(y[1][0],y[1][1]),n.lineEnd(),n.lineStart(),n.point(y[0][0],y[0][1])))}!v||e&&wt(e,g)||n.point(g[0],g[1]),e=g,l=v,u=d},lineEnd:function(){l&&n.lineEnd(),e=null},clean:function(){return f|(c&&l)<<1}}}function r(n,t,e){var r=dt(n),i=dt(t),o=[1,0,0],a=mt(r,i),l=yt(a,a),c=a[0],f=l-c*c;if(!f)return!e&&n;var s=u*l/f,h=-u*c/f,p=mt(o,a),g=xt(o,s),v=xt(a,h);Mt(g,v);var d=p,y=yt(g,d),m=yt(d,d),M=y*y-m*(yt(g,g)-1);if(!(0>M)){var x=Math.sqrt(M),b=xt(d,(-y-x)/m);if(Mt(b,g),b=_t(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],N=t[1];w>S&&(_=w,w=S,S=_);var E=S-w,A=xo(E-Fo)E;if(!A&&k>N&&(_=k,k=N,N=_),C?A?k+N>0^b[1]<(xo(b[0]-w)Fo^(w<=b[0]&&b[0]<=S)){var z=xt(d,(-y+x)/m);return Mt(z,g),[b,_t(z)]}}}function i(t,e){var r=o?n:Fo-n,i=0;return-r>t?i|=1:t>r&&(i|=2),-r>e?i|=4:e>r&&(i|=8),i}var u=Math.cos(n),o=u>0,a=xo(u)>Uo,l=ve(n,6*Yo);return Rt(t,e,l,o?[0,-n]:[-Fo,n-Fo])}function Yt(n,t,e,r){return function(i){var u,o=i.a,a=i.b,l=o.x,c=o.y,f=a.x,s=a.y,h=0,p=1,g=f-l,v=s-c;if(u=n-l,g||!(u>0)){if(u/=g,0>g){if(h>u)return;p>u&&(p=u)}else if(g>0){if(u>p)return;u>h&&(h=u)}if(u=e-l,g||!(0>u)){if(u/=g,0>g){if(u>p)return;u>h&&(h=u)}else if(g>0){if(h>u)return;p>u&&(p=u)}if(u=t-c,v||!(u>0)){if(u/=v,0>v){if(h>u)return;p>u&&(p=u)}else if(v>0){if(u>p)return;u>h&&(h=u)}if(u=r-c,v||!(0>u)){if(u/=v,0>v){if(u>p)return;u>h&&(h=u)}else if(v>0){if(h>u)return;p>u&&(p=u)}return h>0&&(i.a={x:l+h*g,y:c+h*v}),1>p&&(i.b={x:l+p*g,y:c+p*v}),i}}}}}}function Zt(n,t,e,r){function i(r,i){return xo(r[0]-n)0?0:3:xo(r[0]-e)0?2:1:xo(r[1]-t)0?1:0:i>0?3:2}function u(n,t){return o(n.x,t.x)}function o(n,t){var e=i(n,1),r=i(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function l(n){for(var t=0,e=d.length,r=n[1],i=0;e>i;++i)for(var u,o=1,a=d[i],l=a.length,c=a[0];l>o;++o)u=a[o],c[1]<=r?u[1]>r&&Q(c,u,n)>0&&++t:u[1]<=r&&Q(c,u,n)<0&&--t,c=u;return 0!==t}function c(u,a,l,c){var f=0,s=0;if(null==u||(f=i(u,l))!==(s=i(a,l))||o(u,a)<0^l>0){do c.point(0===f||3===f?n:e,f>1?r:t);while((f=(f+l+4)%4)!==s)}else c.point(a[0],a[1])}function f(i,u){return i>=n&&e>=i&&u>=t&&r>=u}function s(n,t){f(n,t)&&a.point(n,t)}function h(){C.point=g,d&&d.push(y=[]),S=!0,w=!1,b=_=NaN}function p(){v&&(g(m,M),x&&w&&E.rejoin(),v.push(E.buffer())),C.point=s,w&&a.lineEnd()}function g(n,t){n=Math.max(-Ha,Math.min(Ha,n)),t=Math.max(-Ha,Math.min(Ha,t));var e=f(n,t);if(d&&y.push([n,t]),S)m=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};A(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,y,m,M,x,b,_,w,S,k,N=a,E=Pt(),A=Yt(n,t,e,r),C={point:s,lineStart:h,lineEnd:p,polygonStart:function(){a=E,v=[],d=[],k=!0},polygonEnd:function(){a=N,v=ao.merge(v);var t=l([n,r]),e=k&&t,i=v.length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),c(null,null,1,a),a.lineEnd()),i&&Lt(v,u,t,c,a),a.polygonEnd()),v=d=y=null}};return C}}function Vt(n){var t=0,e=Fo/3,r=ae(n),i=r(t,e);return i.parallels=function(n){return arguments.length?r(t=n[0]*Fo/180,e=n[1]*Fo/180):[t/Fo*180,e/Fo*180]},i}function Xt(n,t){function e(n,t){var e=Math.sqrt(u-2*i*Math.sin(t))/i;return[e*Math.sin(n*=i),o-e*Math.cos(n)]}var r=Math.sin(n),i=(r+Math.sin(t))/2,u=1+r*(2*i-r),o=Math.sqrt(u)/i;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/i,tn((u-(n*n+e*e)*i*i)/(2*i))]},e}function $t(){function n(n,t){Ia+=i*n-r*t,r=n,i=t}var t,e,r,i;$a.point=function(u,o){$a.point=n,t=r=u,e=i=o},$a.lineEnd=function(){n(t,e)}}function Bt(n,t){Ya>n&&(Ya=n),n>Va&&(Va=n),Za>t&&(Za=t),t>Xa&&(Xa=t)}function Wt(){function n(n,t){o.push("M",n,",",t,u)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function i(){o.push("Z")}var u=Jt(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return u=Jt(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Jt(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Gt(n,t){Ca+=n,za+=t,++La}function Kt(){function n(n,r){var i=n-t,u=r-e,o=Math.sqrt(i*i+u*u);qa+=o*(t+n)/2,Ta+=o*(e+r)/2,Ra+=o,Gt(t=n,e=r)}var t,e;Wa.point=function(r,i){Wa.point=n,Gt(t=r,e=i)}}function Qt(){Wa.point=Gt}function ne(){function n(n,t){var e=n-r,u=t-i,o=Math.sqrt(e*e+u*u);qa+=o*(r+n)/2,Ta+=o*(i+t)/2,Ra+=o,o=i*n-r*t,Da+=o*(r+n),Pa+=o*(i+t),Ua+=3*o,Gt(r=n,i=t)}var t,e,r,i;Wa.point=function(u,o){Wa.point=n,Gt(t=r=u,e=i=o)},Wa.lineEnd=function(){n(t,e)}}function te(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,Ho)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function i(){a.point=t}function u(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:i,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=i,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function ee(n){function t(n){return(a?r:e)(n)}function e(t){return ue(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=NaN,S.point=u,t.lineStart()}function u(e,r){var u=dt([e,r]),o=n(e,r);i(M,x,m,b,_,w,M=o[0],x=o[1],m=e,b=u[0],_=u[1],w=u[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function l(){ -r(),S.point=c,S.lineEnd=f}function c(n,t){u(s=n,h=t),p=M,g=x,v=b,d=_,y=w,S.point=u}function f(){i(M,x,m,b,_,w,p,g,s,v,d,y,a,t),S.lineEnd=o,o()}var s,h,p,g,v,d,y,m,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=l},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function i(t,e,r,a,l,c,f,s,h,p,g,v,d,y){var m=f-t,M=s-e,x=m*m+M*M;if(x>4*u&&d--){var b=a+p,_=l+g,w=c+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),N=xo(xo(w)-1)u||xo((m*z+M*L)/x-.5)>.3||o>a*p+l*g+c*v)&&(i(t,e,r,a,l,c,A,C,N,b/=S,_/=S,w,d,y),y.point(A,C),i(A,C,N,b,_,w,f,s,h,p,g,v,d,y))}}var u=.5,o=Math.cos(30*Yo),a=16;return t.precision=function(n){return arguments.length?(a=(u=n*n)>0&&16,t):Math.sqrt(u)},t}function re(n){var t=ee(function(t,e){return n([t*Zo,e*Zo])});return function(n){return le(t(n))}}function ie(n){this.stream=n}function ue(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function oe(n){return ae(function(){return n})()}function ae(n){function t(n){return n=a(n[0]*Yo,n[1]*Yo),[n[0]*h+l,c-n[1]*h]}function e(n){return n=a.invert((n[0]-l)/h,(c-n[1])/h),n&&[n[0]*Zo,n[1]*Zo]}function r(){a=Ct(o=se(y,M,x),u);var n=u(v,d);return l=p-n[0]*h,c=g+n[1]*h,i()}function i(){return f&&(f.valid=!1,f=null),t}var u,o,a,l,c,f,s=ee(function(n,t){return n=u(n,t),[n[0]*h+l,c-n[1]*h]}),h=150,p=480,g=250,v=0,d=0,y=0,M=0,x=0,b=Fa,_=m,w=null,S=null;return t.stream=function(n){return f&&(f.valid=!1),f=le(b(o,s(_(n)))),f.valid=!0,f},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Fa):It((w=+n)*Yo),i()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Zt(n[0][0],n[0][1],n[1][0],n[1][1]):m,i()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(p=+n[0],g=+n[1],r()):[p,g]},t.center=function(n){return arguments.length?(v=n[0]%360*Yo,d=n[1]%360*Yo,r()):[v*Zo,d*Zo]},t.rotate=function(n){return arguments.length?(y=n[0]%360*Yo,M=n[1]%360*Yo,x=n.length>2?n[2]%360*Yo:0,r()):[y*Zo,M*Zo,x*Zo]},ao.rebind(t,s,"precision"),function(){return u=n.apply(this,arguments),t.invert=u.invert&&e,r()}}function le(n){return ue(n,function(t,e){n.point(t*Yo,e*Yo)})}function ce(n,t){return[n,t]}function fe(n,t){return[n>Fo?n-Ho:-Fo>n?n+Ho:n,t]}function se(n,t,e){return n?t||e?Ct(pe(n),ge(t,e)):pe(n):t||e?ge(t,e):fe}function he(n){return function(t,e){return t+=n,[t>Fo?t-Ho:-Fo>t?t+Ho:t,e]}}function pe(n){var t=he(n);return t.invert=he(-n),t}function ge(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*r+a*i;return[Math.atan2(l*u-f*o,a*r-c*i),tn(f*u+l*o)]}var r=Math.cos(n),i=Math.sin(n),u=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*u-l*o;return[Math.atan2(l*u+c*o,a*r+f*i),tn(f*r-a*i)]},e}function ve(n,t){var e=Math.cos(n),r=Math.sin(n);return function(i,u,o,a){var l=o*t;null!=i?(i=de(e,i),u=de(e,u),(o>0?u>i:i>u)&&(i+=o*Ho)):(i=n+o*Ho,u=n-.5*l);for(var c,f=i;o>0?f>u:u>f;f-=l)a.point((c=_t([e,-r*Math.cos(f),-r*Math.sin(f)]))[0],c[1])}}function de(n,t){var e=dt(t);e[0]-=n,bt(e);var r=nn(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Uo)%(2*Math.PI)}function ye(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function me(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function Me(n){return n.source}function xe(n){return n.target}function be(n,t,e,r){var i=Math.cos(t),u=Math.sin(t),o=Math.cos(r),a=Math.sin(r),l=i*Math.cos(n),c=i*Math.sin(n),f=o*Math.cos(e),s=o*Math.sin(e),h=2*Math.asin(Math.sqrt(on(r-t)+i*o*on(e-n))),p=1/Math.sin(h),g=h?function(n){var t=Math.sin(n*=h)*p,e=Math.sin(h-n)*p,r=e*l+t*f,i=e*c+t*s,o=e*u+t*a;return[Math.atan2(i,r)*Zo,Math.atan2(o,Math.sqrt(r*r+i*i))*Zo]}:function(){return[n*Zo,t*Zo]};return g.distance=h,g}function _e(){function n(n,i){var u=Math.sin(i*=Yo),o=Math.cos(i),a=xo((n*=Yo)-t),l=Math.cos(a);Ja+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*u-e*o*l)*a),e*u+r*o*l),t=n,e=u,r=o}var t,e,r;Ga.point=function(i,u){t=i*Yo,e=Math.sin(u*=Yo),r=Math.cos(u),Ga.point=n},Ga.lineEnd=function(){Ga.point=Ga.lineEnd=b}}function we(n,t){function e(t,e){var r=Math.cos(t),i=Math.cos(e),u=n(r*i);return[u*i*Math.sin(t),u*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),i=t(r),u=Math.sin(i),o=Math.cos(i);return[Math.atan2(n*u,r*o),Math.asin(r&&e*u/r)]},e}function Se(n,t){function e(n,t){o>0?-Io+Uo>t&&(t=-Io+Uo):t>Io-Uo&&(t=Io-Uo);var e=o/Math.pow(i(t),u);return[e*Math.sin(u*n),o-e*Math.cos(u*n)]}var r=Math.cos(n),i=function(n){return Math.tan(Fo/4+n/2)},u=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(i(t)/i(n)),o=r*Math.pow(i(n),u)/u;return u?(e.invert=function(n,t){var e=o-t,r=K(u)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/u,2*Math.atan(Math.pow(o/r,1/u))-Io]},e):Ne}function ke(n,t){function e(n,t){var e=u-t;return[e*Math.sin(i*n),u-e*Math.cos(i*n)]}var r=Math.cos(n),i=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),u=r/i+n;return xo(i)i;i++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function qe(n,t){return n[0]-t[0]||n[1]-t[1]}function Te(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Re(n,t,e,r){var i=n[0],u=e[0],o=t[0]-i,a=r[0]-u,l=n[1],c=e[1],f=t[1]-l,s=r[1]-c,h=(a*(l-c)-s*(i-u))/(s*o-a*f);return[i+h*o,l+h*f]}function De(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Pe(){rr(this),this.edge=this.site=this.circle=null}function Ue(n){var t=cl.pop()||new Pe;return t.site=n,t}function je(n){Be(n),ol.remove(n),cl.push(n),rr(n)}function Fe(n){var t=n.circle,e=t.x,r=t.cy,i={x:e,y:r},u=n.P,o=n.N,a=[n];je(n);for(var l=u;l.circle&&xo(e-l.circle.x)f;++f)c=a[f],l=a[f-1],nr(c.edge,l.site,c.site,i);l=a[0],c=a[s-1],c.edge=Ke(l.site,c.site,null,i),$e(l),$e(c)}function He(n){for(var t,e,r,i,u=n.x,o=n.y,a=ol._;a;)if(r=Oe(a,o)-u,r>Uo)a=a.L;else{if(i=u-Ie(a,o),!(i>Uo)){r>-Uo?(t=a.P,e=a):i>-Uo?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var l=Ue(n);if(ol.insert(t,l),t||e){if(t===e)return Be(t),e=Ue(t.site),ol.insert(l,e),l.edge=e.edge=Ke(t.site,l.site),$e(t),void $e(e);if(!e)return void(l.edge=Ke(t.site,l.site));Be(t),Be(e);var c=t.site,f=c.x,s=c.y,h=n.x-f,p=n.y-s,g=e.site,v=g.x-f,d=g.y-s,y=2*(h*d-p*v),m=h*h+p*p,M=v*v+d*d,x={x:(d*m-p*M)/y+f,y:(h*M-v*m)/y+s};nr(e.edge,c,g,x),l.edge=Ke(c,n,null,x),e.edge=Ke(n,g,null,x),$e(t),$e(e)}}function Oe(n,t){var e=n.site,r=e.x,i=e.y,u=i-t;if(!u)return r;var o=n.P;if(!o)return-(1/0);e=o.site;var a=e.x,l=e.y,c=l-t;if(!c)return a;var f=a-r,s=1/u-1/c,h=f/c;return s?(-h+Math.sqrt(h*h-2*s*(f*f/(-2*c)-l+c/2+i-u/2)))/s+r:(r+a)/2}function Ie(n,t){var e=n.N;if(e)return Oe(e,t);var r=n.site;return r.y===t?r.x:1/0}function Ye(n){this.site=n,this.edges=[]}function Ze(n){for(var t,e,r,i,u,o,a,l,c,f,s=n[0][0],h=n[1][0],p=n[0][1],g=n[1][1],v=ul,d=v.length;d--;)if(u=v[d],u&&u.prepare())for(a=u.edges,l=a.length,o=0;l>o;)f=a[o].end(),r=f.x,i=f.y,c=a[++o%l].start(),t=c.x,e=c.y,(xo(r-t)>Uo||xo(i-e)>Uo)&&(a.splice(o,0,new tr(Qe(u.site,f,xo(r-s)Uo?{x:s,y:xo(t-s)Uo?{x:xo(e-g)Uo?{x:h,y:xo(t-h)Uo?{x:xo(e-p)=-jo)){var p=l*l+c*c,g=f*f+s*s,v=(s*p-c*g)/h,d=(l*g-f*p)/h,s=d+a,y=fl.pop()||new Xe;y.arc=n,y.site=i,y.x=v+o,y.y=s+Math.sqrt(v*v+d*d),y.cy=s,n.circle=y;for(var m=null,M=ll._;M;)if(y.yd||d>=a)return;if(h>g){if(u){if(u.y>=c)return}else u={x:d,y:l};e={x:d,y:c}}else{if(u){if(u.yr||r>1)if(h>g){if(u){if(u.y>=c)return}else u={x:(l-i)/r,y:l};e={x:(c-i)/r,y:c}}else{if(u){if(u.yp){if(u){if(u.x>=a)return}else u={x:o,y:r*o+i};e={x:a,y:r*a+i}}else{if(u){if(u.xu||s>o||r>h||i>p)){if(g=n.point){var g,v=t-n.x,d=e-n.y,y=v*v+d*d;if(l>y){var m=Math.sqrt(l=y);r=t-m,i=e-m,u=t+m,o=e+m,a=g}}for(var M=n.nodes,x=.5*(f+h),b=.5*(s+p),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:c(n,f,s,x,b);break;case 1:c(n,x,s,h,b);break;case 2:c(n,f,b,x,p);break;case 3:c(n,x,b,h,p)}}}(n,r,i,u,o),a}function vr(n,t){n=ao.rgb(n),t=ao.rgb(t);var e=n.r,r=n.g,i=n.b,u=t.r-e,o=t.g-r,a=t.b-i;return function(n){return"#"+bn(Math.round(e+u*n))+bn(Math.round(r+o*n))+bn(Math.round(i+a*n))}}function dr(n,t){var e,r={},i={};for(e in n)e in t?r[e]=Mr(n[e],t[e]):i[e]=n[e];for(e in t)e in n||(i[e]=t[e]);return function(n){for(e in r)i[e]=r[e](n);return i}}function yr(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function mr(n,t){var e,r,i,u=hl.lastIndex=pl.lastIndex=0,o=-1,a=[],l=[];for(n+="",t+="";(e=hl.exec(n))&&(r=pl.exec(t));)(i=r.index)>u&&(i=t.slice(u,i),a[o]?a[o]+=i:a[++o]=i),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,l.push({i:o,x:yr(e,r)})),u=pl.lastIndex;return ur;++r)a[(e=l[r]).i]=e.x(n);return a.join("")})}function Mr(n,t){for(var e,r=ao.interpolators.length;--r>=0&&!(e=ao.interpolators[r](n,t)););return e}function xr(n,t){var e,r=[],i=[],u=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(Mr(n[e],t[e]));for(;u>e;++e)i[e]=n[e];for(;o>e;++e)i[e]=t[e];return function(n){for(e=0;a>e;++e)i[e]=r[e](n);return i}}function br(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function _r(n){return function(t){return 1-n(1-t)}}function wr(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function Sr(n){return n*n}function kr(n){return n*n*n}function Nr(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function Er(n){return function(t){return Math.pow(t,n)}}function Ar(n){return 1-Math.cos(n*Io)}function Cr(n){return Math.pow(2,10*(n-1))}function zr(n){return 1-Math.sqrt(1-n*n)}function Lr(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/Ho*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*Ho/t)}}function qr(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Tr(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Rr(n,t){n=ao.hcl(n),t=ao.hcl(t);var e=n.h,r=n.c,i=n.l,u=t.h-e,o=t.c-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return sn(e+u*n,r+o*n,i+a*n)+""}}function Dr(n,t){n=ao.hsl(n),t=ao.hsl(t);var e=n.h,r=n.s,i=n.l,u=t.h-e,o=t.s-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return cn(e+u*n,r+o*n,i+a*n)+""}}function Pr(n,t){n=ao.lab(n),t=ao.lab(t);var e=n.l,r=n.a,i=n.b,u=t.l-e,o=t.a-r,a=t.b-i;return function(n){return pn(e+u*n,r+o*n,i+a*n)+""}}function Ur(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function jr(n){var t=[n.a,n.b],e=[n.c,n.d],r=Hr(t),i=Fr(t,e),u=Hr(Or(e,t,-i))||0;t[0]*e[1]180?t+=360:t-n>180&&(n+=360),r.push({i:e.push(Ir(e)+"rotate(",null,")")-2,x:yr(n,t)})):t&&e.push(Ir(e)+"rotate("+t+")")}function Vr(n,t,e,r){n!==t?r.push({i:e.push(Ir(e)+"skewX(",null,")")-2,x:yr(n,t)}):t&&e.push(Ir(e)+"skewX("+t+")")}function Xr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push(Ir(e)+"scale(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else 1===t[0]&&1===t[1]||e.push(Ir(e)+"scale("+t+")")}function $r(n,t){var e=[],r=[];return n=ao.transform(n),t=ao.transform(t),Yr(n.translate,t.translate,e,r),Zr(n.rotate,t.rotate,e,r),Vr(n.skew,t.skew,e,r),Xr(n.scale,t.scale,e,r),n=t=null,function(n){for(var t,i=-1,u=r.length;++i=0;)e.push(i[r])}function oi(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(u=n.children)&&(i=u.length))for(var i,u,o=-1;++oe;++e)(t=n[e][1])>i&&(r=e,i=t);return r}function yi(n){return n.reduce(mi,0)}function mi(n,t){return n+t[1]}function Mi(n,t){return xi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function xi(n,t){for(var e=-1,r=+n[0],i=(n[1]-r)/t,u=[];++e<=t;)u[e]=i*e+r;return u}function bi(n){return[ao.min(n),ao.max(n)]}function _i(n,t){return n.value-t.value}function wi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Si(n,t){n._pack_next=t,t._pack_prev=n}function ki(n,t){var e=t.x-n.x,r=t.y-n.y,i=n.r+t.r;return.999*i*i>e*e+r*r}function Ni(n){function t(n){f=Math.min(n.x-n.r,f),s=Math.max(n.x+n.r,s),h=Math.min(n.y-n.r,h),p=Math.max(n.y+n.r,p)}if((e=n.children)&&(c=e.length)){var e,r,i,u,o,a,l,c,f=1/0,s=-(1/0),h=1/0,p=-(1/0);if(e.forEach(Ei),r=e[0],r.x=-r.r,r.y=0,t(r),c>1&&(i=e[1],i.x=i.r,i.y=0,t(i),c>2))for(u=e[2],zi(r,i,u),t(u),wi(r,u),r._pack_prev=u,wi(u,i),i=r._pack_next,o=3;c>o;o++){zi(r,i,u=e[o]);var g=0,v=1,d=1;for(a=i._pack_next;a!==i;a=a._pack_next,v++)if(ki(a,u)){g=1;break}if(1==g)for(l=r._pack_prev;l!==a._pack_prev&&!ki(l,u);l=l._pack_prev,d++);g?(d>v||v==d&&i.ro;o++)u=e[o],u.x-=y,u.y-=m,M=Math.max(M,u.r+Math.sqrt(u.x*u.x+u.y*u.y));n.r=M,e.forEach(Ai)}}function Ei(n){n._pack_next=n._pack_prev=n}function Ai(n){delete n._pack_next,delete n._pack_prev}function Ci(n,t,e,r){var i=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,i)for(var u=-1,o=i.length;++u=0;)t=i[u],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Pi(n,t,e){return n.a.parent===t.parent?n.a:e}function Ui(n){return 1+ao.max(n,function(n){return n.y})}function ji(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Fi(n){var t=n.children;return t&&t.length?Fi(t[0]):n}function Hi(n){var t,e=n.children;return e&&(t=e.length)?Hi(e[t-1]):n}function Oi(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Ii(n,t){var e=n.x+t[3],r=n.y+t[0],i=n.dx-t[1]-t[3],u=n.dy-t[0]-t[2];return 0>i&&(e+=i/2,i=0),0>u&&(r+=u/2,u=0),{x:e,y:r,dx:i,dy:u}}function Yi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Zi(n){return n.rangeExtent?n.rangeExtent():Yi(n.range())}function Vi(n,t,e,r){var i=e(n[0],n[1]),u=r(t[0],t[1]);return function(n){return u(i(n))}}function Xi(n,t){var e,r=0,i=n.length-1,u=n[r],o=n[i];return u>o&&(e=r,r=i,i=e,e=u,u=o,o=e),n[r]=t.floor(u),n[i]=t.ceil(o),n}function $i(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:Sl}function Bi(n,t,e,r){var i=[],u=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]2?Bi:Vi,l=r?Wr:Br;return o=i(n,t,l,e),a=i(t,n,l,Mr),u}function u(n){return o(n)}var o,a;return u.invert=function(n){return a(n)},u.domain=function(t){return arguments.length?(n=t.map(Number),i()):n},u.range=function(n){return arguments.length?(t=n,i()):t},u.rangeRound=function(n){return u.range(n).interpolate(Ur)},u.clamp=function(n){return arguments.length?(r=n,i()):r},u.interpolate=function(n){return arguments.length?(e=n,i()):e},u.ticks=function(t){return Qi(n,t)},u.tickFormat=function(t,e){return nu(n,t,e)},u.nice=function(t){return Gi(n,t),i()},u.copy=function(){return Wi(n,t,e,r)},i()}function Ji(n,t){return ao.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Gi(n,t){return Xi(n,$i(Ki(n,t)[2])),Xi(n,$i(Ki(n,t)[2])),n}function Ki(n,t){null==t&&(t=10);var e=Yi(n),r=e[1]-e[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),u=t/r*i;return.15>=u?i*=10:.35>=u?i*=5:.75>=u&&(i*=2),e[0]=Math.ceil(e[0]/i)*i,e[1]=Math.floor(e[1]/i)*i+.5*i,e[2]=i,e}function Qi(n,t){return ao.range.apply(ao,Ki(n,t))}function nu(n,t,e){var r=Ki(n,t);if(e){var i=ha.exec(e);if(i.shift(),"s"===i[8]){var u=ao.formatPrefix(Math.max(xo(r[0]),xo(r[1])));return i[7]||(i[7]="."+tu(u.scale(r[2]))),i[8]="f",e=ao.format(i.join("")),function(n){return e(u.scale(n))+u.symbol}}i[7]||(i[7]="."+eu(i[8],r)),e=i.join("")}else e=",."+tu(r[2])+"f";return ao.format(e)}function tu(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function eu(n,t){var e=tu(t[2]);return n in kl?Math.abs(e-tu(Math.max(xo(t[0]),xo(t[1]))))+ +("e"!==n):e-2*("%"===n)}function ru(n,t,e,r){function i(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function u(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(i(t))}return o.invert=function(t){return u(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(i)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(i)),o):t},o.nice=function(){var t=Xi(r.map(i),e?Math:El);return n.domain(t),r=t.map(u),o},o.ticks=function(){var n=Yi(r),o=[],a=n[0],l=n[1],c=Math.floor(i(a)),f=Math.ceil(i(l)),s=t%1?2:t;if(isFinite(f-c)){if(e){for(;f>c;c++)for(var h=1;s>h;h++)o.push(u(c)*h);o.push(u(c))}else for(o.push(u(c));c++0;h--)o.push(u(c)*h);for(c=0;o[c]l;f--);o=o.slice(c,f)}return o},o.tickFormat=function(n,e){if(!arguments.length)return Nl;arguments.length<2?e=Nl:"function"!=typeof e&&(e=ao.format(e));var r=Math.max(1,t*n/o.ticks().length);return function(n){var o=n/u(Math.round(i(n)));return t-.5>o*t&&(o*=t),r>=o?e(n):""}},o.copy=function(){return ru(n.copy(),t,e,r)},Ji(o,n)}function iu(n,t,e){function r(t){return n(i(t))}var i=uu(t),u=uu(1/t);return r.invert=function(t){return u(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(i)),r):e},r.ticks=function(n){return Qi(e,n)},r.tickFormat=function(n,t){return nu(e,n,t)},r.nice=function(n){return r.domain(Gi(e,n))},r.exponent=function(o){return arguments.length?(i=uu(t=o),u=uu(1/t),n.domain(e.map(i)),r):t},r.copy=function(){return iu(n.copy(),t,e)},Ji(r,n)}function uu(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function ou(n,t){function e(e){return u[((i.get(e)||("range"===t.t?i.set(e,n.push(e)):NaN))-1)%u.length]}function r(t,e){return ao.range(n.length).map(function(n){return t+e*n})}var i,u,o;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new c;for(var u,o=-1,a=r.length;++oe?[NaN,NaN]:[e>0?a[e-1]:n[0],et?NaN:t/u+n,[t,t+1/u]},r.copy=function(){return lu(n,t,e)},i()}function cu(n,t){function e(e){return e>=e?t[ao.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return cu(n,t)},e}function fu(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Qi(n,t)},t.tickFormat=function(t,e){return nu(n,t,e)},t.copy=function(){return fu(n)},t}function su(){return 0}function hu(n){return n.innerRadius}function pu(n){return n.outerRadius}function gu(n){return n.startAngle}function vu(n){return n.endAngle}function du(n){return n&&n.padAngle}function yu(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function mu(n,t,e,r,i){var u=n[0]-t[0],o=n[1]-t[1],a=(i?r:-r)/Math.sqrt(u*u+o*o),l=a*o,c=-a*u,f=n[0]+l,s=n[1]+c,h=t[0]+l,p=t[1]+c,g=(f+h)/2,v=(s+p)/2,d=h-f,y=p-s,m=d*d+y*y,M=e-r,x=f*p-h*s,b=(0>y?-1:1)*Math.sqrt(Math.max(0,M*M*m-x*x)),_=(x*y-d*b)/m,w=(-x*d-y*b)/m,S=(x*y+d*b)/m,k=(-x*d+y*b)/m,N=_-g,E=w-v,A=S-g,C=k-v;return N*N+E*E>A*A+C*C&&(_=S,w=k),[[_-l,w-c],[_*e/M,w*e/M]]}function Mu(n){function t(t){function o(){c.push("M",u(n(f),a))}for(var l,c=[],f=[],s=-1,h=t.length,p=En(e),g=En(r);++s1?n.join("L"):n+"Z"}function bu(n){return n.join("L")+"Z"}function _u(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t1&&i.push("H",r[0]),i.join("")}function wu(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t1){a=t[1],u=n[l],l++,r+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1];for(var c=2;c9&&(i=3*t/Math.sqrt(i),o[a]=i*e,o[a+1]=i*r));for(a=-1;++a<=l;)i=(n[Math.min(l,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),u.push([i||0,o[a]*i||0]);return u}function Fu(n){return n.length<3?xu(n):n[0]+Au(n,ju(n))}function Hu(n){for(var t,e,r,i=-1,u=n.length;++i=t?o(n-t):void(f.c=o)}function o(e){var i=g.active,u=g[i];u&&(u.timer.c=null,u.timer.t=NaN,--g.count,delete g[i],u.event&&u.event.interrupt.call(n,n.__data__,u.index));for(var o in g)if(r>+o){var c=g[o];c.timer.c=null,c.timer.t=NaN,--g.count,delete g[o]}f.c=a,qn(function(){return f.c&&a(e||1)&&(f.c=null,f.t=NaN),1},0,l),g.active=r,v.event&&v.event.start.call(n,n.__data__,t),p=[],v.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&p.push(r)}),h=v.ease,s=v.duration}function a(i){for(var u=i/s,o=h(u),a=p.length;a>0;)p[--a].call(n,o);return u>=1?(v.event&&v.event.end.call(n,n.__data__,t),--g.count?delete g[r]:delete n[e],1):void 0}var l,f,s,h,p,g=n[e]||(n[e]={active:0,count:0}),v=g[r];v||(l=i.time,f=qn(u,0,l),v=g[r]={tween:new c,time:l,timer:f,delay:i.delay,duration:i.duration,ease:i.ease,index:t},i=null,++g.count)}function no(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function to(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function eo(n){return n.toISOString()}function ro(n,t,e){function r(t){return n(t)}function i(n,e){var r=n[1]-n[0],i=r/e,u=ao.bisect(Kl,i);return u==Kl.length?[t.year,Ki(n.map(function(n){return n/31536e6}),e)[2]]:u?t[i/Kl[u-1]1?{floor:function(t){for(;e(t=n.floor(t));)t=io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Yi(r.domain()),u=null==n?i(e,10):"number"==typeof n?i(e,n):!n.range&&[{range:n},t];return u&&(n=u[0],t=u[1]),n.range(e[0],io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return ro(n.copy(),t,e)},Ji(r,n)}function io(n){return new Date(n)}function uo(n){return JSON.parse(n.responseText)}function oo(n){var t=fo.createRange();return t.selectNode(fo.body),t.createContextualFragment(n.responseText)}var ao={version:"3.5.17"},lo=[].slice,co=function(n){return lo.call(n)},fo=this.document;if(fo)try{co(fo.documentElement.childNodes)[0].nodeType}catch(so){co=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),fo)try{fo.createElement("DIV").style.setProperty("opacity",0,"")}catch(ho){var po=this.Element.prototype,go=po.setAttribute,vo=po.setAttributeNS,yo=this.CSSStyleDeclaration.prototype,mo=yo.setProperty;po.setAttribute=function(n,t){go.call(this,n,t+"")},po.setAttributeNS=function(n,t,e){vo.call(this,n,t,e+"")},yo.setProperty=function(n,t,e){mo.call(this,n,t+"",e)}}ao.ascending=e,ao.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:NaN},ao.min=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i=r){e=r;break}for(;++ir&&(e=r)}else{for(;++i=r){e=r;break}for(;++ir&&(e=r)}return e},ao.max=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i=r){e=r;break}for(;++ie&&(e=r)}else{for(;++i=r){e=r;break}for(;++ie&&(e=r)}return e},ao.extent=function(n,t){var e,r,i,u=-1,o=n.length;if(1===arguments.length){for(;++u=r){e=i=r;break}for(;++ur&&(e=r),r>i&&(i=r))}else{for(;++u=r){e=i=r;break}for(;++ur&&(e=r),r>i&&(i=r))}return[e,i]},ao.sum=function(n,t){var e,r=0,u=n.length,o=-1;if(1===arguments.length)for(;++o1?l/(f-1):void 0},ao.deviation=function(){var n=ao.variance.apply(this,arguments);return n?Math.sqrt(n):n};var Mo=u(e);ao.bisectLeft=Mo.left,ao.bisect=ao.bisectRight=Mo.right,ao.bisector=function(n){return u(1===n.length?function(t,r){return e(n(t),r)}:n)},ao.shuffle=function(n,t,e){(u=arguments.length)<3&&(e=n.length,2>u&&(t=0));for(var r,i,u=e-t;u;)i=Math.random()*u--|0,r=n[u+t],n[u+t]=n[i+t],n[i+t]=r;return n},ao.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ao.pairs=function(n){for(var t,e=0,r=n.length-1,i=n[0],u=new Array(0>r?0:r);r>e;)u[e]=[t=i,i=n[++e]];return u},ao.transpose=function(n){if(!(i=n.length))return[];for(var t=-1,e=ao.min(n,o),r=new Array(e);++t=0;)for(r=n[i],t=r.length;--t>=0;)e[--o]=r[t];return e};var xo=Math.abs;ao.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,i=[],u=a(xo(e)),o=-1;if(n*=u,t*=u,e*=u,0>e)for(;(r=n+e*++o)>t;)i.push(r/u);else for(;(r=n+e*++o)=u.length)return r?r.call(i,o):e?o.sort(e):o;for(var l,f,s,h,p=-1,g=o.length,v=u[a++],d=new c;++p=u.length)return n;var r=[],i=o[e++];return n.forEach(function(n,i){r.push({key:n,values:t(i,e)})}),i?r.sort(function(n,t){return i(n.key,t.key)}):r}var e,r,i={},u=[],o=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(ao.map,e,0),0)},i.key=function(n){return u.push(n),i},i.sortKeys=function(n){return o[u.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},ao.set=function(n){var t=new y;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},l(y,{has:h,add:function(n){return this._[f(n+="")]=!0,n},remove:p,values:g,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t))}}),ao.behavior={},ao.rebind=function(n,t){for(var e,r=1,i=arguments.length;++r=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ao.event=null,ao.requote=function(n){return n.replace(So,"\\$&")};var So=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ko={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},No=function(n,t){return t.querySelector(n)},Eo=function(n,t){return t.querySelectorAll(n)},Ao=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(Ao=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(No=function(n,t){return Sizzle(n,t)[0]||null},Eo=Sizzle,Ao=Sizzle.matchesSelector),ao.selection=function(){return ao.select(fo.documentElement)};var Co=ao.selection.prototype=[];Co.select=function(n){var t,e,r,i,u=[];n=A(n);for(var o=-1,a=this.length;++o=0&&"xmlns"!==(e=n.slice(0,t))&&(n=n.slice(t+1)),Lo.hasOwnProperty(e)?{space:Lo[e],local:n}:n}},Co.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ao.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},Co.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,i=-1;if(t=e.classList){for(;++ii){if("string"!=typeof n){2>i&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>i){var u=this.node();return t(u).getComputedStyle(u,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},Co.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},Co.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},Co.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},Co.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},Co.insert=function(n,t){return n=j(n),t=A(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},Co.remove=function(){return this.each(F)},Co.data=function(n,t){function e(n,e){var r,i,u,o=n.length,s=e.length,h=Math.min(o,s),p=new Array(s),g=new Array(s),v=new Array(o);if(t){var d,y=new c,m=new Array(o);for(r=-1;++rr;++r)g[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}g.update=p,g.parentNode=p.parentNode=v.parentNode=n.parentNode,a.push(g),l.push(p),f.push(v)}var r,i,u=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++uu;u++){i.push(t=[]),t.parentNode=(e=this[u]).parentNode;for(var a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return E(i)},Co.order=function(){for(var n=-1,t=this.length;++n=0;)(e=r[i])&&(u&&u!==e.nextSibling&&u.parentNode.insertBefore(e,u),u=e);return this},Co.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++tn;n++)for(var e=this[n],r=0,i=e.length;i>r;r++){var u=e[r];if(u)return u}return null},Co.size=function(){var n=0;return Y(this,function(){++n}),n};var qo=[];ao.selection.enter=Z,ao.selection.enter.prototype=qo,qo.append=Co.append,qo.empty=Co.empty,qo.node=Co.node,qo.call=Co.call,qo.size=Co.size,qo.select=function(n){for(var t,e,r,i,u,o=[],a=-1,l=this.length;++ar){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var To=ao.map({mouseenter:"mouseover",mouseleave:"mouseout"});fo&&To.forEach(function(n){"on"+n in fo&&To.remove(n)});var Ro,Do=0;ao.mouse=function(n){return J(n,k())};var Po=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ao.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,i=0,u=t.length;u>i;++i)if((r=t[i]).identifier===e)return J(n,r)},ao.behavior.drag=function(){function n(){this.on("mousedown.drag",u).on("touchstart.drag",o)}function e(n,t,e,u,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],g|=n|e,M=r,p({type:"drag",x:r[0]+c[0],y:r[1]+c[1],dx:n,dy:e}))}function l(){t(h,v)&&(y.on(u+d,null).on(o+d,null),m(g),p({type:"dragend"}))}var c,f=this,s=ao.event.target.correspondingElement||ao.event.target,h=f.parentNode,p=r.of(f,arguments),g=0,v=n(),d=".drag"+(null==v?"":"-"+v),y=ao.select(e(s)).on(u+d,a).on(o+d,l),m=W(s),M=t(h,v);i?(c=i.apply(f,arguments),c=[c.x-M[0],c.y-M[1]]):c=[0,0],p({type:"dragstart"})}}var r=N(n,"drag","dragstart","dragend"),i=null,u=e(b,ao.mouse,t,"mousemove","mouseup"),o=e(G,ao.touch,m,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},ao.rebind(n,r,"on")},ao.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?co(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Uo=1e-6,jo=Uo*Uo,Fo=Math.PI,Ho=2*Fo,Oo=Ho-Uo,Io=Fo/2,Yo=Fo/180,Zo=180/Fo,Vo=Math.SQRT2,Xo=2,$o=4;ao.interpolateZoom=function(n,t){var e,r,i=n[0],u=n[1],o=n[2],a=t[0],l=t[1],c=t[2],f=a-i,s=l-u,h=f*f+s*s;if(jo>h)r=Math.log(c/o)/Vo,e=function(n){return[i+n*f,u+n*s,o*Math.exp(Vo*n*r)]};else{var p=Math.sqrt(h),g=(c*c-o*o+$o*h)/(2*o*Xo*p),v=(c*c-o*o-$o*h)/(2*c*Xo*p),d=Math.log(Math.sqrt(g*g+1)-g),y=Math.log(Math.sqrt(v*v+1)-v);r=(y-d)/Vo,e=function(n){var t=n*r,e=rn(d),a=o/(Xo*p)*(e*un(Vo*t+d)-en(d));return[i+a*f,u+a*s,o*e/rn(Vo*t+d)]}}return e.duration=1e3*r,e},ao.behavior.zoom=function(){function n(n){n.on(L,s).on(Wo+".zoom",p).on("dblclick.zoom",g).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function i(n){k.k=Math.max(A[0],Math.min(A[1],n))}function u(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},i(Math.pow(2,o)),u(d=e,r),t=ao.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function l(n){z++||n({type:"zoomstart"})}function c(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function f(n){--z||(n({type:"zoomend"}),d=null)}function s(){function n(){a=1,u(ao.mouse(i),h),c(o)}function r(){s.on(q,null).on(T,null),p(a),f(o)}var i=this,o=D.of(i,arguments),a=0,s=ao.select(t(i)).on(q,n).on(T,r),h=e(ao.mouse(i)),p=W(i);Il.call(i),l(o)}function h(){function n(){var n=ao.touches(g);return p=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ao.event.target;ao.select(t).on(x,r).on(b,a),_.push(t);for(var e=ao.event.changedTouches,i=0,u=e.length;u>i;++i)d[e[i].identifier]=null;var l=n(),c=Date.now();if(1===l.length){if(500>c-M){var f=l[0];o(g,f,d[f.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=c}else if(l.length>1){var f=l[0],s=l[1],h=f[0]-s[0],p=f[1]-s[1];y=h*h+p*p}}function r(){var n,t,e,r,o=ao.touches(g);Il.call(g);for(var a=0,l=o.length;l>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var f=(f=e[0]-n[0])*f+(f=e[1]-n[1])*f,s=y&&Math.sqrt(f/y);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],i(s*p)}M=null,u(n,t),c(v)}function a(){if(ao.event.touches.length){for(var t=ao.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var i in d)return void n()}ao.selectAll(_).on(m,null),w.on(L,s).on(R,h),N(),f(v)}var p,g=this,v=D.of(g,arguments),d={},y=0,m=".zoom-"+ao.event.changedTouches[0].identifier,x="touchmove"+m,b="touchend"+m,_=[],w=ao.select(g),N=W(g);t(),l(v),w.on(L,null).on(R,t)}function p(){var n=D.of(this,arguments);m?clearTimeout(m):(Il.call(this),v=e(d=y||ao.mouse(this)),l(n)),m=setTimeout(function(){m=null,f(n)},50),S(),i(Math.pow(2,.002*Bo())*k.k),u(d,v),c(n)}function g(){var n=ao.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ao.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,y,m,M,x,b,_,w,k={x:0,y:0,k:1},E=[960,500],A=Jo,C=250,z=0,L="mousedown.zoom",q="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=N(n,"zoomstart","zoom","zoomend");return Wo||(Wo="onwheel"in fo?(Bo=function(){return-ao.event.deltaY*(ao.event.deltaMode?120:1)},"wheel"):"onmousewheel"in fo?(Bo=function(){return ao.event.wheelDelta},"mousewheel"):(Bo=function(){return-ao.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Hl?ao.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},l(n)}).tween("zoom:zoom",function(){var e=E[0],r=E[1],i=d?d[0]:e/2,u=d?d[1]:r/2,o=ao.interpolateZoom([(i-k.x)/k.k,(u-k.y)/k.k,e/k.k],[(i-t.x)/t.k,(u-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:i-r[0]*a,y:u-r[1]*a,k:a},c(n)}}).each("interrupt.zoom",function(){f(n)}).each("end.zoom",function(){f(n)}):(this.__chart__=k,l(n),c(n),f(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:null},i(+t),a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(A=null==t?Jo:[+t[0],+t[1]],n):A},n.center=function(t){return arguments.length?(y=t&&[+t[0],+t[1]],n):y},n.size=function(t){return arguments.length?(E=t&&[+t[0],+t[1]],n):E},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ao.rebind(n,D,"on")};var Bo,Wo,Jo=[0,1/0];ao.color=an,an.prototype.toString=function(){return this.rgb()+""},ao.hsl=ln;var Go=ln.prototype=new an;Go.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,this.l/n)},Go.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,n*this.l)},Go.rgb=function(){return cn(this.h,this.s,this.l)},ao.hcl=fn;var Ko=fn.prototype=new an;Ko.brighter=function(n){return new fn(this.h,this.c,Math.min(100,this.l+Qo*(arguments.length?n:1)))},Ko.darker=function(n){return new fn(this.h,this.c,Math.max(0,this.l-Qo*(arguments.length?n:1)))},Ko.rgb=function(){return sn(this.h,this.c,this.l).rgb()},ao.lab=hn;var Qo=18,na=.95047,ta=1,ea=1.08883,ra=hn.prototype=new an;ra.brighter=function(n){return new hn(Math.min(100,this.l+Qo*(arguments.length?n:1)),this.a,this.b)},ra.darker=function(n){return new hn(Math.max(0,this.l-Qo*(arguments.length?n:1)),this.a,this.b)},ra.rgb=function(){return pn(this.l,this.a,this.b)},ao.rgb=mn;var ia=mn.prototype=new an;ia.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,i=30;return t||e||r?(t&&i>t&&(t=i),e&&i>e&&(e=i),r&&i>r&&(r=i),new mn(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mn(i,i,i)},ia.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mn(n*this.r,n*this.g,n*this.b)},ia.hsl=function(){return wn(this.r,this.g,this.b)},ia.toString=function(){return"#"+bn(this.r)+bn(this.g)+bn(this.b)};var ua=ao.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});ua.forEach(function(n,t){ua.set(n,Mn(t))}),ao.functor=En,ao.xhr=An(m),ao.dsv=function(n,t){function e(n,e,u){arguments.length<3&&(u=e,e=null);var o=Cn(n,t,null==e?r:i(e),u);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:i(n)):e},o}function r(n){return e.parse(n.responseText)}function i(n){return function(t){return e.parse(t.responseText,n)}}function u(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),l=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var i=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(i(n),e)}:i})},e.parseRows=function(n,t){function e(){if(f>=c)return o;if(i)return i=!1,u;var t=f;if(34===n.charCodeAt(t)){for(var e=t;e++f;){var r=n.charCodeAt(f++),a=1;if(10===r)i=!0;else if(13===r)i=!0,10===n.charCodeAt(f)&&(++f,++a);else if(r!==l)continue;return n.slice(t,f-a)}return n.slice(t)}for(var r,i,u={},o={},a=[],c=n.length,f=0,s=0;(r=e())!==o;){for(var h=[];r!==u&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,s++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new y,i=[];return t.forEach(function(n){for(var t in n)r.has(t)||i.push(r.add(t))}),[i.map(o).join(n)].concat(t.map(function(t){return i.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(u).join("\n")},e},ao.csv=ao.dsv(",","text/csv"),ao.tsv=ao.dsv(" ","text/tab-separated-values");var oa,aa,la,ca,fa=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ao.timer=function(){qn.apply(this,arguments)},ao.timer.flush=function(){Rn(),Dn()},ao.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var sa=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Un);ao.formatPrefix=function(n,t){var e=0;return(n=+n)&&(0>n&&(n*=-1),t&&(n=ao.round(n,Pn(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),sa[8+e/3]};var ha=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,pa=ao.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ao.round(n,Pn(n,t))).toFixed(Math.max(0,Math.min(20,Pn(n*(1+1e-15),t))))}}),ga=ao.time={},va=Date;Hn.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){da.setUTCDate.apply(this._,arguments)},setDay:function(){da.setUTCDay.apply(this._,arguments)},setFullYear:function(){da.setUTCFullYear.apply(this._,arguments)},setHours:function(){da.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){da.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){da.setUTCMinutes.apply(this._,arguments)},setMonth:function(){da.setUTCMonth.apply(this._,arguments)},setSeconds:function(){da.setUTCSeconds.apply(this._,arguments)},setTime:function(){da.setTime.apply(this._,arguments)}};var da=Date.prototype;ga.year=On(function(n){return n=ga.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ga.years=ga.year.range,ga.years.utc=ga.year.utc.range,ga.day=On(function(n){var t=new va(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ga.days=ga.day.range,ga.days.utc=ga.day.utc.range,ga.dayOfYear=function(n){var t=ga.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ga[n]=On(function(n){return(n=ga.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ga[n+"s"]=e.range,ga[n+"s"].utc=e.utc.range,ga[n+"OfYear"]=function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)}}),ga.week=ga.sunday,ga.weeks=ga.sunday.range,ga.weeks.utc=ga.sunday.utc.range,ga.weekOfYear=ga.sundayOfYear;var ya={"-":"",_:" ",0:"0"},ma=/^\s*\d+/,Ma=/^%/;ao.locale=function(n){return{numberFormat:jn(n),timeFormat:Yn(n)}};var xa=ao.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"], -shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ao.format=xa.numberFormat,ao.geo={},ft.prototype={s:0,t:0,add:function(n){st(n,this.t,ba),st(ba.s,this.s,this),this.s?this.t+=ba.t:this.s=ba.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var ba=new ft;ao.geo.stream=function(n,t){n&&_a.hasOwnProperty(n.type)?_a[n.type](n,t):ht(n,t)};var _a={Feature:function(n,t){ht(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,i=e.length;++rn?4*Fo+n:n,Na.lineStart=Na.lineEnd=Na.point=b}};ao.geo.bounds=function(){function n(n,t){M.push(x=[f=n,h=n]),s>t&&(s=t),t>p&&(p=t)}function t(t,e){var r=dt([t*Yo,e*Yo]);if(y){var i=mt(y,r),u=[i[1],-i[0],0],o=mt(u,i);bt(o),o=_t(o);var l=t-g,c=l>0?1:-1,v=o[0]*Zo*c,d=xo(l)>180;if(d^(v>c*g&&c*t>v)){var m=o[1]*Zo;m>p&&(p=m)}else if(v=(v+360)%360-180,d^(v>c*g&&c*t>v)){var m=-o[1]*Zo;s>m&&(s=m)}else s>e&&(s=e),e>p&&(p=e);d?g>t?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t):h>=f?(f>t&&(f=t),t>h&&(h=t)):t>g?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t)}else n(t,e);y=r,g=t}function e(){b.point=t}function r(){x[0]=f,x[1]=h,b.point=n,y=null}function i(n,e){if(y){var r=n-g;m+=xo(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Na.point(n,e),t(n,e)}function u(){Na.lineStart()}function o(){i(v,d),Na.lineEnd(),xo(m)>Uo&&(f=-(h=180)),x[0]=f,x[1]=h,y=null}function a(n,t){return(t-=n)<0?t+360:t}function l(n,t){return n[0]-t[0]}function c(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nka?(f=-(h=180),s=-(p=90)):m>Uo?p=90:-Uo>m&&(s=-90),x[0]=f,x[1]=h}};return function(n){p=h=-(f=s=1/0),M=[],ao.geo.stream(n,b);var t=M.length;if(t){M.sort(l);for(var e,r=1,i=M[0],u=[i];t>r;++r)e=M[r],c(e[0],i)||c(e[1],i)?(a(i[0],e[1])>a(i[0],i[1])&&(i[1]=e[1]),a(e[0],i[1])>a(i[0],i[1])&&(i[0]=e[0])):u.push(i=e);for(var o,e,g=-(1/0),t=u.length-1,r=0,i=u[t];t>=r;i=e,++r)e=u[r],(o=a(i[1],e[0]))>g&&(g=o,f=e[0],h=i[1])}return M=x=null,f===1/0||s===1/0?[[NaN,NaN],[NaN,NaN]]:[[f,s],[h,p]]}}(),ao.geo.centroid=function(n){Ea=Aa=Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,ja);var t=Da,e=Pa,r=Ua,i=t*t+e*e+r*r;return jo>i&&(t=qa,e=Ta,r=Ra,Uo>Aa&&(t=Ca,e=za,r=La),i=t*t+e*e+r*r,jo>i)?[NaN,NaN]:[Math.atan2(e,t)*Zo,tn(r/Math.sqrt(i))*Zo]};var Ea,Aa,Ca,za,La,qa,Ta,Ra,Da,Pa,Ua,ja={sphere:b,point:St,lineStart:Nt,lineEnd:Et,polygonStart:function(){ja.lineStart=At},polygonEnd:function(){ja.lineStart=Nt}},Fa=Rt(zt,jt,Ht,[-Fo,-Fo/2]),Ha=1e9;ao.geo.clipExtent=function(){var n,t,e,r,i,u,o={stream:function(n){return i&&(i.valid=!1),i=u(n),i.valid=!0,i},extent:function(a){return arguments.length?(u=Zt(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),i&&(i.valid=!1,i=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ao.geo.conicEqualArea=function(){return Vt(Xt)}).raw=Xt,ao.geo.albers=function(){return ao.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ao.geo.albersUsa=function(){function n(n){var u=n[0],o=n[1];return t=null,e(u,o),t||(r(u,o),t)||i(u,o),t}var t,e,r,i,u=ao.geo.albers(),o=ao.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ao.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),l={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=u.scale(),e=u.translate(),r=(n[0]-e[0])/t,i=(n[1]-e[1])/t;return(i>=.12&&.234>i&&r>=-.425&&-.214>r?o:i>=.166&&.234>i&&r>=-.214&&-.115>r?a:u).invert(n)},n.stream=function(n){var t=u.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,i){t.point(n,i),e.point(n,i),r.point(n,i)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(u.precision(t),o.precision(t),a.precision(t),n):u.precision()},n.scale=function(t){return arguments.length?(u.scale(t),o.scale(.35*t),a.scale(t),n.translate(u.translate())):u.scale()},n.translate=function(t){if(!arguments.length)return u.translate();var c=u.scale(),f=+t[0],s=+t[1];return e=u.translate(t).clipExtent([[f-.455*c,s-.238*c],[f+.455*c,s+.238*c]]).stream(l).point,r=o.translate([f-.307*c,s+.201*c]).clipExtent([[f-.425*c+Uo,s+.12*c+Uo],[f-.214*c-Uo,s+.234*c-Uo]]).stream(l).point,i=a.translate([f-.205*c,s+.212*c]).clipExtent([[f-.214*c+Uo,s+.166*c+Uo],[f-.115*c-Uo,s+.234*c-Uo]]).stream(l).point,n},n.scale(1070)};var Oa,Ia,Ya,Za,Va,Xa,$a={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Ia=0,$a.lineStart=$t},polygonEnd:function(){$a.lineStart=$a.lineEnd=$a.point=b,Oa+=xo(Ia/2)}},Ba={point:Bt,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Wa={point:Gt,lineStart:Kt,lineEnd:Qt,polygonStart:function(){Wa.lineStart=ne},polygonEnd:function(){Wa.point=Gt,Wa.lineStart=Kt,Wa.lineEnd=Qt}};ao.geo.path=function(){function n(n){return n&&("function"==typeof a&&u.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=i(u)),ao.geo.stream(n,o)),u.result()}function t(){return o=null,n}var e,r,i,u,o,a=4.5;return n.area=function(n){return Oa=0,ao.geo.stream(n,i($a)),Oa},n.centroid=function(n){return Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,i(Wa)),Ua?[Da/Ua,Pa/Ua]:Ra?[qa/Ra,Ta/Ra]:La?[Ca/La,za/La]:[NaN,NaN]},n.bounds=function(n){return Va=Xa=-(Ya=Za=1/0),ao.geo.stream(n,i(Ba)),[[Ya,Za],[Va,Xa]]},n.projection=function(n){return arguments.length?(i=(e=n)?n.stream||re(n):m,t()):e},n.context=function(n){return arguments.length?(u=null==(r=n)?new Wt:new te(n),"function"!=typeof a&&u.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(u.pointRadius(+t),+t),n):a},n.projection(ao.geo.albersUsa()).context(null)},ao.geo.transform=function(n){return{stream:function(t){var e=new ie(t);for(var r in n)e[r]=n[r];return e}}},ie.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ao.geo.projection=oe,ao.geo.projectionMutator=ae,(ao.geo.equirectangular=function(){return oe(ce)}).raw=ce.invert=ce,ao.geo.rotation=function(n){function t(t){return t=n(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t}return n=se(n[0]%360*Yo,n[1]*Yo,n.length>2?n[2]*Yo:0),t.invert=function(t){return t=n.invert(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t},t},fe.invert=ce,ao.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=se(-n[0]*Yo,-n[1]*Yo,0).invert,i=[];return e(null,null,1,{point:function(n,e){i.push(n=t(n,e)),n[0]*=Zo,n[1]*=Zo}}),{type:"Polygon",coordinates:[i]}}var t,e,r=[0,0],i=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=ve((t=+r)*Yo,i*Yo),n):t},n.precision=function(r){return arguments.length?(e=ve(t*Yo,(i=+r)*Yo),n):i},n.angle(90)},ao.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Yo,i=n[1]*Yo,u=t[1]*Yo,o=Math.sin(r),a=Math.cos(r),l=Math.sin(i),c=Math.cos(i),f=Math.sin(u),s=Math.cos(u);return Math.atan2(Math.sqrt((e=s*o)*e+(e=c*f-l*s*a)*e),l*f+c*s*a)},ao.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ao.range(Math.ceil(u/d)*d,i,d).map(h).concat(ao.range(Math.ceil(c/y)*y,l,y).map(p)).concat(ao.range(Math.ceil(r/g)*g,e,g).filter(function(n){return xo(n%d)>Uo}).map(f)).concat(ao.range(Math.ceil(a/v)*v,o,v).filter(function(n){return xo(n%y)>Uo}).map(s))}var e,r,i,u,o,a,l,c,f,s,h,p,g=10,v=g,d=90,y=360,m=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(u).concat(p(l).slice(1),h(i).reverse().slice(1),p(c).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(u=+t[0][0],i=+t[1][0],c=+t[0][1],l=+t[1][1],u>i&&(t=u,u=i,i=t),c>l&&(t=c,c=l,l=t),n.precision(m)):[[u,c],[i,l]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(m)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],y=+t[1],n):[d,y]},n.minorStep=function(t){return arguments.length?(g=+t[0],v=+t[1],n):[g,v]},n.precision=function(t){return arguments.length?(m=+t,f=ye(a,o,90),s=me(r,e,m),h=ye(c,l,90),p=me(u,i,m),n):m},n.majorExtent([[-180,-90+Uo],[180,90-Uo]]).minorExtent([[-180,-80-Uo],[180,80+Uo]])},ao.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||i.apply(this,arguments)]}}var t,e,r=Me,i=xe;return n.distance=function(){return ao.geo.distance(t||r.apply(this,arguments),e||i.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(i=t,e="function"==typeof t?null:t,n):i},n.precision=function(){return arguments.length?n:0},n},ao.geo.interpolate=function(n,t){return be(n[0]*Yo,n[1]*Yo,t[0]*Yo,t[1]*Yo)},ao.geo.length=function(n){return Ja=0,ao.geo.stream(n,Ga),Ja};var Ja,Ga={sphere:b,point:b,lineStart:_e,lineEnd:b,polygonStart:b,polygonEnd:b},Ka=we(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ao.geo.azimuthalEqualArea=function(){return oe(Ka)}).raw=Ka;var Qa=we(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},m);(ao.geo.azimuthalEquidistant=function(){return oe(Qa)}).raw=Qa,(ao.geo.conicConformal=function(){return Vt(Se)}).raw=Se,(ao.geo.conicEquidistant=function(){return Vt(ke)}).raw=ke;var nl=we(function(n){return 1/n},Math.atan);(ao.geo.gnomonic=function(){return oe(nl)}).raw=nl,Ne.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Io]},(ao.geo.mercator=function(){return Ee(Ne)}).raw=Ne;var tl=we(function(){return 1},Math.asin);(ao.geo.orthographic=function(){return oe(tl)}).raw=tl;var el=we(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ao.geo.stereographic=function(){return oe(el)}).raw=el,Ae.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Io]},(ao.geo.transverseMercator=function(){var n=Ee(Ae),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Ae,ao.geom={},ao.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,i=En(e),u=En(r),o=n.length,a=[],l=[];for(t=0;o>t;t++)a.push([+i.call(this,n[t],t),+u.call(this,n[t],t),t]);for(a.sort(qe),t=0;o>t;t++)l.push([a[t][0],-a[t][1]]);var c=Le(a),f=Le(l),s=f[0]===c[0],h=f[f.length-1]===c[c.length-1],p=[];for(t=c.length-1;t>=0;--t)p.push(n[a[c[t]][2]]);for(t=+s;t=r&&c.x<=u&&c.y>=i&&c.y<=o?[[r,o],[u,o],[u,i],[r,i]]:[];f.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(u(n,t)/Uo)*Uo,y:Math.round(o(n,t)/Uo)*Uo,i:t}})}var r=Ce,i=ze,u=r,o=i,a=sl;return n?t(n):(t.links=function(n){return ar(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return ar(e(n)).cells.forEach(function(e,r){for(var i,u,o=e.site,a=e.edges.sort(Ve),l=-1,c=a.length,f=a[c-1].edge,s=f.l===o?f.r:f.l;++l=c,h=r>=f,p=h<<1|s;n.leaf=!1,n=n.nodes[p]||(n.nodes[p]=hr()),s?i=c:a=c,h?o=f:l=f,u(n,t,e,r,i,o,a,l)}var f,s,h,p,g,v,d,y,m,M=En(a),x=En(l);if(null!=t)v=t,d=e,y=r,m=i;else if(y=m=-(v=d=1/0),s=[],h=[],g=n.length,o)for(p=0;g>p;++p)f=n[p],f.xy&&(y=f.x),f.y>m&&(m=f.y),s.push(f.x),h.push(f.y);else for(p=0;g>p;++p){var b=+M(f=n[p],p),_=+x(f,p);v>b&&(v=b),d>_&&(d=_),b>y&&(y=b),_>m&&(m=_),s.push(b),h.push(_)}var w=y-v,S=m-d;w>S?m=d+w:y=v+S;var k=hr();if(k.add=function(n){u(k,n,+M(n,++p),+x(n,p),v,d,y,m)},k.visit=function(n){pr(n,k,v,d,y,m)},k.find=function(n){return gr(k,n[0],n[1],v,d,y,m)},p=-1,null==t){for(;++p=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=vl.get(e)||gl,r=dl.get(r)||m,br(r(e.apply(null,lo.call(arguments,1))))},ao.interpolateHcl=Rr,ao.interpolateHsl=Dr,ao.interpolateLab=Pr,ao.interpolateRound=Ur,ao.transform=function(n){var t=fo.createElementNS(ao.ns.prefix.svg,"g");return(ao.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new jr(e?e.matrix:yl)})(n)},jr.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var yl={a:1,b:0,c:0,d:1,e:0,f:0};ao.interpolateTransform=$r,ao.layout={},ao.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++ea*a/y){if(v>l){var c=t.charge/l;n.px-=u*c,n.py-=o*c}return!0}if(t.point&&l&&v>l){var c=t.pointCharge/l;n.px-=u*c,n.py-=o*c}}return!t.charge}}function t(n){n.px=ao.event.x,n.py=ao.event.y,l.resume()}var e,r,i,u,o,a,l={},c=ao.dispatch("start","tick","end"),f=[1,1],s=.9,h=ml,p=Ml,g=-30,v=xl,d=.1,y=.64,M=[],x=[];return l.tick=function(){if((i*=.99)<.005)return e=null,c.end({type:"end",alpha:i=0}),!0;var t,r,l,h,p,v,y,m,b,_=M.length,w=x.length;for(r=0;w>r;++r)l=x[r],h=l.source,p=l.target,m=p.x-h.x,b=p.y-h.y,(v=m*m+b*b)&&(v=i*o[r]*((v=Math.sqrt(v))-u[r])/v,m*=v,b*=v,p.x-=m*(y=h.weight+p.weight?h.weight/(h.weight+p.weight):.5),p.y-=b*y,h.x+=m*(y=1-y),h.y+=b*y);if((y=i*d)&&(m=f[0]/2,b=f[1]/2,r=-1,y))for(;++r<_;)l=M[r],l.x+=(m-l.x)*y,l.y+=(b-l.y)*y;if(g)for(ri(t=ao.geom.quadtree(M),i,a),r=-1;++r<_;)(l=M[r]).fixed||t.visit(n(l));for(r=-1;++r<_;)l=M[r],l.fixed?(l.x=l.px,l.y=l.py):(l.x-=(l.px-(l.px=l.x))*s,l.y-=(l.py-(l.py=l.y))*s);c.tick({type:"tick",alpha:i})},l.nodes=function(n){return arguments.length?(M=n,l):M},l.links=function(n){return arguments.length?(x=n,l):x},l.size=function(n){return arguments.length?(f=n,l):f},l.linkDistance=function(n){return arguments.length?(h="function"==typeof n?n:+n,l):h},l.distance=l.linkDistance,l.linkStrength=function(n){return arguments.length?(p="function"==typeof n?n:+n,l):p},l.friction=function(n){return arguments.length?(s=+n,l):s},l.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,l):g},l.chargeDistance=function(n){return arguments.length?(v=n*n,l):Math.sqrt(v)},l.gravity=function(n){return arguments.length?(d=+n,l):d},l.theta=function(n){return arguments.length?(y=n*n,l):Math.sqrt(y)},l.alpha=function(n){return arguments.length?(n=+n,i?n>0?i=n:(e.c=null,e.t=NaN,e=null,c.end({type:"end",alpha:i=0})):n>0&&(c.start({type:"start",alpha:i=n}),e=qn(l.tick)),l):i},l.start=function(){function n(n,r){if(!e){for(e=new Array(i),l=0;i>l;++l)e[l]=[];for(l=0;c>l;++l){var u=x[l];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var o,a=e[t],l=-1,f=a.length;++lt;++t)(r=M[t]).index=t,r.weight=0;for(t=0;c>t;++t)r=x[t],"number"==typeof r.source&&(r.source=M[r.source]),"number"==typeof r.target&&(r.target=M[r.target]),++r.source.weight,++r.target.weight;for(t=0;i>t;++t)r=M[t],isNaN(r.x)&&(r.x=n("x",s)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof h)for(t=0;c>t;++t)u[t]=+h.call(this,x[t],t);else for(t=0;c>t;++t)u[t]=h;if(o=[],"function"==typeof p)for(t=0;c>t;++t)o[t]=+p.call(this,x[t],t);else for(t=0;c>t;++t)o[t]=p;if(a=[],"function"==typeof g)for(t=0;i>t;++t)a[t]=+g.call(this,M[t],t);else for(t=0;i>t;++t)a[t]=g;return l.resume()},l.resume=function(){return l.alpha(.1)},l.stop=function(){return l.alpha(0)},l.drag=function(){return r||(r=ao.behavior.drag().origin(m).on("dragstart.force",Qr).on("drag.force",t).on("dragend.force",ni)),arguments.length?void this.on("mouseover.force",ti).on("mouseout.force",ei).call(r):r},ao.rebind(l,c,"on")};var ml=20,Ml=1,xl=1/0;ao.layout.hierarchy=function(){function n(i){var u,o=[i],a=[];for(i.depth=0;null!=(u=o.pop());)if(a.push(u),(c=e.call(n,u,u.depth))&&(l=c.length)){for(var l,c,f;--l>=0;)o.push(f=c[l]),f.parent=u,f.depth=u.depth+1;r&&(u.value=0),u.children=c}else r&&(u.value=+r.call(n,u,u.depth)||0),delete u.children;return oi(i,function(n){var e,i;t&&(e=n.children)&&e.sort(t),r&&(i=n.parent)&&(i.value+=n.value)}),a}var t=ci,e=ai,r=li;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(ui(t,function(n){n.children&&(n.value=0)}),oi(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ao.layout.partition=function(){function n(t,e,r,i){var u=t.children;if(t.x=e,t.y=t.depth*i,t.dx=r,t.dy=i,u&&(o=u.length)){var o,a,l,c=-1;for(r=t.value?r/t.value:0;++cs?-1:1),g=ao.sum(c),v=g?(s-l*p)/g:0,d=ao.range(l),y=[];return null!=e&&d.sort(e===bl?function(n,t){return c[t]-c[n]}:function(n,t){return e(o[n],o[t])}),d.forEach(function(n){y[n]={data:o[n],value:a=c[n],startAngle:f,endAngle:f+=a*v+p,padAngle:h}}),y}var t=Number,e=bl,r=0,i=Ho,u=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(i=t,n):i},n.padAngle=function(t){return arguments.length?(u=t,n):u},n};var bl={};ao.layout.stack=function(){function n(a,l){if(!(h=a.length))return a;var c=a.map(function(e,r){return t.call(n,e,r)}),f=c.map(function(t){return t.map(function(t,e){return[u.call(n,t,e),o.call(n,t,e)]})}),s=e.call(n,f,l);c=ao.permute(c,s),f=ao.permute(f,s);var h,p,g,v,d=r.call(n,f,l),y=c[0].length;for(g=0;y>g;++g)for(i.call(n,c[0][g],v=d[g],f[0][g][1]),p=1;h>p;++p)i.call(n,c[p][g],v+=f[p-1][g][1],f[p][g][1]);return a}var t=m,e=gi,r=vi,i=pi,u=si,o=hi;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:_l.get(t)||gi,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:wl.get(t)||vi,n):r},n.x=function(t){return arguments.length?(u=t,n):u},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(i=t,n):i},n};var _l=ao.map({"inside-out":function(n){var t,e,r=n.length,i=n.map(di),u=n.map(yi),o=ao.range(r).sort(function(n,t){return i[n]-i[t]}),a=0,l=0,c=[],f=[];for(t=0;r>t;++t)e=o[t],l>a?(a+=u[e],c.push(e)):(l+=u[e],f.push(e));return f.reverse().concat(c)},reverse:function(n){return ao.range(n.length).reverse()},"default":gi}),wl=ao.map({silhouette:function(n){var t,e,r,i=n.length,u=n[0].length,o=[],a=0,l=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;u>e;++e)l[e]=(a-o[e])/2;return l},wiggle:function(n){var t,e,r,i,u,o,a,l,c,f=n.length,s=n[0],h=s.length,p=[];for(p[0]=l=c=0,e=1;h>e;++e){for(t=0,i=0;f>t;++t)i+=n[t][e][1];for(t=0,u=0,a=s[e][0]-s[e-1][0];f>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;u+=o*n[t][e][1]}p[e]=l-=i?u/i*a:0,c>l&&(c=l)}for(e=0;h>e;++e)p[e]-=c;return p},expand:function(n){var t,e,r,i=n.length,u=n[0].length,o=1/i,a=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];if(r)for(t=0;i>t;t++)n[t][e][1]/=r;else for(t=0;i>t;t++)n[t][e][1]=o}for(e=0;u>e;++e)a[e]=0;return a},zero:vi});ao.layout.histogram=function(){function n(n,u){for(var o,a,l=[],c=n.map(e,this),f=r.call(this,c,u),s=i.call(this,f,c,u),u=-1,h=c.length,p=s.length-1,g=t?1:1/h;++u0)for(u=-1;++u=f[0]&&a<=f[1]&&(o=l[ao.bisect(s,a,1,p)-1],o.y+=g,o.push(n[u]));return l}var t=!0,e=Number,r=bi,i=Mi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=En(t),n):r},n.bins=function(t){return arguments.length?(i="number"==typeof t?function(n){return xi(n,t)}:En(t),n):i},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ao.layout.pack=function(){function n(n,u){var o=e.call(this,n,u),a=o[0],l=i[0],c=i[1],f=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,oi(a,function(n){n.r=+f(n.value)}),oi(a,Ni),r){var s=r*(t?1:Math.max(2*a.r/l,2*a.r/c))/2;oi(a,function(n){n.r+=s}),oi(a,Ni),oi(a,function(n){n.r-=s})}return Ci(a,l/2,c/2,t?1:1/Math.max(2*a.r/l,2*a.r/c)),o}var t,e=ao.layout.hierarchy().sort(_i),r=0,i=[1,1];return n.size=function(t){return arguments.length?(i=t,n):i},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},ii(n,e)},ao.layout.tree=function(){function n(n,i){var f=o.call(this,n,i),s=f[0],h=t(s);if(oi(h,e),h.parent.m=-h.z,ui(h,r),c)ui(s,u);else{var p=s,g=s,v=s;ui(s,function(n){n.xg.x&&(g=n),n.depth>v.depth&&(v=n)});var d=a(p,g)/2-p.x,y=l[0]/(g.x+a(g,p)/2+d),m=l[1]/(v.depth||1);ui(s,function(n){n.x=(n.x+d)*y,n.y=n.depth*m})}return f}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var i,u=t.children,o=0,a=u.length;a>o;++o)r.push((u[o]=i={_:u[o],parent:t,children:(i=u[o].children)&&i.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=i);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Di(n);var u=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-u):n.z=u}else r&&(n.z=r.z+a(n._,r._));n.parent.A=i(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function i(n,t,e){if(t){for(var r,i=n,u=n,o=t,l=i.parent.children[0],c=i.m,f=u.m,s=o.m,h=l.m;o=Ti(o),i=qi(i),o&&i;)l=qi(l),u=Ti(u),u.a=n,r=o.z+s-i.z-c+a(o._,i._),r>0&&(Ri(Pi(o,n,e),n,r),c+=r,f+=r),s+=o.m,c+=i.m,h+=l.m,f+=u.m;o&&!Ti(u)&&(u.t=o,u.m+=s-f),i&&!qi(l)&&(l.t=i,l.m+=c-h,e=n)}return e}function u(n){n.x*=l[0],n.y=n.depth*l[1]}var o=ao.layout.hierarchy().sort(null).value(null),a=Li,l=[1,1],c=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(c=null==(l=t)?u:null,n):c?null:l},n.nodeSize=function(t){return arguments.length?(c=null==(l=t)?null:u,n):c?l:null},ii(n,o)},ao.layout.cluster=function(){function n(n,u){var o,a=t.call(this,n,u),l=a[0],c=0;oi(l,function(n){var t=n.children;t&&t.length?(n.x=ji(t),n.y=Ui(t)):(n.x=o?c+=e(n,o):0,n.y=0,o=n)});var f=Fi(l),s=Hi(l),h=f.x-e(f,s)/2,p=s.x+e(s,f)/2;return oi(l,i?function(n){n.x=(n.x-l.x)*r[0],n.y=(l.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(p-h)*r[0],n.y=(1-(l.y?n.y/l.y:1))*r[1]}),a}var t=ao.layout.hierarchy().sort(null).value(null),e=Li,r=[1,1],i=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},ii(n,t)},ao.layout.treemap=function(){function n(n,t){for(var e,r,i=-1,u=n.length;++it?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var u=e.children;if(u&&u.length){var o,a,l,c=s(e),f=[],h=u.slice(),g=1/0,v="slice"===p?c.dx:"dice"===p?c.dy:"slice-dice"===p?1&e.depth?c.dy:c.dx:Math.min(c.dx,c.dy);for(n(h,c.dx*c.dy/e.value),f.area=0;(l=h.length)>0;)f.push(o=h[l-1]),f.area+=o.area,"squarify"!==p||(a=r(f,v))<=g?(h.pop(),g=a):(f.area-=f.pop().area,i(f,v,c,!1),v=Math.min(c.dx,c.dy),f.length=f.area=0,g=1/0);f.length&&(i(f,v,c,!0),f.length=f.area=0),u.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var u,o=s(t),a=r.slice(),l=[];for(n(a,o.dx*o.dy/t.value),l.area=0;u=a.pop();)l.push(u),l.area+=u.area,null!=u.z&&(i(l,u.z?o.dx:o.dy,o,!a.length),l.length=l.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,i=0,u=1/0,o=-1,a=n.length;++oe&&(u=e),e>i&&(i=e));return r*=r,t*=t,r?Math.max(t*i*g/r,r/(t*u*g)):1/0}function i(n,t,e,r){var i,u=-1,o=n.length,a=e.x,c=e.y,f=t?l(n.area/t):0; -if(t==e.dx){for((r||f>e.dy)&&(f=e.dy);++ue.dx)&&(f=e.dx);++ue&&(t=1),1>e&&(n=0),function(){var e,r,i;do e=2*Math.random()-1,r=2*Math.random()-1,i=e*e+r*r;while(!i||i>1);return n+t*e*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var n=ao.random.normal.apply(ao,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ao.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ao.scale={};var Sl={floor:m,ceil:m};ao.scale.linear=function(){return Wi([0,1],[0,1],Mr,!1)};var kl={s:1,g:1,p:1,r:1,e:1};ao.scale.log=function(){return ru(ao.scale.linear().domain([0,1]),10,!0,[1,10])};var Nl=ao.format(".0e"),El={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ao.scale.pow=function(){return iu(ao.scale.linear(),1,[0,1])},ao.scale.sqrt=function(){return ao.scale.pow().exponent(.5)},ao.scale.ordinal=function(){return ou([],{t:"range",a:[[]]})},ao.scale.category10=function(){return ao.scale.ordinal().range(Al)},ao.scale.category20=function(){return ao.scale.ordinal().range(Cl)},ao.scale.category20b=function(){return ao.scale.ordinal().range(zl)},ao.scale.category20c=function(){return ao.scale.ordinal().range(Ll)};var Al=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(xn),Cl=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(xn),zl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(xn),Ll=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(xn);ao.scale.quantile=function(){return au([],[])},ao.scale.quantize=function(){return lu(0,1,[0,1])},ao.scale.threshold=function(){return cu([.5],[0,1])},ao.scale.identity=function(){return fu([0,1])},ao.svg={},ao.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),c=Math.max(0,+r.apply(this,arguments)),f=o.apply(this,arguments)-Io,s=a.apply(this,arguments)-Io,h=Math.abs(s-f),p=f>s?0:1;if(n>c&&(g=c,c=n,n=g),h>=Oo)return t(c,p)+(n?t(n,1-p):"")+"Z";var g,v,d,y,m,M,x,b,_,w,S,k,N=0,E=0,A=[];if((y=(+l.apply(this,arguments)||0)/2)&&(d=u===ql?Math.sqrt(n*n+c*c):+u.apply(this,arguments),p||(E*=-1),c&&(E=tn(d/c*Math.sin(y))),n&&(N=tn(d/n*Math.sin(y)))),c){m=c*Math.cos(f+E),M=c*Math.sin(f+E),x=c*Math.cos(s-E),b=c*Math.sin(s-E);var C=Math.abs(s-f-2*E)<=Fo?0:1;if(E&&yu(m,M,x,b)===p^C){var z=(f+s)/2;m=c*Math.cos(z),M=c*Math.sin(z),x=b=null}}else m=M=0;if(n){_=n*Math.cos(s-N),w=n*Math.sin(s-N),S=n*Math.cos(f+N),k=n*Math.sin(f+N);var L=Math.abs(f-s+2*N)<=Fo?0:1;if(N&&yu(_,w,S,k)===1-p^L){var q=(f+s)/2;_=n*Math.cos(q),w=n*Math.sin(q),S=k=null}}else _=w=0;if(h>Uo&&(g=Math.min(Math.abs(c-n)/2,+i.apply(this,arguments)))>.001){v=c>n^p?0:1;var T=g,R=g;if(Fo>h){var D=null==S?[_,w]:null==x?[m,M]:Re([m,M],[S,k],[x,b],[_,w]),P=m-D[0],U=M-D[1],j=x-D[0],F=b-D[1],H=1/Math.sin(Math.acos((P*j+U*F)/(Math.sqrt(P*P+U*U)*Math.sqrt(j*j+F*F)))/2),O=Math.sqrt(D[0]*D[0]+D[1]*D[1]);R=Math.min(g,(n-O)/(H-1)),T=Math.min(g,(c-O)/(H+1))}if(null!=x){var I=mu(null==S?[_,w]:[S,k],[m,M],c,T,p),Y=mu([x,b],[_,w],c,T,p);g===T?A.push("M",I[0],"A",T,",",T," 0 0,",v," ",I[1],"A",c,",",c," 0 ",1-p^yu(I[1][0],I[1][1],Y[1][0],Y[1][1]),",",p," ",Y[1],"A",T,",",T," 0 0,",v," ",Y[0]):A.push("M",I[0],"A",T,",",T," 0 1,",v," ",Y[0])}else A.push("M",m,",",M);if(null!=S){var Z=mu([m,M],[S,k],n,-R,p),V=mu([_,w],null==x?[m,M]:[x,b],n,-R,p);g===R?A.push("L",V[0],"A",R,",",R," 0 0,",v," ",V[1],"A",n,",",n," 0 ",p^yu(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-p," ",Z[1],"A",R,",",R," 0 0,",v," ",Z[0]):A.push("L",V[0],"A",R,",",R," 0 0,",v," ",Z[0])}else A.push("L",_,",",w)}else A.push("M",m,",",M),null!=x&&A.push("A",c,",",c," 0 ",C,",",p," ",x,",",b),A.push("L",_,",",w),null!=S&&A.push("A",n,",",n," 0 ",L,",",1-p," ",S,",",k);return A.push("Z"),A.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=hu,r=pu,i=su,u=ql,o=gu,a=vu,l=du;return n.innerRadius=function(t){return arguments.length?(e=En(t),n):e},n.outerRadius=function(t){return arguments.length?(r=En(t),n):r},n.cornerRadius=function(t){return arguments.length?(i=En(t),n):i},n.padRadius=function(t){return arguments.length?(u=t==ql?ql:En(t),n):u},n.startAngle=function(t){return arguments.length?(o=En(t),n):o},n.endAngle=function(t){return arguments.length?(a=En(t),n):a},n.padAngle=function(t){return arguments.length?(l=En(t),n):l},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Io;return[Math.cos(t)*n,Math.sin(t)*n]},n};var ql="auto";ao.svg.line=function(){return Mu(m)};var Tl=ao.map({linear:xu,"linear-closed":bu,step:_u,"step-before":wu,"step-after":Su,basis:zu,"basis-open":Lu,"basis-closed":qu,bundle:Tu,cardinal:Eu,"cardinal-open":ku,"cardinal-closed":Nu,monotone:Fu});Tl.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Rl=[0,2/3,1/3,0],Dl=[0,1/3,2/3,0],Pl=[0,1/6,2/3,1/6];ao.svg.line.radial=function(){var n=Mu(Hu);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},wu.reverse=Su,Su.reverse=wu,ao.svg.area=function(){return Ou(m)},ao.svg.area.radial=function(){var n=Ou(Hu);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ao.svg.chord=function(){function n(n,a){var l=t(this,u,n,a),c=t(this,o,n,a);return"M"+l.p0+r(l.r,l.p1,l.a1-l.a0)+(e(l,c)?i(l.r,l.p1,l.r,l.p0):i(l.r,l.p1,c.r,c.p0)+r(c.r,c.p1,c.a1-c.a0)+i(c.r,c.p1,l.r,l.p0))+"Z"}function t(n,t,e,r){var i=t.call(n,e,r),u=a.call(n,i,r),o=l.call(n,i,r)-Io,f=c.call(n,i,r)-Io;return{r:u,a0:o,a1:f,p0:[u*Math.cos(o),u*Math.sin(o)],p1:[u*Math.cos(f),u*Math.sin(f)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Fo)+",1 "+t}function i(n,t,e,r){return"Q 0,0 "+r}var u=Me,o=xe,a=Iu,l=gu,c=vu;return n.radius=function(t){return arguments.length?(a=En(t),n):a},n.source=function(t){return arguments.length?(u=En(t),n):u},n.target=function(t){return arguments.length?(o=En(t),n):o},n.startAngle=function(t){return arguments.length?(l=En(t),n):l},n.endAngle=function(t){return arguments.length?(c=En(t),n):c},n},ao.svg.diagonal=function(){function n(n,i){var u=t.call(this,n,i),o=e.call(this,n,i),a=(u.y+o.y)/2,l=[u,{x:u.x,y:a},{x:o.x,y:a},o];return l=l.map(r),"M"+l[0]+"C"+l[1]+" "+l[2]+" "+l[3]}var t=Me,e=xe,r=Yu;return n.source=function(e){return arguments.length?(t=En(e),n):t},n.target=function(t){return arguments.length?(e=En(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ao.svg.diagonal.radial=function(){var n=ao.svg.diagonal(),t=Yu,e=n.projection;return n.projection=function(n){return arguments.length?e(Zu(t=n)):t},n},ao.svg.symbol=function(){function n(n,r){return(Ul.get(t.call(this,n,r))||$u)(e.call(this,n,r))}var t=Xu,e=Vu;return n.type=function(e){return arguments.length?(t=En(e),n):t},n.size=function(t){return arguments.length?(e=En(t),n):e},n};var Ul=ao.map({circle:$u,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Fl)),e=t*Fl;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ao.svg.symbolTypes=Ul.keys();var jl=Math.sqrt(3),Fl=Math.tan(30*Yo);Co.transition=function(n){for(var t,e,r=Hl||++Zl,i=Ku(n),u=[],o=Ol||{time:Date.now(),ease:Nr,delay:0,duration:250},a=-1,l=this.length;++au;u++){i.push(t=[]);for(var e=this[u],a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return Wu(i,this.namespace,this.id)},Yl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(i){i[r][e].tween.set(n,t)})},Yl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function i(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function u(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?$r:Mr,a=ao.ns.qualify(n);return Ju(this,"attr."+n,t,a.local?u:i)},Yl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(i));return r&&function(n){this.setAttribute(i,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(i.space,i.local));return r&&function(n){this.setAttributeNS(i.space,i.local,r(n))}}var i=ao.ns.qualify(n);return this.tween("attr."+n,i.local?r:e)},Yl.style=function(n,e,r){function i(){this.style.removeProperty(n)}function u(e){return null==e?i:(e+="",function(){var i,u=t(this).getComputedStyle(this,null).getPropertyValue(n);return u!==e&&(i=Mr(u,e),function(t){this.style.setProperty(n,i(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Ju(this,"style."+n,e,u)},Yl.styleTween=function(n,e,r){function i(i,u){var o=e.call(this,i,u,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,i)},Yl.text=function(n){return Ju(this,"text",n,Gu)},Yl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Yl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ao.ease.apply(ao,arguments)),Y(this,function(r){r[e][t].ease=n}))},Yl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,i,u){r[e][t].delay=+n.call(r,r.__data__,i,u)}:(n=+n,function(r){r[e][t].delay=n}))},Yl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,i,u){r[e][t].duration=Math.max(1,n.call(r,r.__data__,i,u))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Yl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var i=Ol,u=Hl;try{Hl=e,Y(this,function(t,i,u){Ol=t[r][e],n.call(t,t.__data__,i,u)})}finally{Ol=i,Hl=u}}else Y(this,function(i){var u=i[r][e];(u.event||(u.event=ao.dispatch("start","end","interrupt"))).on(n,t)});return this},Yl.transition=function(){for(var n,t,e,r,i=this.id,u=++Zl,o=this.namespace,a=[],l=0,c=this.length;c>l;l++){a.push(n=[]);for(var t=this[l],f=0,s=t.length;s>f;f++)(e=t[f])&&(r=e[o][i],Qu(e,f,o,u,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Wu(a,o,u)},ao.svg.axis=function(){function n(n){n.each(function(){var n,c=ao.select(this),f=this.__chart__||e,s=this.__chart__=e.copy(),h=null==l?s.ticks?s.ticks.apply(s,a):s.domain():l,p=null==t?s.tickFormat?s.tickFormat.apply(s,a):m:t,g=c.selectAll(".tick").data(h,s),v=g.enter().insert("g",".domain").attr("class","tick").style("opacity",Uo),d=ao.transition(g.exit()).style("opacity",Uo).remove(),y=ao.transition(g.order()).style("opacity",1),M=Math.max(i,0)+o,x=Zi(s),b=c.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ao.transition(b));v.append("line"),v.append("text");var w,S,k,N,E=v.select("line"),A=y.select("line"),C=g.select("text").text(p),z=v.select("text"),L=y.select("text"),q="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=no,w="x",k="y",S="x2",N="y2",C.attr("dy",0>q?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+q*u+"V0H"+x[1]+"V"+q*u)):(n=to,w="y",k="x",S="y2",N="x2",C.attr("dy",".32em").style("text-anchor",0>q?"end":"start"),_.attr("d","M"+q*u+","+x[0]+"H0V"+x[1]+"H"+q*u)),E.attr(N,q*i),z.attr(k,q*M),A.attr(S,0).attr(N,q*i),L.attr(w,0).attr(k,q*M),s.rangeBand){var T=s,R=T.rangeBand()/2;f=s=function(n){return T(n)+R}}else f.rangeBand?f=s:d.call(n,s,f);v.call(n,f,s),y.call(n,s,s)})}var t,e=ao.scale.linear(),r=Vl,i=6,u=6,o=3,a=[10],l=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Xl?t+"":Vl,n):r},n.ticks=function(){return arguments.length?(a=co(arguments),n):a},n.tickValues=function(t){return arguments.length?(l=t,n):l},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(i=+t,u=+arguments[e-1],n):i},n.innerTickSize=function(t){return arguments.length?(i=+t,n):i},n.outerTickSize=function(t){return arguments.length?(u=+t,n):u},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Vl="bottom",Xl={top:1,right:1,bottom:1,left:1};ao.svg.brush=function(){function n(t){t.each(function(){var t=ao.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,m);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return $l[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,s=ao.transition(t),h=ao.transition(o);c&&(l=Zi(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),r(s)),f&&(l=Zi(f),h.attr("y",l[0]).attr("height",l[1]-l[0]),i(s)),e(s)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+s[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",s[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",s[1]-s[0])}function i(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function u(){function u(){32==ao.event.keyCode&&(C||(M=null,L[0]-=s[1],L[1]-=h[1],C=2),S())}function v(){32==ao.event.keyCode&&2==C&&(L[0]+=s[1],L[1]+=h[1],C=0,S())}function d(){var n=ao.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ao.event.altKey?(M||(M=[(s[0]+s[1])/2,(h[0]+h[1])/2]),L[0]=s[+(n[0]f?(i=r,r=f):i=f),v[0]!=r||v[1]!=i?(e?a=null:o=null,v[0]=r,v[1]=i,!0):void 0}function m(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ao.select("body").style("cursor",null),q.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ao.select(ao.event.target),w=l.of(b,arguments),k=ao.select(b),N=_.datum(),E=!/^(n|s)$/.test(N)&&c,A=!/^(e|w)$/.test(N)&&f,C=_.classed("extent"),z=W(b),L=ao.mouse(b),q=ao.select(t(b)).on("keydown.brush",u).on("keyup.brush",v);if(ao.event.changedTouches?q.on("touchmove.brush",d).on("touchend.brush",m):q.on("mousemove.brush",d).on("mouseup.brush",m),k.interrupt().selectAll("*").interrupt(),C)L[0]=s[0]-L[0],L[1]=h[0]-L[1];else if(N){var T=+/w$/.test(N),R=+/^n/.test(N);x=[s[1-T]-L[0],h[1-R]-L[1]],L[0]=s[T],L[1]=h[R]}else ao.event.altKey&&(M=L.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ao.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,l=N(n,"brushstart","brush","brushend"),c=null,f=null,s=[0,0],h=[0,0],p=!0,g=!0,v=Bl[0];return n.event=function(n){n.each(function(){var n=l.of(this,arguments),t={x:s,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Hl?ao.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,s=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=xr(s,t.x),r=xr(h,t.y);return o=a=null,function(i){s=t.x=e(i),h=t.y=r(i),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,v=Bl[!c<<1|!f],n):c},n.y=function(t){return arguments.length?(f=t,v=Bl[!c<<1|!f],n):f},n.clamp=function(t){return arguments.length?(c&&f?(p=!!t[0],g=!!t[1]):c?p=!!t:f&&(g=!!t),n):c&&f?[p,g]:c?p:f?g:null},n.extent=function(t){var e,r,i,u,l;return arguments.length?(c&&(e=t[0],r=t[1],f&&(e=e[0],r=r[0]),o=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(l=e,e=r,r=l),e==s[0]&&r==s[1]||(s=[e,r])),f&&(i=t[0],u=t[1],c&&(i=i[1],u=u[1]),a=[i,u],f.invert&&(i=f(i),u=f(u)),i>u&&(l=i,i=u,u=l),i==h[0]&&u==h[1]||(h=[i,u])),n):(c&&(o?(e=o[0],r=o[1]):(e=s[0],r=s[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(l=e,e=r,r=l))),f&&(a?(i=a[0],u=a[1]):(i=h[0],u=h[1],f.invert&&(i=f.invert(i),u=f.invert(u)),i>u&&(l=i,i=u,u=l))),c&&f?[[e,i],[r,u]]:c?[e,r]:f&&[i,u])},n.clear=function(){return n.empty()||(s=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!c&&s[0]==s[1]||!!f&&h[0]==h[1]},ao.rebind(n,l,"on")};var $l={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Bl=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Wl=ga.format=xa.timeFormat,Jl=Wl.utc,Gl=Jl("%Y-%m-%dT%H:%M:%S.%LZ");Wl.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?eo:Gl,eo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},eo.toString=Gl.toString,ga.second=On(function(n){return new va(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ga.seconds=ga.second.range,ga.seconds.utc=ga.second.utc.range,ga.minute=On(function(n){return new va(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ga.minutes=ga.minute.range,ga.minutes.utc=ga.minute.utc.range,ga.hour=On(function(n){var t=n.getTimezoneOffset()/60;return new va(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ga.hours=ga.hour.range,ga.hours.utc=ga.hour.utc.range,ga.month=On(function(n){return n=ga.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ga.months=ga.month.range,ga.months.utc=ga.month.utc.range;var Kl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Ql=[[ga.second,1],[ga.second,5],[ga.second,15],[ga.second,30],[ga.minute,1],[ga.minute,5],[ga.minute,15],[ga.minute,30],[ga.hour,1],[ga.hour,3],[ga.hour,6],[ga.hour,12],[ga.day,1],[ga.day,2],[ga.week,1],[ga.month,1],[ga.month,3],[ga.year,1]],nc=Wl.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",zt]]),tc={range:function(n,t,e){return ao.range(Math.ceil(n/e)*e,+t,e).map(io)},floor:m,ceil:m};Ql.year=ga.year,ga.scale=function(){return ro(ao.scale.linear(),Ql,nc)};var ec=Ql.map(function(n){return[n[0].utc,n[1]]}),rc=Jl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",zt]]);ec.year=ga.year.utc,ga.scale.utc=function(){return ro(ao.scale.linear(),ec,rc)},ao.text=An(function(n){return n.responseText}),ao.json=function(n,t){return Cn(n,"application/json",uo,t)},ao.html=function(n,t){return Cn(n,"text/html",oo,t)},ao.xml=An(function(n){return n.responseXML}),"function"==typeof define&&define.amd?(this.d3=ao,define(ao)):"object"==typeof module&&module.exports?module.exports=ao:this.d3=ao}(); $(function() { - var $window = $(window) - , $top_link = $('#toplink') - , $body = $('body, html') - , offset = $('#code').offset().top - , hidePopover = function ($target) { - $target.data('popover-hover', false); - - setTimeout(function () { - if (!$target.data('popover-hover')) { - $target.popover('hide'); - } - }, 300); - }; + offset) { - $top_link.fadeIn(); - } else { - $top_link.fadeOut(); +use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Example; +use function array_slice; +use function file; +use function getcwd; +use function implode; +use function is_readable; +use function rtrim; +use function sprintf; +use function trim; +use const DIRECTORY_SEPARATOR; +/** + * Class used to find an example file's location based on a given ExampleDescriptor. + */ +class ExampleFinder +{ + /** @var string */ + private $sourceDirectory = ''; + /** @var string[] */ + private $exampleDirectories = []; + /** + * Attempts to find the example contents for the given descriptor. + */ + public function find(Example $example) : string + { + $filename = $example->getFilePath(); + $file = $this->getExampleFileContents($filename); + if (!$file) { + return sprintf('** File not found : %s **', $filename); + } + return implode('', array_slice($file, $example->getStartingLine() - 1, $example->getLineCount())); } - }).scroll(); - - $('.popin') - .popover({trigger: 'manual'}) - .on({ - 'mouseenter.popover': function () { - var $target = $(this); - var $container = $target.children().first(); - - $target.data('popover-hover', true); - - // popover already displayed - if ($target.next('.popover').length) { - return; - } - - // show the popover - $container.popover('show'); - - // register mouse events on the popover - $target.next('.popover:not(.popover-initialized)') - .on({ - 'mouseenter': function () { - $target.data('popover-hover', true); - }, - 'mouseleave': function () { - hidePopover($container); + /** + * Registers the project's root directory where an 'examples' folder can be expected. + */ + public function setSourceDirectory(string $directory = '') : void + { + $this->sourceDirectory = $directory; + } + /** + * Returns the project's root directory where an 'examples' folder can be expected. + */ + public function getSourceDirectory() : string + { + return $this->sourceDirectory; + } + /** + * Registers a series of directories that may contain examples. + * + * @param string[] $directories + */ + public function setExampleDirectories(array $directories) : void + { + $this->exampleDirectories = $directories; + } + /** + * Returns a series of directories that may contain examples. + * + * @return string[] + */ + public function getExampleDirectories() : array + { + return $this->exampleDirectories; + } + /** + * Attempts to find the requested example file and returns its contents or null if no file was found. + * + * This method will try several methods in search of the given example file, the first one it encounters is + * returned: + * + * 1. Iterates through all examples folders for the given filename + * 2. Checks the source folder for the given filename + * 3. Checks the 'examples' folder in the current working directory for examples + * 4. Checks the path relative to the current working directory for the given filename + * + * @return string[] all lines of the example file + */ + private function getExampleFileContents(string $filename) : ?array + { + $normalizedPath = null; + foreach ($this->exampleDirectories as $directory) { + $exampleFileFromConfig = $this->constructExamplePath($directory, $filename); + if (is_readable($exampleFileFromConfig)) { + $normalizedPath = $exampleFileFromConfig; + break; + } } - }) - .addClass('popover-initialized'); - }, - 'mouseleave.popover': function () { - hidePopover($(this).children().first()); - } - }); - }); -/* - Copyright (C) Federico Zivolo 2019 - Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT). - */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=e.ownerDocument.defaultView,n=o.getComputedStyle(e,null);return t?n[t]:n}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function r(e){return 11===e?pe:10===e?se:pe||se}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent||null;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TH','TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=Q(f[o],a[e]-('right'===e?f.width:f.height))),le({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=fe({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!K(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-us[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,w=t(e.instance.popper),y=parseFloat(w['margin'+f],10),E=parseFloat(w['border'+f+'Width'],10),v=b-e.offsets.popper[m]-y-E;return v=ee(Q(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},le(n,m,$(v)),le(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case ge.FLIP:p=[n,i];break;case ge.CLOCKWISE:p=G(n);break;case ge.COUNTERCLOCKWISE:p=G(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)f(l.top)||'bottom'===n&&f(a.top)f(o.right),g=f(a.top)f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,w=-1!==['top','bottom'].indexOf(n),y=!!t.flipVariations&&(w&&'start'===r&&h||w&&'end'===r&&c||!w&&'start'===r&&g||!w&&'end'===r&&u),E=!!t.flipVariationsByContent&&(w&&'start'===r&&c||w&&'end'===r&&h||!w&&'start'===r&&u||!w&&'end'===r&&g),v=y||E;(m||b||v)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),v&&(r=z(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=fe({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport',flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!K(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.rightwindow.devicePixelRatio||!me),c='bottom'===o?'top':'bottom',g='right'===n?'left':'right',b=B('transform');if(d='bottom'==c?'HTML'===l.nodeName?-l.clientHeight+h.bottom:-f.height+h.bottom:h.top,s='right'==g?'HTML'===l.nodeName?-l.clientWidth+h.right:-f.width+h.right:h.left,a&&b)m[b]='translate3d('+s+'px, '+d+'px, 0)',m[c]=0,m[g]=0,m.willChange='transform';else{var w='bottom'==c?-1:1,y='right'==g?-1:1;m[c]=d*w,m[g]=s*y,m.willChange=c+', '+g}var E={"x-placement":e.placement};return e.attributes=fe({},E,e.attributes),e.styles=fe({},m,e.styles),e.arrowStyles=fe({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:'bottom',y:'right'},applyStyle:{order:900,enabled:!0,fn:function(e){return V(e.instance.popper,e.styles),j(e.instance.popper,e.attributes),e.arrowElement&&Object.keys(e.arrowStyles).length&&V(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,o,n,i){var r=L(i,t,e,o.positionFixed),p=O(o.placement,r,t,e,o.modifiers.flip.boundariesElement,o.modifiers.flip.padding);return t.setAttribute('x-placement',p),V(t,{position:o.positionFixed?'fixed':'absolute'}),o},gpuAcceleration:void 0}}},ue}); -//# sourceMappingURL=popper.min.js.map + if (!$normalizedPath) { + if (is_readable($this->getExamplePathFromSource($filename))) { + $normalizedPath = $this->getExamplePathFromSource($filename); + } elseif (is_readable($this->getExamplePathFromExampleDirectory($filename))) { + $normalizedPath = $this->getExamplePathFromExampleDirectory($filename); + } elseif (is_readable($filename)) { + $normalizedPath = $filename; + } + } + $lines = $normalizedPath && is_readable($normalizedPath) ? file($normalizedPath) : \false; + return $lines !== \false ? $lines : null; + } + /** + * Get example filepath based on the example directory inside your project. + */ + private function getExamplePathFromExampleDirectory(string $file) : string + { + return getcwd() . \DIRECTORY_SEPARATOR . 'examples' . \DIRECTORY_SEPARATOR . $file; + } + /** + * Returns a path to the example file in the given directory.. + */ + private function constructExamplePath(string $directory, string $file) : string + { + return rtrim($directory, '\\/') . \DIRECTORY_SEPARATOR . $file; + } + /** + * Get example filepath based on sourcecode. + */ + private function getExamplePathFromSource(string $file) : string + { + return sprintf('%s%s%s', trim($this->getSourceDirectory(), '\\/'), \DIRECTORY_SEPARATOR, trim($file, '"')); + } +} +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Html; +namespace PHPUnit\phpDocumentor\Reflection\DocBlock; -use const ENT_COMPAT; -use const ENT_HTML401; -use const ENT_SUBSTITUTE; -use const T_ABSTRACT; -use const T_ARRAY; -use const T_AS; -use const T_BREAK; -use const T_CALLABLE; -use const T_CASE; -use const T_CATCH; -use const T_CLASS; -use const T_CLONE; -use const T_COMMENT; -use const T_CONST; -use const T_CONTINUE; -use const T_DECLARE; -use const T_DEFAULT; -use const T_DO; -use const T_DOC_COMMENT; -use const T_ECHO; -use const T_ELSE; -use const T_ELSEIF; -use const T_EMPTY; -use const T_ENDDECLARE; -use const T_ENDFOR; -use const T_ENDFOREACH; -use const T_ENDIF; -use const T_ENDSWITCH; -use const T_ENDWHILE; -use const T_EVAL; -use const T_EXIT; -use const T_EXTENDS; -use const T_FINAL; -use const T_FINALLY; -use const T_FOR; -use const T_FOREACH; -use const T_FUNCTION; -use const T_GLOBAL; -use const T_GOTO; -use const T_HALT_COMPILER; -use const T_IF; -use const T_IMPLEMENTS; -use const T_INCLUDE; -use const T_INCLUDE_ONCE; -use const T_INLINE_HTML; -use const T_INSTANCEOF; -use const T_INSTEADOF; -use const T_INTERFACE; -use const T_ISSET; -use const T_LIST; -use const T_NAMESPACE; -use const T_NEW; -use const T_PRINT; -use const T_PRIVATE; -use const T_PROTECTED; -use const T_PUBLIC; -use const T_REQUIRE; -use const T_REQUIRE_ONCE; -use const T_RETURN; -use const T_STATIC; -use const T_SWITCH; -use const T_THROW; -use const T_TRAIT; -use const T_TRY; -use const T_UNSET; -use const T_USE; -use const T_VAR; -use const T_WHILE; -use const T_YIELD; -use const T_YIELD_FROM; -use function array_key_exists; -use function array_pop; -use function array_unique; -use function constant; +use PHPUnit\phpDocumentor\Reflection\Types\Context as TypeContext; +use PHPUnit\phpDocumentor\Reflection\Utils; use function count; -use function defined; use function explode; -use function file_get_contents; -use function htmlspecialchars; -use function is_string; -use function sprintf; +use function implode; +use function ltrim; +use function min; use function str_replace; +use function strlen; +use function strpos; use function substr; -use function token_get_all; use function trim; -use PHPUnit\Runner\BaseTestRunner; -use PHPUnit\SebastianBergmann\CodeCoverage\Node\File as FileNode; -use PHPUnit\SebastianBergmann\CodeCoverage\Percentage; -use PHPUnit\SebastianBergmann\Template\Template; +use const PREG_SPLIT_DELIM_CAPTURE; /** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * Creates a new Description object given a body of text. + * + * Descriptions in phpDocumentor are somewhat complex entities as they can contain one or more tags inside their + * body that can be replaced with a readable output. The replacing is done by passing a Formatter object to the + * Description object's `render` method. + * + * In addition to the above does a Description support two types of escape sequences: + * + * 1. `{@}` to escape the `@` character to prevent it from being interpreted as part of a tag, i.e. `{{@}link}` + * 2. `{}` to escape the `}` character, this can be used if you want to use the `}` character in the description + * of an inline tag. + * + * If a body consists of multiple lines then this factory will also remove any superfluous whitespace at the beginning + * of each line while maintaining any indentation that is used. This will prevent formatting parsers from tripping + * over unexpected spaces as can be observed with tag descriptions. */ -final class File extends \PHPUnit\SebastianBergmann\CodeCoverage\Report\Html\Renderer +class DescriptionFactory { + /** @var TagFactory */ + private $tagFactory; /** - * @psalm-var array - */ - private static $keywordTokens = []; - /** - * @var array + * Initializes this factory with the means to construct (inline) tags. */ - private static $formattedSourceCache = []; + public function __construct(TagFactory $tagFactory) + { + $this->tagFactory = $tagFactory; + } /** - * @var int + * Returns the parsed text of this description. */ - private $htmlSpecialCharsFlags = \ENT_COMPAT | \ENT_HTML401 | \ENT_SUBSTITUTE; - public function render(\PHPUnit\SebastianBergmann\CodeCoverage\Node\File $node, string $file) : void + public function create(string $contents, ?TypeContext $context = null) : Description { - $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'file_branch.html' : 'file.html'); - $template = new \PHPUnit\SebastianBergmann\Template\Template($templateName, '{{', '}}'); - $this->setCommonTemplateVariables($template, $node); - $template->setVar(['items' => $this->renderItems($node), 'lines' => $this->renderSourceWithLineCoverage($node), 'legend' => '

ExecutedNot ExecutedDead Code

', 'structure' => '']); - $template->renderTo($file . '.html'); - if ($this->hasBranchCoverage) { - $template->setVar(['items' => $this->renderItems($node), 'lines' => $this->renderSourceWithBranchCoverage($node), 'legend' => '

Fully coveredPartially coveredNot covered

', 'structure' => $this->renderBranchStructure($node)]); - $template->renderTo($file . '_branch.html'); - $template->setVar(['items' => $this->renderItems($node), 'lines' => $this->renderSourceWithPathCoverage($node), 'legend' => '

Fully coveredPartially coveredNot covered

', 'structure' => $this->renderPathStructure($node)]); - $template->renderTo($file . '_path.html'); + $tokens = $this->lex($contents); + $count = count($tokens); + $tagCount = 0; + $tags = []; + for ($i = 1; $i < $count; $i += 2) { + $tags[] = $this->tagFactory->create($tokens[$i], $context); + $tokens[$i] = '%' . ++$tagCount . '$s'; + } + //In order to allow "literal" inline tags, the otherwise invalid + //sequence "{@}" is changed to "@", and "{}" is changed to "}". + //"%" is escaped to "%%" because of vsprintf. + //See unit tests for examples. + for ($i = 0; $i < $count; $i += 2) { + $tokens[$i] = str_replace(['{@}', '{}', '%'], ['@', '}', '%%'], $tokens[$i]); } + return new Description(implode('', $tokens), $tags); } - private function renderItems(\PHPUnit\SebastianBergmann\CodeCoverage\Node\File $node) : string + /** + * Strips the contents from superfluous whitespace and splits the description into a series of tokens. + * + * @return string[] A series of tokens of which the description text is composed. + */ + private function lex(string $contents) : array { - $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'file_item_branch.html' : 'file_item.html'); - $template = new \PHPUnit\SebastianBergmann\Template\Template($templateName, '{{', '}}'); - $methodTemplateName = $this->templatePath . ($this->hasBranchCoverage ? 'method_item_branch.html' : 'method_item.html'); - $methodItemTemplate = new \PHPUnit\SebastianBergmann\Template\Template($methodTemplateName, '{{', '}}'); - $items = $this->renderItemTemplate($template, ['name' => 'Total', 'numClasses' => $node->numberOfClassesAndTraits(), 'numTestedClasses' => $node->numberOfTestedClassesAndTraits(), 'numMethods' => $node->numberOfFunctionsAndMethods(), 'numTestedMethods' => $node->numberOfTestedFunctionsAndMethods(), 'linesExecutedPercent' => $node->percentageOfExecutedLines()->asFloat(), 'linesExecutedPercentAsString' => $node->percentageOfExecutedLines()->asString(), 'numExecutedLines' => $node->numberOfExecutedLines(), 'numExecutableLines' => $node->numberOfExecutableLines(), 'branchesExecutedPercent' => $node->percentageOfExecutedBranches()->asFloat(), 'branchesExecutedPercentAsString' => $node->percentageOfExecutedBranches()->asString(), 'numExecutedBranches' => $node->numberOfExecutedBranches(), 'numExecutableBranches' => $node->numberOfExecutableBranches(), 'pathsExecutedPercent' => $node->percentageOfExecutedPaths()->asFloat(), 'pathsExecutedPercentAsString' => $node->percentageOfExecutedPaths()->asString(), 'numExecutedPaths' => $node->numberOfExecutedPaths(), 'numExecutablePaths' => $node->numberOfExecutablePaths(), 'testedMethodsPercent' => $node->percentageOfTestedFunctionsAndMethods()->asFloat(), 'testedMethodsPercentAsString' => $node->percentageOfTestedFunctionsAndMethods()->asString(), 'testedClassesPercent' => $node->percentageOfTestedClassesAndTraits()->asFloat(), 'testedClassesPercentAsString' => $node->percentageOfTestedClassesAndTraits()->asString(), 'crap' => 'CRAP']); - $items .= $this->renderFunctionItems($node->functions(), $methodItemTemplate); - $items .= $this->renderTraitOrClassItems($node->traits(), $template, $methodItemTemplate); - $items .= $this->renderTraitOrClassItems($node->classes(), $template, $methodItemTemplate); - return $items; + $contents = $this->removeSuperfluousStartingWhitespace($contents); + // performance optimalization; if there is no inline tag, don't bother splitting it up. + if (strpos($contents, '{@') === \false) { + return [$contents]; + } + return Utils::pregSplit('/\\{ + # "{@}" is not a valid inline tag. This ensures that we do not treat it as one, but treat it literally. + (?!@\\}) + # We want to capture the whole tag line, but without the inline tag delimiters. + (\\@ + # Match everything up to the next delimiter. + [^{}]* + # Nested inline tag content should not be captured, or it will appear in the result separately. + (?: + # Match nested inline tags. + (?: + # Because we did not catch the tag delimiters earlier, we must be explicit with them here. + # Notice that this also matches "{}", as a way to later introduce it as an escape sequence. + \\{(?1)?\\} + | + # Make sure we match hanging "{". + \\{ + ) + # Match content after the nested inline tag. + [^{}]* + )* # If there are more inline tags, match them as well. We use "*" since there may not be any + # nested inline tags. + ) + \\}/Sux', $contents, 0, \PREG_SPLIT_DELIM_CAPTURE); } - private function renderTraitOrClassItems(array $items, \PHPUnit\SebastianBergmann\Template\Template $template, \PHPUnit\SebastianBergmann\Template\Template $methodItemTemplate) : string + /** + * Removes the superfluous from a multi-line description. + * + * When a description has more than one line then it can happen that the second and subsequent lines have an + * additional indentation. This is commonly in use with tags like this: + * + * {@}since 1.1.0 This is an example + * description where we have an + * indentation in the second and + * subsequent lines. + * + * If we do not normalize the indentation then we have superfluous whitespace on the second and subsequent + * lines and this may cause rendering issues when, for example, using a Markdown converter. + */ + private function removeSuperfluousStartingWhitespace(string $contents) : string { - $buffer = ''; - if (empty($items)) { - return $buffer; + $lines = explode("\n", $contents); + // if there is only one line then we don't have lines with superfluous whitespace and + // can use the contents as-is + if (count($lines) <= 1) { + return $contents; } - foreach ($items as $name => $item) { - $numMethods = 0; - $numTestedMethods = 0; - foreach ($item['methods'] as $method) { - if ($method['executableLines'] > 0) { - $numMethods++; - if ($method['executedLines'] === $method['executableLines']) { - $numTestedMethods++; - } - } - } - if ($item['executableLines'] > 0) { - $numClasses = 1; - $numTestedClasses = $numTestedMethods === $numMethods ? 1 : 0; - $linesExecutedPercentAsString = \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($item['executedLines'], $item['executableLines'])->asString(); - $branchesExecutedPercentAsString = \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($item['executedBranches'], $item['executableBranches'])->asString(); - $pathsExecutedPercentAsString = \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($item['executedPaths'], $item['executablePaths'])->asString(); - } else { - $numClasses = 0; - $numTestedClasses = 0; - $linesExecutedPercentAsString = 'n/a'; - $branchesExecutedPercentAsString = 'n/a'; - $pathsExecutedPercentAsString = 'n/a'; + // determine how many whitespace characters need to be stripped + $startingSpaceCount = 9999999; + for ($i = 1, $iMax = count($lines); $i < $iMax; ++$i) { + // lines with a no length do not count as they are not indented at all + if (trim($lines[$i]) === '') { + continue; } - $testedMethodsPercentage = \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($numTestedMethods, $numMethods); - $testedClassesPercentage = \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($numTestedMethods === $numMethods ? 1 : 0, 1); - $buffer .= $this->renderItemTemplate($template, ['name' => $this->abbreviateClassName($name), 'numClasses' => $numClasses, 'numTestedClasses' => $numTestedClasses, 'numMethods' => $numMethods, 'numTestedMethods' => $numTestedMethods, 'linesExecutedPercent' => \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($item['executedLines'], $item['executableLines'])->asFloat(), 'linesExecutedPercentAsString' => $linesExecutedPercentAsString, 'numExecutedLines' => $item['executedLines'], 'numExecutableLines' => $item['executableLines'], 'branchesExecutedPercent' => \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($item['executedBranches'], $item['executableBranches'])->asFloat(), 'branchesExecutedPercentAsString' => $branchesExecutedPercentAsString, 'numExecutedBranches' => $item['executedBranches'], 'numExecutableBranches' => $item['executableBranches'], 'pathsExecutedPercent' => \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($item['executedPaths'], $item['executablePaths'])->asFloat(), 'pathsExecutedPercentAsString' => $pathsExecutedPercentAsString, 'numExecutedPaths' => $item['executedPaths'], 'numExecutablePaths' => $item['executablePaths'], 'testedMethodsPercent' => $testedMethodsPercentage->asFloat(), 'testedMethodsPercentAsString' => $testedMethodsPercentage->asString(), 'testedClassesPercent' => $testedClassesPercentage->asFloat(), 'testedClassesPercentAsString' => $testedClassesPercentage->asString(), 'crap' => $item['crap']]); - foreach ($item['methods'] as $method) { - $buffer .= $this->renderFunctionOrMethodItem($methodItemTemplate, $method, ' '); + // determine the number of prefixing spaces by checking the difference in line length before and after + // an ltrim + $startingSpaceCount = min($startingSpaceCount, strlen($lines[$i]) - strlen(ltrim($lines[$i]))); + } + // strip the number of spaces from each line + if ($startingSpaceCount > 0) { + for ($i = 1, $iMax = count($lines); $i < $iMax; ++$i) { + $lines[$i] = substr($lines[$i], $startingSpaceCount); } } - return $buffer; + return implode("\n", $lines); + } +} +create('This is a {@see Description}', $context); + * + * The description factory will interpret the given body and create a body template and list of tags from them, and pass + * that onto the constructor if this class. + * + * > The $context variable is a class of type {@see \phpDocumentor\Reflection\Types\Context} and contains the namespace + * > and the namespace aliases that apply to this DocBlock. These are used by the Factory to resolve and expand partial + * > type names and FQSENs. + * + * If you do not want to use the DescriptionFactory you can pass a body template and tag listing like this: + * + * $description = new Description( + * 'This is a %1$s', + * [ new See(new Fqsen('\phpDocumentor\Reflection\DocBlock\Description')) ] + * ); + * + * It is generally recommended to use the Factory as that will also apply escaping rules, while the Description object + * is mainly responsible for rendering. + * + * @see DescriptionFactory to create a new Description. + * @see Description\Formatter for the formatting of the body and tags. + */ +class Description +{ + /** @var string */ + private $bodyTemplate; + /** @var Tag[] */ + private $tags; + /** + * Initializes a Description with its body (template) and a listing of the tags used in the body template. + * + * @param Tag[] $tags + */ + public function __construct(string $bodyTemplate, array $tags = []) + { + $this->bodyTemplate = $bodyTemplate; + $this->tags = $tags; } - private function renderFunctionItems(array $functions, \PHPUnit\SebastianBergmann\Template\Template $template) : string + /** + * Returns the body template. + */ + public function getBodyTemplate() : string { - if (empty($functions)) { - return ''; + return $this->bodyTemplate; + } + /** + * Returns the tags for this DocBlock. + * + * @return Tag[] + */ + public function getTags() : array + { + return $this->tags; + } + /** + * Renders this description as a string where the provided formatter will format the tags in the expected string + * format. + */ + public function render(?Formatter $formatter = null) : string + { + if ($formatter === null) { + $formatter = new PassthroughFormatter(); } - $buffer = ''; - foreach ($functions as $function) { - $buffer .= $this->renderFunctionOrMethodItem($template, $function); + $tags = []; + foreach ($this->tags as $tag) { + $tags[] = '{' . $formatter->format($tag) . '}'; } - return $buffer; + return vsprintf($this->bodyTemplate, $tags); } - private function renderFunctionOrMethodItem(\PHPUnit\SebastianBergmann\Template\Template $template, array $item, string $indent = '') : string + /** + * Returns a plain string representation of this description. + */ + public function __toString() : string { - $numMethods = 0; - $numTestedMethods = 0; - if ($item['executableLines'] > 0) { - $numMethods = 1; - if ($item['executedLines'] === $item['executableLines']) { - $numTestedMethods = 1; - } + return $this->render(); + } +} +descriptionFactory = $descriptionFactory; + $this->tagFactory = $tagFactory; + } + /** + * Factory method for easy instantiation. + * + * @param array> $additionalTags + */ + public static function createInstance(array $additionalTags = []) : self + { + $fqsenResolver = new FqsenResolver(); + $tagFactory = new StandardTagFactory($fqsenResolver); + $descriptionFactory = new DescriptionFactory($tagFactory); + $tagFactory->addService($descriptionFactory); + $tagFactory->addService(new TypeResolver($fqsenResolver)); + $docBlockFactory = new self($descriptionFactory, $tagFactory); + foreach ($additionalTags as $tagName => $tagHandler) { + $docBlockFactory->registerTagHandler($tagName, $tagHandler); } - $executedLinesPercentage = \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($item['executedLines'], $item['executableLines']); - $executedBranchesPercentage = \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($item['executedBranches'], $item['executableBranches']); - $executedPathsPercentage = \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($item['executedPaths'], $item['executablePaths']); - $testedMethodsPercentage = \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($numTestedMethods, 1); - return $this->renderItemTemplate($template, ['name' => \sprintf('%s%s', $indent, $item['startLine'], \htmlspecialchars($item['signature'], $this->htmlSpecialCharsFlags), $item['functionName'] ?? $item['methodName']), 'numMethods' => $numMethods, 'numTestedMethods' => $numTestedMethods, 'linesExecutedPercent' => $executedLinesPercentage->asFloat(), 'linesExecutedPercentAsString' => $executedLinesPercentage->asString(), 'numExecutedLines' => $item['executedLines'], 'numExecutableLines' => $item['executableLines'], 'branchesExecutedPercent' => $executedBranchesPercentage->asFloat(), 'branchesExecutedPercentAsString' => $executedBranchesPercentage->asString(), 'numExecutedBranches' => $item['executedBranches'], 'numExecutableBranches' => $item['executableBranches'], 'pathsExecutedPercent' => $executedPathsPercentage->asFloat(), 'pathsExecutedPercentAsString' => $executedPathsPercentage->asString(), 'numExecutedPaths' => $item['executedPaths'], 'numExecutablePaths' => $item['executablePaths'], 'testedMethodsPercent' => $testedMethodsPercentage->asFloat(), 'testedMethodsPercentAsString' => $testedMethodsPercentage->asString(), 'crap' => $item['crap']]); + return $docBlockFactory; } - private function renderSourceWithLineCoverage(\PHPUnit\SebastianBergmann\CodeCoverage\Node\File $node) : string + /** + * @param object|string $docblock A string containing the DocBlock to parse or an object supporting the + * getDocComment method (such as a ReflectionClass object). + */ + public function create($docblock, ?Types\Context $context = null, ?Location $location = null) : DocBlock { - $linesTemplate = new \PHPUnit\SebastianBergmann\Template\Template($this->templatePath . 'lines.html.dist', '{{', '}}'); - $singleLineTemplate = new \PHPUnit\SebastianBergmann\Template\Template($this->templatePath . 'line.html.dist', '{{', '}}'); - $coverageData = $node->lineCoverageData(); - $testData = $node->testData(); - $codeLines = $this->loadFile($node->pathAsString()); - $lines = ''; - $i = 1; - foreach ($codeLines as $line) { - $trClass = ''; - $popoverContent = ''; - $popoverTitle = ''; - if (\array_key_exists($i, $coverageData)) { - $numTests = $coverageData[$i] ? \count($coverageData[$i]) : 0; - if ($coverageData[$i] === null) { - $trClass = 'warning'; - } elseif ($numTests === 0) { - $trClass = 'danger'; - } else { - if ($numTests > 1) { - $popoverTitle = $numTests . ' tests cover line ' . $i; - } else { - $popoverTitle = '1 test covers line ' . $i; - } - $lineCss = 'covered-by-large-tests'; - $popoverContent = '
    '; - foreach ($coverageData[$i] as $test) { - if ($lineCss === 'covered-by-large-tests' && $testData[$test]['size'] === 'medium') { - $lineCss = 'covered-by-medium-tests'; - } elseif ($testData[$test]['size'] === 'small') { - $lineCss = 'covered-by-small-tests'; - } - $popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]); - } - $popoverContent .= '
'; - $trClass = $lineCss . ' popin'; - } - } - $popover = ''; - if (!empty($popoverTitle)) { - $popover = \sprintf(' data-title="%s" data-content="%s" data-placement="top" data-html="true"', $popoverTitle, \htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags)); + if (is_object($docblock)) { + if (!method_exists($docblock, 'getDocComment')) { + $exceptionMessage = 'Invalid object passed; the given object must support the getDocComment method'; + throw new InvalidArgumentException($exceptionMessage); } - $lines .= $this->renderLine($singleLineTemplate, $i, $line, $trClass, $popover); - $i++; + $docblock = $docblock->getDocComment(); + Assert::string($docblock); + } + Assert::stringNotEmpty($docblock); + if ($context === null) { + $context = new Types\Context(''); + } + $parts = $this->splitDocBlock($this->stripDocComment($docblock)); + [$templateMarker, $summary, $description, $tags] = $parts; + return new DocBlock($summary, $description ? $this->descriptionFactory->create($description, $context) : null, $this->parseTagBlock($tags, $context), $context, $location, $templateMarker === '#@+', $templateMarker === '#@-'); + } + /** + * @param class-string $handler + */ + public function registerTagHandler(string $tagName, string $handler) : void + { + $this->tagFactory->registerTagHandler($tagName, $handler); + } + /** + * Strips the asterisks from the DocBlock comment. + * + * @param string $comment String containing the comment text. + */ + private function stripDocComment(string $comment) : string + { + $comment = preg_replace('#[ \\t]*(?:\\/\\*\\*|\\*\\/|\\*)?[ \\t]?(.*)?#u', '$1', $comment); + Assert::string($comment); + $comment = trim($comment); + // reg ex above is not able to remove */ from a single line docblock + if (substr($comment, -2) === '*/') { + $comment = trim(substr($comment, 0, -2)); + } + return str_replace(["\r\n", "\r"], "\n", $comment); + } + // phpcs:disable + /** + * Splits the DocBlock into a template marker, summary, description and block of tags. + * + * @param string $comment Comment to split into the sub-parts. + * + * @return string[] containing the template marker (if any), summary, description and a string containing the tags. + * + * @author Mike van Riel for extending the regex with template marker support. + * + * @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split. + */ + private function splitDocBlock(string $comment) : array + { + // phpcs:enable + // Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This + // method does not split tags so we return this verbatim as the fourth result (tags). This saves us the + // performance impact of running a regular expression + if (strpos($comment, '@') === 0) { + return ['', '', '', $comment]; + } + // clears all extra horizontal whitespace from the line endings to prevent parsing issues + $comment = preg_replace('/\\h*$/Sum', '', $comment); + Assert::string($comment); + /* + * Splits the docblock into a template marker, summary, description and tags section. + * + * - The template marker is empty, #@+ or #@- if the DocBlock starts with either of those (a newline may + * occur after it and will be stripped). + * - The short description is started from the first character until a dot is encountered followed by a + * newline OR two consecutive newlines (horizontal whitespace is taken into account to consider spacing + * errors). This is optional. + * - The long description, any character until a new line is encountered followed by an @ and word + * characters (a tag). This is optional. + * - Tags; the remaining characters + * + * Big thanks to RichardJ for contributing this Regular Expression + */ + preg_match('/ + \\A + # 1. Extract the template marker + (?:(\\#\\@\\+|\\#\\@\\-)\\n?)? + + # 2. Extract the summary + (?: + (?! @\\pL ) # The summary may not start with an @ + ( + [^\\n.]+ + (?: + (?! \\. \\n | \\n{2} ) # End summary upon a dot followed by newline or two newlines + [\\n.]* (?! [ \\t]* @\\pL ) # End summary when an @ is found as first character on a new line + [^\\n.]+ # Include anything else + )* + \\.? + )? + ) + + # 3. Extract the description + (?: + \\s* # Some form of whitespace _must_ precede a description because a summary must be there + (?! @\\pL ) # The description may not start with an @ + ( + [^\\n]+ + (?: \\n+ + (?! [ \\t]* @\\pL ) # End description when an @ is found as first character on a new line + [^\\n]+ # Include anything else + )* + ) + )? + + # 4. Extract the tags (anything that follows) + (\\s+ [\\s\\S]*)? # everything that follows + /ux', $comment, $matches); + array_shift($matches); + while (count($matches) < 4) { + $matches[] = ''; } - $linesTemplate->setVar(['lines' => $lines]); - return $linesTemplate->render(); + return $matches; } - private function renderSourceWithBranchCoverage(\PHPUnit\SebastianBergmann\CodeCoverage\Node\File $node) : string + /** + * Creates the tag objects. + * + * @param string $tags Tag block to parse. + * @param Types\Context $context Context of the parsed Tag + * + * @return DocBlock\Tag[] + */ + private function parseTagBlock(string $tags, Types\Context $context) : array { - $linesTemplate = new \PHPUnit\SebastianBergmann\Template\Template($this->templatePath . 'lines.html.dist', '{{', '}}'); - $singleLineTemplate = new \PHPUnit\SebastianBergmann\Template\Template($this->templatePath . 'line.html.dist', '{{', '}}'); - $functionCoverageData = $node->functionCoverageData(); - $testData = $node->testData(); - $codeLines = $this->loadFile($node->pathAsString()); - $lineData = []; - /** @var int $line */ - foreach (\array_keys($codeLines) as $line) { - $lineData[$line + 1] = ['includedInBranches' => 0, 'includedInHitBranches' => 0, 'tests' => []]; + $tags = $this->filterTagBlock($tags); + if ($tags === null) { + return []; } - foreach ($functionCoverageData as $method) { - foreach ($method['branches'] as $branch) { - foreach (\range($branch['line_start'], $branch['line_end']) as $line) { - if (!isset($lineData[$line])) { - // blank line at end of file is sometimes included here - continue; - } - $lineData[$line]['includedInBranches']++; - if ($branch['hit']) { - $lineData[$line]['includedInHitBranches']++; - $lineData[$line]['tests'] = \array_unique(\array_merge($lineData[$line]['tests'], $branch['hit'])); - } - } - } + $result = []; + $lines = $this->splitTagBlockIntoTagLines($tags); + foreach ($lines as $key => $tagLine) { + $result[$key] = $this->tagFactory->create(trim($tagLine), $context); } - $lines = ''; - $i = 1; - /** @var string $line */ - foreach ($codeLines as $line) { - $trClass = ''; - $popover = ''; - if ($lineData[$i]['includedInBranches'] > 0) { - $lineCss = 'success'; - if ($lineData[$i]['includedInHitBranches'] === 0) { - $lineCss = 'danger'; - } elseif ($lineData[$i]['includedInHitBranches'] !== $lineData[$i]['includedInBranches']) { - $lineCss = 'warning'; - } - $popoverContent = '
    '; - if (\count($lineData[$i]['tests']) === 1) { - $popoverTitle = '1 test covers line ' . $i; - } else { - $popoverTitle = \count($lineData[$i]['tests']) . ' tests cover line ' . $i; - } - $popoverTitle .= '. These are covering ' . $lineData[$i]['includedInHitBranches'] . ' out of the ' . $lineData[$i]['includedInBranches'] . ' code branches.'; - foreach ($lineData[$i]['tests'] as $test) { - $popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]); - } - $popoverContent .= '
'; - $trClass = $lineCss . ' popin'; - $popover = \sprintf(' data-title="%s" data-content="%s" data-placement="top" data-html="true"', $popoverTitle, \htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags)); + return $result; + } + /** + * @return string[] + */ + private function splitTagBlockIntoTagLines(string $tags) : array + { + $result = []; + foreach (explode("\n", $tags) as $tagLine) { + if ($tagLine !== '' && strpos($tagLine, '@') === 0) { + $result[] = $tagLine; + } else { + $result[count($result) - 1] .= "\n" . $tagLine; } - $lines .= $this->renderLine($singleLineTemplate, $i, $line, $trClass, $popover); - $i++; } - $linesTemplate->setVar(['lines' => $lines]); - return $linesTemplate->render(); + return $result; } - private function renderSourceWithPathCoverage(\PHPUnit\SebastianBergmann\CodeCoverage\Node\File $node) : string + private function filterTagBlock(string $tags) : ?string { - $linesTemplate = new \PHPUnit\SebastianBergmann\Template\Template($this->templatePath . 'lines.html.dist', '{{', '}}'); - $singleLineTemplate = new \PHPUnit\SebastianBergmann\Template\Template($this->templatePath . 'line.html.dist', '{{', '}}'); - $functionCoverageData = $node->functionCoverageData(); - $testData = $node->testData(); - $codeLines = $this->loadFile($node->pathAsString()); - $lineData = []; - /** @var int $line */ - foreach (\array_keys($codeLines) as $line) { - $lineData[$line + 1] = ['includedInPaths' => [], 'includedInHitPaths' => [], 'tests' => []]; - } - foreach ($functionCoverageData as $method) { - foreach ($method['paths'] as $pathId => $path) { - foreach ($path['path'] as $branchTaken) { - foreach (\range($method['branches'][$branchTaken]['line_start'], $method['branches'][$branchTaken]['line_end']) as $line) { - if (!isset($lineData[$line])) { - continue; - } - $lineData[$line]['includedInPaths'][] = $pathId; - if ($path['hit']) { - $lineData[$line]['includedInHitPaths'][] = $pathId; - $lineData[$line]['tests'] = \array_unique(\array_merge($lineData[$line]['tests'], $path['hit'])); - } - } - } - } + $tags = trim($tags); + if (!$tags) { + return null; } - $lines = ''; - $i = 1; - /** @var string $line */ - foreach ($codeLines as $line) { - $trClass = ''; - $popover = ''; - $includedInPathsCount = \count(\array_unique($lineData[$i]['includedInPaths'])); - $includedInHitPathsCount = \count(\array_unique($lineData[$i]['includedInHitPaths'])); - if ($includedInPathsCount > 0) { - $lineCss = 'success'; - if ($includedInHitPathsCount === 0) { - $lineCss = 'danger'; - } elseif ($includedInHitPathsCount !== $includedInPathsCount) { - $lineCss = 'warning'; - } - $popoverContent = '
    '; - if (\count($lineData[$i]['tests']) === 1) { - $popoverTitle = '1 test covers line ' . $i; - } else { - $popoverTitle = \count($lineData[$i]['tests']) . ' tests cover line ' . $i; - } - $popoverTitle .= '. These are covering ' . $includedInHitPathsCount . ' out of the ' . $includedInPathsCount . ' code paths.'; - foreach ($lineData[$i]['tests'] as $test) { - $popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]); - } - $popoverContent .= '
'; - $trClass = $lineCss . ' popin'; - $popover = \sprintf(' data-title="%s" data-content="%s" data-placement="top" data-html="true"', $popoverTitle, \htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags)); - } - $lines .= $this->renderLine($singleLineTemplate, $i, $line, $trClass, $popover); - $i++; + if ($tags[0] !== '@') { + // @codeCoverageIgnoreStart + // Can't simulate this; this only happens if there is an error with the parsing of the DocBlock that + // we didn't foresee. + throw new LogicException('A tag block started with text instead of an at-sign(@): ' . $tags); + // @codeCoverageIgnoreEnd } - $linesTemplate->setVar(['lines' => $lines]); - return $linesTemplate->render(); + return $tags; } - private function renderBranchStructure(\PHPUnit\SebastianBergmann\CodeCoverage\Node\File $node) : string +} +templatePath . 'branches.html.dist', '{{', '}}'); - $coverageData = $node->functionCoverageData(); - $testData = $node->testData(); - $codeLines = $this->loadFile($node->pathAsString()); - $branches = ''; - \ksort($coverageData); - foreach ($coverageData as $methodName => $methodData) { - if (!$methodData['branches']) { - continue; - } - $branchStructure = ''; - foreach ($methodData['branches'] as $branch) { - $branchStructure .= $this->renderBranchLines($branch, $codeLines, $testData); - } - if ($branchStructure !== '') { - // don't show empty branches - $branches .= '
' . $this->abbreviateMethodName($methodName) . '
' . "\n"; - $branches .= $branchStructure; - } + $parts = php_preg_split($pattern, $subject, $limit, $flags); + if ($parts === \false) { + throw PcreException::createFromPhpError(preg_last_error()); } - $branchesTemplate->setVar(['branches' => $branches]); - return $branchesTemplate->render(); + return $parts; } - private function renderBranchLines(array $branch, array $codeLines, array $testData) : string +} +templatePath . 'lines.html.dist', '{{', '}}'); - $singleLineTemplate = new \PHPUnit\SebastianBergmann\Template\Template($this->templatePath . 'line.html.dist', '{{', '}}'); - $lines = ''; - $branchLines = \range($branch['line_start'], $branch['line_end']); - \sort($branchLines); - // sometimes end_line < start_line - /** @var int $line */ - foreach ($branchLines as $line) { - if (!isset($codeLines[$line])) { - // blank line at end of file is sometimes included here - continue; - } - $popoverContent = ''; - $popoverTitle = ''; - $numTests = \count($branch['hit']); - if ($numTests === 0) { - $trClass = 'danger'; - } else { - $lineCss = 'covered-by-large-tests'; - $popoverContent = '
    '; - if ($numTests > 1) { - $popoverTitle = $numTests . ' tests cover this branch'; - } else { - $popoverTitle = '1 test covers this branch'; - } - foreach ($branch['hit'] as $test) { - if ($lineCss === 'covered-by-large-tests' && $testData[$test]['size'] === 'medium') { - $lineCss = 'covered-by-medium-tests'; - } elseif ($testData[$test]['size'] === 'small') { - $lineCss = 'covered-by-small-tests'; - } - $popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]); - } - $trClass = $lineCss . ' popin'; - } - $popover = ''; - if (!empty($popoverTitle)) { - $popover = \sprintf(' data-title="%s" data-content="%s" data-placement="top" data-html="true"', $popoverTitle, \htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags)); - } - $lines .= $this->renderLine($singleLineTemplate, $line, $codeLines[$line - 1], $trClass, $popover); - } - if ($lines === '') { - return ''; + switch ($errorCode) { + case \PREG_BACKTRACK_LIMIT_ERROR: + return new self('Backtrack limit error'); + case \PREG_RECURSION_LIMIT_ERROR: + return new self('Recursion limit error'); + case \PREG_BAD_UTF8_ERROR: + return new self('Bad UTF8 error'); + case \PREG_BAD_UTF8_OFFSET_ERROR: + return new self('Bad UTF8 offset error'); + case \PREG_JIT_STACKLIMIT_ERROR: + return new self('Jit stacklimit error'); + case \PREG_NO_ERROR: + case \PREG_INTERNAL_ERROR: + default: } - $linesTemplate->setVar(['lines' => $lines]); - return $linesTemplate->render(); + return new self('Unknown Pcre error'); } - private function renderPathStructure(\PHPUnit\SebastianBergmann\CodeCoverage\Node\File $node) : string +} +templatePath . 'paths.html.dist', '{{', '}}'); - $coverageData = $node->functionCoverageData(); - $testData = $node->testData(); - $codeLines = $this->loadFile($node->pathAsString()); - $paths = ''; - \ksort($coverageData); - foreach ($coverageData as $methodName => $methodData) { - if (!$methodData['paths']) { - continue; - } - $pathStructure = ''; - if (\count($methodData['paths']) > 100) { - $pathStructure .= '

    ' . \count($methodData['paths']) . ' is too many paths to sensibly render, consider refactoring your code to bring this number down.

    '; - continue; - } - foreach ($methodData['paths'] as $path) { - $pathStructure .= $this->renderPathLines($path, $methodData['branches'], $codeLines, $testData); - } - if ($pathStructure !== '') { - $paths .= '
    ' . $this->abbreviateMethodName($methodName) . '
    ' . "\n"; - $paths .= $pathStructure; - } + Assert::allIsInstanceOf($tags, Tag::class); + $this->summary = $summary; + $this->description = $description ?: new DocBlock\Description(''); + foreach ($tags as $tag) { + $this->addTag($tag); } - $pathsTemplate->setVar(['paths' => $paths]); - return $pathsTemplate->render(); + $this->context = $context; + $this->location = $location; + $this->isTemplateEnd = $isTemplateEnd; + $this->isTemplateStart = $isTemplateStart; } - private function renderPathLines(array $path, array $branches, array $codeLines, array $testData) : string + public function getSummary() : string { - $linesTemplate = new \PHPUnit\SebastianBergmann\Template\Template($this->templatePath . 'lines.html.dist', '{{', '}}'); - $singleLineTemplate = new \PHPUnit\SebastianBergmann\Template\Template($this->templatePath . 'line.html.dist', '{{', '}}'); - $lines = ''; - foreach ($path['path'] as $branchId) { - $branchLines = \range($branches[$branchId]['line_start'], $branches[$branchId]['line_end']); - \sort($branchLines); - // sometimes end_line < start_line - /** @var int $line */ - foreach ($branchLines as $line) { - if (!isset($codeLines[$line])) { - // blank line at end of file is sometimes included here - continue; - } - $popoverContent = ''; - $popoverTitle = ''; - $numTests = \count($path['hit']); - if ($numTests === 0) { - $trClass = 'danger'; - } else { - $lineCss = 'covered-by-large-tests'; - $popoverContent = '
      '; - if ($numTests > 1) { - $popoverTitle = $numTests . ' tests cover this path'; - } else { - $popoverTitle = '1 test covers this path'; - } - foreach ($path['hit'] as $test) { - if ($lineCss === 'covered-by-large-tests' && $testData[$test]['size'] === 'medium') { - $lineCss = 'covered-by-medium-tests'; - } elseif ($testData[$test]['size'] === 'small') { - $lineCss = 'covered-by-small-tests'; - } - $popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]); - } - $trClass = $lineCss . ' popin'; - } - $popover = ''; - if (!empty($popoverTitle)) { - $popover = \sprintf(' data-title="%s" data-content="%s" data-placement="top" data-html="true"', $popoverTitle, \htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags)); - } - $lines .= $this->renderLine($singleLineTemplate, $line, $codeLines[$line - 1], $trClass, $popover); - } - } - if ($lines === '') { - return ''; - } - $linesTemplate->setVar(['lines' => $lines]); - return $linesTemplate->render(); + return $this->summary; } - private function renderLine(\PHPUnit\SebastianBergmann\Template\Template $template, int $lineNumber, string $lineContent, string $class, string $popover) : string + public function getDescription() : DocBlock\Description { - $template->setVar(['lineNumber' => $lineNumber, 'lineContent' => $lineContent, 'class' => $class, 'popover' => $popover]); - return $template->render(); + return $this->description; } - private function loadFile(string $file) : array + /** + * Returns the current context. + */ + public function getContext() : ?Types\Context { - if (isset(self::$formattedSourceCache[$file])) { - return self::$formattedSourceCache[$file]; - } - $buffer = \file_get_contents($file); - $tokens = \token_get_all($buffer); - $result = ['']; - $i = 0; - $stringFlag = \false; - $fileEndsWithNewLine = \substr($buffer, -1) === "\n"; - unset($buffer); - foreach ($tokens as $j => $token) { - if (\is_string($token)) { - if ($token === '"' && $tokens[$j - 1] !== '\\') { - $result[$i] .= \sprintf('%s', \htmlspecialchars($token, $this->htmlSpecialCharsFlags)); - $stringFlag = !$stringFlag; - } else { - $result[$i] .= \sprintf('%s', \htmlspecialchars($token, $this->htmlSpecialCharsFlags)); - } - continue; - } - [$token, $value] = $token; - $value = \str_replace(["\t", ' '], ['    ', ' '], \htmlspecialchars($value, $this->htmlSpecialCharsFlags)); - if ($value === "\n") { - $result[++$i] = ''; - } else { - $lines = \explode("\n", $value); - foreach ($lines as $jj => $line) { - $line = \trim($line); - if ($line !== '') { - if ($stringFlag) { - $colour = 'string'; - } else { - $colour = 'default'; - if ($this->isInlineHtml($token)) { - $colour = 'html'; - } elseif ($this->isComment($token)) { - $colour = 'comment'; - } elseif ($this->isKeyword($token)) { - $colour = 'keyword'; - } - } - $result[$i] .= \sprintf('%s', $colour, $line); - } - if (isset($lines[$jj + 1])) { - $result[++$i] = ''; - } - } - } - } - if ($fileEndsWithNewLine) { - unset($result[\count($result) - 1]); - } - self::$formattedSourceCache[$file] = $result; - return $result; + return $this->context; } - private function abbreviateClassName(string $className) : string + /** + * Returns the current location. + */ + public function getLocation() : ?Location + { + return $this->location; + } + /** + * Returns whether this DocBlock is the start of a Template section. + * + * A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker + * (`#@+`) that is appended directly after the opening `/**` of a DocBlock. + * + * An example of such an opening is: + * + * ``` + * /**#@+ + * * My DocBlock + * * / + * ``` + * + * The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all + * elements that follow until another DocBlock is found that contains the closing marker (`#@-`). + * + * @see self::isTemplateEnd() for the check whether a closing marker was provided. + */ + public function isTemplateStart() : bool + { + return $this->isTemplateStart; + } + /** + * Returns whether this DocBlock is the end of a Template section. + * + * @see self::isTemplateStart() for a more complete description of the Docblock Template functionality. + */ + public function isTemplateEnd() : bool + { + return $this->isTemplateEnd; + } + /** + * Returns the tags for this DocBlock. + * + * @return Tag[] + */ + public function getTags() : array { - $tmp = \explode('\\', $className); - if (\count($tmp) > 1) { - $className = \sprintf('%s', $className, \array_pop($tmp)); - } - return $className; + return $this->tags; } - private function abbreviateMethodName(string $methodName) : string + /** + * Returns an array of tags matching the given name. If no tags are found + * an empty array is returned. + * + * @param string $name String to search by. + * + * @return Tag[] + */ + public function getTagsByName(string $name) : array { - $parts = \explode('->', $methodName); - if (\count($parts) === 2) { - return $this->abbreviateClassName($parts[0]) . '->' . $parts[1]; + $result = []; + foreach ($this->getTags() as $tag) { + if ($tag->getName() !== $name) { + continue; + } + $result[] = $tag; } - return $methodName; + return $result; } - private function createPopoverContentForTest(string $test, array $testData) : string + /** + * Checks if a tag of a certain type is present in this DocBlock. + * + * @param string $name Tag name to check for. + */ + public function hasTag(string $name) : bool { - $testCSS = ''; - if ($testData['fromTestcase']) { - switch ($testData['status']) { - case \PHPUnit\Runner\BaseTestRunner::STATUS_PASSED: - switch ($testData['size']) { - case 'small': - $testCSS = ' class="covered-by-small-tests"'; - break; - case 'medium': - $testCSS = ' class="covered-by-medium-tests"'; - break; - default: - $testCSS = ' class="covered-by-large-tests"'; - break; - } - break; - case \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED: - case \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE: - case \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY: - case \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING: - $testCSS = ' class="warning"'; - break; - case \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE: - case \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR: - $testCSS = ' class="danger"'; - break; + foreach ($this->getTags() as $tag) { + if ($tag->getName() === $name) { + return \true; } } - return \sprintf('%s', $testCSS, \htmlspecialchars($test, $this->htmlSpecialCharsFlags)); - } - private function isComment(int $token) : bool - { - return $token === \T_COMMENT || $token === \T_DOC_COMMENT; - } - private function isInlineHtml(int $token) : bool - { - return $token === \T_INLINE_HTML; + return \false; } - private function isKeyword(int $token) : bool + /** + * Remove a tag from this DocBlock. + * + * @param Tag $tagToRemove The tag to remove. + */ + public function removeTag(Tag $tagToRemove) : void { - return isset(self::keywordTokens()[$token]); + foreach ($this->tags as $key => $tag) { + if ($tag === $tagToRemove) { + unset($this->tags[$key]); + break; + } + } } /** - * @psalm-return array + * Adds a tag to this DocBlock. + * + * @param Tag $tag The tag to add. */ - private static function keywordTokens() : array + private function addTag(Tag $tag) : void { - if (self::$keywordTokens !== []) { - return self::$keywordTokens; - } - self::$keywordTokens = [\T_ABSTRACT => \true, \T_ARRAY => \true, \T_AS => \true, \T_BREAK => \true, \T_CALLABLE => \true, \T_CASE => \true, \T_CATCH => \true, \T_CLASS => \true, \T_CLONE => \true, \T_CONST => \true, \T_CONTINUE => \true, \T_DECLARE => \true, \T_DEFAULT => \true, \T_DO => \true, \T_ECHO => \true, \T_ELSE => \true, \T_ELSEIF => \true, \T_EMPTY => \true, \T_ENDDECLARE => \true, \T_ENDFOR => \true, \T_ENDFOREACH => \true, \T_ENDIF => \true, \T_ENDSWITCH => \true, \T_ENDWHILE => \true, \T_EVAL => \true, \T_EXIT => \true, \T_EXTENDS => \true, \T_FINAL => \true, \T_FINALLY => \true, \T_FOR => \true, \T_FOREACH => \true, \T_FUNCTION => \true, \T_GLOBAL => \true, \T_GOTO => \true, \T_HALT_COMPILER => \true, \T_IF => \true, \T_IMPLEMENTS => \true, \T_INCLUDE => \true, \T_INCLUDE_ONCE => \true, \T_INSTANCEOF => \true, \T_INSTEADOF => \true, \T_INTERFACE => \true, \T_ISSET => \true, \T_LIST => \true, \T_NAMESPACE => \true, \T_NEW => \true, \T_PRINT => \true, \T_PRIVATE => \true, \T_PROTECTED => \true, \T_PUBLIC => \true, \T_REQUIRE => \true, \T_REQUIRE_ONCE => \true, \T_RETURN => \true, \T_STATIC => \true, \T_SWITCH => \true, \T_THROW => \true, \T_TRAIT => \true, \T_TRY => \true, \T_UNSET => \true, \T_USE => \true, \T_VAR => \true, \T_WHILE => \true, \T_YIELD => \true, \T_YIELD_FROM => \true]; - if (\defined('T_FN')) { - self::$keywordTokens[\constant('T_FN')] = \true; - } - if (\defined('T_MATCH')) { - self::$keywordTokens[\constant('T_MATCH')] = \true; - } - return self::$keywordTokens; + $this->tags[] = $tag; } } +The MIT License (MIT) + +Copyright (c) 2010 Mike van Riel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Html; +namespace PHPUnit\phpDocumentor\Reflection; -use const DIRECTORY_SEPARATOR; -use function copy; -use function date; -use function dirname; -use function substr; -use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; -use PHPUnit\SebastianBergmann\CodeCoverage\Directory as DirectoryUtil; -use PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; -final class Facade +use PHPUnit\phpDocumentor\Reflection\DocBlock\Tag; +// phpcs:ignore SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming.SuperfluousSuffix +interface DocBlockFactoryInterface { /** - * @var string - */ - private $templatePath; - /** - * @var string + * Factory method for easy instantiation. + * + * @param array> $additionalTags */ - private $generator; + public static function createInstance(array $additionalTags = []) : DocBlockFactory; /** - * @var int + * @param string|object $docblock */ - private $lowUpperBound; + public function create($docblock, ?Types\Context $context = null, ?Location $location = null) : DocBlock; +} +generator = $generator; - $this->highLowerBound = $highLowerBound; - $this->lowUpperBound = $lowUpperBound; - $this->templatePath = __DIR__ . '/Renderer/Template/'; + return 'resource'; } - public function process(\PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage $coverage, string $target) : void +} + $types + */ + public function __construct(array $types) { - $target = $this->directory($target); - $report = $coverage->getReport(); - $date = (string) \date('D M j G:i:s T Y'); - $dashboard = new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Html\Dashboard($this->templatePath, $this->generator, $date, $this->lowUpperBound, $this->highLowerBound, $coverage->collectsBranchAndPathCoverage()); - $directory = new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Html\Directory($this->templatePath, $this->generator, $date, $this->lowUpperBound, $this->highLowerBound, $coverage->collectsBranchAndPathCoverage()); - $file = new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Html\File($this->templatePath, $this->generator, $date, $this->lowUpperBound, $this->highLowerBound, $coverage->collectsBranchAndPathCoverage()); - $directory->render($report, $target . 'index.html'); - $dashboard->render($report, $target . 'dashboard.html'); - foreach ($report as $node) { - $id = $node->id(); - if ($node instanceof \PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory) { - \PHPUnit\SebastianBergmann\CodeCoverage\Directory::create($target . $id); - $directory->render($node, $target . $id . '/index.html'); - $dashboard->render($node, $target . $id . '/dashboard.html'); - } else { - $dir = \dirname($target . $id); - \PHPUnit\SebastianBergmann\CodeCoverage\Directory::create($dir); - $file->render($node, $target . $id); - } - } - $this->copyFiles($target); + parent::__construct($types, '|'); } - private function copyFiles(string $target) : void +} +directory($target . '_css'); - \copy($this->templatePath . 'css/bootstrap.min.css', $dir . 'bootstrap.min.css'); - \copy($this->templatePath . 'css/nv.d3.min.css', $dir . 'nv.d3.min.css'); - \copy($this->templatePath . 'css/style.css', $dir . 'style.css'); - \copy($this->templatePath . 'css/custom.css', $dir . 'custom.css'); - \copy($this->templatePath . 'css/octicons.css', $dir . 'octicons.css'); - $dir = $this->directory($target . '_icons'); - \copy($this->templatePath . 'icons/file-code.svg', $dir . 'file-code.svg'); - \copy($this->templatePath . 'icons/file-directory.svg', $dir . 'file-directory.svg'); - $dir = $this->directory($target . '_js'); - \copy($this->templatePath . 'js/bootstrap.min.js', $dir . 'bootstrap.min.js'); - \copy($this->templatePath . 'js/popper.min.js', $dir . 'popper.min.js'); - \copy($this->templatePath . 'js/d3.min.js', $dir . 'd3.min.js'); - \copy($this->templatePath . 'js/jquery.min.js', $dir . 'jquery.min.js'); - \copy($this->templatePath . 'js/nv.d3.min.js', $dir . 'nv.d3.min.js'); - \copy($this->templatePath . 'js/file.js', $dir . 'file.js'); + return 'int'; } - private function directory(string $directory) : string +} + +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report; +namespace PHPUnit\phpDocumentor\Reflection\Types; -use function addcslashes; -use function dirname; -use function file_put_contents; -use function serialize; -use function sprintf; -use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; -use PHPUnit\SebastianBergmann\CodeCoverage\Directory; -use PHPUnit\SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException; -final class PHP +use PHPUnit\phpDocumentor\Reflection\Type; +/** + * Value Object representing a null value or type. + * + * @psalm-immutable + */ +final class Null_ implements Type { - public function process(\PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage $coverage, ?string $target = null) : string + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string { - $buffer = \sprintf(' +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report; +namespace PHPUnit\phpDocumentor\Reflection\Types; -use function count; -use function dirname; -use function file_put_contents; -use function is_string; -use function ksort; -use function max; -use function range; -use function time; -use DOMDocument; -use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; -use PHPUnit\SebastianBergmann\CodeCoverage\Directory; -use PHPUnit\SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException; -use PHPUnit\SebastianBergmann\CodeCoverage\Node\File; -final class Clover +use PHPUnit\phpDocumentor\Reflection\Type; +/** + * Value Object representing the 'parent' type. + * + * Parent, as a Type, represents the parent class of class in which the associated element was defined. + * + * @psalm-immutable + */ +final class Parent_ implements Type { /** - * @throws WriteOperationFailedException + * Returns a rendered output of the Type as it would be used in a DocBlock. */ - public function process(\PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage $coverage, ?string $target = null, ?string $name = null) : string + public function __toString() : string { - $time = (string) \time(); - $xmlDocument = new \DOMDocument('1.0', 'UTF-8'); - $xmlDocument->formatOutput = \true; - $xmlCoverage = $xmlDocument->createElement('coverage'); - $xmlCoverage->setAttribute('generated', $time); - $xmlDocument->appendChild($xmlCoverage); - $xmlProject = $xmlDocument->createElement('project'); - $xmlProject->setAttribute('timestamp', $time); - if (\is_string($name)) { - $xmlProject->setAttribute('name', $name); - } - $xmlCoverage->appendChild($xmlProject); - $packages = []; - $report = $coverage->getReport(); - foreach ($report as $item) { - if (!$item instanceof \PHPUnit\SebastianBergmann\CodeCoverage\Node\File) { - continue; - } - /* @var File $item */ - $xmlFile = $xmlDocument->createElement('file'); - $xmlFile->setAttribute('name', $item->pathAsString()); - $classes = $item->classesAndTraits(); - $coverageData = $item->lineCoverageData(); - $lines = []; - $namespace = 'global'; - foreach ($classes as $className => $class) { - $classStatements = 0; - $coveredClassStatements = 0; - $coveredMethods = 0; - $classMethods = 0; - foreach ($class['methods'] as $methodName => $method) { - if ($method['executableLines'] == 0) { - continue; - } - $classMethods++; - $classStatements += $method['executableLines']; - $coveredClassStatements += $method['executedLines']; - if ($method['coverage'] == 100) { - $coveredMethods++; - } - $methodCount = 0; - foreach (\range($method['startLine'], $method['endLine']) as $line) { - if (isset($coverageData[$line]) && $coverageData[$line] !== null) { - $methodCount = \max($methodCount, \count($coverageData[$line])); - } - } - $lines[$method['startLine']] = ['ccn' => $method['ccn'], 'count' => $methodCount, 'crap' => $method['crap'], 'type' => 'method', 'visibility' => $method['visibility'], 'name' => $methodName]; - } - if (!empty($class['package']['namespace'])) { - $namespace = $class['package']['namespace']; - } - $xmlClass = $xmlDocument->createElement('class'); - $xmlClass->setAttribute('name', $className); - $xmlClass->setAttribute('namespace', $namespace); - if (!empty($class['package']['fullPackage'])) { - $xmlClass->setAttribute('fullPackage', $class['package']['fullPackage']); - } - if (!empty($class['package']['category'])) { - $xmlClass->setAttribute('category', $class['package']['category']); - } - if (!empty($class['package']['package'])) { - $xmlClass->setAttribute('package', $class['package']['package']); - } - if (!empty($class['package']['subpackage'])) { - $xmlClass->setAttribute('subpackage', $class['package']['subpackage']); - } - $xmlFile->appendChild($xmlClass); - $xmlMetrics = $xmlDocument->createElement('metrics'); - $xmlMetrics->setAttribute('complexity', (string) $class['ccn']); - $xmlMetrics->setAttribute('methods', (string) $classMethods); - $xmlMetrics->setAttribute('coveredmethods', (string) $coveredMethods); - $xmlMetrics->setAttribute('conditionals', (string) $class['executableBranches']); - $xmlMetrics->setAttribute('coveredconditionals', (string) $class['executedBranches']); - $xmlMetrics->setAttribute('statements', (string) $classStatements); - $xmlMetrics->setAttribute('coveredstatements', (string) $coveredClassStatements); - $xmlMetrics->setAttribute('elements', (string) ($classMethods + $classStatements + $class['executableBranches'])); - $xmlMetrics->setAttribute('coveredelements', (string) ($coveredMethods + $coveredClassStatements + $class['executedBranches'])); - $xmlClass->appendChild($xmlMetrics); - } - foreach ($coverageData as $line => $data) { - if ($data === null || isset($lines[$line])) { - continue; - } - $lines[$line] = ['count' => \count($data), 'type' => 'stmt']; - } - \ksort($lines); - foreach ($lines as $line => $data) { - $xmlLine = $xmlDocument->createElement('line'); - $xmlLine->setAttribute('num', (string) $line); - $xmlLine->setAttribute('type', $data['type']); - if (isset($data['name'])) { - $xmlLine->setAttribute('name', $data['name']); - } - if (isset($data['visibility'])) { - $xmlLine->setAttribute('visibility', $data['visibility']); - } - if (isset($data['ccn'])) { - $xmlLine->setAttribute('complexity', (string) $data['ccn']); - } - if (isset($data['crap'])) { - $xmlLine->setAttribute('crap', (string) $data['crap']); - } - $xmlLine->setAttribute('count', (string) $data['count']); - $xmlFile->appendChild($xmlLine); - } - $linesOfCode = $item->linesOfCode(); - $xmlMetrics = $xmlDocument->createElement('metrics'); - $xmlMetrics->setAttribute('loc', (string) $linesOfCode->linesOfCode()); - $xmlMetrics->setAttribute('ncloc', (string) $linesOfCode->nonCommentLinesOfCode()); - $xmlMetrics->setAttribute('classes', (string) $item->numberOfClassesAndTraits()); - $xmlMetrics->setAttribute('methods', (string) $item->numberOfMethods()); - $xmlMetrics->setAttribute('coveredmethods', (string) $item->numberOfTestedMethods()); - $xmlMetrics->setAttribute('conditionals', (string) $item->numberOfExecutableBranches()); - $xmlMetrics->setAttribute('coveredconditionals', (string) $item->numberOfExecutedBranches()); - $xmlMetrics->setAttribute('statements', (string) $item->numberOfExecutableLines()); - $xmlMetrics->setAttribute('coveredstatements', (string) $item->numberOfExecutedLines()); - $xmlMetrics->setAttribute('elements', (string) ($item->numberOfMethods() + $item->numberOfExecutableLines() + $item->numberOfExecutableBranches())); - $xmlMetrics->setAttribute('coveredelements', (string) ($item->numberOfTestedMethods() + $item->numberOfExecutedLines() + $item->numberOfExecutedBranches())); - $xmlFile->appendChild($xmlMetrics); - if ($namespace === 'global') { - $xmlProject->appendChild($xmlFile); - } else { - if (!isset($packages[$namespace])) { - $packages[$namespace] = $xmlDocument->createElement('package'); - $packages[$namespace]->setAttribute('name', $namespace); - $xmlProject->appendChild($packages[$namespace]); - } - $packages[$namespace]->appendChild($xmlFile); - } - } - $linesOfCode = $report->linesOfCode(); - $xmlMetrics = $xmlDocument->createElement('metrics'); - $xmlMetrics->setAttribute('files', (string) \count($report)); - $xmlMetrics->setAttribute('loc', (string) $linesOfCode->linesOfCode()); - $xmlMetrics->setAttribute('ncloc', (string) $linesOfCode->nonCommentLinesOfCode()); - $xmlMetrics->setAttribute('classes', (string) $report->numberOfClassesAndTraits()); - $xmlMetrics->setAttribute('methods', (string) $report->numberOfMethods()); - $xmlMetrics->setAttribute('coveredmethods', (string) $report->numberOfTestedMethods()); - $xmlMetrics->setAttribute('conditionals', (string) $report->numberOfExecutableBranches()); - $xmlMetrics->setAttribute('coveredconditionals', (string) $report->numberOfExecutedBranches()); - $xmlMetrics->setAttribute('statements', (string) $report->numberOfExecutableLines()); - $xmlMetrics->setAttribute('coveredstatements', (string) $report->numberOfExecutedLines()); - $xmlMetrics->setAttribute('elements', (string) ($report->numberOfMethods() + $report->numberOfExecutableLines() + $report->numberOfExecutableBranches())); - $xmlMetrics->setAttribute('coveredelements', (string) ($report->numberOfTestedMethods() + $report->numberOfExecutedLines() + $report->numberOfExecutedBranches())); - $xmlProject->appendChild($xmlMetrics); - $buffer = $xmlDocument->saveXML(); - if ($target !== null) { - \PHPUnit\SebastianBergmann\CodeCoverage\Directory::create(\dirname($target)); - if (@\file_put_contents($target, $buffer) === \false) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException($target); - } - } - return $buffer; + return 'parent'; + } +} + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @link http://phpdoc.org + */ +namespace PHPUnit\phpDocumentor\Reflection\Types; + +use PHPUnit\phpDocumentor\Reflection\Type; +/** + * Value Object representing a Boolean type. + * + * @psalm-immutable + */ +class Boolean implements Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string + { + return 'bool'; + } +} +threshold = $threshold; + if ($reflector instanceof ReflectionClass) { + //phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable + /** @var ReflectionClass $reflector */ + return $this->createFromReflectionClass($reflector); + } + if ($reflector instanceof ReflectionParameter) { + return $this->createFromReflectionParameter($reflector); + } + if ($reflector instanceof ReflectionMethod) { + return $this->createFromReflectionMethod($reflector); + } + if ($reflector instanceof ReflectionProperty) { + return $this->createFromReflectionProperty($reflector); + } + if ($reflector instanceof ReflectionClassConstant) { + return $this->createFromReflectionClassConstant($reflector); + } + throw new UnexpectedValueException('Unhandled \\Reflector instance given: ' . get_class($reflector)); + } + private function createFromReflectionParameter(ReflectionParameter $parameter) : Context + { + $class = $parameter->getDeclaringClass(); + if (!$class) { + throw new InvalidArgumentException('Unable to get class of ' . $parameter->getName()); + } + return $this->createFromReflectionClass($class); + } + private function createFromReflectionMethod(ReflectionMethod $method) : Context + { + $class = $method->getDeclaringClass(); + return $this->createFromReflectionClass($class); + } + private function createFromReflectionProperty(ReflectionProperty $property) : Context + { + $class = $property->getDeclaringClass(); + return $this->createFromReflectionClass($class); + } + private function createFromReflectionClassConstant(ReflectionClassConstant $constant) : Context + { + //phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable + /** @phpstan-var ReflectionClass $class */ + $class = $constant->getDeclaringClass(); + return $this->createFromReflectionClass($class); } /** - * @throws WriteOperationFailedException + * @phpstan-param ReflectionClass $class */ - public function process(\PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage $coverage, ?string $target = null, ?string $name = null) : string + private function createFromReflectionClass(ReflectionClass $class) : Context { - $document = new \DOMDocument('1.0', 'UTF-8'); - $document->formatOutput = \true; - $root = $document->createElement('crap_result'); - $document->appendChild($root); - $project = $document->createElement('project', \is_string($name) ? $name : ''); - $root->appendChild($project); - $root->appendChild($document->createElement('timestamp', \date('Y-m-d H:i:s'))); - $stats = $document->createElement('stats'); - $methodsNode = $document->createElement('methods'); - $report = $coverage->getReport(); - unset($coverage); - $fullMethodCount = 0; - $fullCrapMethodCount = 0; - $fullCrapLoad = 0; - $fullCrap = 0; - foreach ($report as $item) { - $namespace = 'global'; - if (!$item instanceof \PHPUnit\SebastianBergmann\CodeCoverage\Node\File) { - continue; + $fileName = $class->getFileName(); + $namespace = $class->getNamespaceName(); + if (is_string($fileName) && file_exists($fileName)) { + $contents = file_get_contents($fileName); + if ($contents === \false) { + throw new RuntimeException('Unable to read file "' . $fileName . '"'); } - $file = $document->createElement('file'); - $file->setAttribute('name', $item->pathAsString()); - $classes = $item->classesAndTraits(); - foreach ($classes as $className => $class) { - foreach ($class['methods'] as $methodName => $method) { - $crapLoad = $this->crapLoad((float) $method['crap'], $method['ccn'], $method['coverage']); - $fullCrap += $method['crap']; - $fullCrapLoad += $crapLoad; - $fullMethodCount++; - if ($method['crap'] >= $this->threshold) { - $fullCrapMethodCount++; + return $this->createForNamespace($namespace, $contents); + } + return new Context($namespace, []); + } + /** + * Build a Context for a namespace in the provided file contents. + * + * @see Context for more information on Contexts. + * + * @param string $namespace It does not matter if a `\` precedes the namespace name, + * this method first normalizes. + * @param string $fileContents The file's contents to retrieve the aliases from with the given namespace. + */ + public function createForNamespace(string $namespace, string $fileContents) : Context + { + $namespace = trim($namespace, '\\'); + $useStatements = []; + $currentNamespace = ''; + $tokens = new ArrayIterator(token_get_all($fileContents)); + while ($tokens->valid()) { + $currentToken = $tokens->current(); + switch ($currentToken[0]) { + case \T_NAMESPACE: + $currentNamespace = $this->parseNamespace($tokens); + break; + case \T_CLASS: + // Fast-forward the iterator through the class so that any + // T_USE tokens found within are skipped - these are not + // valid namespace use statements so should be ignored. + $braceLevel = 0; + $firstBraceFound = \false; + while ($tokens->valid() && ($braceLevel > 0 || !$firstBraceFound)) { + $currentToken = $tokens->current(); + if ($currentToken === '{' || in_array($currentToken[0], [\T_CURLY_OPEN, \T_DOLLAR_OPEN_CURLY_BRACES], \true)) { + if (!$firstBraceFound) { + $firstBraceFound = \true; + } + ++$braceLevel; + } + if ($currentToken === '}') { + --$braceLevel; + } + $tokens->next(); } - $methodNode = $document->createElement('method'); - if (!empty($class['namespace'])) { - $namespace = $class['namespace']; + break; + case \T_USE: + if ($currentNamespace === $namespace) { + $useStatements += $this->parseUseStatement($tokens); } - $methodNode->appendChild($document->createElement('package', $namespace)); - $methodNode->appendChild($document->createElement('className', $className)); - $methodNode->appendChild($document->createElement('methodName', $methodName)); - $methodNode->appendChild($document->createElement('methodSignature', \htmlspecialchars($method['signature']))); - $methodNode->appendChild($document->createElement('fullMethod', \htmlspecialchars($method['signature']))); - $methodNode->appendChild($document->createElement('crap', (string) $this->roundValue((float) $method['crap']))); - $methodNode->appendChild($document->createElement('complexity', (string) $method['ccn'])); - $methodNode->appendChild($document->createElement('coverage', (string) $this->roundValue($method['coverage']))); - $methodNode->appendChild($document->createElement('crapLoad', (string) \round($crapLoad))); - $methodsNode->appendChild($methodNode); - } - } - } - $stats->appendChild($document->createElement('name', 'Method Crap Stats')); - $stats->appendChild($document->createElement('methodCount', (string) $fullMethodCount)); - $stats->appendChild($document->createElement('crapMethodCount', (string) $fullCrapMethodCount)); - $stats->appendChild($document->createElement('crapLoad', (string) \round($fullCrapLoad))); - $stats->appendChild($document->createElement('totalCrap', (string) $fullCrap)); - $crapMethodPercent = 0; - if ($fullMethodCount > 0) { - $crapMethodPercent = $this->roundValue(100 * $fullCrapMethodCount / $fullMethodCount); - } - $stats->appendChild($document->createElement('crapMethodPercent', (string) $crapMethodPercent)); - $root->appendChild($stats); - $root->appendChild($methodsNode); - $buffer = $document->saveXML(); - if ($target !== null) { - \PHPUnit\SebastianBergmann\CodeCoverage\Directory::create(\dirname($target)); - if (@\file_put_contents($target, $buffer) === \false) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException($target); + break; } + $tokens->next(); } - return $buffer; + return new Context($namespace, $useStatements); } - private function crapLoad(float $crapValue, int $cyclomaticComplexity, float $coveragePercent) : float + /** + * Deduce the name from tokens when we are at the T_NAMESPACE token. + * + * @param ArrayIterator $tokens + */ + private function parseNamespace(ArrayIterator $tokens) : string { - $crapLoad = 0; - if ($crapValue >= $this->threshold) { - $crapLoad += $cyclomaticComplexity * (1.0 - $coveragePercent / 100); - $crapLoad += $cyclomaticComplexity / $this->threshold; + // skip to the first string or namespace separator + $this->skipToNextStringOrNamespaceSeparator($tokens); + $name = ''; + $acceptedTokens = [\T_STRING, \T_NS_SEPARATOR, \T_NAME_QUALIFIED]; + while ($tokens->valid() && in_array($tokens->current()[0], $acceptedTokens, \true)) { + $name .= $tokens->current()[1]; + $tokens->next(); } - return $crapLoad; + return $name; } - private function roundValue(float $value) : float + /** + * Deduce the names of all imports when we are at the T_USE token. + * + * @param ArrayIterator $tokens + * + * @return string[] + * @psalm-return array + */ + private function parseUseStatement(ArrayIterator $tokens) : array { - return \round($value, 2); + $uses = []; + while ($tokens->valid()) { + $this->skipToNextStringOrNamespaceSeparator($tokens); + $uses += $this->extractUseStatements($tokens); + $currentToken = $tokens->current(); + if ($currentToken[0] === self::T_LITERAL_END_OF_USE) { + return $uses; + } + } + return $uses; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report; - -use function count; -use function dirname; -use function file_put_contents; -use function range; -use function time; -use DOMImplementation; -use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; -use PHPUnit\SebastianBergmann\CodeCoverage\Directory; -use PHPUnit\SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException; -use PHPUnit\SebastianBergmann\CodeCoverage\Node\File; -final class Cobertura -{ /** - * @throws WriteOperationFailedException + * Fast-forwards the iterator as longs as we don't encounter a T_STRING or T_NS_SEPARATOR token. + * + * @param ArrayIterator $tokens */ - public function process(\PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage $coverage, ?string $target = null, ?string $name = null) : string + private function skipToNextStringOrNamespaceSeparator(ArrayIterator $tokens) : void { - $time = (string) \time(); - $report = $coverage->getReport(); - $implementation = new \DOMImplementation(); - $documentType = $implementation->createDocumentType('coverage', '', 'http://cobertura.sourceforge.net/xml/coverage-04.dtd'); - $document = $implementation->createDocument('', '', $documentType); - $document->xmlVersion = '1.0'; - $document->encoding = 'UTF-8'; - $document->formatOutput = \true; - $coverageElement = $document->createElement('coverage'); - $linesValid = $report->numberOfExecutableLines(); - $linesCovered = $report->numberOfExecutedLines(); - $lineRate = $linesValid === 0 ? 0 : $linesCovered / $linesValid; - $coverageElement->setAttribute('line-rate', (string) $lineRate); - $branchesValid = $report->numberOfExecutableBranches(); - $branchesCovered = $report->numberOfExecutedBranches(); - $branchRate = $branchesValid === 0 ? 0 : $branchesCovered / $branchesValid; - $coverageElement->setAttribute('branch-rate', (string) $branchRate); - $coverageElement->setAttribute('lines-covered', (string) $report->numberOfExecutedLines()); - $coverageElement->setAttribute('lines-valid', (string) $report->numberOfExecutableLines()); - $coverageElement->setAttribute('branches-covered', (string) $report->numberOfExecutedBranches()); - $coverageElement->setAttribute('branches-valid', (string) $report->numberOfExecutableBranches()); - $coverageElement->setAttribute('complexity', ''); - $coverageElement->setAttribute('version', '0.4'); - $coverageElement->setAttribute('timestamp', $time); - $document->appendChild($coverageElement); - $sourcesElement = $document->createElement('sources'); - $coverageElement->appendChild($sourcesElement); - $sourceElement = $document->createElement('source', $report->pathAsString()); - $sourcesElement->appendChild($sourceElement); - $packagesElement = $document->createElement('packages'); - $coverageElement->appendChild($packagesElement); - $complexity = 0; - foreach ($report as $item) { - if (!$item instanceof \PHPUnit\SebastianBergmann\CodeCoverage\Node\File) { - continue; + while ($tokens->valid()) { + $currentToken = $tokens->current(); + if (in_array($currentToken[0], [\T_STRING, \T_NS_SEPARATOR], \true)) { + break; } - $packageElement = $document->createElement('package'); - $packageComplexity = 0; - $packageName = $name ?? ''; - $packageElement->setAttribute('name', $packageName); - $linesValid = $item->numberOfExecutableLines(); - $linesCovered = $item->numberOfExecutedLines(); - $lineRate = $linesValid === 0 ? 0 : $linesCovered / $linesValid; - $packageElement->setAttribute('line-rate', (string) $lineRate); - $branchesValid = $item->numberOfExecutableBranches(); - $branchesCovered = $item->numberOfExecutedBranches(); - $branchRate = $branchesValid === 0 ? 0 : $branchesCovered / $branchesValid; - $packageElement->setAttribute('branch-rate', (string) $branchRate); - $packageElement->setAttribute('complexity', ''); - $packagesElement->appendChild($packageElement); - $classesElement = $document->createElement('classes'); - $packageElement->appendChild($classesElement); - $classes = $item->classesAndTraits(); - $coverageData = $item->lineCoverageData(); - foreach ($classes as $className => $class) { - $complexity += $class['ccn']; - $packageComplexity += $class['ccn']; - if (!empty($class['package']['namespace'])) { - $className = $class['package']['namespace'] . '\\' . $className; - } - $linesValid = $class['executableLines']; - $linesCovered = $class['executedLines']; - $lineRate = $linesValid === 0 ? 0 : $linesCovered / $linesValid; - $branchesValid = $class['executableBranches']; - $branchesCovered = $class['executedBranches']; - $branchRate = $branchesValid === 0 ? 0 : $branchesCovered / $branchesValid; - $classElement = $document->createElement('class'); - $classElement->setAttribute('name', $className); - $classElement->setAttribute('filename', \str_replace($report->pathAsString() . \DIRECTORY_SEPARATOR, '', $item->pathAsString())); - $classElement->setAttribute('line-rate', (string) $lineRate); - $classElement->setAttribute('branch-rate', (string) $branchRate); - $classElement->setAttribute('complexity', (string) $class['ccn']); - $classesElement->appendChild($classElement); - $methodsElement = $document->createElement('methods'); - $classElement->appendChild($methodsElement); - $classLinesElement = $document->createElement('lines'); - $classElement->appendChild($classLinesElement); - foreach ($class['methods'] as $methodName => $method) { - if ($method['executableLines'] === 0) { - continue; + if ($currentToken[0] === \T_NAME_QUALIFIED) { + break; + } + if (defined('T_NAME_FULLY_QUALIFIED') && $currentToken[0] === \T_NAME_FULLY_QUALIFIED) { + break; + } + $tokens->next(); + } + } + /** + * Deduce the namespace name and alias of an import when we are at the T_USE token or have not reached the end of + * a USE statement yet. This will return a key/value array of the alias => namespace. + * + * @param ArrayIterator $tokens + * + * @return string[] + * @psalm-return array + * + * @psalm-suppress TypeDoesNotContainType + */ + private function extractUseStatements(ArrayIterator $tokens) : array + { + $extractedUseStatements = []; + $groupedNs = ''; + $currentNs = ''; + $currentAlias = ''; + $state = 'start'; + while ($tokens->valid()) { + $currentToken = $tokens->current(); + $tokenId = is_string($currentToken) ? $currentToken : $currentToken[0]; + $tokenValue = is_string($currentToken) ? null : $currentToken[1]; + switch ($state) { + case 'start': + switch ($tokenId) { + case \T_STRING: + case \T_NS_SEPARATOR: + $currentNs .= (string) $tokenValue; + $currentAlias = $tokenValue; + break; + case \T_NAME_QUALIFIED: + case \T_NAME_FULLY_QUALIFIED: + $currentNs .= (string) $tokenValue; + $currentAlias = substr((string) $tokenValue, (int) strrpos((string) $tokenValue, '\\') + 1); + break; + case \T_CURLY_OPEN: + case '{': + $state = 'grouped'; + $groupedNs = $currentNs; + break; + case \T_AS: + $state = 'start-alias'; + break; + case self::T_LITERAL_USE_SEPARATOR: + case self::T_LITERAL_END_OF_USE: + $state = 'end'; + break; + default: + break; } - $linesValid = $method['executableLines']; - $linesCovered = $method['executedLines']; - $lineRate = $linesValid === 0 ? 0 : $linesCovered / $linesValid; - $branchesValid = $method['executableBranches']; - $branchesCovered = $method['executedBranches']; - $branchRate = $branchesValid === 0 ? 0 : $branchesCovered / $branchesValid; - $methodElement = $document->createElement('method'); - $methodElement->setAttribute('name', $methodName); - $methodElement->setAttribute('signature', $method['signature']); - $methodElement->setAttribute('line-rate', (string) $lineRate); - $methodElement->setAttribute('branch-rate', (string) $branchRate); - $methodElement->setAttribute('complexity', (string) $method['ccn']); - $methodLinesElement = $document->createElement('lines'); - $methodElement->appendChild($methodLinesElement); - foreach (\range($method['startLine'], $method['endLine']) as $line) { - if (!isset($coverageData[$line]) || $coverageData[$line] === null) { - continue; - } - $methodLineElement = $document->createElement('line'); - $methodLineElement->setAttribute('number', (string) $line); - $methodLineElement->setAttribute('hits', (string) \count($coverageData[$line])); - $methodLinesElement->appendChild($methodLineElement); - $classLineElement = $methodLineElement->cloneNode(); - $classLinesElement->appendChild($classLineElement); + break; + case 'start-alias': + switch ($tokenId) { + case \T_STRING: + $currentAlias = $tokenValue; + break; + case self::T_LITERAL_USE_SEPARATOR: + case self::T_LITERAL_END_OF_USE: + $state = 'end'; + break; + default: + break; + } + break; + case 'grouped': + switch ($tokenId) { + case \T_STRING: + case \T_NS_SEPARATOR: + $currentNs .= (string) $tokenValue; + $currentAlias = $tokenValue; + break; + case \T_AS: + $state = 'grouped-alias'; + break; + case self::T_LITERAL_USE_SEPARATOR: + $state = 'grouped'; + $extractedUseStatements[(string) $currentAlias] = $currentNs; + $currentNs = $groupedNs; + $currentAlias = ''; + break; + case self::T_LITERAL_END_OF_USE: + $state = 'end'; + break; + default: + break; } - $methodsElement->appendChild($methodElement); - } - } - if ($report->numberOfFunctions() === 0) { - $packageElement->setAttribute('complexity', (string) $packageComplexity); - continue; - } - $functionsComplexity = 0; - $functionsLinesValid = 0; - $functionsLinesCovered = 0; - $functionsBranchesValid = 0; - $functionsBranchesCovered = 0; - $classElement = $document->createElement('class'); - $classElement->setAttribute('name', \basename($item->pathAsString())); - $classElement->setAttribute('filename', \str_replace($report->pathAsString() . \DIRECTORY_SEPARATOR, '', $item->pathAsString())); - $methodsElement = $document->createElement('methods'); - $classElement->appendChild($methodsElement); - $classLinesElement = $document->createElement('lines'); - $classElement->appendChild($classLinesElement); - $functions = $report->functions(); - foreach ($functions as $functionName => $function) { - if ($function['executableLines'] === 0) { - continue; - } - $complexity += $function['ccn']; - $packageComplexity += $function['ccn']; - $functionsComplexity += $function['ccn']; - $linesValid = $function['executableLines']; - $linesCovered = $function['executedLines']; - $lineRate = $linesValid === 0 ? 0 : $linesCovered / $linesValid; - $functionsLinesValid += $linesValid; - $functionsLinesCovered += $linesCovered; - $branchesValid = $function['executableBranches']; - $branchesCovered = $function['executedBranches']; - $branchRate = $branchesValid === 0 ? 0 : $branchesCovered / $branchesValid; - $functionsBranchesValid += $branchesValid; - $functionsBranchesCovered += $branchesValid; - $methodElement = $document->createElement('method'); - $methodElement->setAttribute('name', $functionName); - $methodElement->setAttribute('signature', $function['signature']); - $methodElement->setAttribute('line-rate', (string) $lineRate); - $methodElement->setAttribute('branch-rate', (string) $branchRate); - $methodElement->setAttribute('complexity', (string) $function['ccn']); - $methodLinesElement = $document->createElement('lines'); - $methodElement->appendChild($methodLinesElement); - foreach (\range($function['startLine'], $function['endLine']) as $line) { - if (!isset($coverageData[$line]) || $coverageData[$line] === null) { - continue; + break; + case 'grouped-alias': + switch ($tokenId) { + case \T_STRING: + $currentAlias = $tokenValue; + break; + case self::T_LITERAL_USE_SEPARATOR: + $state = 'grouped'; + $extractedUseStatements[(string) $currentAlias] = $currentNs; + $currentNs = $groupedNs; + $currentAlias = ''; + break; + case self::T_LITERAL_END_OF_USE: + $state = 'end'; + break; + default: + break; } - $methodLineElement = $document->createElement('line'); - $methodLineElement->setAttribute('number', (string) $line); - $methodLineElement->setAttribute('hits', (string) \count($coverageData[$line])); - $methodLinesElement->appendChild($methodLineElement); - $classLineElement = $methodLineElement->cloneNode(); - $classLinesElement->appendChild($classLineElement); - } - $methodsElement->appendChild($methodElement); } - $packageElement->setAttribute('complexity', (string) $packageComplexity); - if ($functionsLinesValid === 0) { - continue; + if ($state === 'end') { + break; } - $lineRate = $functionsLinesCovered / $functionsLinesValid; - $branchRate = $functionsBranchesValid === 0 ? 0 : $functionsBranchesCovered / $functionsBranchesValid; - $classElement->setAttribute('line-rate', (string) $lineRate); - $classElement->setAttribute('branch-rate', (string) $branchRate); - $classElement->setAttribute('complexity', (string) $functionsComplexity); - $classesElement->appendChild($classElement); + $tokens->next(); } - $coverageElement->setAttribute('complexity', (string) $complexity); - $buffer = $document->saveXML(); - if ($target !== null) { - \PHPUnit\SebastianBergmann\CodeCoverage\Directory::create(\dirname($target)); - if (@\file_put_contents($target, $buffer) === \false) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException($target); - } + if ($groupedNs !== $currentNs) { + $extractedUseStatements[(string) $currentAlias] = $currentNs; } - return $buffer; + return $extractedUseStatements; } } +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Report; +namespace PHPUnit\phpDocumentor\Reflection\Types; -use const PHP_EOL; -use function array_map; -use function date; -use function ksort; -use function max; -use function sprintf; -use function str_pad; -use function strlen; -use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; -use PHPUnit\SebastianBergmann\CodeCoverage\Node\File; -use PHPUnit\SebastianBergmann\CodeCoverage\Percentage; -final class Text +use PHPUnit\phpDocumentor\Reflection\Type; +/** + * Represents a list of values. This is an abstract class for Array_ and Collection. + * + * @psalm-immutable + */ +abstract class AbstractList implements Type { + /** @var Type */ + protected $valueType; + /** @var Type|null */ + protected $keyType; + /** @var Type */ + protected $defaultKeyType; /** - * @var string - */ - private const COLOR_GREEN = "\33[30;42m"; - /** - * @var string - */ - private const COLOR_YELLOW = "\33[30;43m"; - /** - * @var string - */ - private const COLOR_RED = "\33[37;41m"; - /** - * @var string - */ - private const COLOR_HEADER = "\33[1;37;40m"; - /** - * @var string - */ - private const COLOR_RESET = "\33[0m"; - /** - * @var string - */ - private const COLOR_EOL = "\33[2K"; - /** - * @var int - */ - private $lowUpperBound; - /** - * @var int + * Initializes this representation of an array with the given Type. */ - private $highLowerBound; + public function __construct(?Type $valueType = null, ?Type $keyType = null) + { + if ($valueType === null) { + $valueType = new Mixed_(); + } + $this->valueType = $valueType; + $this->defaultKeyType = new Compound([new String_(), new Integer()]); + $this->keyType = $keyType; + } /** - * @var bool + * Returns the type for the keys of this array. */ - private $showUncoveredFiles; + public function getKeyType() : Type + { + return $this->keyType ?? $this->defaultKeyType; + } /** - * @var bool + * Returns the value for the keys of this array. */ - private $showOnlySummary; - public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, bool $showUncoveredFiles = \false, bool $showOnlySummary = \false) + public function getValueType() : Type { - $this->lowUpperBound = $lowUpperBound; - $this->highLowerBound = $highLowerBound; - $this->showUncoveredFiles = $showUncoveredFiles; - $this->showOnlySummary = $showOnlySummary; + return $this->valueType; } - public function process(\PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage $coverage, bool $showColors = \false) : string + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string { - $hasBranchCoverage = !empty($coverage->getData(\true)->functionCoverage()); - $output = \PHP_EOL . \PHP_EOL; - $report = $coverage->getReport(); - $colors = ['header' => '', 'classes' => '', 'methods' => '', 'lines' => '', 'branches' => '', 'paths' => '', 'reset' => '', 'eol' => '']; - if ($showColors) { - $colors['classes'] = $this->coverageColor($report->numberOfTestedClassesAndTraits(), $report->numberOfClassesAndTraits()); - $colors['methods'] = $this->coverageColor($report->numberOfTestedMethods(), $report->numberOfMethods()); - $colors['lines'] = $this->coverageColor($report->numberOfExecutedLines(), $report->numberOfExecutableLines()); - $colors['branches'] = $this->coverageColor($report->numberOfExecutedBranches(), $report->numberOfExecutableBranches()); - $colors['paths'] = $this->coverageColor($report->numberOfExecutedPaths(), $report->numberOfExecutablePaths()); - $colors['reset'] = self::COLOR_RESET; - $colors['header'] = self::COLOR_HEADER; - $colors['eol'] = self::COLOR_EOL; - } - $classes = \sprintf(' Classes: %6s (%d/%d)', \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($report->numberOfTestedClassesAndTraits(), $report->numberOfClassesAndTraits())->asString(), $report->numberOfTestedClassesAndTraits(), $report->numberOfClassesAndTraits()); - $methods = \sprintf(' Methods: %6s (%d/%d)', \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($report->numberOfTestedMethods(), $report->numberOfMethods())->asString(), $report->numberOfTestedMethods(), $report->numberOfMethods()); - $paths = ''; - $branches = ''; - if ($hasBranchCoverage) { - $paths = \sprintf(' Paths: %6s (%d/%d)', \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($report->numberOfExecutedPaths(), $report->numberOfExecutablePaths())->asString(), $report->numberOfExecutedPaths(), $report->numberOfExecutablePaths()); - $branches = \sprintf(' Branches: %6s (%d/%d)', \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($report->numberOfExecutedBranches(), $report->numberOfExecutableBranches())->asString(), $report->numberOfExecutedBranches(), $report->numberOfExecutableBranches()); - } - $lines = \sprintf(' Lines: %6s (%d/%d)', \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($report->numberOfExecutedLines(), $report->numberOfExecutableLines())->asString(), $report->numberOfExecutedLines(), $report->numberOfExecutableLines()); - $padding = \max(\array_map('strlen', [$classes, $methods, $lines])); - if ($this->showOnlySummary) { - $title = 'Code Coverage Report Summary:'; - $padding = \max($padding, \strlen($title)); - $output .= $this->format($colors['header'], $padding, $title); - } else { - $date = \date(' Y-m-d H:i:s'); - $title = 'Code Coverage Report:'; - $output .= $this->format($colors['header'], $padding, $title); - $output .= $this->format($colors['header'], $padding, $date); - $output .= $this->format($colors['header'], $padding, ''); - $output .= $this->format($colors['header'], $padding, ' Summary:'); - } - $output .= $this->format($colors['classes'], $padding, $classes); - $output .= $this->format($colors['methods'], $padding, $methods); - if ($hasBranchCoverage) { - $output .= $this->format($colors['paths'], $padding, $paths); - $output .= $this->format($colors['branches'], $padding, $branches); - } - $output .= $this->format($colors['lines'], $padding, $lines); - if ($this->showOnlySummary) { - return $output . \PHP_EOL; - } - $classCoverage = []; - foreach ($report as $item) { - if (!$item instanceof \PHPUnit\SebastianBergmann\CodeCoverage\Node\File) { - continue; - } - $classes = $item->classesAndTraits(); - foreach ($classes as $className => $class) { - $classExecutableLines = 0; - $classExecutedLines = 0; - $classExecutableBranches = 0; - $classExecutedBranches = 0; - $classExecutablePaths = 0; - $classExecutedPaths = 0; - $coveredMethods = 0; - $classMethods = 0; - foreach ($class['methods'] as $method) { - if ($method['executableLines'] == 0) { - continue; - } - $classMethods++; - $classExecutableLines += $method['executableLines']; - $classExecutedLines += $method['executedLines']; - $classExecutableBranches += $method['executableBranches']; - $classExecutedBranches += $method['executedBranches']; - $classExecutablePaths += $method['executablePaths']; - $classExecutedPaths += $method['executedPaths']; - if ($method['coverage'] == 100) { - $coveredMethods++; - } - } - $classCoverage[$className] = ['namespace' => $class['namespace'], 'className' => $className, 'methodsCovered' => $coveredMethods, 'methodCount' => $classMethods, 'statementsCovered' => $classExecutedLines, 'statementCount' => $classExecutableLines, 'branchesCovered' => $classExecutedBranches, 'branchesCount' => $classExecutableBranches, 'pathsCovered' => $classExecutedPaths, 'pathsCount' => $classExecutablePaths]; - } - } - \ksort($classCoverage); - $methodColor = ''; - $pathsColor = ''; - $branchesColor = ''; - $linesColor = ''; - $resetColor = ''; - foreach ($classCoverage as $fullQualifiedPath => $classInfo) { - if ($this->showUncoveredFiles || $classInfo['statementsCovered'] != 0) { - if ($showColors) { - $methodColor = $this->coverageColor($classInfo['methodsCovered'], $classInfo['methodCount']); - $pathsColor = $this->coverageColor($classInfo['pathsCovered'], $classInfo['pathsCount']); - $branchesColor = $this->coverageColor($classInfo['branchesCovered'], $classInfo['branchesCount']); - $linesColor = $this->coverageColor($classInfo['statementsCovered'], $classInfo['statementCount']); - $resetColor = $colors['reset']; - } - $output .= \PHP_EOL . $fullQualifiedPath . \PHP_EOL . ' ' . $methodColor . 'Methods: ' . $this->printCoverageCounts($classInfo['methodsCovered'], $classInfo['methodCount'], 2) . $resetColor . ' '; - if ($hasBranchCoverage) { - $output .= ' ' . $pathsColor . 'Paths: ' . $this->printCoverageCounts($classInfo['pathsCovered'], $classInfo['pathsCount'], 3) . $resetColor . ' ' . ' ' . $branchesColor . 'Branches: ' . $this->printCoverageCounts($classInfo['branchesCovered'], $classInfo['branchesCount'], 3) . $resetColor . ' '; - } - $output .= ' ' . $linesColor . 'Lines: ' . $this->printCoverageCounts($classInfo['statementsCovered'], $classInfo['statementCount'], 3) . $resetColor; - } + if ($this->keyType) { + return 'array<' . $this->keyType . ',' . $this->valueType . '>'; } - return $output . \PHP_EOL; - } - private function coverageColor(int $numberOfCoveredElements, int $totalNumberOfElements) : string - { - $coverage = \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($numberOfCoveredElements, $totalNumberOfElements); - if ($coverage->asFloat() >= $this->highLowerBound) { - return self::COLOR_GREEN; + if ($this->valueType instanceof Mixed_) { + return 'array'; } - if ($coverage->asFloat() > $this->lowUpperBound) { - return self::COLOR_YELLOW; + if ($this->valueType instanceof Compound) { + return '(' . $this->valueType . ')[]'; } - return self::COLOR_RED; - } - private function printCoverageCounts(int $numberOfCoveredElements, int $totalNumberOfElements, int $precision) : string - { - $format = '%' . $precision . 's'; - return \PHPUnit\SebastianBergmann\CodeCoverage\Percentage::fromFractionAndTotal($numberOfCoveredElements, $totalNumberOfElements)->asFixedWidthString() . ' (' . \sprintf($format, $numberOfCoveredElements) . '/' . \sprintf($format, $totalNumberOfElements) . ')'; + return $this->valueType . '[]'; } +} + +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; +namespace PHPUnit\phpDocumentor\Reflection\Types; -use RuntimeException; -final class UnintentionallyCoveredCodeException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception +use PHPUnit\phpDocumentor\Reflection\Fqsen; +use PHPUnit\phpDocumentor\Reflection\Type; +/** + * Value Object representing the type 'string'. + * + * @psalm-immutable + */ +final class ClassString implements Type { + /** @var Fqsen|null */ + private $fqsen; /** - * @var array + * Initializes this representation of a class string with the given Fqsen. */ - private $unintentionallyCoveredUnits; - public function __construct(array $unintentionallyCoveredUnits) + public function __construct(?Fqsen $fqsen = null) { - $this->unintentionallyCoveredUnits = $unintentionallyCoveredUnits; - parent::__construct($this->toString()); + $this->fqsen = $fqsen; } - public function getUnintentionallyCoveredUnits() : array + /** + * Returns the FQSEN associated with this object. + */ + public function getFqsen() : ?Fqsen { - return $this->unintentionallyCoveredUnits; + return $this->fqsen; } - private function toString() : string + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string { - $message = ''; - foreach ($this->unintentionallyCoveredUnits as $unit) { - $message .= '- ' . $unit . "\n"; + if ($this->fqsen === null) { + return 'class-string'; } - return $message; + return 'class-string<' . (string) $this->fqsen . '>'; } } +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; +namespace PHPUnit\phpDocumentor\Reflection\Types; -use RuntimeException; -use PHPUnit\SebastianBergmann\CodeCoverage\Exception; -final class XdebugNotAvailableException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception +use PHPUnit\phpDocumentor\Reflection\Type; +/** + * Value Object representing the 'self' type. + * + * Self, as a Type, represents the class in which the associated element was defined. + * + * @psalm-immutable + */ +final class Self_ implements Type { - public function __construct() + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string { - parent::__construct('The Xdebug extension is not available'); + return 'self'; } } +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; +namespace PHPUnit\phpDocumentor\Reflection\Types; -use RuntimeException; -final class DeadCodeDetectionNotSupportedException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception +use PHPUnit\phpDocumentor\Reflection\Type; +/** + * Value Object representing a Float. + * + * @psalm-immutable + */ +final class Float_ implements Type { + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string + { + return 'float'; + } } + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @link http://phpdoc.org + */ +namespace PHPUnit\phpDocumentor\Reflection\Types; + +use PHPUnit\phpDocumentor\Reflection\Type; +/** + * Value Object representing the 'static' type. + * + * Self, as a Type, represents the class in which the associated element was called. This differs from self as self does + * not take inheritance into account but static means that the return type is always that of the class of the called + * element. + * + * See the documentation on late static binding in the PHP Documentation for more information on the difference between + * static and self. + * + * @psalm-immutable + */ +final class Static_ implements Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string + { + return 'static'; + } +} + Fully Qualified Namespace. + * @psalm-var array + */ + private $namespaceAliases; + /** + * Initializes the new context and normalizes all passed namespaces to be in Qualified Namespace Name (QNN) + * format (without a preceding `\`). + * + * @param string $namespace The namespace where this DocBlock resides in. + * @param string[] $namespaceAliases List of namespace aliases => Fully Qualified Namespace. + * @psalm-param array $namespaceAliases + */ + public function __construct(string $namespace, array $namespaceAliases = []) + { + $this->namespace = $namespace !== 'global' && $namespace !== 'default' ? trim($namespace, '\\') : ''; + foreach ($namespaceAliases as $alias => $fqnn) { + if ($fqnn[0] === '\\') { + $fqnn = substr($fqnn, 1); + } + if ($fqnn[strlen($fqnn) - 1] === '\\') { + $fqnn = substr($fqnn, 0, -1); + } + $namespaceAliases[$alias] = $fqnn; + } + $this->namespaceAliases = $namespaceAliases; + } + /** + * Returns the Qualified Namespace Name (thus without `\` in front) where the associated element is in. + */ + public function getNamespace() : string + { + return $this->namespace; + } + /** + * Returns a list of Qualified Namespace Names (thus without `\` in front) that are imported, the keys represent + * the alias for the imported Namespace. + * + * @return string[] + * @psalm-return array + */ + public function getNamespaceAliases() : array { - parent::__construct('Test ID is missing'); + return $this->namespaceAliases; } } +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; - -use RuntimeException; -final class ParserException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception -{ -} - + * An array can be represented in two forms: * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * 1. Untyped (`array`), where the key and value type is unknown and hence classified as 'Mixed_'. + * 2. Types (`string[]`), where the value type is provided by preceding an opening and closing square bracket with a + * type name. + * + * @psalm-immutable */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; - -use RuntimeException; -final class ReportAlreadyFinalizedException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception +final class Array_ extends AbstractList { - public function __construct() - { - parent::__construct('The code coverage report has already been finalized'); - } } +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; +namespace PHPUnit\phpDocumentor\Reflection\Types; -use RuntimeException; -use PHPUnit\SebastianBergmann\CodeCoverage\Exception; -final class Xdebug3NotEnabledException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception +use InvalidArgumentException; +use PHPUnit\phpDocumentor\Reflection\Fqsen; +use PHPUnit\phpDocumentor\Reflection\Type; +use function strpos; +/** + * Value Object representing an object. + * + * An object can be either typed or untyped. When an object is typed it means that it has an identifier, the FQSEN, + * pointing to an element in PHP. Object types that are untyped do not refer to a specific class but represent objects + * in general. + * + * @psalm-immutable + */ +final class Object_ implements Type { - public function __construct() + /** @var Fqsen|null */ + private $fqsen; + /** + * Initializes this object with an optional FQSEN, if not provided this object is considered 'untyped'. + * + * @throws InvalidArgumentException When provided $fqsen is not a valid type. + */ + public function __construct(?Fqsen $fqsen = null) { - parent::__construct('XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set'); + if (strpos((string) $fqsen, '::') !== \false || strpos((string) $fqsen, '()') !== \false) { + throw new InvalidArgumentException('Object types can only refer to a class, interface or trait but a method, function, constant or ' . 'property was received: ' . (string) $fqsen); + } + $this->fqsen = $fqsen; + } + /** + * Returns the FQSEN associated with this object. + */ + public function getFqsen() : ?Fqsen + { + return $this->fqsen; + } + public function __toString() : string + { + if ($this->fqsen) { + return (string) $this->fqsen; + } + return 'object'; } } +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; +namespace PHPUnit\phpDocumentor\Reflection\Types; -use function sprintf; -use RuntimeException; -use PHPUnit\SebastianBergmann\CodeCoverage\Exception; -final class WriteOperationFailedException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception +use PHPUnit\phpDocumentor\Reflection\Type; +/** + * Value Object representing a Callable type. + * + * @psalm-immutable + */ +final class Callable_ implements Type { - public function __construct(string $path) + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string { - parent::__construct(\sprintf('Cannot write to "%s"', $path)); + return 'callable'; } } +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; - -use Throwable; -interface Exception extends \Throwable -{ -} - + * A collection can be represented in two forms: * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; - -use RuntimeException; -final class DirectoryCouldNotBeCreatedException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception -{ -} -` + * 2. `ACollectionObject` * - * (c) Sebastian Bergmann + * - ACollectionObject can be 'array' or an object that can act as an array + * - aValueType and aKeyType can be any type expression * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * @psalm-immutable */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; - -use RuntimeException; -final class NoCodeCoverageDriverAvailableException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception +final class Collection extends AbstractList { - public function __construct() + /** @var Fqsen|null */ + private $fqsen; + /** + * Initializes this representation of an array with the given Type or Fqsen. + */ + public function __construct(?Fqsen $fqsen, Type $valueType, ?Type $keyType = null) { - parent::__construct('No code coverage driver available'); + parent::__construct($valueType, $keyType); + $this->fqsen = $fqsen; + } + /** + * Returns the FQSEN associated with this object. + */ + public function getFqsen() : ?Fqsen + { + return $this->fqsen; + } + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string + { + $objectType = (string) ($this->fqsen ?? 'object'); + if ($this->keyType === null) { + return $objectType . '<' . $this->valueType . '>'; + } + return $objectType . '<' . $this->keyType . ',' . $this->valueType . '>'; } } +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; - -use RuntimeException; -final class XmlException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception -{ -} - +use PHPUnit\phpDocumentor\Reflection\Type; +/** + * Value Object representing the 'scalar' pseudo-type, which is either a string, integer, float or boolean. * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * @psalm-immutable */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; - -final class InvalidArgumentException extends \InvalidArgumentException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception +final class Scalar implements Type { + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string + { + return 'scalar'; + } } +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; +namespace PHPUnit\phpDocumentor\Reflection\Types; -use function sprintf; -use RuntimeException; -use PHPUnit\SebastianBergmann\CodeCoverage\Exception; -final class PathExistsButIsNotDirectoryException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception +use PHPUnit\phpDocumentor\Reflection\Type; +/** + * Represents an expression type as described in the PSR-5, the PHPDoc Standard. + * + * @psalm-immutable + */ +final class Expression implements Type { - public function __construct(string $path) + /** @var Type */ + protected $valueType; + /** + * Initializes this representation of an array with the given Type. + */ + public function __construct(Type $valueType) + { + $this->valueType = $valueType; + } + /** + * Returns the value for the keys of this array. + */ + public function getValueType() : Type + { + return $this->valueType; + } + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string { - parent::__construct(\sprintf('"%s" exists but is not a directory', $path)); + return '(' . $this->valueType . ')'; } } +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; +namespace PHPUnit\phpDocumentor\Reflection\Types; -use RuntimeException; -final class StaticAnalysisCacheNotConfiguredException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception +use PHPUnit\phpDocumentor\Reflection\Type; +/** + * Value Object representing an unknown, or mixed, type. + * + * @psalm-immutable + */ +final class Mixed_ implements Type { + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string + { + return 'mixed'; + } } + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; - -use RuntimeException; -use PHPUnit\SebastianBergmann\CodeCoverage\Exception; -final class WrongXdebugVersionException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception -{ -} - + * A Aggregated Type is not so much a special keyword or object reference but is a series of Types that are separated + * using separator. * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * @psalm-immutable + * @template-implements IteratorAggregate */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; - -use RuntimeException; -use PHPUnit\SebastianBergmann\CodeCoverage\Exception; -final class PcovNotAvailableException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception +abstract class AggregatedType implements Type, IteratorAggregate { - public function __construct() + /** + * @psalm-allow-private-mutation + * @var array + */ + private $types = []; + /** @var string */ + private $token; + /** + * @param array $types + */ + public function __construct(array $types, string $token) { - parent::__construct('The PCOV extension is not available'); + foreach ($types as $type) { + $this->add($type); + } + $this->token = $token; + } + /** + * Returns the type at the given index. + */ + public function get(int $index) : ?Type + { + if (!$this->has($index)) { + return null; + } + return $this->types[$index]; + } + /** + * Tests if this compound type has a type with the given index. + */ + public function has(int $index) : bool + { + return array_key_exists($index, $this->types); + } + /** + * Tests if this compound type contains the given type. + */ + public function contains(Type $type) : bool + { + foreach ($this->types as $typePart) { + // if the type is duplicate; do not add it + if ((string) $typePart === (string) $type) { + return \true; + } + } + return \false; + } + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string + { + return implode($this->token, $this->types); + } + /** + * @return ArrayIterator + */ + public function getIterator() : ArrayIterator + { + return new ArrayIterator($this->types); + } + /** + * @psalm-suppress ImpureMethodCall + */ + private function add(Type $type) : void + { + if ($type instanceof self) { + foreach ($type->getIterator() as $subType) { + $this->add($subType); + } + return; + } + // if the type is duplicate; do not add it + if ($this->contains($type)) { + return; + } + $this->types[] = $type; } } +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; +namespace PHPUnit\phpDocumentor\Reflection\Types; -use RuntimeException; -final class NoCodeCoverageDriverWithPathCoverageSupportAvailableException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception +/** + * Value Object representing iterable type + * + * @psalm-immutable + */ +final class Iterable_ extends AbstractList { - public function __construct() + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string { - parent::__construct('No code coverage driver with path coverage support available'); + if ($this->keyType) { + return 'iterable<' . $this->keyType . ',' . $this->valueType . '>'; + } + if ($this->valueType instanceof Mixed_) { + return 'iterable'; + } + return 'iterable<' . $this->valueType . '>'; } } + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; - -use RuntimeException; -final class BranchAndPathCoverageNotSupportedException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; - -use RuntimeException; -final class ReflectionException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception -{ -} - + * A Intersection Type is not so much a special keyword or object reference but is a series of Types that are separated + * using an AND operator (`&`). This combination of types signifies that whatever is associated with this Intersection + * type may contain a value with any of the given types. * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * @psalm-immutable */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; - -use RuntimeException; -use PHPUnit\SebastianBergmann\CodeCoverage\Exception; -final class PhpdbgNotAvailableException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception +final class Intersection extends AggregatedType { - public function __construct() + /** + * Initializes a intersection type (i.e. `\A&\B`) and tests if the provided types all implement the Type interface. + * + * @param array $types + */ + public function __construct(array $types) { - parent::__construct('The PHPDBG SAPI is not available'); + parent::__construct($types, '&'); } } +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; +namespace PHPUnit\phpDocumentor\Reflection\Types; -use RuntimeException; -use PHPUnit\SebastianBergmann\CodeCoverage\Exception; -final class Xdebug2NotEnabledException extends \RuntimeException implements \PHPUnit\SebastianBergmann\CodeCoverage\Exception +use PHPUnit\phpDocumentor\Reflection\Type; +/** + * Value Object representing the '$this' pseudo-type. + * + * $this, as a Type, represents the instance of the class associated with the element as it was called. $this is + * commonly used when documenting fluent interfaces since it represents that the same object is returned. + * + * @psalm-immutable + */ +final class This implements Type { - public function __construct() + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string { - parent::__construct('xdebug.coverage_enable=On has to be set'); + return '$this'; } } +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; +namespace PHPUnit\phpDocumentor\Reflection\Types; -use function sprintf; +use PHPUnit\phpDocumentor\Reflection\Type; /** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * Value Object representing a nullable type. The real type is wrapped. + * + * @psalm-immutable */ -final class Percentage +final class Nullable implements Type { + /** @var Type The actual type that is wrapped */ + private $realType; /** - * @var float - */ - private $fraction; - /** - * @var float + * Initialises this nullable type using the real type embedded */ - private $total; - public static function fromFractionAndTotal(float $fraction, float $total) : self - { - return new self($fraction, $total); - } - private function __construct(float $fraction, float $total) - { - $this->fraction = $fraction; - $this->total = $total; - } - public function asFloat() : float + public function __construct(Type $realType) { - if ($this->total > 0) { - return (float) ($this->fraction / $this->total) * 100; - } - return 100.0; + $this->realType = $realType; } - public function asString() : string + /** + * Provide access to the actual type directly, if needed. + */ + public function getActualType() : Type { - if ($this->total > 0) { - return \sprintf('%01.2F%%', $this->asFloat()); - } - return ''; + return $this->realType; } - public function asFixedWidthString() : string + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string { - if ($this->total > 0) { - return \sprintf('%6.2F%%', $this->asFloat()); - } - return ''; + return '?' . $this->realType->__toString(); } } +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; +namespace PHPUnit\phpDocumentor\Reflection\Types; -use function array_key_exists; -use function array_keys; -use function array_merge; -use function array_unique; -use function count; -use function is_array; -use function ksort; -use PHPUnit\SebastianBergmann\CodeCoverage\Driver\Driver; +use PHPUnit\phpDocumentor\Reflection\Fqsen; +use PHPUnit\phpDocumentor\Reflection\Type; /** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * Value Object representing the type 'string'. + * + * @psalm-immutable */ -final class ProcessedCodeCoverageData +final class InterfaceString implements Type { + /** @var Fqsen|null */ + private $fqsen; /** - * Line coverage data. - * An array of filenames, each having an array of linenumbers, each executable line having an array of testcase ids. - * - * @var array - */ - private $lineCoverage = []; - /** - * Function coverage data. - * Maintains base format of raw data (@see https://xdebug.org/docs/code_coverage), but each 'hit' entry is an array - * of testcase ids. - * - * @var array + * Initializes this representation of a class string with the given Fqsen. */ - private $functionCoverage = []; - public function initializeUnseenData(\PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData $rawData) : void - { - foreach ($rawData->lineCoverage() as $file => $lines) { - if (!isset($this->lineCoverage[$file])) { - $this->lineCoverage[$file] = []; - foreach ($lines as $k => $v) { - $this->lineCoverage[$file][$k] = $v === \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Driver::LINE_NOT_EXECUTABLE ? null : []; - } - } - } - foreach ($rawData->functionCoverage() as $file => $functions) { - foreach ($functions as $functionName => $functionData) { - if (isset($this->functionCoverage[$file][$functionName])) { - $this->initPreviouslySeenFunction($file, $functionName, $functionData); - } else { - $this->initPreviouslyUnseenFunction($file, $functionName, $functionData); - } - } - } - } - public function markCodeAsExecutedByTestCase(string $testCaseId, \PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData $executedCode) : void - { - foreach ($executedCode->lineCoverage() as $file => $lines) { - foreach ($lines as $k => $v) { - if ($v === \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Driver::LINE_EXECUTED) { - $this->lineCoverage[$file][$k][] = $testCaseId; - } - } - } - foreach ($executedCode->functionCoverage() as $file => $functions) { - foreach ($functions as $functionName => $functionData) { - foreach ($functionData['branches'] as $branchId => $branchData) { - if ($branchData['hit'] === \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Driver::BRANCH_HIT) { - $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'][] = $testCaseId; - } - } - foreach ($functionData['paths'] as $pathId => $pathData) { - if ($pathData['hit'] === \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Driver::BRANCH_HIT) { - $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'][] = $testCaseId; - } - } - } - } - } - public function setLineCoverage(array $lineCoverage) : void - { - $this->lineCoverage = $lineCoverage; - } - public function lineCoverage() : array - { - \ksort($this->lineCoverage); - return $this->lineCoverage; - } - public function setFunctionCoverage(array $functionCoverage) : void - { - $this->functionCoverage = $functionCoverage; - } - public function functionCoverage() : array - { - \ksort($this->functionCoverage); - return $this->functionCoverage; - } - public function coveredFiles() : array - { - \ksort($this->lineCoverage); - return \array_keys($this->lineCoverage); - } - public function renameFile(string $oldFile, string $newFile) : void + public function __construct(?Fqsen $fqsen = null) { - $this->lineCoverage[$newFile] = $this->lineCoverage[$oldFile]; - if (isset($this->functionCoverage[$oldFile])) { - $this->functionCoverage[$newFile] = $this->functionCoverage[$oldFile]; - } - unset($this->lineCoverage[$oldFile], $this->functionCoverage[$oldFile]); + $this->fqsen = $fqsen; } - public function merge(self $newData) : void + /** + * Returns the FQSEN associated with this object. + */ + public function getFqsen() : ?Fqsen { - foreach ($newData->lineCoverage as $file => $lines) { - if (!isset($this->lineCoverage[$file])) { - $this->lineCoverage[$file] = $lines; - continue; - } - // we should compare the lines if any of two contains data - $compareLineNumbers = \array_unique(\array_merge(\array_keys($this->lineCoverage[$file]), \array_keys($newData->lineCoverage[$file]))); - foreach ($compareLineNumbers as $line) { - $thatPriority = $this->priorityForLine($newData->lineCoverage[$file], $line); - $thisPriority = $this->priorityForLine($this->lineCoverage[$file], $line); - if ($thatPriority > $thisPriority) { - $this->lineCoverage[$file][$line] = $newData->lineCoverage[$file][$line]; - } elseif ($thatPriority === $thisPriority && \is_array($this->lineCoverage[$file][$line])) { - $this->lineCoverage[$file][$line] = \array_unique(\array_merge($this->lineCoverage[$file][$line], $newData->lineCoverage[$file][$line])); - } - } - } - foreach ($newData->functionCoverage as $file => $functions) { - if (!isset($this->functionCoverage[$file])) { - $this->functionCoverage[$file] = $functions; - continue; - } - foreach ($functions as $functionName => $functionData) { - if (isset($this->functionCoverage[$file][$functionName])) { - $this->initPreviouslySeenFunction($file, $functionName, $functionData); - } else { - $this->initPreviouslyUnseenFunction($file, $functionName, $functionData); - } - foreach ($functionData['branches'] as $branchId => $branchData) { - $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = \array_unique(\array_merge($this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'], $branchData['hit'])); - } - foreach ($functionData['paths'] as $pathId => $pathData) { - $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = \array_unique(\array_merge($this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'], $pathData['hit'])); - } - } - } + return $this->fqsen; } /** - * Determine the priority for a line. - * - * 1 = the line is not set - * 2 = the line has not been tested - * 3 = the line is dead code - * 4 = the line has been tested - * - * During a merge, a higher number is better. + * Returns a rendered output of the Type as it would be used in a DocBlock. */ - private function priorityForLine(array $data, int $line) : int + public function __toString() : string { - if (!\array_key_exists($line, $data)) { - return 1; + if ($this->fqsen === null) { + return 'interface-string'; } - if (\is_array($data[$line]) && \count($data[$line]) === 0) { - return 2; + return 'interface-string<' . (string) $this->fqsen . '>'; + } +} +isFqsen($fqsen)) { + return new Fqsen($fqsen); } - return 4; + return $this->resolvePartialStructuralElementName($fqsen, $context); } /** - * For a function we have never seen before, copy all data over and simply init the 'hit' array. + * Tests whether the given type is a Fully Qualified Structural Element Name. */ - private function initPreviouslyUnseenFunction(string $file, string $functionName, array $functionData) : void + private function isFqsen(string $type) : bool { - $this->functionCoverage[$file][$functionName] = $functionData; - foreach (\array_keys($functionData['branches']) as $branchId) { - $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = []; - } - foreach (\array_keys($functionData['paths']) as $pathId) { - $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = []; - } + return strpos($type, self::OPERATOR_NAMESPACE) === 0; } /** - * For a function we have seen before, only copy over and init the 'hit' array for any unseen branches and paths. - * Techniques such as mocking and where the contents of a file are different vary during tests (e.g. compiling - * containers) mean that the functions inside a file cannot be relied upon to be static. + * Resolves a partial Structural Element Name (i.e. `Reflection\DocBlock`) to its FQSEN representation + * (i.e. `\phpDocumentor\Reflection\DocBlock`) based on the Namespace and aliases mentioned in the Context. + * + * @throws InvalidArgumentException When type is not a valid FQSEN. */ - private function initPreviouslySeenFunction(string $file, string $functionName, array $functionData) : void + private function resolvePartialStructuralElementName(string $type, Context $context) : Fqsen { - foreach ($functionData['branches'] as $branchId => $branchData) { - if (!isset($this->functionCoverage[$file][$functionName]['branches'][$branchId])) { - $this->functionCoverage[$file][$functionName]['branches'][$branchId] = $branchData; - $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = []; - } - } - foreach ($functionData['paths'] as $pathId => $pathData) { - if (!isset($this->functionCoverage[$file][$functionName]['paths'][$pathId])) { - $this->functionCoverage[$file][$functionName]['paths'][$pathId] = $pathData; - $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = []; + $typeParts = explode(self::OPERATOR_NAMESPACE, $type, 2); + $namespaceAliases = $context->getNamespaceAliases(); + // if the first segment is not an alias; prepend namespace name and return + if (!isset($namespaceAliases[$typeParts[0]])) { + $namespace = $context->getNamespace(); + if ($namespace !== '') { + $namespace .= self::OPERATOR_NAMESPACE; } + return new Fqsen(self::OPERATOR_NAMESPACE . $namespace . $type); } + $typeParts[0] = $namespaceAliases[$typeParts[0]]; + return new Fqsen(self::OPERATOR_NAMESPACE . implode(self::OPERATOR_NAMESPACE, $typeParts)); } } +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; +namespace PHPUnit\phpDocumentor\Reflection\PseudoTypes; -use function array_diff; -use function array_diff_key; -use function array_flip; -use function array_intersect; -use function array_intersect_key; -use function count; -use function file; -use function in_array; -use function range; -use PHPUnit\SebastianBergmann\CodeCoverage\Driver\Driver; -use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\UncoveredFileAnalyser; +use PHPUnit\phpDocumentor\Reflection\PseudoType; +use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnit\phpDocumentor\Reflection\Types\String_; /** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * Value Object representing the type 'string'. + * + * @psalm-immutable */ -final class RawCodeCoverageData +final class HtmlEscapedString extends String_ implements PseudoType { - /** - * @var array> - */ - private static $emptyLineCache = []; - /** - * @var array - * - * @see https://xdebug.org/docs/code_coverage for format - */ - private $lineCoverage; - /** - * @var array - * - * @see https://xdebug.org/docs/code_coverage for format - */ - private $functionCoverage; - public static function fromXdebugWithoutPathCoverage(array $rawCoverage) : self - { - return new self($rawCoverage, []); - } - public static function fromXdebugWithPathCoverage(array $rawCoverage) : self - { - $lineCoverage = []; - $functionCoverage = []; - foreach ($rawCoverage as $file => $fileCoverageData) { - $lineCoverage[$file] = $fileCoverageData['lines']; - $functionCoverage[$file] = $fileCoverageData['functions']; - } - return new self($lineCoverage, $functionCoverage); - } - public static function fromXdebugWithMixedCoverage(array $rawCoverage) : self - { - $lineCoverage = []; - $functionCoverage = []; - foreach ($rawCoverage as $file => $fileCoverageData) { - if (!isset($fileCoverageData['functions'])) { - // Current file does not have functions, so line coverage - // is stored in $fileCoverageData, not in $fileCoverageData['lines'] - $lineCoverage[$file] = $fileCoverageData; - continue; - } - $lineCoverage[$file] = $fileCoverageData['lines']; - $functionCoverage[$file] = $fileCoverageData['functions']; - } - return new self($lineCoverage, $functionCoverage); - } - public static function fromUncoveredFile(string $filename, \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\UncoveredFileAnalyser $uncoveredFileAnalyser) : self - { - $lineCoverage = []; - foreach ($uncoveredFileAnalyser->executableLinesIn($filename) as $line) { - $lineCoverage[$line] = \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Driver::LINE_NOT_EXECUTED; - } - return new self([$filename => $lineCoverage], []); - } - private function __construct(array $lineCoverage, array $functionCoverage) + public function underlyingType() : Type { - $this->lineCoverage = $lineCoverage; - $this->functionCoverage = $functionCoverage; - $this->skipEmptyLines(); + return new String_(); } - public function clear() : void + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string { - $this->lineCoverage = $this->functionCoverage = []; + return 'html-escaped-string'; } - public function lineCoverage() : array +} +lineCoverage; + return new Boolean(); } - public function functionCoverage() : array + public function __toString() : string { - return $this->functionCoverage; + return 'false'; } - public function removeCoverageDataForFile(string $filename) : void +} +class_alias('PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\False_', 'PHPUnit\\phpDocumentor\\Reflection\\Types\\False_', \false); +lineCoverage[$filename], $this->functionCoverage[$filename]); + return new String_(); } /** - * @param int[] $lines + * Returns a rendered output of the Type as it would be used in a DocBlock. */ - public function keepCoverageDataOnlyForLines(string $filename, array $lines) : void + public function __toString() : string { - if (!isset($this->lineCoverage[$filename])) { - return; - } - $this->lineCoverage[$filename] = \array_intersect_key($this->lineCoverage[$filename], \array_flip($lines)); - if (isset($this->functionCoverage[$filename])) { - foreach ($this->functionCoverage[$filename] as $functionName => $functionData) { - foreach ($functionData['branches'] as $branchId => $branch) { - if (\count(\array_diff(\range($branch['line_start'], $branch['line_end']), $lines)) > 0) { - unset($this->functionCoverage[$filename][$functionName]['branches'][$branchId]); - foreach ($functionData['paths'] as $pathId => $path) { - if (\in_array($branchId, $path['path'], \true)) { - unset($this->functionCoverage[$filename][$functionName]['paths'][$pathId]); - } - } - } - } - } - } + return 'non-empty-string'; } - /** - * @param int[] $lines - */ - public function removeCoverageDataForLines(string $filename, array $lines) : void +} +lineCoverage[$filename])) { - return; - } - $this->lineCoverage[$filename] = \array_diff_key($this->lineCoverage[$filename], \array_flip($lines)); - if (isset($this->functionCoverage[$filename])) { - foreach ($this->functionCoverage[$filename] as $functionName => $functionData) { - foreach ($functionData['branches'] as $branchId => $branch) { - if (\count(\array_intersect($lines, \range($branch['line_start'], $branch['line_end']))) > 0) { - unset($this->functionCoverage[$filename][$functionName]['branches'][$branchId]); - foreach ($functionData['paths'] as $pathId => $path) { - if (\in_array($branchId, $path['path'], \true)) { - unset($this->functionCoverage[$filename][$functionName]['paths'][$pathId]); - } - } - } - } - } - } + return new String_(); } /** - * At the end of a file, the PHP interpreter always sees an implicit return. Where this occurs in a file that has - * e.g. a class definition, that line cannot be invoked from a test and results in confusing coverage. This engine - * implementation detail therefore needs to be masked which is done here by simply ensuring that all empty lines - * are skipped over for coverage purposes. - * - * @see https://github.com/sebastianbergmann/php-code-coverage/issues/799 + * Returns a rendered output of the Type as it would be used in a DocBlock. */ - private function skipEmptyLines() : void + public function __toString() : string + { + return 'trait-string'; + } +} +lineCoverage as $filename => $coverage) { - foreach ($this->getEmptyLinesForFile($filename) as $emptyLine) { - unset($this->lineCoverage[$filename][$emptyLine]); - } - } + return new Boolean(); } - private function getEmptyLinesForFile(string $filename) : array + public function __toString() : string { - if (!isset(self::$emptyLineCache[$filename])) { - self::$emptyLineCache[$filename] = []; - if (\is_file($filename)) { - $sourceLines = \explode("\n", \file_get_contents($filename)); - foreach ($sourceLines as $line => $source) { - if (\trim($source) === '') { - self::$emptyLineCache[$filename][] = $line + 1; - } - } - } - } - return self::$emptyLineCache[$filename]; + return 'true'; } } +class_alias('PHPUnit\\phpDocumentor\\Reflection\\PseudoTypes\\True_', 'PHPUnit\\phpDocumentor\\Reflection\\Types\\True_', \false); +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; +namespace PHPUnit\phpDocumentor\Reflection\PseudoTypes; -use function is_dir; -use function mkdir; -use function sprintf; +use PHPUnit\phpDocumentor\Reflection\PseudoType; +use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnit\phpDocumentor\Reflection\Types\String_; /** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * Value Object representing the type 'string'. + * + * @psalm-immutable */ -final class Directory +final class CallableString extends String_ implements PseudoType { + public function underlyingType() : Type + { + return new String_(); + } /** - * @throws DirectoryCouldNotBeCreatedException + * Returns a rendered output of the Type as it would be used in a DocBlock. */ - public static function create(string $directory) : void + public function __toString() : string { - $success = !(!\is_dir($directory) && !@\mkdir($directory, 0777, \true) && !\is_dir($directory)); - if (!$success) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\DirectoryCouldNotBeCreatedException(\sprintf('Directory "%s" could not be created', $directory)); - } + return 'callable-string'; } } +/** + * This file is part of phpDocumentor. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. + * + * @link http://phpdoc.org */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; +namespace PHPUnit\phpDocumentor\Reflection\PseudoTypes; -use const XDEBUG_CC_BRANCH_CHECK; -use const XDEBUG_CC_DEAD_CODE; -use const XDEBUG_CC_UNUSED; -use const XDEBUG_FILTER_CODE_COVERAGE; -use const PHPUnit\XDEBUG_PATH_INCLUDE; -use function explode; -use function extension_loaded; -use function getenv; -use function in_array; -use function ini_get; -use function phpversion; -use function sprintf; -use function version_compare; -use function xdebug_get_code_coverage; -use function xdebug_set_filter; -use function xdebug_start_code_coverage; -use function xdebug_stop_code_coverage; -use PHPUnit\SebastianBergmann\CodeCoverage\Filter; -use PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData; +use PHPUnit\phpDocumentor\Reflection\PseudoType; +use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnit\phpDocumentor\Reflection\Types\String_; /** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + * Value Object representing the type 'string'. + * + * @psalm-immutable */ -final class Xdebug3Driver extends \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Driver +final class NonEmptyLowercaseString extends String_ implements PseudoType { + public function underlyingType() : Type + { + return new String_(); + } /** - * @throws XdebugNotAvailableException - * @throws WrongXdebugVersionException - * @throws Xdebug3NotEnabledException + * Returns a rendered output of the Type as it would be used in a DocBlock. */ - public function __construct(\PHPUnit\SebastianBergmann\CodeCoverage\Filter $filter) + public function __toString() : string { - if (!\extension_loaded('xdebug')) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\XdebugNotAvailableException(); - } - if (\version_compare(\phpversion('xdebug'), '3', '<')) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\WrongXdebugVersionException(\sprintf('This driver requires Xdebug 3 but version %s is loaded', \phpversion('xdebug'))); - } - $mode = \getenv('XDEBUG_MODE'); - if ($mode === \false) { - $mode = \ini_get('xdebug.mode'); - } - if ($mode === \false || !\in_array('coverage', \explode(',', $mode), \true)) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Xdebug3NotEnabledException(); - } - if (!$filter->isEmpty()) { - \xdebug_set_filter(\XDEBUG_FILTER_CODE_COVERAGE, \XDEBUG_PATH_INCLUDE, $filter->files()); - } + return 'non-empty-lowercase-string'; } - public function canCollectBranchAndPathCoverage() : bool +} +detectsDeadCode() || $this->collectsBranchAndPathCoverage()) { - $flags |= \XDEBUG_CC_DEAD_CODE; - } - if ($this->collectsBranchAndPathCoverage()) { - $flags |= \XDEBUG_CC_BRANCH_CHECK; - } - \xdebug_start_code_coverage($flags); + return new String_(); } - public function stop() : \PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string { - $data = \xdebug_get_code_coverage(); - \xdebug_stop_code_coverage(); - if ($this->collectsBranchAndPathCoverage()) { - return \PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData::fromXdebugWithPathCoverage($data); - } - return \PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData::fromXdebugWithoutPathCoverage($data); + return 'lowercase-string'; } - public function nameAndVersion() : string +} + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @link http://phpdoc.org + */ +namespace PHPUnit\phpDocumentor\Reflection; + +/** + * @psalm-immutable + */ +interface Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString() : string; +} + List of recognized keywords and unto which Value Object they map + * @psalm-var array> */ - private $pathCoverageIsMixedCoverage; + private $keywords = ['string' => Types\String_::class, 'class-string' => Types\ClassString::class, 'interface-string' => Types\InterfaceString::class, 'html-escaped-string' => PseudoTypes\HtmlEscapedString::class, 'lowercase-string' => PseudoTypes\LowercaseString::class, 'non-empty-lowercase-string' => PseudoTypes\NonEmptyLowercaseString::class, 'non-empty-string' => PseudoTypes\NonEmptyString::class, 'numeric-string' => PseudoTypes\NumericString::class, 'trait-string' => PseudoTypes\TraitString::class, 'int' => Types\Integer::class, 'integer' => Types\Integer::class, 'positive-int' => PseudoTypes\PositiveInteger::class, 'bool' => Types\Boolean::class, 'boolean' => Types\Boolean::class, 'real' => Types\Float_::class, 'float' => Types\Float_::class, 'double' => Types\Float_::class, 'object' => Types\Object_::class, 'mixed' => Types\Mixed_::class, 'array' => Types\Array_::class, 'array-key' => Types\ArrayKey::class, 'resource' => Types\Resource_::class, 'void' => Types\Void_::class, 'null' => Types\Null_::class, 'scalar' => Types\Scalar::class, 'callback' => Types\Callable_::class, 'callable' => Types\Callable_::class, 'callable-string' => PseudoTypes\CallableString::class, 'false' => PseudoTypes\False_::class, 'true' => PseudoTypes\True_::class, 'self' => Types\Self_::class, '$this' => Types\This::class, 'static' => Types\Static_::class, 'parent' => Types\Parent_::class, 'iterable' => Types\Iterable_::class, 'never' => Types\Never_::class]; /** - * @throws XdebugNotAvailableException - * @throws WrongXdebugVersionException - * @throws Xdebug2NotEnabledException + * @var FqsenResolver + * @psalm-readonly + */ + private $fqsenResolver; + /** + * Initializes this TypeResolver with the means to create and resolve Fqsen objects. + */ + public function __construct(?FqsenResolver $fqsenResolver = null) + { + $this->fqsenResolver = $fqsenResolver ?: new FqsenResolver(); + } + /** + * Analyzes the given type and returns the FQCN variant. + * + * When a type is provided this method checks whether it is not a keyword or + * Fully Qualified Class Name. If so it will use the given namespace and + * aliases to expand the type to a FQCN representation. + * + * This method only works as expected if the namespace and aliases are set; + * no dynamic reflection is being performed here. + * + * @uses Context::getNamespaceAliases() to check whether the first part of the relative type name should not be + * replaced with another namespace. + * @uses Context::getNamespace() to determine with what to prefix the type name. + * + * @param string $type The relative or absolute type. */ - public function __construct(\PHPUnit\SebastianBergmann\CodeCoverage\Filter $filter) + public function resolve(string $type, ?Context $context = null) : Type { - if (!\extension_loaded('xdebug')) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\XdebugNotAvailableException(); + $type = trim($type); + if (!$type) { + throw new InvalidArgumentException('Attempted to resolve "' . $type . '" but it appears to be empty'); } - if (\version_compare(\phpversion('xdebug'), '3', '>=')) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\WrongXdebugVersionException(\sprintf('This driver requires Xdebug 2 but version %s is loaded', \phpversion('xdebug'))); + if ($context === null) { + $context = new Context(''); } - if (!\ini_get('xdebug.coverage_enable')) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Xdebug2NotEnabledException(); + // split the type string into tokens `|`, `?`, `<`, `>`, `,`, `(`, `)`, `[]`, '<', '>' and type names + $tokens = preg_split('/(\\||\\?|<|>|&|, ?|\\(|\\)|\\[\\]+)/', $type, -1, \PREG_SPLIT_NO_EMPTY | \PREG_SPLIT_DELIM_CAPTURE); + if ($tokens === \false) { + throw new InvalidArgumentException('Unable to split the type string "' . $type . '" into tokens'); } - if (!$filter->isEmpty()) { - if (\defined('XDEBUG_PATH_WHITELIST')) { - $listType = \XDEBUG_PATH_WHITELIST; + /** @var ArrayIterator $tokenIterator */ + $tokenIterator = new ArrayIterator($tokens); + return $this->parseTypes($tokenIterator, $context, self::PARSER_IN_COMPOUND); + } + /** + * Analyse each tokens and creates types + * + * @param ArrayIterator $tokens the iterator on tokens + * @param int $parserContext on of self::PARSER_* constants, indicating + * the context where we are in the parsing + */ + private function parseTypes(ArrayIterator $tokens, Context $context, int $parserContext) : Type + { + $types = []; + $token = ''; + $compoundToken = '|'; + while ($tokens->valid()) { + $token = $tokens->current(); + if ($token === null) { + throw new RuntimeException('Unexpected nullable character'); + } + if ($token === '|' || $token === '&') { + if (count($types) === 0) { + throw new RuntimeException('A type is missing before a type separator'); + } + if (!in_array($parserContext, [self::PARSER_IN_COMPOUND, self::PARSER_IN_ARRAY_EXPRESSION, self::PARSER_IN_COLLECTION_EXPRESSION], \true)) { + throw new RuntimeException('Unexpected type separator'); + } + $compoundToken = $token; + $tokens->next(); + } elseif ($token === '?') { + if (!in_array($parserContext, [self::PARSER_IN_COMPOUND, self::PARSER_IN_ARRAY_EXPRESSION, self::PARSER_IN_COLLECTION_EXPRESSION], \true)) { + throw new RuntimeException('Unexpected nullable character'); + } + $tokens->next(); + $type = $this->parseTypes($tokens, $context, self::PARSER_IN_NULLABLE); + $types[] = new Nullable($type); + } elseif ($token === '(') { + $tokens->next(); + $type = $this->parseTypes($tokens, $context, self::PARSER_IN_ARRAY_EXPRESSION); + $token = $tokens->current(); + if ($token === null) { + // Someone did not properly close their array expression .. + break; + } + $tokens->next(); + $resolvedType = new Expression($type); + $types[] = $resolvedType; + } elseif ($parserContext === self::PARSER_IN_ARRAY_EXPRESSION && isset($token[0]) && $token[0] === ')') { + break; + } elseif ($token === '<') { + if (count($types) === 0) { + throw new RuntimeException('Unexpected collection operator "<", class name is missing'); + } + $classType = array_pop($types); + if ($classType !== null) { + if ((string) $classType === 'class-string') { + $types[] = $this->resolveClassString($tokens, $context); + } elseif ((string) $classType === 'interface-string') { + $types[] = $this->resolveInterfaceString($tokens, $context); + } else { + $types[] = $this->resolveCollection($tokens, $classType, $context); + } + } + $tokens->next(); + } elseif ($parserContext === self::PARSER_IN_COLLECTION_EXPRESSION && ($token === '>' || trim($token) === ',')) { + break; + } elseif ($token === self::OPERATOR_ARRAY) { + end($types); + $last = key($types); + $lastItem = $types[$last]; + if ($lastItem instanceof Expression) { + $lastItem = $lastItem->getValueType(); + } + $types[$last] = new Array_($lastItem); + $tokens->next(); } else { - $listType = \XDEBUG_PATH_INCLUDE; + $type = $this->resolveSingleType($token, $context); + $tokens->next(); + if ($parserContext === self::PARSER_IN_NULLABLE) { + return $type; + } + $types[] = $type; + } + } + if ($token === '|' || $token === '&') { + throw new RuntimeException('A type is missing after a type separator'); + } + if (count($types) === 0) { + if ($parserContext === self::PARSER_IN_NULLABLE) { + throw new RuntimeException('A type is missing after a nullable character'); + } + if ($parserContext === self::PARSER_IN_ARRAY_EXPRESSION) { + throw new RuntimeException('A type is missing in an array expression'); + } + if ($parserContext === self::PARSER_IN_COLLECTION_EXPRESSION) { + throw new RuntimeException('A type is missing in a collection expression'); } - \xdebug_set_filter(\XDEBUG_FILTER_CODE_COVERAGE, $listType, $filter->files()); + } elseif (count($types) === 1) { + return $types[0]; } - $this->pathCoverageIsMixedCoverage = \version_compare(\phpversion('xdebug'), '2.9.6', '<'); + if ($compoundToken === '|') { + return new Compound(array_values($types)); + } + return new Intersection(array_values($types)); } - public function canCollectBranchAndPathCoverage() : bool + /** + * resolve the given type into a type object + * + * @param string $type the type string, representing a single type + * + * @return Type|Array_|Object_ + * + * @psalm-mutation-free + */ + private function resolveSingleType(string $type, Context $context) : object { - return \true; + switch (\true) { + case $this->isKeyword($type): + return $this->resolveKeyword($type); + case $this->isFqsen($type): + return $this->resolveTypedObject($type); + case $this->isPartialStructuralElementName($type): + return $this->resolveTypedObject($type, $context); + // @codeCoverageIgnoreStart + default: + // I haven't got the foggiest how the logic would come here but added this as a defense. + throw new RuntimeException('Unable to resolve type "' . $type . '", there is no known method to resolve it'); + } + // @codeCoverageIgnoreEnd } - public function canDetectDeadCode() : bool + /** + * Adds a keyword to the list of Keywords and associates it with a specific Value Object. + * + * @psalm-param class-string $typeClassName + */ + public function addKeyword(string $keyword, string $typeClassName) : void { - return \true; + if (!class_exists($typeClassName)) { + throw new InvalidArgumentException('The Value Object that needs to be created with a keyword "' . $keyword . '" must be an existing class' . ' but we could not find the class ' . $typeClassName); + } + $interfaces = class_implements($typeClassName); + if ($interfaces === \false) { + throw new InvalidArgumentException('The Value Object that needs to be created with a keyword "' . $keyword . '" must be an existing class' . ' but we could not find the class ' . $typeClassName); + } + if (!in_array(Type::class, $interfaces, \true)) { + throw new InvalidArgumentException('The class "' . $typeClassName . '" must implement the interface "phpDocumentor\\Reflection\\Type"'); + } + $this->keywords[$keyword] = $typeClassName; } - public function start() : void + /** + * Detects whether the given type represents a PHPDoc keyword. + * + * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. + * + * @psalm-mutation-free + */ + private function isKeyword(string $type) : bool + { + return array_key_exists(strtolower($type), $this->keywords); + } + /** + * Detects whether the given type represents a relative structural element name. + * + * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. + * + * @psalm-mutation-free + */ + private function isPartialStructuralElementName(string $type) : bool + { + return isset($type[0]) && $type[0] !== self::OPERATOR_NAMESPACE && !$this->isKeyword($type); + } + /** + * Tests whether the given type is a Fully Qualified Structural Element Name. + * + * @psalm-mutation-free + */ + private function isFqsen(string $type) : bool { - $flags = \XDEBUG_CC_UNUSED; - if ($this->detectsDeadCode() || $this->collectsBranchAndPathCoverage()) { - $flags |= \XDEBUG_CC_DEAD_CODE; - } - if ($this->collectsBranchAndPathCoverage()) { - $flags |= \XDEBUG_CC_BRANCH_CHECK; - } - \xdebug_start_code_coverage($flags); + return strpos($type, self::OPERATOR_NAMESPACE) === 0; } - public function stop() : \PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData + /** + * Resolves the given keyword (such as `string`) into a Type object representing that keyword. + * + * @psalm-mutation-free + */ + private function resolveKeyword(string $type) : Type { - $data = \xdebug_get_code_coverage(); - \xdebug_stop_code_coverage(); - if ($this->collectsBranchAndPathCoverage()) { - if ($this->pathCoverageIsMixedCoverage) { - return \PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData::fromXdebugWithMixedCoverage($data); - } - return \PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData::fromXdebugWithPathCoverage($data); - } - return \PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData::fromXdebugWithoutPathCoverage($data); + $className = $this->keywords[strtolower($type)]; + return new $className(); } - public function nameAndVersion() : string + /** + * Resolves the given FQSEN string into an FQSEN object. + * + * @psalm-mutation-free + */ + private function resolveTypedObject(string $type, ?Context $context = null) : Object_ { - return 'Xdebug ' . \phpversion('xdebug'); + return new Object_($this->fqsenResolver->resolve($type, $context)); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; - -use function phpversion; -use function version_compare; -use PHPUnit\SebastianBergmann\CodeCoverage\Filter; -use PHPUnit\SebastianBergmann\CodeCoverage\NoCodeCoverageDriverAvailableException; -use PHPUnit\SebastianBergmann\CodeCoverage\NoCodeCoverageDriverWithPathCoverageSupportAvailableException; -use PHPUnit\SebastianBergmann\Environment\Runtime; -final class Selector -{ /** - * @throws NoCodeCoverageDriverAvailableException - * @throws PcovNotAvailableException - * @throws PhpdbgNotAvailableException - * @throws XdebugNotAvailableException - * @throws Xdebug2NotEnabledException - * @throws Xdebug3NotEnabledException + * Resolves class string + * + * @param ArrayIterator $tokens */ - public function forLineCoverage(\PHPUnit\SebastianBergmann\CodeCoverage\Filter $filter) : \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Driver + private function resolveClassString(ArrayIterator $tokens, Context $context) : Type { - $runtime = new \PHPUnit\SebastianBergmann\Environment\Runtime(); - if ($runtime->hasPHPDBGCodeCoverage()) { - return new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\PhpdbgDriver(); - } - if ($runtime->hasPCOV()) { - return new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\PcovDriver($filter); + $tokens->next(); + $classType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION); + if (!$classType instanceof Object_ || $classType->getFqsen() === null) { + throw new RuntimeException($classType . ' is not a class string'); } - if ($runtime->hasXdebug()) { - if (\version_compare(\phpversion('xdebug'), '3', '>=')) { - $driver = new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Xdebug3Driver($filter); - } else { - $driver = new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Xdebug2Driver($filter); + $token = $tokens->current(); + if ($token !== '>') { + if (empty($token)) { + throw new RuntimeException('class-string: ">" is missing'); } - $driver->enableDeadCodeDetection(); - return $driver; + throw new RuntimeException('Unexpected character "' . $token . '", ">" is missing'); } - throw new \PHPUnit\SebastianBergmann\CodeCoverage\NoCodeCoverageDriverAvailableException(); + return new ClassString($classType->getFqsen()); } /** - * @throws NoCodeCoverageDriverWithPathCoverageSupportAvailableException - * @throws XdebugNotAvailableException - * @throws Xdebug2NotEnabledException - * @throws Xdebug3NotEnabledException + * Resolves class string + * + * @param ArrayIterator $tokens */ - public function forLineAndPathCoverage(\PHPUnit\SebastianBergmann\CodeCoverage\Filter $filter) : \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Driver + private function resolveInterfaceString(ArrayIterator $tokens, Context $context) : Type { - if ((new \PHPUnit\SebastianBergmann\Environment\Runtime())->hasXdebug()) { - if (\version_compare(\phpversion('xdebug'), '3', '>=')) { - $driver = new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Xdebug3Driver($filter); - } else { - $driver = new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Xdebug2Driver($filter); + $tokens->next(); + $classType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION); + if (!$classType instanceof Object_ || $classType->getFqsen() === null) { + throw new RuntimeException($classType . ' is not a interface string'); + } + $token = $tokens->current(); + if ($token !== '>') { + if (empty($token)) { + throw new RuntimeException('interface-string: ">" is missing'); } - $driver->enableDeadCodeDetection(); - $driver->enableBranchAndPathCoverage(); - return $driver; + throw new RuntimeException('Unexpected character "' . $token . '", ">" is missing'); } - throw new \PHPUnit\SebastianBergmann\CodeCoverage\NoCodeCoverageDriverWithPathCoverageSupportAvailableException(); + return new InterfaceString($classType->getFqsen()); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; - -use const PHP_SAPI; -use const PHP_VERSION; -use function array_diff; -use function array_keys; -use function array_merge; -use function get_included_files; -use function phpdbg_end_oplog; -use function phpdbg_get_executable; -use function phpdbg_start_oplog; -use PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class PhpdbgDriver extends \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Driver -{ /** - * @throws PhpdbgNotAvailableException + * Resolves the collection values and keys + * + * @param ArrayIterator $tokens + * + * @return Array_|Iterable_|Collection */ - public function __construct() + private function resolveCollection(ArrayIterator $tokens, Type $classType, Context $context) : Type { - if (\PHP_SAPI !== 'phpdbg') { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\PhpdbgNotAvailableException(); + $isArray = (string) $classType === 'array'; + $isIterable = (string) $classType === 'iterable'; + // allow only "array", "iterable" or class name before "<" + if (!$isArray && !$isIterable && (!$classType instanceof Object_ || $classType->getFqsen() === null)) { + throw new RuntimeException($classType . ' is not a collection'); } - } - public function start() : void - { - \phpdbg_start_oplog(); - } - public function stop() : \PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData - { - static $fetchedLines = []; - $dbgData = \phpdbg_end_oplog(); - if ($fetchedLines === []) { - $sourceLines = \phpdbg_get_executable(); - } else { - $newFiles = \array_diff(\get_included_files(), \array_keys($fetchedLines)); - $sourceLines = []; - if ($newFiles) { - $sourceLines = \phpdbg_get_executable(['files' => $newFiles]); + $tokens->next(); + $valueType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION); + $keyType = null; + $token = $tokens->current(); + if ($token !== null && trim($token) === ',') { + // if we have a comma, then we just parsed the key type, not the value type + $keyType = $valueType; + if ($isArray) { + // check the key type for an "array" collection. We allow only + // strings or integers. + if (!$keyType instanceof String_ && !$keyType instanceof Integer && !$keyType instanceof Compound) { + throw new RuntimeException('An array can have only integers or strings as keys'); + } + if ($keyType instanceof Compound) { + foreach ($keyType->getIterator() as $item) { + if (!$item instanceof String_ && !$item instanceof Integer) { + throw new RuntimeException('An array can have only integers or strings as keys'); + } + } + } } + $tokens->next(); + // now let's parse the value type + $valueType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION); } - foreach ($sourceLines as $file => $lines) { - foreach ($lines as $lineNo => $numExecuted) { - $sourceLines[$file][$lineNo] = self::LINE_NOT_EXECUTED; + $token = $tokens->current(); + if ($token !== '>') { + if (empty($token)) { + throw new RuntimeException('Collection: ">" is missing'); } + throw new RuntimeException('Unexpected character "' . $token . '", ">" is missing'); } - $fetchedLines = \array_merge($fetchedLines, $sourceLines); - return \PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData::fromXdebugWithoutPathCoverage($this->detectExecutedLines($fetchedLines, $dbgData)); - } - public function nameAndVersion() : string - { - return 'PHPDBG ' . \PHP_VERSION; + if ($isArray) { + return new Array_($valueType, $keyType); + } + if ($isIterable) { + return new Iterable_($valueType, $keyType); + } + if ($classType instanceof Object_) { + return $this->makeCollectionFromObject($classType, $valueType, $keyType); + } + throw new RuntimeException('Invalid $classType provided'); } - private function detectExecutedLines(array $sourceLines, array $dbgData) : array + /** + * @psalm-pure + */ + private function makeCollectionFromObject(Object_ $object, Type $valueType, ?Type $keyType = null) : Collection { - foreach ($dbgData as $file => $coveredLines) { - foreach ($coveredLines as $lineNo => $numExecuted) { - // phpdbg also reports $lineNo=0 when e.g. exceptions get thrown. - // make sure we only mark lines executed which are actually executable. - if (isset($sourceLines[$file][$lineNo])) { - $sourceLines[$file][$lineNo] = self::LINE_EXECUTED; - } - } - } - return $sourceLines; + return new Collection($object->getFqsen(), $valueType, $keyType); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; +namespace PHPUnit\SebastianBergmann\Environment; -use function sprintf; -use PHPUnit\SebastianBergmann\CodeCoverage\BranchAndPathCoverageNotSupportedException; -use PHPUnit\SebastianBergmann\CodeCoverage\DeadCodeDetectionNotSupportedException; -use PHPUnit\SebastianBergmann\CodeCoverage\Filter; -use PHPUnit\SebastianBergmann\CodeCoverage\NoCodeCoverageDriverAvailableException; -use PHPUnit\SebastianBergmann\CodeCoverage\NoCodeCoverageDriverWithPathCoverageSupportAvailableException; -use PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -abstract class Driver +use const DIRECTORY_SEPARATOR; +use const STDIN; +use const STDOUT; +use function defined; +use function fclose; +use function fstat; +use function function_exists; +use function getenv; +use function is_resource; +use function is_string; +use function posix_isatty; +use function preg_match; +use function proc_close; +use function proc_open; +use function sapi_windows_vt100_support; +use function shell_exec; +use function stream_get_contents; +use function stream_isatty; +use function trim; +final class Console { /** * @var int - * - * @see http://xdebug.org/docs/code_coverage - */ - public const LINE_NOT_EXECUTABLE = -2; - /** - * @var int - * - * @see http://xdebug.org/docs/code_coverage - */ - public const LINE_NOT_EXECUTED = -1; - /** - * @var int - * - * @see http://xdebug.org/docs/code_coverage */ - public const LINE_EXECUTED = 1; + public const STDIN = 0; /** * @var int - * - * @see http://xdebug.org/docs/code_coverage */ - public const BRANCH_NOT_HIT = 0; + public const STDOUT = 1; /** * @var int - * - * @see http://xdebug.org/docs/code_coverage */ - public const BRANCH_HIT = 1; - /** - * @var bool - */ - private $collectBranchAndPathCoverage = \false; - /** - * @var bool - */ - private $detectDeadCode = \false; + public const STDERR = 2; /** - * @throws NoCodeCoverageDriverAvailableException - * @throws PcovNotAvailableException - * @throws PhpdbgNotAvailableException - * @throws XdebugNotAvailableException - * @throws Xdebug2NotEnabledException - * @throws Xdebug3NotEnabledException + * Returns true if STDOUT supports colorization. * - * @deprecated Use DriverSelector::forLineCoverage() instead + * This code has been copied and adapted from + * Symfony\Component\Console\Output\StreamOutput. */ - public static function forLineCoverage(\PHPUnit\SebastianBergmann\CodeCoverage\Filter $filter) : self + public function hasColorSupport() : bool { - return (new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Selector())->forLineCoverage($filter); + if ('Hyper' === getenv('TERM_PROGRAM')) { + return \true; + } + if ($this->isWindows()) { + // @codeCoverageIgnoreStart + return defined('STDOUT') && function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(\STDOUT) || \false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); + // @codeCoverageIgnoreEnd + } + if (!defined('STDOUT')) { + // @codeCoverageIgnoreStart + return \false; + // @codeCoverageIgnoreEnd + } + return $this->isInteractive(\STDOUT); } /** - * @throws NoCodeCoverageDriverWithPathCoverageSupportAvailableException - * @throws XdebugNotAvailableException - * @throws Xdebug2NotEnabledException - * @throws Xdebug3NotEnabledException + * Returns the number of columns of the terminal. * - * @deprecated Use DriverSelector::forLineAndPathCoverage() instead + * @codeCoverageIgnore */ - public static function forLineAndPathCoverage(\PHPUnit\SebastianBergmann\CodeCoverage\Filter $filter) : self - { - return (new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Selector())->forLineAndPathCoverage($filter); - } - public function canCollectBranchAndPathCoverage() : bool - { - return \false; - } - public function collectsBranchAndPathCoverage() : bool + public function getNumberOfColumns() : int { - return $this->collectBranchAndPathCoverage; + if (!$this->isInteractive(defined('STDIN') ? \STDIN : self::STDIN)) { + return 80; + } + if ($this->isWindows()) { + return $this->getNumberOfColumnsWindows(); + } + return $this->getNumberOfColumnsInteractive(); } /** - * @throws BranchAndPathCoverageNotSupportedException + * Returns if the file descriptor is an interactive terminal or not. + * + * Normally, we want to use a resource as a parameter, yet sadly it's not always awailable, + * eg when running code in interactive console (`php -a`), STDIN/STDOUT/STDERR constants are not defined. + * + * @param int|resource $fileDescriptor */ - public function enableBranchAndPathCoverage() : void + public function isInteractive($fileDescriptor = self::STDOUT) : bool { - if (!$this->canCollectBranchAndPathCoverage()) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\BranchAndPathCoverageNotSupportedException(\sprintf('%s does not support branch and path coverage', $this->nameAndVersion())); + if (is_resource($fileDescriptor)) { + // These functions require a descriptor that is a real resource, not a numeric ID of it + if (function_exists('stream_isatty') && @stream_isatty($fileDescriptor)) { + return \true; + } + // Check if formatted mode is S_IFCHR + if (function_exists('fstat') && @stream_isatty($fileDescriptor)) { + $stat = @fstat(\STDOUT); + return $stat ? 020000 === ($stat['mode'] & 0170000) : \false; + } + return \false; } - $this->collectBranchAndPathCoverage = \true; - } - public function disableBranchAndPathCoverage() : void - { - $this->collectBranchAndPathCoverage = \false; - } - public function canDetectDeadCode() : bool - { - return \false; + return function_exists('posix_isatty') && @posix_isatty($fileDescriptor); } - public function detectsDeadCode() : bool + private function isWindows() : bool { - return $this->detectDeadCode; + return \DIRECTORY_SEPARATOR === '\\'; } /** - * @throws DeadCodeDetectionNotSupportedException + * @codeCoverageIgnore */ - public function enableDeadCodeDetection() : void + private function getNumberOfColumnsInteractive() : int { - if (!$this->canDetectDeadCode()) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\DeadCodeDetectionNotSupportedException(\sprintf('%s does not support dead code detection', $this->nameAndVersion())); + if (function_exists('shell_exec') && preg_match('#\\d+ (\\d+)#', shell_exec('stty size') ?: '', $match) === 1) { + if ((int) $match[1] > 0) { + return (int) $match[1]; + } } - $this->detectDeadCode = \true; - } - public function disableDeadCodeDetection() : void - { - $this->detectDeadCode = \false; + if (function_exists('shell_exec') && preg_match('#columns = (\\d+);#', shell_exec('stty') ?: '', $match) === 1) { + if ((int) $match[1] > 0) { + return (int) $match[1]; + } + } + return 80; } - public abstract function nameAndVersion() : string; - public abstract function start() : void; - public abstract function stop() : \PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; - -use function extension_loaded; -use function phpversion; -use PHPUnit\SebastianBergmann\CodeCoverage\Filter; -use PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class PcovDriver extends \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Driver -{ - /** - * @var Filter - */ - private $filter; /** - * @throws PcovNotAvailableException + * @codeCoverageIgnore */ - public function __construct(\PHPUnit\SebastianBergmann\CodeCoverage\Filter $filter) + private function getNumberOfColumnsWindows() : int { - if (!\extension_loaded('pcov')) { - throw new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\PcovNotAvailableException(); + $ansicon = getenv('ANSICON'); + $columns = 80; + if (is_string($ansicon) && preg_match('/^(\\d+)x\\d+ \\(\\d+x(\\d+)\\)$/', trim($ansicon), $matches)) { + $columns = (int) $matches[1]; + } elseif (function_exists('proc_open')) { + $process = proc_open('mode CON', [1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes, null, null, ['suppress_errors' => \true]); + if (is_resource($process)) { + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + if (preg_match('/--------+\\r?\\n.+?(\\d+)\\r?\\n.+?(\\d+)\\r?\\n/', $info, $matches)) { + $columns = (int) $matches[2]; + } + } } - $this->filter = $filter; - } - public function start() : void - { - \pcov\start(); - } - public function stop() : \PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData - { - \pcov\stop(); - $collect = \pcov\collect(\pcov\inclusive, !$this->filter->isEmpty() ? $this->filter->files() : \pcov\waiting()); - \pcov\clear(); - return \PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData::fromXdebugWithoutPathCoverage($collect); - } - public function nameAndVersion() : string - { - return 'PCOV ' . \phpversion('pcov'); + return $columns - 1; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; +namespace PHPUnit\SebastianBergmann\Environment; -use function sprintf; -/** - * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage - */ -final class CrapIndex +use const DIRECTORY_SEPARATOR; +use const PHP_OS; +use const PHP_OS_FAMILY; +use function defined; +final class OperatingSystem { /** - * @var int - */ - private $cyclomaticComplexity; - /** - * @var float + * Returns PHP_OS_FAMILY (if defined (which it is on PHP >= 7.2)). + * Returns a string (compatible with PHP_OS_FAMILY) derived from PHP_OS otherwise. */ - private $codeCoverage; - public static function fromCyclomaticComplexityAndCoveragePercentage(int $cyclomaticComplexity, float $codeCoverage) : self - { - return new self($cyclomaticComplexity, $codeCoverage); - } - public function __construct(int $cyclomaticComplexity, float $codeCoverage) - { - $this->cyclomaticComplexity = $cyclomaticComplexity; - $this->codeCoverage = $codeCoverage; - } - public function asString() : string + public function getFamily() : string { - if ($this->codeCoverage === 0.0) { - return (string) ($this->cyclomaticComplexity ** 2 + $this->cyclomaticComplexity); + if (defined('PHP_OS_FAMILY')) { + return \PHP_OS_FAMILY; } - if ($this->codeCoverage >= 95) { - return (string) $this->cyclomaticComplexity; + if (\DIRECTORY_SEPARATOR === '\\') { + return 'Windows'; + } + switch (\PHP_OS) { + case 'Darwin': + return 'Darwin'; + case 'DragonFly': + case 'FreeBSD': + case 'NetBSD': + case 'OpenBSD': + return 'BSD'; + case 'Linux': + return 'Linux'; + case 'SunOS': + return 'Solaris'; + default: + return 'Unknown'; } - return \sprintf('%01.2F', $this->cyclomaticComplexity ** 2 * (1 - $this->codeCoverage / 100) ** 3 + $this->cyclomaticComplexity); } } -php-code-coverage - -Copyright (c) 2009-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeCoverage; +namespace PHPUnit\SebastianBergmann\Environment; -use function array_keys; -use function is_file; -use function realpath; +use const PHP_BINARY; +use const PHP_BINDIR; +use const PHP_MAJOR_VERSION; +use const PHP_SAPI; +use const PHP_VERSION; +use function array_map; +use function array_merge; +use function defined; +use function escapeshellarg; +use function explode; +use function extension_loaded; +use function getenv; +use function ini_get; +use function is_readable; +use function parse_ini_file; +use function php_ini_loaded_file; +use function php_ini_scanned_files; +use function phpversion; +use function sprintf; use function strpos; -use PHPUnit\SebastianBergmann\FileIterator\Facade as FileIteratorFacade; -final class Filter +/** + * Utility class for HHVM/PHP environment handling. + */ +final class Runtime { /** - * @psalm-var array + * @var string */ - private $files = []; + private static $binary; /** - * @psalm-var array + * Returns true when Xdebug or PCOV is available or + * the runtime used is PHPDBG. */ - private $isFileCache = []; - public function includeDirectory(string $directory, string $suffix = '.php', string $prefix = '') : void + public function canCollectCodeCoverage() : bool { - foreach ((new \PHPUnit\SebastianBergmann\FileIterator\Facade())->getFilesAsArray($directory, $suffix, $prefix) as $file) { - $this->includeFile($file); - } + return $this->hasXdebug() || $this->hasPCOV() || $this->hasPHPDBGCodeCoverage(); } /** - * @psalm-param list $files + * Returns true when Zend OPcache is loaded, enabled, + * and is configured to discard comments. */ - public function includeFiles(array $filenames) : void + public function discardsComments() : bool { - foreach ($filenames as $filename) { - $this->includeFile($filename); + if (!$this->isOpcacheActive()) { + return \false; + } + if (ini_get('opcache.save_comments') !== '0') { + return \false; } + return \true; } - public function includeFile(string $filename) : void + /** + * Returns true when Zend OPcache is loaded, enabled, + * and is configured to perform just-in-time compilation. + */ + public function performsJustInTimeCompilation() : bool { - $filename = \realpath($filename); - if (!$filename) { - return; + if (\PHP_MAJOR_VERSION < 8) { + return \false; } - $this->files[$filename] = \true; + if (!$this->isOpcacheActive()) { + return \false; + } + if (strpos(ini_get('opcache.jit'), '0') === 0) { + return \false; + } + return \true; } - public function excludeDirectory(string $directory, string $suffix = '.php', string $prefix = '') : void + /** + * Returns the path to the binary of the current runtime. + * Appends ' --php' to the path when the runtime is HHVM. + */ + public function getBinary() : string { - foreach ((new \PHPUnit\SebastianBergmann\FileIterator\Facade())->getFilesAsArray($directory, $suffix, $prefix) as $file) { - $this->excludeFile($file); + // HHVM + if (self::$binary === null && $this->isHHVM()) { + // @codeCoverageIgnoreStart + if ((self::$binary = getenv('PHP_BINARY')) === \false) { + self::$binary = \PHP_BINARY; + } + self::$binary = escapeshellarg(self::$binary) . ' --php' . ' -d hhvm.php7.all=1'; + // @codeCoverageIgnoreEnd + } + if (self::$binary === null && \PHP_BINARY !== '') { + self::$binary = escapeshellarg(\PHP_BINARY); + } + if (self::$binary === null) { + // @codeCoverageIgnoreStart + $possibleBinaryLocations = [\PHP_BINDIR . '/php', \PHP_BINDIR . '/php-cli.exe', \PHP_BINDIR . '/php.exe']; + foreach ($possibleBinaryLocations as $binary) { + if (is_readable($binary)) { + self::$binary = escapeshellarg($binary); + break; + } + } + // @codeCoverageIgnoreEnd + } + if (self::$binary === null) { + // @codeCoverageIgnoreStart + self::$binary = 'php'; + // @codeCoverageIgnoreEnd } + return self::$binary; } - public function excludeFile(string $filename) : void + public function getNameWithVersion() : string { - $filename = \realpath($filename); - if (!$filename || !isset($this->files[$filename])) { - return; + return $this->getName() . ' ' . $this->getVersion(); + } + public function getNameWithVersionAndCodeCoverageDriver() : string + { + if (!$this->canCollectCodeCoverage() || $this->hasPHPDBGCodeCoverage()) { + return $this->getNameWithVersion(); + } + if ($this->hasPCOV()) { + return sprintf('%s with PCOV %s', $this->getNameWithVersion(), phpversion('pcov')); + } + if ($this->hasXdebug()) { + return sprintf('%s with Xdebug %s', $this->getNameWithVersion(), phpversion('xdebug')); } - unset($this->files[$filename]); } - public function isFile(string $filename) : bool + public function getName() : string { - if (isset($this->isFileCache[$filename])) { - return $this->isFileCache[$filename]; + if ($this->isHHVM()) { + // @codeCoverageIgnoreStart + return 'HHVM'; + // @codeCoverageIgnoreEnd } - if ($filename === '-' || \strpos($filename, 'vfs://') === 0 || \strpos($filename, 'xdebug://debug-eval') !== \false || \strpos($filename, 'eval()\'d code') !== \false || \strpos($filename, 'runtime-created function') !== \false || \strpos($filename, 'runkit created function') !== \false || \strpos($filename, 'assert code') !== \false || \strpos($filename, 'regexp code') !== \false || \strpos($filename, 'Standard input code') !== \false) { - $isFile = \false; - } else { - $isFile = \is_file($filename); + if ($this->isPHPDBG()) { + // @codeCoverageIgnoreStart + return 'PHPDBG'; + // @codeCoverageIgnoreEnd } - $this->isFileCache[$filename] = $isFile; - return $isFile; + return 'PHP'; } - public function isExcluded(string $filename) : bool + public function getVendorUrl() : string { - if (!$this->isFile($filename)) { - return \true; + if ($this->isHHVM()) { + // @codeCoverageIgnoreStart + return 'http://hhvm.com/'; + // @codeCoverageIgnoreEnd } - return !isset($this->files[$filename]); + return 'https://secure.php.net/'; + } + public function getVersion() : string + { + if ($this->isHHVM()) { + // @codeCoverageIgnoreStart + return HHVM_VERSION; + // @codeCoverageIgnoreEnd + } + return \PHP_VERSION; } /** - * @psalm-return list + * Returns true when the runtime used is PHP and Xdebug is loaded. */ - public function files() : array + public function hasXdebug() : bool { - return \array_keys($this->files); + return ($this->isPHP() || $this->isHHVM()) && extension_loaded('xdebug'); } - public function isEmpty() : bool + /** + * Returns true when the runtime used is HHVM. + */ + public function isHHVM() : bool { - return empty($this->files); + return defined('HHVM_VERSION'); } -} - - - - - This Schema file defines the rules by which the XML configuration file of PHPUnit 9.4 may be structured. - - - - - - Root Element - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The main type specifying the document structure - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + /** + * Returns true when the runtime used is PHP without the PHPDBG SAPI. + */ + public function isPHP() : bool + { + return !$this->isHHVM() && !$this->isPHPDBG(); + } + /** + * Returns true when the runtime used is PHP with the PHPDBG SAPI. + */ + public function isPHPDBG() : bool + { + return \PHP_SAPI === 'phpdbg' && !$this->isHHVM(); + } + /** + * Returns true when the runtime used is PHP with the PHPDBG SAPI + * and the phpdbg_*_oplog() functions are available (PHP >= 7.0). + */ + public function hasPHPDBGCodeCoverage() : bool + { + return $this->isPHPDBG(); + } + /** + * Returns true when the runtime used is PHP with PCOV loaded and enabled. + */ + public function hasPCOV() : bool + { + return $this->isPHP() && extension_loaded('pcov') && ini_get('pcov.enabled'); + } + /** + * Parses the loaded php.ini file (if any) as well as all + * additional php.ini files from the additional ini dir for + * a list of all configuration settings loaded from files + * at startup. Then checks for each php.ini setting passed + * via the `$values` parameter whether this setting has + * been changed at runtime. Returns an array of strings + * where each string has the format `key=value` denoting + * the name of a changed php.ini setting with its new value. + * + * @return string[] + */ + public function getCurrentSettings(array $values) : array + { + $diff = []; + $files = []; + if ($file = php_ini_loaded_file()) { + $files[] = $file; + } + if ($scanned = php_ini_scanned_files()) { + $files = array_merge($files, array_map('trim', explode(",\n", $scanned))); + } + foreach ($files as $ini) { + $config = parse_ini_file($ini, \true); + foreach ($values as $value) { + $set = ini_get($value); + if (isset($config[$value]) && $set != $config[$value]) { + $diff[] = sprintf('%s=%s', $value, $set); + } + } + } + return $diff; + } + private function isOpcacheActive() : bool + { + if (!extension_loaded('Zend OPcache')) { + return \false; + } + if ((\PHP_SAPI === 'cli' || \PHP_SAPI === 'phpdbg') && ini_get('opcache.enable_cli') === '1') { + return \true; + } + if (\PHP_SAPI !== 'cli' && \PHP_SAPI !== 'phpdbg' && ini_get('opcache.enable') === '1') { + return \true; + } + return \false; + } +} +sebastian/environment + +Copyright (c) 2014-2020, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. , Sebastian Heuer , Sebastian Bergmann + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnit\SebastianBergmann\Comparator; -class ExtensionElement extends \PHPUnit\PharIo\Manifest\ManifestElement +use RuntimeException; +use PHPUnit\SebastianBergmann\Diff\Differ; +use PHPUnit\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; +/** + * Thrown when an assertion for string equality failed. + */ +class ComparisonFailure extends RuntimeException { - public function getFor() : string + /** + * Expected value of the retrieval which does not match $actual. + * + * @var mixed + */ + protected $expected; + /** + * Actually retrieved value which does not match $expected. + * + * @var mixed + */ + protected $actual; + /** + * The string representation of the expected value. + * + * @var string + */ + protected $expectedAsString; + /** + * The string representation of the actual value. + * + * @var string + */ + protected $actualAsString; + /** + * @var bool + */ + protected $identical; + /** + * Optional message which is placed in front of the first line + * returned by toString(). + * + * @var string + */ + protected $message; + /** + * Initialises with the expected value and the actual value. + * + * @param mixed $expected expected value retrieved + * @param mixed $actual actual value retrieved + * @param string $expectedAsString + * @param string $actualAsString + * @param bool $identical + * @param string $message a string which is prefixed on all returned lines + * in the difference output + */ + public function __construct($expected, $actual, $expectedAsString, $actualAsString, $identical = \false, $message = '') { - return $this->getAttributeValue('for'); + $this->expected = $expected; + $this->actual = $actual; + $this->expectedAsString = $expectedAsString; + $this->actualAsString = $actualAsString; + $this->message = $message; } - public function getCompatible() : string + public function getActual() { - return $this->getAttributeValue('compatible'); + return $this->actual; + } + public function getExpected() + { + return $this->expected; + } + /** + * @return string + */ + public function getActualAsString() + { + return $this->actualAsString; + } + /** + * @return string + */ + public function getExpectedAsString() + { + return $this->expectedAsString; + } + /** + * @return string + */ + public function getDiff() + { + if (!$this->actualAsString && !$this->expectedAsString) { + return ''; + } + $differ = new Differ(new UnifiedDiffOutputBuilder("\n--- Expected\n+++ Actual\n")); + return $differ->diff($this->expectedAsString, $this->actualAsString); + } + /** + * @return string + */ + public function toString() + { + return $this->message . $this->getDiff(); } } , Sebastian Heuer , Sebastian Bergmann + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnit\SebastianBergmann\Comparator; -class ExtElementCollection extends \PHPUnit\PharIo\Manifest\ElementCollection +use function is_resource; +/** + * Compares resources for equality. + */ +class ResourceComparator extends Comparator { - public function current() : \PHPUnit\PharIo\Manifest\ExtElement + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) + { + return is_resource($expected) && is_resource($actual); + } + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false) { - return new \PHPUnit\PharIo\Manifest\ExtElement($this->getCurrentElement()); + if ($actual != $expected) { + throw new ComparisonFailure($expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual)); + } } } , Sebastian Heuer , Sebastian Bergmann + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnit\SebastianBergmann\Comparator; -class PhpElement extends \PHPUnit\PharIo\Manifest\ManifestElement +use function is_float; +use function is_numeric; +/** + * Compares doubles for equality. + */ +class DoubleComparator extends NumericComparator { - public function getVersion() : string - { - return $this->getAttributeValue('version'); - } - public function hasExtElements() : bool + /** + * Smallest value available in PHP. + * + * @var float + */ + public const EPSILON = 1.0E-10; + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) { - return $this->hasChild('ext'); + return (is_float($expected) || is_float($actual)) && is_numeric($expected) && is_numeric($actual); } - public function getExtElements() : \PHPUnit\PharIo\Manifest\ExtElementCollection + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false) { - return new \PHPUnit\PharIo\Manifest\ExtElementCollection($this->getChildrenByName('ext')); + if ($delta == 0) { + $delta = self::EPSILON; + } + parent::assertEquals($expected, $actual, $delta, $canonicalize, $ignoreCase); } } , Sebastian Heuer , Sebastian Bergmann + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnit\SebastianBergmann\Comparator; -use DOMDocument; -use DOMElement; -class ManifestDocument +use function abs; +use function is_float; +use function is_infinite; +use function is_nan; +use function is_numeric; +use function is_string; +use function sprintf; +/** + * Compares numerical values for equality. + */ +class NumericComparator extends ScalarComparator { - public const XMLNS = 'https://phar.io/xml/manifest/1.0'; - /** @var DOMDocument */ - private $dom; - public static function fromFile(string $filename) : \PHPUnit\PharIo\Manifest\ManifestDocument + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) { - if (!\file_exists($filename)) { - throw new \PHPUnit\PharIo\Manifest\ManifestDocumentException(\sprintf('File "%s" not found', $filename)); - } - return self::fromString(\file_get_contents($filename)); + // all numerical values, but not if one of them is a double + // or both of them are strings + return is_numeric($expected) && is_numeric($actual) && !(is_float($expected) || is_float($actual)) && !(is_string($expected) && is_string($actual)); } - public static function fromString(string $xmlString) : \PHPUnit\PharIo\Manifest\ManifestDocument + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false) { - $prev = \libxml_use_internal_errors(\true); - \libxml_clear_errors(); - $dom = new \DOMDocument(); - $dom->loadXML($xmlString); - $errors = \libxml_get_errors(); - \libxml_use_internal_errors($prev); - if (\count($errors) !== 0) { - throw new \PHPUnit\PharIo\Manifest\ManifestDocumentLoadingException($errors); + if ($this->isInfinite($actual) && $this->isInfinite($expected)) { + return; } - return new self($dom); - } - private function __construct(\DOMDocument $dom) - { - $this->ensureCorrectDocumentType($dom); - $this->dom = $dom; - } - public function getContainsElement() : \PHPUnit\PharIo\Manifest\ContainsElement - { - return new \PHPUnit\PharIo\Manifest\ContainsElement($this->fetchElementByName('contains')); - } - public function getCopyrightElement() : \PHPUnit\PharIo\Manifest\CopyrightElement - { - return new \PHPUnit\PharIo\Manifest\CopyrightElement($this->fetchElementByName('copyright')); - } - public function getRequiresElement() : \PHPUnit\PharIo\Manifest\RequiresElement - { - return new \PHPUnit\PharIo\Manifest\RequiresElement($this->fetchElementByName('requires')); - } - public function hasBundlesElement() : bool - { - return $this->dom->getElementsByTagNameNS(self::XMLNS, 'bundles')->length === 1; - } - public function getBundlesElement() : \PHPUnit\PharIo\Manifest\BundlesElement - { - return new \PHPUnit\PharIo\Manifest\BundlesElement($this->fetchElementByName('bundles')); - } - private function ensureCorrectDocumentType(\DOMDocument $dom) : void - { - $root = $dom->documentElement; - if ($root->localName !== 'phar' || $root->namespaceURI !== self::XMLNS) { - throw new \PHPUnit\PharIo\Manifest\ManifestDocumentException('Not a phar.io manifest document'); + if (($this->isInfinite($actual) xor $this->isInfinite($expected)) || ($this->isNan($actual) || $this->isNan($expected)) || abs($actual - $expected) > $delta) { + throw new ComparisonFailure($expected, $actual, '', '', \false, sprintf('Failed asserting that %s matches expected %s.', $this->exporter->export($actual), $this->exporter->export($expected))); } } - private function fetchElementByName(string $elementName) : \DOMElement + private function isInfinite($value) : bool { - $element = $this->dom->getElementsByTagNameNS(self::XMLNS, $elementName)->item(0); - if (!$element instanceof \DOMElement) { - throw new \PHPUnit\PharIo\Manifest\ManifestDocumentException(\sprintf('Element %s missing', $elementName)); - } - return $element; + return is_float($value) && is_infinite($value); } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\PharIo\Manifest; - -class RequiresElement extends \PHPUnit\PharIo\Manifest\ManifestElement -{ - public function getPHPElement() : \PHPUnit\PharIo\Manifest\PhpElement + private function isNan($value) : bool { - return new \PHPUnit\PharIo\Manifest\PhpElement($this->getChildByName('php')); + return is_float($value) && is_nan($value); } } , Sebastian Heuer , Sebastian Bergmann + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnit\SebastianBergmann\Comparator; -use DOMElement; -use DOMNodeList; -abstract class ElementCollection implements \Iterator +use function array_unshift; +/** + * Factory for comparators which compare values for equality. + */ +class Factory { - /** @var DOMElement[] */ - private $nodes = []; - /** @var int */ - private $position; - public function __construct(\DOMNodeList $nodeList) + /** + * @var Factory + */ + private static $instance; + /** + * @var Comparator[] + */ + private $customComparators = []; + /** + * @var Comparator[] + */ + private $defaultComparators = []; + /** + * @return Factory + */ + public static function getInstance() { - $this->position = 0; - $this->importNodes($nodeList); + if (self::$instance === null) { + self::$instance = new self(); + // @codeCoverageIgnore + } + return self::$instance; } - public abstract function current(); - public function next() : void + /** + * Constructs a new factory. + */ + public function __construct() { - $this->position++; + $this->registerDefaultComparators(); } - public function key() + /** + * Returns the correct comparator for comparing two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return Comparator + */ + public function getComparatorFor($expected, $actual) { - return $this->position; + foreach ($this->customComparators as $comparator) { + if ($comparator->accepts($expected, $actual)) { + return $comparator; + } + } + foreach ($this->defaultComparators as $comparator) { + if ($comparator->accepts($expected, $actual)) { + return $comparator; + } + } + throw new RuntimeException('No suitable Comparator implementation found'); } - public function valid() + /** + * Registers a new comparator. + * + * This comparator will be returned by getComparatorFor() if its accept() method + * returns TRUE for the compared values. It has higher priority than the + * existing comparators, meaning that its accept() method will be invoked + * before those of the other comparators. + * + * @param Comparator $comparator The comparator to be registered + */ + public function register(Comparator $comparator) { - return $this->position < \count($this->nodes); + array_unshift($this->customComparators, $comparator); + $comparator->setFactory($this); } - public function rewind() : void + /** + * Unregisters a comparator. + * + * This comparator will no longer be considered by getComparatorFor(). + * + * @param Comparator $comparator The comparator to be unregistered + */ + public function unregister(Comparator $comparator) { - $this->position = 0; + foreach ($this->customComparators as $key => $_comparator) { + if ($comparator === $_comparator) { + unset($this->customComparators[$key]); + } + } } - protected function getCurrentElement() : \DOMElement + /** + * Unregisters all non-default comparators. + */ + public function reset() { - return $this->nodes[$this->position]; + $this->customComparators = []; } - private function importNodes(\DOMNodeList $nodeList) : void + private function registerDefaultComparators() : void { - foreach ($nodeList as $node) { - if (!$node instanceof \DOMElement) { - throw new \PHPUnit\PharIo\Manifest\ElementCollectionException(\sprintf('\\DOMElement expected, got \\%s', \get_class($node))); - } - $this->nodes[] = $node; - } + $this->registerDefaultComparator(new MockObjectComparator()); + $this->registerDefaultComparator(new DateTimeComparator()); + $this->registerDefaultComparator(new DOMNodeComparator()); + $this->registerDefaultComparator(new SplObjectStorageComparator()); + $this->registerDefaultComparator(new ExceptionComparator()); + $this->registerDefaultComparator(new ObjectComparator()); + $this->registerDefaultComparator(new ResourceComparator()); + $this->registerDefaultComparator(new ArrayComparator()); + $this->registerDefaultComparator(new DoubleComparator()); + $this->registerDefaultComparator(new NumericComparator()); + $this->registerDefaultComparator(new ScalarComparator()); + $this->registerDefaultComparator(new TypeComparator()); + } + private function registerDefaultComparator(Comparator $comparator) : void + { + $this->defaultComparators[] = $comparator; + $comparator->setFactory($this); } } , Sebastian Heuer , Sebastian Bergmann + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnit\SebastianBergmann\Comparator; -use DOMElement; -use DOMNodeList; -class ManifestElement +use function sprintf; +use function strtolower; +use DOMDocument; +use DOMNode; +use ValueError; +/** + * Compares DOMNode instances for equality. + */ +class DOMNodeComparator extends ObjectComparator { - public const XMLNS = 'https://phar.io/xml/manifest/1.0'; - /** @var DOMElement */ - private $element; - public function __construct(\DOMElement $element) - { - $this->element = $element; - } - protected function getAttributeValue(string $name) : string + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) { - if (!$this->element->hasAttribute($name)) { - throw new \PHPUnit\PharIo\Manifest\ManifestElementException(\sprintf('Attribute %s not set on element %s', $name, $this->element->localName)); - } - return $this->element->getAttribute($name); + return $expected instanceof DOMNode && $actual instanceof DOMNode; } - protected function getChildByName(string $elementName) : \DOMElement + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * @param array $processed List of already processed elements (used to prevent infinite recursion) + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false, array &$processed = []) { - $element = $this->element->getElementsByTagNameNS(self::XMLNS, $elementName)->item(0); - if (!$element instanceof \DOMElement) { - throw new \PHPUnit\PharIo\Manifest\ManifestElementException(\sprintf('Element %s missing', $elementName)); + $expectedAsString = $this->nodeToText($expected, \true, $ignoreCase); + $actualAsString = $this->nodeToText($actual, \true, $ignoreCase); + if ($expectedAsString !== $actualAsString) { + $type = $expected instanceof DOMDocument ? 'documents' : 'nodes'; + throw new ComparisonFailure($expected, $actual, $expectedAsString, $actualAsString, \false, sprintf("Failed asserting that two DOM %s are equal.\n", $type)); } - return $element; } - protected function getChildrenByName(string $elementName) : \DOMNodeList + /** + * Returns the normalized, whitespace-cleaned, and indented textual + * representation of a DOMNode. + */ + private function nodeToText(DOMNode $node, bool $canonicalize, bool $ignoreCase) : string { - $elementList = $this->element->getElementsByTagNameNS(self::XMLNS, $elementName); - if ($elementList->length === 0) { - throw new \PHPUnit\PharIo\Manifest\ManifestElementException(\sprintf('Element(s) %s missing', $elementName)); + if ($canonicalize) { + $document = new DOMDocument(); + try { + @$document->loadXML($node->C14N()); + } catch (ValueError $e) { + } + $node = $document; } - return $elementList; - } - protected function hasChild(string $elementName) : bool - { - return $this->element->getElementsByTagNameNS(self::XMLNS, $elementName)->length !== 0; + $document = $node instanceof DOMDocument ? $node : $node->ownerDocument; + $document->formatOutput = \true; + $document->normalizeDocument(); + $text = $node instanceof DOMDocument ? $node->saveXML() : $document->saveXML($node); + return $ignoreCase ? strtolower($text) : $text; } } , Sebastian Heuer , Sebastian Bergmann + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnit\SebastianBergmann\Comparator; -class AuthorElementCollection extends \PHPUnit\PharIo\Manifest\ElementCollection +use function get_class; +use function in_array; +use function is_object; +use function sprintf; +use function substr_replace; +/** + * Compares objects for equality. + */ +class ObjectComparator extends ArrayComparator { - public function current() : \PHPUnit\PharIo\Manifest\AuthorElement + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) { - return new \PHPUnit\PharIo\Manifest\AuthorElement($this->getCurrentElement()); + return is_object($expected) && is_object($actual); } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\PharIo\Manifest; - -class AuthorElement extends \PHPUnit\PharIo\Manifest\ManifestElement -{ - public function getName() : string + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * @param array $processed List of already processed elements (used to prevent infinite recursion) + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false, array &$processed = []) { - return $this->getAttributeValue('name'); + if (get_class($actual) !== get_class($expected)) { + throw new ComparisonFailure($expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual), \false, sprintf('%s is not instance of expected class "%s".', $this->exporter->export($actual), get_class($expected))); + } + // don't compare twice to allow for cyclic dependencies + if (in_array([$actual, $expected], $processed, \true) || in_array([$expected, $actual], $processed, \true)) { + return; + } + $processed[] = [$actual, $expected]; + // don't compare objects if they are identical + // this helps to avoid the error "maximum function nesting level reached" + // CAUTION: this conditional clause is not tested + if ($actual !== $expected) { + try { + parent::assertEquals($this->toArray($expected), $this->toArray($actual), $delta, $canonicalize, $ignoreCase, $processed); + } catch (ComparisonFailure $e) { + throw new ComparisonFailure( + $expected, + $actual, + // replace "Array" with "MyClass object" + substr_replace($e->getExpectedAsString(), get_class($expected) . ' Object', 0, 5), + substr_replace($e->getActualAsString(), get_class($actual) . ' Object', 0, 5), + \false, + 'Failed asserting that two objects are equal.' + ); + } + } } - public function getEmail() : string + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * + * @return array + */ + protected function toArray($object) { - return $this->getAttributeValue('email'); + return $this->exporter->toArray($object); } } , Sebastian Heuer , Sebastian Bergmann + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnit\SebastianBergmann\Comparator; -class ComponentElementCollection extends \PHPUnit\PharIo\Manifest\ElementCollection +use function is_object; +use function is_scalar; +use function is_string; +use function method_exists; +use function sprintf; +use function strtolower; +/** + * Compares scalar or NULL values for equality. + */ +class ScalarComparator extends Comparator { - public function current() : \PHPUnit\PharIo\Manifest\ComponentElement + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + * + * @since Method available since Release 3.6.0 + */ + public function accepts($expected, $actual) + { + return (is_scalar($expected) xor null === $expected) && (is_scalar($actual) xor null === $actual) || is_string($expected) && is_object($actual) && method_exists($actual, '__toString') || is_object($expected) && method_exists($expected, '__toString') && is_string($actual); + } + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false) { - return new \PHPUnit\PharIo\Manifest\ComponentElement($this->getCurrentElement()); + $expectedToCompare = $expected; + $actualToCompare = $actual; + // always compare as strings to avoid strange behaviour + // otherwise 0 == 'Foobar' + if (is_string($expected) || is_string($actual)) { + $expectedToCompare = (string) $expectedToCompare; + $actualToCompare = (string) $actualToCompare; + if ($ignoreCase) { + $expectedToCompare = strtolower($expectedToCompare); + $actualToCompare = strtolower($actualToCompare); + } + } + if ($expectedToCompare !== $actualToCompare && is_string($expected) && is_string($actual)) { + throw new ComparisonFailure($expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual), \false, 'Failed asserting that two strings are equal.'); + } + if ($expectedToCompare != $actualToCompare) { + throw new ComparisonFailure( + $expected, + $actual, + // no diff is required + '', + '', + \false, + sprintf('Failed asserting that %s matches expected %s.', $this->exporter->export($actual), $this->exporter->export($expected)) + ); + } } } , Sebastian Heuer , Sebastian Bergmann + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnit\SebastianBergmann\Comparator; -class BundlesElement extends \PHPUnit\PharIo\Manifest\ManifestElement +use function abs; +use function floor; +use function sprintf; +use DateInterval; +use DateTime; +use DateTimeInterface; +use DateTimeZone; +use Exception; +/** + * Compares DateTimeInterface instances for equality. + */ +class DateTimeComparator extends ObjectComparator { - public function getComponentElements() : \PHPUnit\PharIo\Manifest\ComponentElementCollection + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) + { + return ($expected instanceof DateTime || $expected instanceof DateTimeInterface) && ($actual instanceof DateTime || $actual instanceof DateTimeInterface); + } + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * @param array $processed List of already processed elements (used to prevent infinite recursion) + * + * @throws Exception + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false, array &$processed = []) + { + /** @var DateTimeInterface $expected */ + /** @var DateTimeInterface $actual */ + $absDelta = abs($delta); + $delta = new DateInterval(sprintf('PT%dS', $absDelta)); + $delta->f = $absDelta - floor($absDelta); + $actualClone = (clone $actual)->setTimezone(new DateTimeZone('UTC')); + $expectedLower = (clone $expected)->setTimezone(new DateTimeZone('UTC'))->sub($delta); + $expectedUpper = (clone $expected)->setTimezone(new DateTimeZone('UTC'))->add($delta); + if ($actualClone < $expectedLower || $actualClone > $expectedUpper) { + throw new ComparisonFailure($expected, $actual, $this->dateTimeToString($expected), $this->dateTimeToString($actual), \false, 'Failed asserting that two DateTime objects are equal.'); + } + } + /** + * Returns an ISO 8601 formatted string representation of a datetime or + * 'Invalid DateTimeInterface object' if the provided DateTimeInterface was not properly + * initialized. + */ + private function dateTimeToString(DateTimeInterface $datetime) : string { - return new \PHPUnit\PharIo\Manifest\ComponentElementCollection($this->getChildrenByName('component')); + $string = $datetime->format('Y-m-d\\TH:i:s.uO'); + return $string ?: 'Invalid DateTimeInterface object'; } } , Sebastian Heuer , Sebastian Bergmann + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnit\SebastianBergmann\Comparator; -class ComponentElement extends \PHPUnit\PharIo\Manifest\ManifestElement +use function gettype; +use function sprintf; +/** + * Compares values for type equality. + */ +class TypeComparator extends Comparator { - public function getName() : string + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) { - return $this->getAttributeValue('name'); + return \true; } - public function getVersion() : string + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false) { - return $this->getAttributeValue('version'); + if (gettype($expected) != gettype($actual)) { + throw new ComparisonFailure( + $expected, + $actual, + // we don't need a diff + '', + '', + \false, + sprintf('%s does not match expected type "%s".', $this->exporter->shortenedExport($actual), gettype($expected)) + ); + } } } , Sebastian Heuer , Sebastian Bergmann + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnit\SebastianBergmann\Comparator; -class ExtElement extends \PHPUnit\PharIo\Manifest\ManifestElement +use Exception; +/** + * Compares Exception instances for equality. + */ +class ExceptionComparator extends ObjectComparator { - public function getName() : string + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) { - return $this->getAttributeValue('name'); + return $expected instanceof Exception && $actual instanceof Exception; + } + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * + * @return array + */ + protected function toArray($object) + { + $array = parent::toArray($object); + unset($array['file'], $array['line'], $array['trace'], $array['string'], $array['xdebug_message']); + return $array; } } , Sebastian Heuer , Sebastian Bergmann + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnit\SebastianBergmann\Comparator; -class LicenseElement extends \PHPUnit\PharIo\Manifest\ManifestElement +final class RuntimeException extends \RuntimeException implements Exception { - public function getType() : string - { - return $this->getAttributeValue('type'); - } - public function getUrl() : string - { - return $this->getAttributeValue('url'); - } } , Sebastian Heuer , Sebastian Bergmann + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnit\SebastianBergmann\Comparator; -class CopyrightElement extends \PHPUnit\PharIo\Manifest\ManifestElement +use Throwable; +interface Exception extends Throwable { - public function getAuthorElements() : \PHPUnit\PharIo\Manifest\AuthorElementCollection - { - return new \PHPUnit\PharIo\Manifest\AuthorElementCollection($this->getChildrenByName('author')); - } - public function getLicenseElement() : \PHPUnit\PharIo\Manifest\LicenseElement - { - return new \PHPUnit\PharIo\Manifest\LicenseElement($this->getChildByName('license')); - } } , Sebastian Heuer , Sebastian Bergmann + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnit\SebastianBergmann\Comparator; -class ContainsElement extends \PHPUnit\PharIo\Manifest\ManifestElement +use SplObjectStorage; +/** + * Compares \SplObjectStorage instances for equality. + */ +class SplObjectStorageComparator extends Comparator { - public function getName() : string - { - return $this->getAttributeValue('name'); - } - public function getVersion() : string - { - return $this->getAttributeValue('version'); - } - public function getType() : string + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) { - return $this->getAttributeValue('type'); + return $expected instanceof SplObjectStorage && $actual instanceof SplObjectStorage; } - public function getExtensionElement() : \PHPUnit\PharIo\Manifest\ExtensionElement + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false) { - return new \PHPUnit\PharIo\Manifest\ExtensionElement($this->getChildByName('extension')); + foreach ($actual as $object) { + if (!$expected->contains($object)) { + throw new ComparisonFailure($expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual), \false, 'Failed asserting that two objects are equal.'); + } + } + foreach ($expected as $object) { + if (!$actual->contains($object)) { + throw new ComparisonFailure($expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual), \false, 'Failed asserting that two objects are equal.'); + } + } } } , Sebastian Heuer , Sebastian Bergmann + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnit\SebastianBergmann\Comparator; -use PHPUnit\PharIo\Version\Version; -class BundledComponent +use PHPUnit\SebastianBergmann\Exporter\Exporter; +/** + * Abstract base class for comparators which compare values for equality. + */ +abstract class Comparator { - /** @var string */ - private $name; - /** @var Version */ - private $version; - public function __construct(string $name, \PHPUnit\PharIo\Version\Version $version) - { - $this->name = $name; - $this->version = $version; - } - public function getName() : string + /** + * @var Factory + */ + protected $factory; + /** + * @var Exporter + */ + protected $exporter; + public function __construct() { - return $this->name; + $this->exporter = new Exporter(); } - public function getVersion() : \PHPUnit\PharIo\Version\Version + public function setFactory(Factory $factory) { - return $this->version; + $this->factory = $factory; } + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public abstract function accepts($expected, $actual); + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * + * @throws ComparisonFailure + */ + public abstract function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false); } , Sebastian Heuer , Sebastian Bergmann + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnit\SebastianBergmann\Comparator; -class CopyrightInformation +use function array_key_exists; +use function is_array; +use function sort; +use function sprintf; +use function str_replace; +use function trim; +/** + * Compares arrays for equality. + * + * Arrays are equal if they contain the same key-value pairs. + * The order of the keys does not matter. + * The types of key-value pairs do not matter. + */ +class ArrayComparator extends Comparator { - /** @var AuthorCollection */ - private $authors; - /** @var License */ - private $license; - public function __construct(\PHPUnit\PharIo\Manifest\AuthorCollection $authors, \PHPUnit\PharIo\Manifest\License $license) + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) { - $this->authors = $authors; - $this->license = $license; + return is_array($expected) && is_array($actual); } - public function getAuthors() : \PHPUnit\PharIo\Manifest\AuthorCollection + /** + * Asserts that two arrays are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * @param array $processed List of already processed elements (used to prevent infinite recursion) + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false, array &$processed = []) { - return $this->authors; + if ($canonicalize) { + sort($expected); + sort($actual); + } + $remaining = $actual; + $actualAsString = "Array (\n"; + $expectedAsString = "Array (\n"; + $equal = \true; + foreach ($expected as $key => $value) { + unset($remaining[$key]); + if (!array_key_exists($key, $actual)) { + $expectedAsString .= sprintf(" %s => %s\n", $this->exporter->export($key), $this->exporter->shortenedExport($value)); + $equal = \false; + continue; + } + try { + $comparator = $this->factory->getComparatorFor($value, $actual[$key]); + $comparator->assertEquals($value, $actual[$key], $delta, $canonicalize, $ignoreCase, $processed); + $expectedAsString .= sprintf(" %s => %s\n", $this->exporter->export($key), $this->exporter->shortenedExport($value)); + $actualAsString .= sprintf(" %s => %s\n", $this->exporter->export($key), $this->exporter->shortenedExport($actual[$key])); + } catch (ComparisonFailure $e) { + $expectedAsString .= sprintf(" %s => %s\n", $this->exporter->export($key), $e->getExpectedAsString() ? $this->indent($e->getExpectedAsString()) : $this->exporter->shortenedExport($e->getExpected())); + $actualAsString .= sprintf(" %s => %s\n", $this->exporter->export($key), $e->getActualAsString() ? $this->indent($e->getActualAsString()) : $this->exporter->shortenedExport($e->getActual())); + $equal = \false; + } + } + foreach ($remaining as $key => $value) { + $actualAsString .= sprintf(" %s => %s\n", $this->exporter->export($key), $this->exporter->shortenedExport($value)); + $equal = \false; + } + $expectedAsString .= ')'; + $actualAsString .= ')'; + if (!$equal) { + throw new ComparisonFailure($expected, $actual, $expectedAsString, $actualAsString, \false, 'Failed asserting that two arrays are equal.'); + } } - public function getLicense() : \PHPUnit\PharIo\Manifest\License + protected function indent($lines) { - return $this->license; + return trim(str_replace("\n", "\n ", $lines)); } } +Comparator + +Copyright (c) 2002-2020, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. , Sebastian Heuer , Sebastian Bergmann + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\PharIo\Manifest; +namespace PHPUnit\SebastianBergmann\Comparator; -class BundledComponentCollection implements \Countable, \IteratorAggregate +use PHPUnit\Framework\MockObject\MockObject; +/** + * Compares PHPUnit\Framework\MockObject\MockObject instances for equality. + */ +class MockObjectComparator extends ObjectComparator { - /** @var BundledComponent[] */ - private $bundledComponents = []; - public function add(\PHPUnit\PharIo\Manifest\BundledComponent $bundledComponent) : void - { - $this->bundledComponents[] = $bundledComponent; - } /** - * @return BundledComponent[] + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool */ - public function getBundledComponents() : array - { - return $this->bundledComponents; - } - public function count() : int + public function accepts($expected, $actual) { - return \count($this->bundledComponents); + return $expected instanceof MockObject && $actual instanceof MockObject; } - public function getIterator() : \PHPUnit\PharIo\Manifest\BundledComponentCollectionIterator + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * + * @return array + */ + protected function toArray($object) { - return new \PHPUnit\PharIo\Manifest\BundledComponentCollectionIterator($this); + $array = parent::toArray($object); + unset($array['__phpunit_invocationMocker']); + return $array; } } bundledComponents = $bundledComponents->getBundledComponents(); + try { + $contains = $document->getContainsElement(); + $type = $this->mapType($contains); + $copyright = $this->mapCopyright($document->getCopyrightElement()); + $requirements = $this->mapRequirements($document->getRequiresElement()); + $bundledComponents = $this->mapBundledComponents($document); + return new Manifest(new ApplicationName($contains->getName()), new Version($contains->getVersion()), $type, $copyright, $requirements, $bundledComponents); + } catch (VersionException $e) { + throw new ManifestDocumentMapperException($e->getMessage(), (int) $e->getCode(), $e); + } catch (Exception $e) { + throw new ManifestDocumentMapperException($e->getMessage(), (int) $e->getCode(), $e); + } } - public function rewind() : void + private function mapType(ContainsElement $contains) : Type { - $this->position = 0; + switch ($contains->getType()) { + case 'application': + return Type::application(); + case 'library': + return Type::library(); + case 'extension': + return $this->mapExtension($contains->getExtensionElement()); + } + throw new ManifestDocumentMapperException(\sprintf('Unsupported type %s', $contains->getType())); } - public function valid() : bool + private function mapCopyright(CopyrightElement $copyright) : CopyrightInformation { - return $this->position < \count($this->bundledComponents); + $authors = new AuthorCollection(); + foreach ($copyright->getAuthorElements() as $authorElement) { + $authors->add(new Author($authorElement->getName(), new Email($authorElement->getEmail()))); + } + $licenseElement = $copyright->getLicenseElement(); + $license = new License($licenseElement->getType(), new Url($licenseElement->getUrl())); + return new CopyrightInformation($authors, $license); } - public function key() : int + private function mapRequirements(RequiresElement $requires) : RequirementCollection { - return $this->position; + $collection = new RequirementCollection(); + $phpElement = $requires->getPHPElement(); + $parser = new VersionConstraintParser(); + try { + $versionConstraint = $parser->parse($phpElement->getVersion()); + } catch (VersionException $e) { + throw new ManifestDocumentMapperException(\sprintf('Unsupported version constraint - %s', $e->getMessage()), (int) $e->getCode(), $e); + } + $collection->add(new PhpVersionRequirement($versionConstraint)); + if (!$phpElement->hasExtElements()) { + return $collection; + } + foreach ($phpElement->getExtElements() as $extElement) { + $collection->add(new PhpExtensionRequirement($extElement->getName())); + } + return $collection; } - public function current() : \PHPUnit\PharIo\Manifest\BundledComponent + private function mapBundledComponents(ManifestDocument $document) : BundledComponentCollection { - return $this->bundledComponents[$this->position]; + $collection = new BundledComponentCollection(); + if (!$document->hasBundlesElement()) { + return $collection; + } + foreach ($document->getBundlesElement()->getComponentElements() as $componentElement) { + $collection->add(new BundledComponent($componentElement->getName(), new Version($componentElement->getVersion()))); + } + return $collection; } - public function next() : void + private function mapExtension(ExtensionElement $extension) : Extension { - $this->position++; + try { + $versionConstraint = (new VersionConstraintParser())->parse($extension->getCompatible()); + return Type::extension(new ApplicationName($extension->getFor()), $versionConstraint); + } catch (VersionException $e) { + throw new ManifestDocumentMapperException(\sprintf('Unsupported version constraint - %s', $e->getMessage()), (int) $e->getCode(), $e); + } } } , Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ namespace PHPUnit\PharIo\Manifest; -class RequirementCollectionIterator implements \Iterator +class ManifestDocumentMapperException extends \RuntimeException implements Exception { - /** @var Requirement[] */ - private $requirements; - /** @var int */ - private $position = 0; - public function __construct(\PHPUnit\PharIo\Manifest\RequirementCollection $requirements) - { - $this->requirements = $requirements->getRequirements(); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < \count($this->requirements); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\PharIo\Manifest\Requirement - { - return $this->requirements[$this->position]; - } - public function next() : void - { - $this->position++; - } } name = $name; - $this->version = $version; - $this->type = $type; - $this->copyrightInformation = $copyrightInformation; - $this->requirements = $requirements; - $this->bundledComponents = $bundledComponents; - } - public function getName() : \PHPUnit\PharIo\Manifest\ApplicationName - { - return $this->name; - } - public function getVersion() : \PHPUnit\PharIo\Version\Version - { - return $this->version; - } - public function getType() : \PHPUnit\PharIo\Manifest\Type - { - return $this->type; - } - public function getCopyrightInformation() : \PHPUnit\PharIo\Manifest\CopyrightInformation - { - return $this->copyrightInformation; - } - public function getRequirements() : \PHPUnit\PharIo\Manifest\RequirementCollection - { - return $this->requirements; - } - public function getBundledComponents() : \PHPUnit\PharIo\Manifest\BundledComponentCollection - { - return $this->bundledComponents; - } - public function isApplication() : bool - { - return $this->type->isApplication(); - } - public function isLibrary() : bool - { - return $this->type->isLibrary(); - } - public function isExtension() : bool - { - return $this->type->isExtension(); - } - public function isExtensionFor(\PHPUnit\PharIo\Manifest\ApplicationName $application, \PHPUnit\PharIo\Version\Version $version = null) : bool - { - if (!$this->isExtension()) { - return \false; - } - /** @var Extension $type */ - $type = $this->type; - if ($version !== null) { - return $type->isCompatibleWith($application, $version); - } - return $type->isExtensionFor($application); - } } name = $name; - $this->email = $email; - } - public function asString() : string - { - return \sprintf('%s <%s>', $this->name, $this->email->asString()); - } - public function getName() : string - { - return $this->name; - } - public function getEmail() : \PHPUnit\PharIo\Manifest\Email - { - return $this->email; - } + public const InvalidFormat = 2; } , Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ namespace PHPUnit\PharIo\Manifest; -interface Requirement +class ManifestElementException extends \RuntimeException implements Exception { } , Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ namespace PHPUnit\PharIo\Manifest; -class RequirementCollection implements \Countable, \IteratorAggregate +class ManifestLoaderException extends \Exception implements Exception +{ +} +requirements[] = $requirement; - } - /** - * @return Requirement[] - */ - public function getRequirements() : array - { - return $this->requirements; - } - public function count() : int - { - return \count($this->requirements); - } - public function getIterator() : \PHPUnit\PharIo\Manifest\RequirementCollectionIterator - { - return new \PHPUnit\PharIo\Manifest\RequirementCollectionIterator($this); - } } application = $application; - $this->versionConstraint = $versionConstraint; - } - public function getApplicationName() : \PHPUnit\PharIo\Manifest\ApplicationName - { - return $this->application; - } - public function getVersionConstraint() : \PHPUnit\PharIo\Version\VersionConstraint - { - return $this->versionConstraint; - } - public function isExtension() : bool - { - return \true; - } - public function isExtensionFor(\PHPUnit\PharIo\Manifest\ApplicationName $name) : bool - { - return $this->application->isEqual($name); - } - public function isCompatibleWith(\PHPUnit\PharIo\Manifest\ApplicationName $name, \PHPUnit\PharIo\Version\Version $version) : bool - { - return $this->isExtensionFor($name) && $this->versionConstraint->complies($version); - } } ensureUrlIsValid($url); - $this->url = $url; - } - public function asString() : string - { - return $this->url; - } - /** - * @param string $url - * - * @throws InvalidUrlException - */ - private function ensureUrlIsValid($url) : void - { - if (\filter_var($url, \FILTER_VALIDATE_URL) === \false) { - throw new \PHPUnit\PharIo\Manifest\InvalidUrlException(); - } - } } versionConstraint = $versionConstraint; + $this->libxmlErrors = $libxmlErrors; + $first = $this->libxmlErrors[0]; + parent::__construct(\sprintf('%s (Line: %d / Column: %d / File: %s)', $first->message, $first->line, $first->column, $first->file), $first->code); } - public function getVersionConstraint() : \PHPUnit\PharIo\Version\VersionConstraint + /** + * @return LibXMLError[] + */ + public function getLibxmlErrors() : array { - return $this->versionConstraint; + return $this->libxmlErrors; } } extension = $extension; - } - public function asString() : string - { - return $this->extension; - } } serializeToString($manifest)); } - public static function extension(\PHPUnit\PharIo\Manifest\ApplicationName $application, \PHPUnit\PharIo\Version\VersionConstraint $versionConstraint) : \PHPUnit\PharIo\Manifest\Extension + public function serializeToString(Manifest $manifest) : string { - return new \PHPUnit\PharIo\Manifest\Extension($application, $versionConstraint); + $this->startDocument(); + $this->addContains($manifest->getName(), $manifest->getVersion(), $manifest->getType()); + $this->addCopyright($manifest->getCopyrightInformation()); + $this->addRequirements($manifest->getRequirements()); + $this->addBundles($manifest->getBundledComponents()); + return $this->finishDocument(); } - /** @psalm-assert-if-true Application $this */ - public function isApplication() : bool + private function startDocument() : void { - return \false; + $xmlWriter = new XMLWriter(); + $xmlWriter->openMemory(); + $xmlWriter->setIndent(\true); + $xmlWriter->setIndentString(\str_repeat(' ', 4)); + $xmlWriter->startDocument('1.0', 'UTF-8'); + $xmlWriter->startElement('phar'); + $xmlWriter->writeAttribute('xmlns', 'https://phar.io/xml/manifest/1.0'); + $this->xmlWriter = $xmlWriter; } - /** @psalm-assert-if-true Library $this */ - public function isLibrary() : bool + private function finishDocument() : string { - return \false; + $this->xmlWriter->endElement(); + $this->xmlWriter->endDocument(); + return $this->xmlWriter->outputMemory(); } - /** @psalm-assert-if-true Extension $this */ - public function isExtension() : bool + private function addContains(ApplicationName $name, Version $version, Type $type) : void { - return \false; + $this->xmlWriter->startElement('contains'); + $this->xmlWriter->writeAttribute('name', $name->asString()); + $this->xmlWriter->writeAttribute('version', $version->getVersionString()); + switch (\true) { + case $type->isApplication(): + $this->xmlWriter->writeAttribute('type', 'application'); + break; + case $type->isLibrary(): + $this->xmlWriter->writeAttribute('type', 'library'); + break; + case $type->isExtension(): + $this->xmlWriter->writeAttribute('type', 'extension'); + /* @var $type Extension */ + $this->addExtension($type->getApplicationName(), $type->getVersionConstraint()); + break; + default: + $this->xmlWriter->writeAttribute('type', 'custom'); + } + $this->xmlWriter->endElement(); } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\PharIo\Manifest; - -class License -{ - /** @var string */ - private $name; - /** @var Url */ - private $url; - public function __construct(string $name, \PHPUnit\PharIo\Manifest\Url $url) + private function addCopyright(CopyrightInformation $copyrightInformation) : void { - $this->name = $name; - $this->url = $url; + $this->xmlWriter->startElement('copyright'); + foreach ($copyrightInformation->getAuthors() as $author) { + $this->xmlWriter->startElement('author'); + $this->xmlWriter->writeAttribute('name', $author->getName()); + $this->xmlWriter->writeAttribute('email', $author->getEmail()->asString()); + $this->xmlWriter->endElement(); + } + $license = $copyrightInformation->getLicense(); + $this->xmlWriter->startElement('license'); + $this->xmlWriter->writeAttribute('type', $license->getName()); + $this->xmlWriter->writeAttribute('url', $license->getUrl()->asString()); + $this->xmlWriter->endElement(); + $this->xmlWriter->endElement(); } - public function getName() : string + private function addRequirements(RequirementCollection $requirementCollection) : void { - return $this->name; + $phpRequirement = new AnyVersionConstraint(); + $extensions = []; + foreach ($requirementCollection as $requirement) { + if ($requirement instanceof PhpVersionRequirement) { + $phpRequirement = $requirement->getVersionConstraint(); + continue; + } + if ($requirement instanceof PhpExtensionRequirement) { + $extensions[] = $requirement->asString(); + } + } + $this->xmlWriter->startElement('requires'); + $this->xmlWriter->startElement('php'); + $this->xmlWriter->writeAttribute('version', $phpRequirement->asString()); + foreach ($extensions as $extension) { + $this->xmlWriter->startElement('ext'); + $this->xmlWriter->writeAttribute('name', $extension); + $this->xmlWriter->endElement(); + } + $this->xmlWriter->endElement(); + $this->xmlWriter->endElement(); } - public function getUrl() : \PHPUnit\PharIo\Manifest\Url + private function addBundles(BundledComponentCollection $bundledComponentCollection) : void { - return $this->url; + if (\count($bundledComponentCollection) === 0) { + return; + } + $this->xmlWriter->startElement('bundles'); + foreach ($bundledComponentCollection as $bundledComponent) { + $this->xmlWriter->startElement('component'); + $this->xmlWriter->writeAttribute('name', $bundledComponent->getName()); + $this->xmlWriter->writeAttribute('version', $bundledComponent->getVersion()->getVersionString()); + $this->xmlWriter->endElement(); + } + $this->xmlWriter->endElement(); } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\PharIo\Manifest; - -class Application extends \PHPUnit\PharIo\Manifest\Type -{ - public function isApplication() : bool + private function addExtension(ApplicationName $applicationName, VersionConstraint $versionConstraint) : void { - return \true; + $this->xmlWriter->startElement('extension'); + $this->xmlWriter->writeAttribute('for', $applicationName->asString()); + $this->xmlWriter->writeAttribute('compatible', $versionConstraint->asString()); + $this->xmlWriter->endElement(); } } authors = $authors->getAuthors(); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < \count($this->authors); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\PharIo\Manifest\Author - { - return $this->authors[$this->position]; - } - public function next() : void + public function getName() : string { - $this->position++; + return $this->getAttributeValue('name'); } } ensureValidFormat($name); - $this->name = $name; - } - public function asString() : string - { - return $this->name; - } - public function isEqual(\PHPUnit\PharIo\Manifest\ApplicationName $name) : bool + public function getAuthorElements() : AuthorElementCollection { - return $this->name === $name->name; + return new AuthorElementCollection($this->getChildrenByName('author')); } - private function ensureValidFormat(string $name) : void + public function getLicenseElement() : LicenseElement { - if (!\preg_match('#\\w/\\w#', $name)) { - throw new \PHPUnit\PharIo\Manifest\InvalidApplicationNameException(\sprintf('Format of name "%s" is not valid - expected: vendor/packagename', $name), \PHPUnit\PharIo\Manifest\InvalidApplicationNameException::InvalidFormat); - } + return new LicenseElement($this->getChildByName('license')); } } getAttributeValue('version'); + } + public function hasExtElements() : bool + { + return $this->hasChild('ext'); + } + public function getExtElements() : ExtElementCollection + { + return new ExtElementCollection($this->getChildrenByName('ext')); } } ensureEmailIsValid($email); - $this->email = $email; - } - public function asString() : string - { - return $this->email; - } - private function ensureEmailIsValid(string $url) : void + public function current() : AuthorElement { - if (\filter_var($url, \FILTER_VALIDATE_EMAIL) === \false) { - throw new \PHPUnit\PharIo\Manifest\InvalidEmailException(); - } + return new AuthorElement($this->getCurrentElement()); } } authors[] = $author; + $this->position = 0; + $this->importNodes($nodeList); } - /** - * @return Author[] - */ - public function getAuthors() : array + #[\ReturnTypeWillChange] + public abstract function current(); + public function next() : void { - return $this->authors; + $this->position++; } - public function count() : int + public function key() : int { - return \count($this->authors); + return $this->position; + } + public function valid() : bool + { + return $this->position < \count($this->nodes); + } + public function rewind() : void + { + $this->position = 0; + } + protected function getCurrentElement() : DOMElement + { + return $this->nodes[$this->position]; } - public function getIterator() : \PHPUnit\PharIo\Manifest\AuthorCollectionIterator + private function importNodes(DOMNodeList $nodeList) : void { - return new \PHPUnit\PharIo\Manifest\AuthorCollectionIterator($this); + foreach ($nodeList as $node) { + if (!$node instanceof DOMElement) { + throw new ElementCollectionException(\sprintf('\\DOMElement expected, got \\%s', \get_class($node))); + } + $this->nodes[] = $node; + } } } , Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPUnit\PharIo\Manifest; -class ManifestLoaderException extends \Exception implements \PHPUnit\PharIo\Manifest\Exception +class ExtensionElement extends ManifestElement { + public function getFor() : string + { + return $this->getAttributeValue('for'); + } + public function getCompatible() : string + { + return $this->getAttributeValue('compatible'); + } } getAttributeValue('type'); + } + public function getUrl() : string + { + return $this->getAttributeValue('url'); + } } getCurrentElement()); + } } getChildByName('php')); + } } , Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPUnit\PharIo\Manifest; -class ManifestDocumentMapperException extends \RuntimeException implements \PHPUnit\PharIo\Manifest\Exception +class BundlesElement extends ManifestElement { + public function getComponentElements() : ComponentElementCollection + { + return new ComponentElementCollection($this->getChildrenByName('component')); + } } libxmlErrors = $libxmlErrors; - $first = $this->libxmlErrors[0]; - parent::__construct(\sprintf('%s (Line: %d / Column: %d / File: %s)', $first->message, $first->line, $first->column, $first->file), $first->code); + return $this->getAttributeValue('name'); } - /** - * @return LibXMLError[] - */ - public function getLibxmlErrors() : array + public function getVersion() : string { - return $this->libxmlErrors; + return $this->getAttributeValue('version'); + } + public function getType() : string + { + return $this->getAttributeValue('type'); + } + public function getExtensionElement() : ExtensionElement + { + return new ExtensionElement($this->getChildByName('extension')); } } loadXML($xmlString); + $errors = \libxml_get_errors(); + \libxml_use_internal_errors($prev); + if (\count($errors) !== 0) { + throw new ManifestDocumentLoadingException($errors); + } + return new self($dom); + } + private function __construct(DOMDocument $dom) + { + $this->ensureCorrectDocumentType($dom); + $this->dom = $dom; + } + public function getContainsElement() : ContainsElement + { + return new ContainsElement($this->fetchElementByName('contains')); + } + public function getCopyrightElement() : CopyrightElement + { + return new CopyrightElement($this->fetchElementByName('copyright')); + } + public function getRequiresElement() : RequiresElement + { + return new RequiresElement($this->fetchElementByName('requires')); + } + public function hasBundlesElement() : bool + { + return $this->dom->getElementsByTagNameNS(self::XMLNS, 'bundles')->length === 1; + } + public function getBundlesElement() : BundlesElement + { + return new BundlesElement($this->fetchElementByName('bundles')); + } + private function ensureCorrectDocumentType(DOMDocument $dom) : void + { + $root = $dom->documentElement; + if ($root->localName !== 'phar' || $root->namespaceURI !== self::XMLNS) { + throw new ManifestDocumentException('Not a phar.io manifest document'); + } + } + private function fetchElementByName(string $elementName) : DOMElement + { + $element = $this->dom->getElementsByTagNameNS(self::XMLNS, $elementName)->item(0); + if (!$element instanceof DOMElement) { + throw new ManifestDocumentException(\sprintf('Element %s missing', $elementName)); + } + return $element; + } } getAttributeValue('name'); + } + public function getVersion() : string + { + return $this->getAttributeValue('version'); + } } map(\PHPUnit\PharIo\Manifest\ManifestDocument::fromFile($filename)); - } catch (\PHPUnit\PharIo\Manifest\Exception $e) { - throw new \PHPUnit\PharIo\Manifest\ManifestLoaderException(\sprintf('Loading %s failed.', $filename), (int) $e->getCode(), $e); + $this->element = $element; + } + protected function getAttributeValue(string $name) : string + { + if (!$this->element->hasAttribute($name)) { + throw new ManifestElementException(\sprintf('Attribute %s not set on element %s', $name, $this->element->localName)); } + return $this->element->getAttribute($name); } - public static function fromPhar(string $filename) : \PHPUnit\PharIo\Manifest\Manifest + protected function getChildByName(string $elementName) : DOMElement { - return self::fromFile('phar://' . $filename . '/manifest.xml'); + $element = $this->element->getElementsByTagNameNS(self::XMLNS, $elementName)->item(0); + if (!$element instanceof DOMElement) { + throw new ManifestElementException(\sprintf('Element %s missing', $elementName)); + } + return $element; } - public static function fromString(string $manifest) : \PHPUnit\PharIo\Manifest\Manifest + protected function getChildrenByName(string $elementName) : DOMNodeList { - try { - return (new \PHPUnit\PharIo\Manifest\ManifestDocumentMapper())->map(\PHPUnit\PharIo\Manifest\ManifestDocument::fromString($manifest)); - } catch (\PHPUnit\PharIo\Manifest\Exception $e) { - throw new \PHPUnit\PharIo\Manifest\ManifestLoaderException('Processing string failed', (int) $e->getCode(), $e); + $elementList = $this->element->getElementsByTagNameNS(self::XMLNS, $elementName); + if ($elementList->length === 0) { + throw new ManifestElementException(\sprintf('Element(s) %s missing', $elementName)); } + return $elementList; + } + protected function hasChild(string $elementName) : bool + { + return $this->element->getElementsByTagNameNS(self::XMLNS, $elementName)->length !== 0; } } getContainsElement(); - $type = $this->mapType($contains); - $copyright = $this->mapCopyright($document->getCopyrightElement()); - $requirements = $this->mapRequirements($document->getRequiresElement()); - $bundledComponents = $this->mapBundledComponents($document); - return new \PHPUnit\PharIo\Manifest\Manifest(new \PHPUnit\PharIo\Manifest\ApplicationName($contains->getName()), new \PHPUnit\PharIo\Version\Version($contains->getVersion()), $type, $copyright, $requirements, $bundledComponents); - } catch (\PHPUnit\PharIo\Version\Exception $e) { - throw new \PHPUnit\PharIo\Manifest\ManifestDocumentMapperException($e->getMessage(), (int) $e->getCode(), $e); - } catch (\PHPUnit\PharIo\Manifest\Exception $e) { - throw new \PHPUnit\PharIo\Manifest\ManifestDocumentMapperException($e->getMessage(), (int) $e->getCode(), $e); - } - } - private function mapType(\PHPUnit\PharIo\Manifest\ContainsElement $contains) : \PHPUnit\PharIo\Manifest\Type - { - switch ($contains->getType()) { - case 'application': - return \PHPUnit\PharIo\Manifest\Type::application(); - case 'library': - return \PHPUnit\PharIo\Manifest\Type::library(); - case 'extension': - return $this->mapExtension($contains->getExtensionElement()); - } - throw new \PHPUnit\PharIo\Manifest\ManifestDocumentMapperException(\sprintf('Unsupported type %s', $contains->getType())); - } - private function mapCopyright(\PHPUnit\PharIo\Manifest\CopyrightElement $copyright) : \PHPUnit\PharIo\Manifest\CopyrightInformation - { - $authors = new \PHPUnit\PharIo\Manifest\AuthorCollection(); - foreach ($copyright->getAuthorElements() as $authorElement) { - $authors->add(new \PHPUnit\PharIo\Manifest\Author($authorElement->getName(), new \PHPUnit\PharIo\Manifest\Email($authorElement->getEmail()))); - } - $licenseElement = $copyright->getLicenseElement(); - $license = new \PHPUnit\PharIo\Manifest\License($licenseElement->getType(), new \PHPUnit\PharIo\Manifest\Url($licenseElement->getUrl())); - return new \PHPUnit\PharIo\Manifest\CopyrightInformation($authors, $license); - } - private function mapRequirements(\PHPUnit\PharIo\Manifest\RequiresElement $requires) : \PHPUnit\PharIo\Manifest\RequirementCollection + public function getName() : string { - $collection = new \PHPUnit\PharIo\Manifest\RequirementCollection(); - $phpElement = $requires->getPHPElement(); - $parser = new \PHPUnit\PharIo\Version\VersionConstraintParser(); - try { - $versionConstraint = $parser->parse($phpElement->getVersion()); - } catch (\PHPUnit\PharIo\Version\Exception $e) { - throw new \PHPUnit\PharIo\Manifest\ManifestDocumentMapperException(\sprintf('Unsupported version constraint - %s', $e->getMessage()), (int) $e->getCode(), $e); - } - $collection->add(new \PHPUnit\PharIo\Manifest\PhpVersionRequirement($versionConstraint)); - if (!$phpElement->hasExtElements()) { - return $collection; - } - foreach ($phpElement->getExtElements() as $extElement) { - $collection->add(new \PHPUnit\PharIo\Manifest\PhpExtensionRequirement($extElement->getName())); - } - return $collection; + return $this->getAttributeValue('name'); } - private function mapBundledComponents(\PHPUnit\PharIo\Manifest\ManifestDocument $document) : \PHPUnit\PharIo\Manifest\BundledComponentCollection + public function getEmail() : string { - $collection = new \PHPUnit\PharIo\Manifest\BundledComponentCollection(); - if (!$document->hasBundlesElement()) { - return $collection; - } - foreach ($document->getBundlesElement()->getComponentElements() as $componentElement) { - $collection->add(new \PHPUnit\PharIo\Manifest\BundledComponent($componentElement->getName(), new \PHPUnit\PharIo\Version\Version($componentElement->getVersion()))); - } - return $collection; + return $this->getAttributeValue('email'); } - private function mapExtension(\PHPUnit\PharIo\Manifest\ExtensionElement $extension) : \PHPUnit\PharIo\Manifest\Extension +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\PharIo\Manifest; + +class ComponentElementCollection extends ElementCollection +{ + public function current() : ComponentElement { - try { - $versionConstraint = (new \PHPUnit\PharIo\Version\VersionConstraintParser())->parse($extension->getCompatible()); - return \PHPUnit\PharIo\Manifest\Type::extension(new \PHPUnit\PharIo\Manifest\ApplicationName($extension->getFor()), $versionConstraint); - } catch (\PHPUnit\PharIo\Version\Exception $e) { - throw new \PHPUnit\PharIo\Manifest\ManifestDocumentMapperException(\sprintf('Unsupported version constraint - %s', $e->getMessage()), (int) $e->getCode(), $e); - } + return new ComponentElement($this->getCurrentElement()); } } Phar.io - Manifest @@ -36362,526 +38548,457 @@ declare (strict_types=1); */ namespace PHPUnit\PharIo\Manifest; -use PHPUnit\PharIo\Version\AnyVersionConstraint; use PHPUnit\PharIo\Version\Version; -use PHPUnit\PharIo\Version\VersionConstraint; -use XMLWriter; -/** @psalm-suppress MissingConstructor */ -class ManifestSerializer +class Manifest { - /** @var XMLWriter */ - private $xmlWriter; - public function serializeToFile(\PHPUnit\PharIo\Manifest\Manifest $manifest, string $filename) : void + /** @var ApplicationName */ + private $name; + /** @var Version */ + private $version; + /** @var Type */ + private $type; + /** @var CopyrightInformation */ + private $copyrightInformation; + /** @var RequirementCollection */ + private $requirements; + /** @var BundledComponentCollection */ + private $bundledComponents; + public function __construct(ApplicationName $name, Version $version, Type $type, CopyrightInformation $copyrightInformation, RequirementCollection $requirements, BundledComponentCollection $bundledComponents) { - \file_put_contents($filename, $this->serializeToString($manifest)); + $this->name = $name; + $this->version = $version; + $this->type = $type; + $this->copyrightInformation = $copyrightInformation; + $this->requirements = $requirements; + $this->bundledComponents = $bundledComponents; } - public function serializeToString(\PHPUnit\PharIo\Manifest\Manifest $manifest) : string + public function getName() : ApplicationName { - $this->startDocument(); - $this->addContains($manifest->getName(), $manifest->getVersion(), $manifest->getType()); - $this->addCopyright($manifest->getCopyrightInformation()); - $this->addRequirements($manifest->getRequirements()); - $this->addBundles($manifest->getBundledComponents()); - return $this->finishDocument(); + return $this->name; } - private function startDocument() : void + public function getVersion() : Version { - $xmlWriter = new \XMLWriter(); - $xmlWriter->openMemory(); - $xmlWriter->setIndent(\true); - $xmlWriter->setIndentString(\str_repeat(' ', 4)); - $xmlWriter->startDocument('1.0', 'UTF-8'); - $xmlWriter->startElement('phar'); - $xmlWriter->writeAttribute('xmlns', 'https://phar.io/xml/manifest/1.0'); - $this->xmlWriter = $xmlWriter; + return $this->version; } - private function finishDocument() : string + public function getType() : Type { - $this->xmlWriter->endElement(); - $this->xmlWriter->endDocument(); - return $this->xmlWriter->outputMemory(); + return $this->type; } - private function addContains(\PHPUnit\PharIo\Manifest\ApplicationName $name, \PHPUnit\PharIo\Version\Version $version, \PHPUnit\PharIo\Manifest\Type $type) : void + public function getCopyrightInformation() : CopyrightInformation { - $this->xmlWriter->startElement('contains'); - $this->xmlWriter->writeAttribute('name', $name->asString()); - $this->xmlWriter->writeAttribute('version', $version->getVersionString()); - switch (\true) { - case $type->isApplication(): - $this->xmlWriter->writeAttribute('type', 'application'); - break; - case $type->isLibrary(): - $this->xmlWriter->writeAttribute('type', 'library'); - break; - case $type->isExtension(): - $this->xmlWriter->writeAttribute('type', 'extension'); - /* @var $type Extension */ - $this->addExtension($type->getApplicationName(), $type->getVersionConstraint()); - break; - default: - $this->xmlWriter->writeAttribute('type', 'custom'); - } - $this->xmlWriter->endElement(); + return $this->copyrightInformation; } - private function addCopyright(\PHPUnit\PharIo\Manifest\CopyrightInformation $copyrightInformation) : void + public function getRequirements() : RequirementCollection { - $this->xmlWriter->startElement('copyright'); - foreach ($copyrightInformation->getAuthors() as $author) { - $this->xmlWriter->startElement('author'); - $this->xmlWriter->writeAttribute('name', $author->getName()); - $this->xmlWriter->writeAttribute('email', $author->getEmail()->asString()); - $this->xmlWriter->endElement(); - } - $license = $copyrightInformation->getLicense(); - $this->xmlWriter->startElement('license'); - $this->xmlWriter->writeAttribute('type', $license->getName()); - $this->xmlWriter->writeAttribute('url', $license->getUrl()->asString()); - $this->xmlWriter->endElement(); - $this->xmlWriter->endElement(); + return $this->requirements; } - private function addRequirements(\PHPUnit\PharIo\Manifest\RequirementCollection $requirementCollection) : void + public function getBundledComponents() : BundledComponentCollection { - $phpRequirement = new \PHPUnit\PharIo\Version\AnyVersionConstraint(); - $extensions = []; - foreach ($requirementCollection as $requirement) { - if ($requirement instanceof \PHPUnit\PharIo\Manifest\PhpVersionRequirement) { - $phpRequirement = $requirement->getVersionConstraint(); - continue; - } - if ($requirement instanceof \PHPUnit\PharIo\Manifest\PhpExtensionRequirement) { - $extensions[] = $requirement->asString(); - } + return $this->bundledComponents; + } + public function isApplication() : bool + { + return $this->type->isApplication(); + } + public function isLibrary() : bool + { + return $this->type->isLibrary(); + } + public function isExtension() : bool + { + return $this->type->isExtension(); + } + public function isExtensionFor(ApplicationName $application, Version $version = null) : bool + { + if (!$this->isExtension()) { + return \false; } - $this->xmlWriter->startElement('requires'); - $this->xmlWriter->startElement('php'); - $this->xmlWriter->writeAttribute('version', $phpRequirement->asString()); - foreach ($extensions as $extension) { - $this->xmlWriter->startElement('ext'); - $this->xmlWriter->writeAttribute('name', $extension); - $this->xmlWriter->endElement(); + /** @var Extension $type */ + $type = $this->type; + if ($version !== null) { + return $type->isCompatibleWith($application, $version); } - $this->xmlWriter->endElement(); - $this->xmlWriter->endElement(); + return $type->isExtensionFor($application); } - private function addBundles(\PHPUnit\PharIo\Manifest\BundledComponentCollection $bundledComponentCollection) : void +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\PharIo\Manifest; + +class Url +{ + /** @var string */ + private $url; + public function __construct(string $url) { - if (\count($bundledComponentCollection) === 0) { - return; - } - $this->xmlWriter->startElement('bundles'); - foreach ($bundledComponentCollection as $bundledComponent) { - $this->xmlWriter->startElement('component'); - $this->xmlWriter->writeAttribute('name', $bundledComponent->getName()); - $this->xmlWriter->writeAttribute('version', $bundledComponent->getVersion()->getVersionString()); - $this->xmlWriter->endElement(); + $this->ensureUrlIsValid($url); + $this->url = $url; + } + public function asString() : string + { + return $this->url; + } + /** + * @param string $url + * + * @throws InvalidUrlException + */ + private function ensureUrlIsValid($url) : void + { + if (\filter_var($url, \FILTER_VALIDATE_URL) === \false) { + throw new InvalidUrlException(); } - $this->xmlWriter->endElement(); } - private function addExtension(\PHPUnit\PharIo\Manifest\ApplicationName $applicationName, \PHPUnit\PharIo\Version\VersionConstraint $versionConstraint) : void +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\PharIo\Manifest; + +class AuthorCollectionIterator implements \Iterator +{ + /** @var Author[] */ + private $authors; + /** @var int */ + private $position = 0; + public function __construct(AuthorCollection $authors) + { + $this->authors = $authors->getAuthors(); + } + public function rewind() : void { - $this->xmlWriter->startElement('extension'); - $this->xmlWriter->writeAttribute('for', $applicationName->asString()); - $this->xmlWriter->writeAttribute('compatible', $versionConstraint->asString()); - $this->xmlWriter->endElement(); + $this->position = 0; + } + public function valid() : bool + { + return $this->position < \count($this->authors); + } + public function key() : int + { + return $this->position; + } + public function current() : Author + { + return $this->authors[$this->position]; + } + public function next() : void + { + $this->position++; } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\ObjectReflector; +namespace PHPUnit\PharIo\Manifest; -use function count; -use function explode; -use function get_class; -use function is_object; -class ObjectReflector +class Author { - /** - * @param object $object - * - * @throws InvalidArgumentException - */ - public function getAttributes($object) : array + /** @var string */ + private $name; + /** @var Email */ + private $email; + public function __construct(string $name, Email $email) { - if (!\is_object($object)) { - throw new \PHPUnit\SebastianBergmann\ObjectReflector\InvalidArgumentException(); - } - $attributes = []; - $className = \get_class($object); - foreach ((array) $object as $name => $value) { - $name = \explode("\0", (string) $name); - if (\count($name) === 1) { - $name = $name[0]; - } else { - if ($name[1] !== $className) { - $name = $name[1] . '::' . $name[2]; - } else { - $name = $name[2]; - } - } - $attributes[$name] = $value; - } - return $attributes; + $this->name = $name; + $this->email = $email; + } + public function asString() : string + { + return \sprintf('%s <%s>', $this->name, $this->email->asString()); + } + public function getName() : string + { + return $this->name; + } + public function getEmail() : Email + { + return $this->email; } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\ObjectReflector; +namespace PHPUnit\PharIo\Manifest; -use Throwable; -interface Exception extends \Throwable +class ApplicationName { + /** @var string */ + private $name; + public function __construct(string $name) + { + $this->ensureValidFormat($name); + $this->name = $name; + } + public function asString() : string + { + return $this->name; + } + public function isEqual(ApplicationName $name) : bool + { + return $this->name === $name->name; + } + private function ensureValidFormat(string $name) : void + { + if (!\preg_match('#\\w/\\w#', $name)) { + throw new InvalidApplicationNameException(\sprintf('Format of name "%s" is not valid - expected: vendor/packagename', $name), InvalidApplicationNameException::InvalidFormat); + } + } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\ObjectReflector; +namespace PHPUnit\PharIo\Manifest; -class InvalidArgumentException extends \InvalidArgumentException implements \PHPUnit\SebastianBergmann\ObjectReflector\Exception +class Application extends Type { + public function isApplication() : bool + { + return \true; + } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Complexity; +namespace PHPUnit\PharIo\Manifest; -use Throwable; -interface Exception extends \Throwable +use PHPUnit\PharIo\Version\VersionConstraint; +class PhpVersionRequirement implements Requirement { + /** @var VersionConstraint */ + private $versionConstraint; + public function __construct(VersionConstraint $versionConstraint) + { + $this->versionConstraint = $versionConstraint; + } + public function getVersionConstraint() : VersionConstraint + { + return $this->versionConstraint; + } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Complexity; +namespace PHPUnit\PharIo\Manifest; -final class RuntimeException extends \RuntimeException implements \PHPUnit\SebastianBergmann\Complexity\Exception +class License { + /** @var string */ + private $name; + /** @var Url */ + private $url; + public function __construct(string $name, Url $url) + { + $this->name = $name; + $this->url = $url; + } + public function getName() : string + { + return $this->name; + } + public function getUrl() : Url + { + return $this->url; + } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Complexity; +namespace PHPUnit\PharIo\Manifest; -use PHPUnit\PhpParser\Error; -use PHPUnit\PhpParser\Lexer; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\NodeTraverser; -use PHPUnit\PhpParser\NodeVisitor\NameResolver; -use PHPUnit\PhpParser\NodeVisitor\ParentConnectingVisitor; -use PHPUnit\PhpParser\Parser; -use PHPUnit\PhpParser\ParserFactory; -final class Calculator +class Library extends Type { - /** - * @throws RuntimeException - */ - public function calculateForSourceFile(string $sourceFile) : \PHPUnit\SebastianBergmann\Complexity\ComplexityCollection - { - return $this->calculateForSourceString(\file_get_contents($sourceFile)); - } - /** - * @throws RuntimeException - */ - public function calculateForSourceString(string $source) : \PHPUnit\SebastianBergmann\Complexity\ComplexityCollection - { - try { - $nodes = $this->parser()->parse($source); - \assert($nodes !== null); - return $this->calculateForAbstractSyntaxTree($nodes); - // @codeCoverageIgnoreStart - } catch (\PHPUnit\PhpParser\Error $error) { - throw new \PHPUnit\SebastianBergmann\Complexity\RuntimeException($error->getMessage(), (int) $error->getCode(), $error); - } - // @codeCoverageIgnoreEnd - } - /** - * @param Node[] $nodes - * - * @throws RuntimeException - */ - public function calculateForAbstractSyntaxTree(array $nodes) : \PHPUnit\SebastianBergmann\Complexity\ComplexityCollection - { - $traverser = new \PHPUnit\PhpParser\NodeTraverser(); - $complexityCalculatingVisitor = new \PHPUnit\SebastianBergmann\Complexity\ComplexityCalculatingVisitor(\true); - $traverser->addVisitor(new \PHPUnit\PhpParser\NodeVisitor\NameResolver()); - $traverser->addVisitor(new \PHPUnit\PhpParser\NodeVisitor\ParentConnectingVisitor()); - $traverser->addVisitor($complexityCalculatingVisitor); - try { - /* @noinspection UnusedFunctionResultInspection */ - $traverser->traverse($nodes); - // @codeCoverageIgnoreStart - } catch (\PHPUnit\PhpParser\Error $error) { - throw new \PHPUnit\SebastianBergmann\Complexity\RuntimeException($error->getMessage(), (int) $error->getCode(), $error); - } - // @codeCoverageIgnoreEnd - return $complexityCalculatingVisitor->result(); - } - private function parser() : \PHPUnit\PhpParser\Parser + public function isLibrary() : bool { - return (new \PHPUnit\PhpParser\ParserFactory())->create(\PHPUnit\PhpParser\ParserFactory::PREFER_PHP7, new \PHPUnit\PhpParser\Lexer()); + return \true; } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Complexity; +namespace PHPUnit\PharIo\Manifest; -use function assert; -use function is_array; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Name; -use PHPUnit\PhpParser\Node\Stmt; -use PHPUnit\PhpParser\Node\Stmt\Class_; -use PHPUnit\PhpParser\Node\Stmt\ClassMethod; -use PHPUnit\PhpParser\Node\Stmt\Function_; -use PHPUnit\PhpParser\Node\Stmt\Trait_; -use PHPUnit\PhpParser\NodeTraverser; -use PHPUnit\PhpParser\NodeVisitorAbstract; -final class ComplexityCalculatingVisitor extends \PHPUnit\PhpParser\NodeVisitorAbstract +class AuthorCollection implements \Countable, \IteratorAggregate { - /** - * @psalm-var list - */ - private $result = []; - /** - * @var bool - */ - private $shortCircuitTraversal; - public function __construct(bool $shortCircuitTraversal) - { - $this->shortCircuitTraversal = $shortCircuitTraversal; - } - public function enterNode(\PHPUnit\PhpParser\Node $node) : ?int - { - if (!$node instanceof \PHPUnit\PhpParser\Node\Stmt\ClassMethod && !$node instanceof \PHPUnit\PhpParser\Node\Stmt\Function_) { - return null; - } - if ($node instanceof \PHPUnit\PhpParser\Node\Stmt\ClassMethod) { - $name = $this->classMethodName($node); - } else { - $name = $this->functionName($node); - } - $statements = $node->getStmts(); - \assert(\is_array($statements)); - $this->result[] = new \PHPUnit\SebastianBergmann\Complexity\Complexity($name, $this->cyclomaticComplexity($statements)); - if ($this->shortCircuitTraversal) { - return \PHPUnit\PhpParser\NodeTraverser::DONT_TRAVERSE_CHILDREN; - } - return null; - } - public function result() : \PHPUnit\SebastianBergmann\Complexity\ComplexityCollection + /** @var Author[] */ + private $authors = []; + public function add(Author $author) : void { - return \PHPUnit\SebastianBergmann\Complexity\ComplexityCollection::fromList(...$this->result); + $this->authors[] = $author; } /** - * @param Stmt[] $statements + * @return Author[] */ - private function cyclomaticComplexity(array $statements) : int + public function getAuthors() : array { - $traverser = new \PHPUnit\PhpParser\NodeTraverser(); - $cyclomaticComplexityCalculatingVisitor = new \PHPUnit\SebastianBergmann\Complexity\CyclomaticComplexityCalculatingVisitor(); - $traverser->addVisitor($cyclomaticComplexityCalculatingVisitor); - /* @noinspection UnusedFunctionResultInspection */ - $traverser->traverse($statements); - return $cyclomaticComplexityCalculatingVisitor->cyclomaticComplexity(); + return $this->authors; } - private function classMethodName(\PHPUnit\PhpParser\Node\Stmt\ClassMethod $node) : string + public function count() : int { - $parent = $node->getAttribute('parent'); - \assert($parent instanceof \PHPUnit\PhpParser\Node\Stmt\Class_ || $parent instanceof \PHPUnit\PhpParser\Node\Stmt\Trait_); - \assert(isset($parent->namespacedName)); - \assert($parent->namespacedName instanceof \PHPUnit\PhpParser\Node\Name); - return $parent->namespacedName->toString() . '::' . $node->name->toString(); + return \count($this->authors); } - private function functionName(\PHPUnit\PhpParser\Node\Stmt\Function_ $node) : string + public function getIterator() : AuthorCollectionIterator { - \assert(isset($node->namespacedName)); - \assert($node->namespacedName instanceof \PHPUnit\PhpParser\Node\Name); - return $node->namespacedName->toString(); + return new AuthorCollectionIterator($this); } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Complexity; +namespace PHPUnit\PharIo\Manifest; -use function get_class; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanAnd; -use PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanOr; -use PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalAnd; -use PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalOr; -use PHPUnit\PhpParser\Node\Expr\Ternary; -use PHPUnit\PhpParser\Node\Stmt\Case_; -use PHPUnit\PhpParser\Node\Stmt\Catch_; -use PHPUnit\PhpParser\Node\Stmt\ElseIf_; -use PHPUnit\PhpParser\Node\Stmt\For_; -use PHPUnit\PhpParser\Node\Stmt\Foreach_; -use PHPUnit\PhpParser\Node\Stmt\If_; -use PHPUnit\PhpParser\Node\Stmt\While_; -use PHPUnit\PhpParser\NodeVisitorAbstract; -final class CyclomaticComplexityCalculatingVisitor extends \PHPUnit\PhpParser\NodeVisitorAbstract +interface Requirement { - /** - * @var int - */ - private $cyclomaticComplexity = 1; - public function enterNode(\PHPUnit\PhpParser\Node $node) : void - { - /* @noinspection GetClassMissUseInspection */ - switch (\get_class($node)) { - case \PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanAnd::class: - case \PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanOr::class: - case \PHPUnit\PhpParser\Node\Stmt\Case_::class: - case \PHPUnit\PhpParser\Node\Stmt\Catch_::class: - case \PHPUnit\PhpParser\Node\Stmt\ElseIf_::class: - case \PHPUnit\PhpParser\Node\Stmt\For_::class: - case \PHPUnit\PhpParser\Node\Stmt\Foreach_::class: - case \PHPUnit\PhpParser\Node\Stmt\If_::class: - case \PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalAnd::class: - case \PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalOr::class: - case \PHPUnit\PhpParser\Node\Expr\Ternary::class: - case \PHPUnit\PhpParser\Node\Stmt\While_::class: - $this->cyclomaticComplexity++; - } - } - public function cyclomaticComplexity() : int - { - return $this->cyclomaticComplexity; - } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Complexity; +namespace PHPUnit\PharIo\Manifest; -/** - * @psalm-immutable - */ -final class Complexity +use PHPUnit\PharIo\Version\Version; +use PHPUnit\PharIo\Version\VersionConstraint; +class Extension extends Type { - /** - * @var string - */ - private $name; - /** - * @var int - */ - private $cyclomaticComplexity; - public function __construct(string $name, int $cyclomaticComplexity) + /** @var ApplicationName */ + private $application; + /** @var VersionConstraint */ + private $versionConstraint; + public function __construct(ApplicationName $application, VersionConstraint $versionConstraint) { - $this->name = $name; - $this->cyclomaticComplexity = $cyclomaticComplexity; + $this->application = $application; + $this->versionConstraint = $versionConstraint; } - public function name() : string + public function getApplicationName() : ApplicationName { - return $this->name; + return $this->application; } - public function cyclomaticComplexity() : int + public function getVersionConstraint() : VersionConstraint { - return $this->cyclomaticComplexity; + return $this->versionConstraint; + } + public function isExtension() : bool + { + return \true; + } + public function isExtensionFor(ApplicationName $name) : bool + { + return $this->application->isEqual($name); + } + public function isCompatibleWith(ApplicationName $name, Version $version) : bool + { + return $this->isExtensionFor($name) && $this->versionConstraint->complies($version); } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Complexity; +namespace PHPUnit\PharIo\Manifest; -use Iterator; -final class ComplexityCollectionIterator implements \Iterator +class BundledComponentCollectionIterator implements \Iterator { - /** - * @psalm-var list - */ - private $items; - /** - * @var int - */ + /** @var BundledComponent[] */ + private $bundledComponents; + /** @var int */ private $position = 0; - public function __construct(\PHPUnit\SebastianBergmann\Complexity\ComplexityCollection $items) + public function __construct(BundledComponentCollection $bundledComponents) { - $this->items = $items->asArray(); + $this->bundledComponents = $bundledComponents->getBundledComponents(); } public function rewind() : void { @@ -36889,15 +39006,15 @@ final class ComplexityCollectionIterator implements \Iterator } public function valid() : bool { - return isset($this->items[$this->position]); + return $this->position < \count($this->bundledComponents); } public function key() : int { return $this->position; } - public function current() : \PHPUnit\SebastianBergmann\Complexity\Complexity + public function current() : BundledComponent { - return $this->items[$this->position]; + return $this->bundledComponents[$this->position]; } public function next() : void { @@ -36908,9891 +39025,11538 @@ final class ComplexityCollectionIterator implements \Iterator declare (strict_types=1); /* - * This file is part of sebastian/complexity. + * This file is part of PharIo\Manifest. * - * (c) Sebastian Bergmann + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Complexity; +namespace PHPUnit\PharIo\Manifest; -use function count; -use Countable; -use IteratorAggregate; -/** - * @psalm-immutable - */ -final class ComplexityCollection implements \Countable, \IteratorAggregate +class RequirementCollection implements \Countable, \IteratorAggregate { - /** - * @psalm-var list - */ - private $items = []; - public static function fromList(\PHPUnit\SebastianBergmann\Complexity\Complexity ...$items) : self - { - return new self($items); - } - /** - * @psalm-param list $items - */ - private function __construct(array $items) + /** @var Requirement[] */ + private $requirements = []; + public function add(Requirement $requirement) : void { - $this->items = $items; + $this->requirements[] = $requirement; } /** - * @psalm-return list + * @return Requirement[] */ - public function asArray() : array - { - return $this->items; - } - public function getIterator() : \PHPUnit\SebastianBergmann\Complexity\ComplexityCollectionIterator + public function getRequirements() : array { - return new \PHPUnit\SebastianBergmann\Complexity\ComplexityCollectionIterator($this); + return $this->requirements; } public function count() : int { - return \count($this->items); - } - public function isEmpty() : bool - { - return empty($this->items); + return \count($this->requirements); } - public function cyclomaticComplexity() : int + public function getIterator() : RequirementCollectionIterator { - $cyclomaticComplexity = 0; - foreach ($this as $item) { - $cyclomaticComplexity += $item->cyclomaticComplexity(); - } - return $cyclomaticComplexity; + return new RequirementCollectionIterator($this); } } -sebastian/complexity - -Copyright (c) 2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\PharIo\Manifest; -use function str_replace; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Generator +class RequirementCollectionIterator implements \Iterator { - /** - * @var string - */ - private const TEMPLATE = <<<'EOT' - - - - - {tests_directory} - - - - - - {src_directory} - - - - -EOT; - public function generateDefaultConfiguration(string $phpunitVersion, string $bootstrapScript, string $testsDirectory, string $srcDirectory, string $cacheDirectory) : string + /** @var Requirement[] */ + private $requirements; + /** @var int */ + private $position = 0; + public function __construct(RequirementCollection $requirements) + { + $this->requirements = $requirements->getRequirements(); + } + public function rewind() : void + { + $this->position = 0; + } + public function valid() : bool + { + return $this->position < \count($this->requirements); + } + public function key() : int + { + return $this->position; + } + public function current() : Requirement + { + return $this->requirements[$this->position]; + } + public function next() : void { - return \str_replace(['{phpunit_version}', '{bootstrap_script}', '{tests_directory}', '{src_directory}', '{cache_directory}'], [$phpunitVersion, $bootstrapScript, $testsDirectory, $srcDirectory, $cacheDirectory], self::TEMPLATE); + $this->position++; } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration\Logging\TestDox; +namespace PHPUnit\PharIo\Manifest; -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class Html +class Email { - /** - * @var File - */ - private $target; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\File $target) + /** @var string */ + private $email; + public function __construct(string $email) { - $this->target = $target; + $this->ensureEmailIsValid($email); + $this->email = $email; } - public function target() : \PHPUnit\TextUI\XmlConfiguration\File + public function asString() : string { - return $this->target; + return $this->email; + } + private function ensureEmailIsValid(string $url) : void + { + if (\filter_var($url, \FILTER_VALIDATE_EMAIL) === \false) { + throw new InvalidEmailException(); + } } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration\Logging\TestDox; +namespace PHPUnit\PharIo\Manifest; -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class Xml +use PHPUnit\PharIo\Version\VersionConstraint; +abstract class Type { - /** - * @var File - */ - private $target; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\File $target) + public static function application() : Application { - $this->target = $target; + return new Application(); } - public function target() : \PHPUnit\TextUI\XmlConfiguration\File + public static function library() : Library { - return $this->target; + return new Library(); + } + public static function extension(ApplicationName $application, VersionConstraint $versionConstraint) : Extension + { + return new Extension($application, $versionConstraint); + } + /** @psalm-assert-if-true Application $this */ + public function isApplication() : bool + { + return \false; + } + /** @psalm-assert-if-true Library $this */ + public function isLibrary() : bool + { + return \false; + } + /** @psalm-assert-if-true Extension $this */ + public function isExtension() : bool + { + return \false; } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration\Logging\TestDox; +namespace PHPUnit\PharIo\Manifest; -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class Text +class BundledComponentCollection implements \Countable, \IteratorAggregate { + /** @var BundledComponent[] */ + private $bundledComponents = []; + public function add(BundledComponent $bundledComponent) : void + { + $this->bundledComponents[] = $bundledComponent; + } /** - * @var File + * @return BundledComponent[] */ - private $target; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\File $target) + public function getBundledComponents() : array { - $this->target = $target; + return $this->bundledComponents; } - public function target() : \PHPUnit\TextUI\XmlConfiguration\File + public function count() : int { - return $this->target; + return \count($this->bundledComponents); + } + public function getIterator() : BundledComponentCollectionIterator + { + return new BundledComponentCollectionIterator($this); } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration\Logging; +namespace PHPUnit\PharIo\Manifest; -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class TeamCity +class PhpExtensionRequirement implements Requirement { - /** - * @var File - */ - private $target; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\File $target) + /** @var string */ + private $extension; + public function __construct(string $extension) { - $this->target = $target; + $this->extension = $extension; } - public function target() : \PHPUnit\TextUI\XmlConfiguration\File + public function asString() : string { - return $this->target; + return $this->extension; } } + * (c) Arne Blankerts , Sebastian Heuer , Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration\Logging; +namespace PHPUnit\PharIo\Manifest; -use PHPUnit\TextUI\XmlConfiguration\Exception; -use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Html as TestDoxHtml; -use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Text as TestDoxText; -use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Xml as TestDoxXml; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class Logging +use PHPUnit\PharIo\Version\Version; +class BundledComponent { - /** - * @var ?Junit - */ - private $junit; - /** - * @var ?Text - */ - private $text; - /** - * @var ?TeamCity - */ - private $teamCity; - /** - * @var ?TestDoxHtml - */ - private $testDoxHtml; - /** - * @var ?TestDoxText - */ - private $testDoxText; - /** - * @var ?TestDoxXml - */ - private $testDoxXml; - public function __construct(?\PHPUnit\TextUI\XmlConfiguration\Logging\Junit $junit, ?\PHPUnit\TextUI\XmlConfiguration\Logging\Text $text, ?\PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity $teamCity, ?\PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Html $testDoxHtml, ?\PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Text $testDoxText, ?\PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Xml $testDoxXml) - { - $this->junit = $junit; - $this->text = $text; - $this->teamCity = $teamCity; - $this->testDoxHtml = $testDoxHtml; - $this->testDoxText = $testDoxText; - $this->testDoxXml = $testDoxXml; - } - public function hasJunit() : bool - { - return $this->junit !== null; - } - public function junit() : \PHPUnit\TextUI\XmlConfiguration\Logging\Junit + /** @var string */ + private $name; + /** @var Version */ + private $version; + public function __construct(string $name, Version $version) { - if ($this->junit === null) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Logger "JUnit XML" is not configured'); - } - return $this->junit; + $this->name = $name; + $this->version = $version; } - public function hasText() : bool + public function getName() : string { - return $this->text !== null; + return $this->name; } - public function text() : \PHPUnit\TextUI\XmlConfiguration\Logging\Text + public function getVersion() : Version { - if ($this->text === null) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Logger "Text" is not configured'); - } - return $this->text; + return $this->version; } - public function hasTeamCity() : bool +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\PharIo\Manifest; + +class CopyrightInformation +{ + /** @var AuthorCollection */ + private $authors; + /** @var License */ + private $license; + public function __construct(AuthorCollection $authors, License $license) { - return $this->teamCity !== null; + $this->authors = $authors; + $this->license = $license; } - public function teamCity() : \PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity + public function getAuthors() : AuthorCollection { - if ($this->teamCity === null) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Logger "Team City" is not configured'); - } - return $this->teamCity; + return $this->authors; } - public function hasTestDoxHtml() : bool + public function getLicense() : License { - return $this->testDoxHtml !== null; + return $this->license; } - public function testDoxHtml() : \PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Html +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\PharIo\Manifest; + +class ManifestLoader +{ + public static function fromFile(string $filename) : Manifest { - if ($this->testDoxHtml === null) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Logger "TestDox HTML" is not configured'); + try { + return (new ManifestDocumentMapper())->map(ManifestDocument::fromFile($filename)); + } catch (Exception $e) { + throw new ManifestLoaderException(\sprintf('Loading %s failed.', $filename), (int) $e->getCode(), $e); } - return $this->testDoxHtml; } - public function hasTestDoxText() : bool + public static function fromPhar(string $filename) : Manifest { - return $this->testDoxText !== null; + return self::fromFile('phar://' . $filename . '/manifest.xml'); } - public function testDoxText() : \PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Text + public static function fromString(string $manifest) : Manifest { - if ($this->testDoxText === null) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Logger "TestDox Text" is not configured'); + try { + return (new ManifestDocumentMapper())->map(ManifestDocument::fromString($manifest)); + } catch (Exception $e) { + throw new ManifestLoaderException('Processing string failed', (int) $e->getCode(), $e); } - return $this->testDoxText; } - public function hasTestDoxXml() : bool - { - return $this->testDoxXml !== null; - } - public function testDoxXml() : \PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Xml +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CliParser; + +use function sprintf; +use RuntimeException; +final class AmbiguousOptionException extends RuntimeException implements Exception +{ + public function __construct(string $option) { - if ($this->testDoxXml === null) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Logger "TestDox XML" is not configured'); - } - return $this->testDoxXml; + parent::__construct(sprintf('Option "%s" is ambiguous', $option)); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration\Logging; +namespace PHPUnit\SebastianBergmann\CliParser; -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class Text +use function sprintf; +use RuntimeException; +final class UnknownOptionException extends RuntimeException implements Exception { - /** - * @var File - */ - private $target; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\File $target) + public function __construct(string $option) { - $this->target = $target; + parent::__construct(sprintf('Unknown option "%s"', $option)); } - public function target() : \PHPUnit\TextUI\XmlConfiguration\File +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CliParser; + +use function sprintf; +use RuntimeException; +final class OptionDoesNotAllowArgumentException extends RuntimeException implements Exception +{ + public function __construct(string $option) { - return $this->target; + parent::__construct(sprintf('Option "%s" does not allow an argument', $option)); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration\Logging; +namespace PHPUnit\SebastianBergmann\CliParser; -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable +use Throwable; +interface Exception extends Throwable +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -final class Junit +namespace PHPUnit\SebastianBergmann\CliParser; + +use function sprintf; +use RuntimeException; +final class RequiredOptionArgumentMissingException extends RuntimeException implements Exception { - /** - * @var File - */ - private $target; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\File $target) - { - $this->target = $target; - } - public function target() : \PHPUnit\TextUI\XmlConfiguration\File + public function __construct(string $option) { - return $this->target; + parent::__construct(sprintf('Required argument for option "%s" is missing', $option)); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage; +namespace PHPUnit\SebastianBergmann\CliParser; +use function array_map; +use function array_merge; +use function array_shift; +use function array_slice; +use function assert; use function count; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Clover; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Cobertura; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Crap4j; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Html; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Php; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Text; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Xml; -use PHPUnit\TextUI\XmlConfiguration\Directory; -use PHPUnit\TextUI\XmlConfiguration\Exception; -use PHPUnit\TextUI\XmlConfiguration\FileCollection; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class CodeCoverage +use function current; +use function explode; +use function is_array; +use function is_int; +use function is_string; +use function key; +use function next; +use function preg_replace; +use function reset; +use function sort; +use function strlen; +use function strpos; +use function strstr; +use function substr; +final class Parser { /** - * @var ?Directory - */ - private $cacheDirectory; - /** - * @var DirectoryCollection - */ - private $directories; - /** - * @var FileCollection - */ - private $files; - /** - * @var DirectoryCollection - */ - private $excludeDirectories; - /** - * @var FileCollection - */ - private $excludeFiles; - /** - * @var bool - */ - private $pathCoverage; - /** - * @var bool - */ - private $includeUncoveredFiles; - /** - * @var bool - */ - private $processUncoveredFiles; - /** - * @var bool - */ - private $ignoreDeprecatedCodeUnits; - /** - * @var bool - */ - private $disableCodeCoverageIgnore; - /** - * @var ?Clover - */ - private $clover; - /** - * @var ?Cobertura - */ - private $cobertura; - /** - * @var ?Crap4j - */ - private $crap4j; - /** - * @var ?Html - */ - private $html; - /** - * @var ?Php - */ - private $php; - /** - * @var ?Text - */ - private $text; - /** - * @var ?Xml + * @psalm-param list $argv + * @psalm-param list $longOptions + * + * @throws AmbiguousOptionException + * @throws RequiredOptionArgumentMissingException + * @throws OptionDoesNotAllowArgumentException + * @throws UnknownOptionException */ - private $xml; - public function __construct(?\PHPUnit\TextUI\XmlConfiguration\Directory $cacheDirectory, \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection $directories, \PHPUnit\TextUI\XmlConfiguration\FileCollection $files, \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection $excludeDirectories, \PHPUnit\TextUI\XmlConfiguration\FileCollection $excludeFiles, bool $pathCoverage, bool $includeUncoveredFiles, bool $processUncoveredFiles, bool $ignoreDeprecatedCodeUnits, bool $disableCodeCoverageIgnore, ?\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Clover $clover, ?\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Cobertura $cobertura, ?\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Crap4j $crap4j, ?\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Html $html, ?\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Php $php, ?\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Text $text, ?\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Xml $xml) + public function parse(array $argv, string $shortOptions, array $longOptions = null) : array { - $this->cacheDirectory = $cacheDirectory; - $this->directories = $directories; - $this->files = $files; - $this->excludeDirectories = $excludeDirectories; - $this->excludeFiles = $excludeFiles; - $this->pathCoverage = $pathCoverage; - $this->includeUncoveredFiles = $includeUncoveredFiles; - $this->processUncoveredFiles = $processUncoveredFiles; - $this->ignoreDeprecatedCodeUnits = $ignoreDeprecatedCodeUnits; - $this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore; - $this->clover = $clover; - $this->cobertura = $cobertura; - $this->crap4j = $crap4j; - $this->html = $html; - $this->php = $php; - $this->text = $text; - $this->xml = $xml; + if (empty($argv)) { + return [[], []]; + } + $options = []; + $nonOptions = []; + if ($longOptions) { + sort($longOptions); + } + if (isset($argv[0][0]) && $argv[0][0] !== '-') { + array_shift($argv); + } + reset($argv); + $argv = array_map('trim', $argv); + while (\false !== ($arg = current($argv))) { + $i = key($argv); + assert(is_int($i)); + next($argv); + if ($arg === '') { + continue; + } + if ($arg === '--') { + $nonOptions = array_merge($nonOptions, array_slice($argv, $i + 1)); + break; + } + if ($arg[0] !== '-' || strlen($arg) > 1 && $arg[1] === '-' && !$longOptions) { + $nonOptions[] = $arg; + continue; + } + if (strlen($arg) > 1 && $arg[1] === '-' && is_array($longOptions)) { + $this->parseLongOption(substr($arg, 2), $longOptions, $options, $argv); + } else { + $this->parseShortOption(substr($arg, 1), $shortOptions, $options, $argv); + } + } + return [$options, $nonOptions]; } /** - * @psalm-assert-if-true !null $this->cacheDirectory + * @throws RequiredOptionArgumentMissingException */ - public function hasCacheDirectory() : bool + private function parseShortOption(string $arg, string $shortOptions, array &$opts, array &$args) : void { - return $this->cacheDirectory !== null; + $argLength = strlen($arg); + for ($i = 0; $i < $argLength; $i++) { + $option = $arg[$i]; + $optionArgument = null; + if ($arg[$i] === ':' || ($spec = strstr($shortOptions, $option)) === \false) { + throw new UnknownOptionException('-' . $option); + } + assert(is_string($spec)); + if (strlen($spec) > 1 && $spec[1] === ':') { + if ($i + 1 < $argLength) { + $opts[] = [$option, substr($arg, $i + 1)]; + break; + } + if (!(strlen($spec) > 2 && $spec[2] === ':')) { + $optionArgument = current($args); + if (!$optionArgument) { + throw new RequiredOptionArgumentMissingException('-' . $option); + } + assert(is_string($optionArgument)); + next($args); + } + } + $opts[] = [$option, $optionArgument]; + } } /** - * @throws Exception + * @psalm-param list $longOptions + * + * @throws AmbiguousOptionException + * @throws RequiredOptionArgumentMissingException + * @throws OptionDoesNotAllowArgumentException + * @throws UnknownOptionException */ - public function cacheDirectory() : \PHPUnit\TextUI\XmlConfiguration\Directory + private function parseLongOption(string $arg, array $longOptions, array &$opts, array &$args) : void { - if (!$this->hasCacheDirectory()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('No cache directory has been configured'); + $count = count($longOptions); + $list = explode('=', $arg); + $option = $list[0]; + $optionArgument = null; + if (count($list) > 1) { + $optionArgument = $list[1]; } - return $this->cacheDirectory; - } - public function hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport() : bool - { - return \count($this->directories) > 0 || \count($this->files) > 0; - } - public function directories() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection - { - return $this->directories; + $optionLength = strlen($option); + foreach ($longOptions as $i => $longOption) { + $opt_start = substr($longOption, 0, $optionLength); + if ($opt_start !== $option) { + continue; + } + $opt_rest = substr($longOption, $optionLength); + if ($opt_rest !== '' && $i + 1 < $count && $option[0] !== '=' && strpos($longOptions[$i + 1], $option) === 0) { + throw new AmbiguousOptionException('--' . $option); + } + if (substr($longOption, -1) === '=') { + /* @noinspection StrlenInEmptyStringCheckContextInspection */ + if (substr($longOption, -2) !== '==' && !strlen((string) $optionArgument)) { + if (\false === ($optionArgument = current($args))) { + throw new RequiredOptionArgumentMissingException('--' . $option); + } + next($args); + } + } elseif ($optionArgument) { + throw new OptionDoesNotAllowArgumentException('--' . $option); + } + $fullOption = '--' . preg_replace('/={1,2}$/', '', $longOption); + $opts[] = [$fullOption, $optionArgument]; + return; + } + throw new UnknownOptionException('--' . $option); } - public function files() : \PHPUnit\TextUI\XmlConfiguration\FileCollection +} +sebastian/cli-parser + +Copyright (c) 2020, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +use PHPUnit\Symfony\Polyfill\Ctype as p; +if (\PHP_VERSION_ID >= 80000) { + return require __DIR__ . '/bootstrap80.php'; +} +if (!\function_exists('ctype_alnum')) { + function ctype_alnum($text) { - return $this->files; + return p\Ctype::ctype_alnum($text); } - public function excludeDirectories() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection +} +if (!\function_exists('ctype_alpha')) { + function ctype_alpha($text) { - return $this->excludeDirectories; + return p\Ctype::ctype_alpha($text); } - public function excludeFiles() : \PHPUnit\TextUI\XmlConfiguration\FileCollection +} +if (!\function_exists('ctype_cntrl')) { + function ctype_cntrl($text) { - return $this->excludeFiles; + return p\Ctype::ctype_cntrl($text); } - public function pathCoverage() : bool +} +if (!\function_exists('ctype_digit')) { + function ctype_digit($text) { - return $this->pathCoverage; + return p\Ctype::ctype_digit($text); } - public function includeUncoveredFiles() : bool +} +if (!\function_exists('ctype_graph')) { + function ctype_graph($text) { - return $this->includeUncoveredFiles; + return p\Ctype::ctype_graph($text); } - public function ignoreDeprecatedCodeUnits() : bool +} +if (!\function_exists('ctype_lower')) { + function ctype_lower($text) { - return $this->ignoreDeprecatedCodeUnits; + return p\Ctype::ctype_lower($text); } - public function disableCodeCoverageIgnore() : bool +} +if (!\function_exists('ctype_print')) { + function ctype_print($text) { - return $this->disableCodeCoverageIgnore; + return p\Ctype::ctype_print($text); } - public function processUncoveredFiles() : bool +} +if (!\function_exists('ctype_punct')) { + function ctype_punct($text) { - return $this->processUncoveredFiles; + return p\Ctype::ctype_punct($text); } - /** - * @psalm-assert-if-true !null $this->clover - */ - public function hasClover() : bool +} +if (!\function_exists('ctype_space')) { + function ctype_space($text) { - return $this->clover !== null; + return p\Ctype::ctype_space($text); } - /** - * @throws Exception - */ - public function clover() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Clover +} +if (!\function_exists('ctype_upper')) { + function ctype_upper($text) { - if (!$this->hasClover()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Code Coverage report "Clover XML" has not been configured'); - } - return $this->clover; + return p\Ctype::ctype_upper($text); } - /** - * @psalm-assert-if-true !null $this->cobertura - */ - public function hasCobertura() : bool +} +if (!\function_exists('ctype_xdigit')) { + function ctype_xdigit($text) { - return $this->cobertura !== null; + return p\Ctype::ctype_xdigit($text); } - /** - * @throws Exception - */ - public function cobertura() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Cobertura +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +use PHPUnit\Symfony\Polyfill\Ctype as p; +if (!\function_exists('ctype_alnum')) { + function ctype_alnum(mixed $text) : bool { - if (!$this->hasCobertura()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Code Coverage report "Cobertura XML" has not been configured'); - } - return $this->cobertura; + return p\Ctype::ctype_alnum($text); } - /** - * @psalm-assert-if-true !null $this->crap4j - */ - public function hasCrap4j() : bool +} +if (!\function_exists('ctype_alpha')) { + function ctype_alpha(mixed $text) : bool { - return $this->crap4j !== null; + return p\Ctype::ctype_alpha($text); } - /** - * @throws Exception - */ - public function crap4j() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Crap4j +} +if (!\function_exists('ctype_cntrl')) { + function ctype_cntrl(mixed $text) : bool { - if (!$this->hasCrap4j()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Code Coverage report "Crap4J" has not been configured'); - } - return $this->crap4j; + return p\Ctype::ctype_cntrl($text); } - /** - * @psalm-assert-if-true !null $this->html - */ - public function hasHtml() : bool +} +if (!\function_exists('ctype_digit')) { + function ctype_digit(mixed $text) : bool { - return $this->html !== null; + return p\Ctype::ctype_digit($text); } - /** - * @throws Exception - */ - public function html() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Html +} +if (!\function_exists('ctype_graph')) { + function ctype_graph(mixed $text) : bool { - if (!$this->hasHtml()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Code Coverage report "HTML" has not been configured'); - } - return $this->html; + return p\Ctype::ctype_graph($text); } - /** - * @psalm-assert-if-true !null $this->php - */ - public function hasPhp() : bool +} +if (!\function_exists('ctype_lower')) { + function ctype_lower(mixed $text) : bool { - return $this->php !== null; + return p\Ctype::ctype_lower($text); } - /** - * @throws Exception - */ - public function php() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Php +} +if (!\function_exists('ctype_print')) { + function ctype_print(mixed $text) : bool { - if (!$this->hasPhp()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Code Coverage report "PHP" has not been configured'); - } - return $this->php; + return p\Ctype::ctype_print($text); } - /** - * @psalm-assert-if-true !null $this->text - */ - public function hasText() : bool +} +if (!\function_exists('ctype_punct')) { + function ctype_punct(mixed $text) : bool { - return $this->text !== null; + return p\Ctype::ctype_punct($text); } - /** - * @throws Exception - */ - public function text() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Text +} +if (!\function_exists('ctype_space')) { + function ctype_space(mixed $text) : bool { - if (!$this->hasText()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Code Coverage report "Text" has not been configured'); - } - return $this->text; + return p\Ctype::ctype_space($text); } - /** - * @psalm-assert-if-true !null $this->xml - */ - public function hasXml() : bool +} +if (!\function_exists('ctype_upper')) { + function ctype_upper(mixed $text) : bool { - return $this->xml !== null; + return p\Ctype::ctype_upper($text); } - /** - * @throws Exception - */ - public function xml() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Xml +} +if (!\function_exists('ctype_xdigit')) { + function ctype_xdigit(mixed $text) : bool { - if (!$this->hasXml()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Code Coverage report "XML" has not been configured'); - } - return $this->xml; + return p\Ctype::ctype_xdigit($text); } } + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; +namespace PHPUnit\Symfony\Polyfill\Ctype; -use PHPUnit\TextUI\XmlConfiguration\Directory; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable + * Ctype implementation through regex. + * + * @internal + * + * @author Gert de Pagter */ -final class Html +final class Ctype { /** - * @var Directory + * Returns TRUE if every character in text is either a letter or a digit, FALSE otherwise. + * + * @see https://php.net/ctype-alnum + * + * @param string|int $text + * + * @return bool */ - private $target; + public static function ctype_alnum($text) + { + $text = self::convert_int_to_char_for_ctype($text); + return \is_string($text) && '' !== $text && !\preg_match('/[^A-Za-z0-9]/', $text); + } /** - * @var int + * Returns TRUE if every character in text is a letter, FALSE otherwise. + * + * @see https://php.net/ctype-alpha + * + * @param string|int $text + * + * @return bool */ - private $lowUpperBound; + public static function ctype_alpha($text) + { + $text = self::convert_int_to_char_for_ctype($text); + return \is_string($text) && '' !== $text && !\preg_match('/[^A-Za-z]/', $text); + } /** - * @var int + * Returns TRUE if every character in text is a control character from the current locale, FALSE otherwise. + * + * @see https://php.net/ctype-cntrl + * + * @param string|int $text + * + * @return bool */ - private $highLowerBound; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\Directory $target, int $lowUpperBound, int $highLowerBound) + public static function ctype_cntrl($text) { - $this->target = $target; - $this->lowUpperBound = $lowUpperBound; - $this->highLowerBound = $highLowerBound; + $text = self::convert_int_to_char_for_ctype($text); + return \is_string($text) && '' !== $text && !\preg_match('/[^\\x00-\\x1f\\x7f]/', $text); } - public function target() : \PHPUnit\TextUI\XmlConfiguration\Directory + /** + * Returns TRUE if every character in the string text is a decimal digit, FALSE otherwise. + * + * @see https://php.net/ctype-digit + * + * @param string|int $text + * + * @return bool + */ + public static function ctype_digit($text) { - return $this->target; + $text = self::convert_int_to_char_for_ctype($text); + return \is_string($text) && '' !== $text && !\preg_match('/[^0-9]/', $text); } - public function lowUpperBound() : int + /** + * Returns TRUE if every character in text is printable and actually creates visible output (no white space), FALSE otherwise. + * + * @see https://php.net/ctype-graph + * + * @param string|int $text + * + * @return bool + */ + public static function ctype_graph($text) { - return $this->lowUpperBound; + $text = self::convert_int_to_char_for_ctype($text); + return \is_string($text) && '' !== $text && !\preg_match('/[^!-~]/', $text); } - public function highLowerBound() : int + /** + * Returns TRUE if every character in text is a lowercase letter. + * + * @see https://php.net/ctype-lower + * + * @param string|int $text + * + * @return bool + */ + public static function ctype_lower($text) { - return $this->highLowerBound; + $text = self::convert_int_to_char_for_ctype($text); + return \is_string($text) && '' !== $text && !\preg_match('/[^a-z]/', $text); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; - -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class Clover -{ /** - * @var File + * Returns TRUE if every character in text will actually create output (including blanks). Returns FALSE if text contains control characters or characters that do not have any output or control function at all. + * + * @see https://php.net/ctype-print + * + * @param string|int $text + * + * @return bool */ - private $target; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\File $target) + public static function ctype_print($text) { - $this->target = $target; + $text = self::convert_int_to_char_for_ctype($text); + return \is_string($text) && '' !== $text && !\preg_match('/[^ -~]/', $text); } - public function target() : \PHPUnit\TextUI\XmlConfiguration\File + /** + * Returns TRUE if every character in text is printable, but neither letter, digit or blank, FALSE otherwise. + * + * @see https://php.net/ctype-punct + * + * @param string|int $text + * + * @return bool + */ + public static function ctype_punct($text) { - return $this->target; + $text = self::convert_int_to_char_for_ctype($text); + return \is_string($text) && '' !== $text && !\preg_match('/[^!-\\/\\:-@\\[-`\\{-~]/', $text); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; - -use PHPUnit\TextUI\XmlConfiguration\Directory; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class Xml -{ /** - * @var Directory + * Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. Besides the blank character this also includes tab, vertical tab, line feed, carriage return and form feed characters. + * + * @see https://php.net/ctype-space + * + * @param string|int $text + * + * @return bool */ - private $target; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\Directory $target) + public static function ctype_space($text) { - $this->target = $target; + $text = self::convert_int_to_char_for_ctype($text); + return \is_string($text) && '' !== $text && !\preg_match('/[^\\s]/', $text); } - public function target() : \PHPUnit\TextUI\XmlConfiguration\Directory + /** + * Returns TRUE if every character in text is an uppercase letter. + * + * @see https://php.net/ctype-upper + * + * @param string|int $text + * + * @return bool + */ + public static function ctype_upper($text) { - return $this->target; + $text = self::convert_int_to_char_for_ctype($text); + return \is_string($text) && '' !== $text && !\preg_match('/[^A-Z]/', $text); + } + /** + * Returns TRUE if every character in text is a hexadecimal 'digit', that is a decimal digit or a character from [A-Fa-f] , FALSE otherwise. + * + * @see https://php.net/ctype-xdigit + * + * @param string|int $text + * + * @return bool + */ + public static function ctype_xdigit($text) + { + $text = self::convert_int_to_char_for_ctype($text); + return \is_string($text) && '' !== $text && !\preg_match('/[^A-Fa-f0-9]/', $text); + } + /** + * Converts integers to their char versions according to normal ctype behaviour, if needed. + * + * If an integer between -128 and 255 inclusive is provided, + * it is interpreted as the ASCII value of a single character + * (negative values have 256 added in order to allow characters in the Extended ASCII range). + * Any other integer is interpreted as a string containing the decimal digits of the integer. + * + * @param string|int $int + * + * @return mixed + */ + private static function convert_int_to_char_for_ctype($int) + { + if (!\is_int($int)) { + return $int; + } + if ($int < -128 || $int > 255) { + return (string) $int; + } + if ($int < 0) { + $int += 256; + } + return \chr($int); } } +Copyright (c) 2018-2019 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; - -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class Php -{ - /** - * @var File - */ - private $target; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\File $target) - { - $this->target = $target; - } - public function target() : \PHPUnit\TextUI\XmlConfiguration\File - { - return $this->target; - } + */ +namespace PHPUnit\SebastianBergmann\Complexity; + +final class RuntimeException extends \RuntimeException implements Exception +{ } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; +namespace PHPUnit\SebastianBergmann\Complexity; -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class Crap4j +use Throwable; +interface Exception extends Throwable { - /** - * @var File - */ - private $target; - /** - * @var int - */ - private $threshold; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\File $target, int $threshold) - { - $this->target = $target; - $this->threshold = $threshold; - } - public function target() : \PHPUnit\TextUI\XmlConfiguration\File - { - return $this->target; - } - public function threshold() : int - { - return $this->threshold; - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; +namespace PHPUnit\SebastianBergmann\Complexity; -use PHPUnit\TextUI\XmlConfiguration\File; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class Cobertura +use PHPUnit\PhpParser\Error; +use PHPUnit\PhpParser\Lexer; +use PHPUnit\PhpParser\Node; +use PHPUnit\PhpParser\NodeTraverser; +use PHPUnit\PhpParser\NodeVisitor\NameResolver; +use PHPUnit\PhpParser\NodeVisitor\ParentConnectingVisitor; +use PHPUnit\PhpParser\Parser; +use PHPUnit\PhpParser\ParserFactory; +final class Calculator { /** - * @var File + * @throws RuntimeException */ - private $target; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\File $target) + public function calculateForSourceFile(string $sourceFile) : ComplexityCollection { - $this->target = $target; + return $this->calculateForSourceString(\file_get_contents($sourceFile)); } - public function target() : \PHPUnit\TextUI\XmlConfiguration\File + /** + * @throws RuntimeException + */ + public function calculateForSourceString(string $source) : ComplexityCollection { - return $this->target; + try { + $nodes = $this->parser()->parse($source); + \assert($nodes !== null); + return $this->calculateForAbstractSyntaxTree($nodes); + // @codeCoverageIgnoreStart + } catch (Error $error) { + throw new RuntimeException($error->getMessage(), (int) $error->getCode(), $error); + } + // @codeCoverageIgnoreEnd + } + /** + * @param Node[] $nodes + * + * @throws RuntimeException + */ + public function calculateForAbstractSyntaxTree(array $nodes) : ComplexityCollection + { + $traverser = new NodeTraverser(); + $complexityCalculatingVisitor = new ComplexityCalculatingVisitor(\true); + $traverser->addVisitor(new NameResolver()); + $traverser->addVisitor(new ParentConnectingVisitor()); + $traverser->addVisitor($complexityCalculatingVisitor); + try { + /* @noinspection UnusedFunctionResultInspection */ + $traverser->traverse($nodes); + // @codeCoverageIgnoreStart + } catch (Error $error) { + throw new RuntimeException($error->getMessage(), (int) $error->getCode(), $error); + } + // @codeCoverageIgnoreEnd + return $complexityCalculatingVisitor->result(); + } + private function parser() : Parser + { + return (new ParserFactory())->create(ParserFactory::PREFER_PHP7, new Lexer()); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; +namespace PHPUnit\SebastianBergmann\Complexity; -use PHPUnit\TextUI\XmlConfiguration\File; +use function count; +use Countable; +use IteratorAggregate; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit * @psalm-immutable */ -final class Text +final class ComplexityCollection implements Countable, IteratorAggregate { /** - * @var File + * @psalm-var list */ - private $target; + private $items = []; + public static function fromList(Complexity ...$items) : self + { + return new self($items); + } /** - * @var bool + * @psalm-param list $items */ - private $showUncoveredFiles; + private function __construct(array $items) + { + $this->items = $items; + } /** - * @var bool + * @psalm-return list */ - private $showOnlySummary; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\File $target, bool $showUncoveredFiles, bool $showOnlySummary) + public function asArray() : array { - $this->target = $target; - $this->showUncoveredFiles = $showUncoveredFiles; - $this->showOnlySummary = $showOnlySummary; + return $this->items; } - public function target() : \PHPUnit\TextUI\XmlConfiguration\File + public function getIterator() : ComplexityCollectionIterator { - return $this->target; + return new ComplexityCollectionIterator($this); } - public function showUncoveredFiles() : bool + public function count() : int { - return $this->showUncoveredFiles; + return count($this->items); } - public function showOnlySummary() : bool + public function isEmpty() : bool { - return $this->showOnlySummary; + return empty($this->items); + } + public function cyclomaticComplexity() : int + { + $cyclomaticComplexity = 0; + foreach ($this as $item) { + $cyclomaticComplexity += $item->cyclomaticComplexity(); + } + return $cyclomaticComplexity; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter; +namespace PHPUnit\SebastianBergmann\Complexity; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit * @psalm-immutable */ -final class Directory +final class Complexity { /** * @var string */ - private $path; - /** - * @var string - */ - private $prefix; - /** - * @var string - */ - private $suffix; + private $name; /** - * @var string + * @var int */ - private $group; - public function __construct(string $path, string $prefix, string $suffix, string $group) - { - $this->path = $path; - $this->prefix = $prefix; - $this->suffix = $suffix; - $this->group = $group; - } - public function path() : string - { - return $this->path; - } - public function prefix() : string + private $cyclomaticComplexity; + public function __construct(string $name, int $cyclomaticComplexity) { - return $this->prefix; + $this->name = $name; + $this->cyclomaticComplexity = $cyclomaticComplexity; } - public function suffix() : string + public function name() : string { - return $this->suffix; + return $this->name; } - public function group() : string + public function cyclomaticComplexity() : int { - return $this->group; + return $this->cyclomaticComplexity; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter; +namespace PHPUnit\SebastianBergmann\Complexity; -use function count; -use Countable; -use IteratorAggregate; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class DirectoryCollection implements \Countable, \IteratorAggregate +use Iterator; +final class ComplexityCollectionIterator implements Iterator { /** - * @var Directory[] + * @psalm-var list */ - private $directories; + private $items; /** - * @param Directory[] $directories + * @var int */ - public static function fromArray(array $directories) : self + private $position = 0; + public function __construct(ComplexityCollection $items) { - return new self(...$directories); + $this->items = $items->asArray(); } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\Directory ...$directories) + public function rewind() : void { - $this->directories = $directories; + $this->position = 0; } - /** - * @return Directory[] - */ - public function asArray() : array + public function valid() : bool { - return $this->directories; + return isset($this->items[$this->position]); } - public function count() : int + public function key() : int { - return \count($this->directories); + return $this->position; } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollectionIterator + public function current() : Complexity { - return new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollectionIterator($this); + return $this->items[$this->position]; + } + public function next() : void + { + $this->position++; } } +sebastian/complexity + +Copyright (c) 2020, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter; +namespace PHPUnit\SebastianBergmann\Complexity; -use function count; -use function iterator_count; -use Countable; -use Iterator; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class DirectoryCollectionIterator implements \Countable, \Iterator +use function assert; +use function is_array; +use PHPUnit\PhpParser\Node; +use PHPUnit\PhpParser\Node\Name; +use PHPUnit\PhpParser\Node\Stmt; +use PHPUnit\PhpParser\Node\Stmt\Class_; +use PHPUnit\PhpParser\Node\Stmt\ClassMethod; +use PHPUnit\PhpParser\Node\Stmt\Function_; +use PHPUnit\PhpParser\Node\Stmt\Trait_; +use PHPUnit\PhpParser\NodeTraverser; +use PHPUnit\PhpParser\NodeVisitorAbstract; +final class ComplexityCalculatingVisitor extends NodeVisitorAbstract { /** - * @var Directory[] + * @psalm-var list */ - private $directories; + private $result = []; /** - * @var int + * @var bool */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection $directories) - { - $this->directories = $directories->asArray(); - } - public function count() : int + private $shortCircuitTraversal; + public function __construct(bool $shortCircuitTraversal) { - return \iterator_count($this); + $this->shortCircuitTraversal = $shortCircuitTraversal; } - public function rewind() : void + public function enterNode(Node $node) : ?int { - $this->position = 0; + if (!$node instanceof ClassMethod && !$node instanceof Function_) { + return null; + } + if ($node instanceof ClassMethod) { + $name = $this->classMethodName($node); + } else { + $name = $this->functionName($node); + } + $statements = $node->getStmts(); + assert(is_array($statements)); + $this->result[] = new Complexity($name, $this->cyclomaticComplexity($statements)); + if ($this->shortCircuitTraversal) { + return NodeTraverser::DONT_TRAVERSE_CHILDREN; + } + return null; } - public function valid() : bool + public function result() : ComplexityCollection { - return $this->position < \count($this->directories); + return ComplexityCollection::fromList(...$this->result); } - public function key() : int + /** + * @param Stmt[] $statements + */ + private function cyclomaticComplexity(array $statements) : int { - return $this->position; + $traverser = new NodeTraverser(); + $cyclomaticComplexityCalculatingVisitor = new CyclomaticComplexityCalculatingVisitor(); + $traverser->addVisitor($cyclomaticComplexityCalculatingVisitor); + /* @noinspection UnusedFunctionResultInspection */ + $traverser->traverse($statements); + return $cyclomaticComplexityCalculatingVisitor->cyclomaticComplexity(); } - public function current() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\Directory + private function classMethodName(ClassMethod $node) : string { - return $this->directories[$this->position]; + $parent = $node->getAttribute('parent'); + assert($parent instanceof Class_ || $parent instanceof Trait_); + assert(isset($parent->namespacedName)); + assert($parent->namespacedName instanceof Name); + return $parent->namespacedName->toString() . '::' . $node->name->toString(); } - public function next() : void + private function functionName(Function_ $node) : string { - $this->position++; + assert(isset($node->namespacedName)); + assert($node->namespacedName instanceof Name); + return $node->namespacedName->toString(); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage; +namespace PHPUnit\SebastianBergmann\Complexity; -use PHPUnit\SebastianBergmann\CodeCoverage\Filter; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class FilterMapper +use function get_class; +use PHPUnit\PhpParser\Node; +use PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanAnd; +use PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanOr; +use PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalAnd; +use PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalOr; +use PHPUnit\PhpParser\Node\Expr\Ternary; +use PHPUnit\PhpParser\Node\Stmt\Case_; +use PHPUnit\PhpParser\Node\Stmt\Catch_; +use PHPUnit\PhpParser\Node\Stmt\ElseIf_; +use PHPUnit\PhpParser\Node\Stmt\For_; +use PHPUnit\PhpParser\Node\Stmt\Foreach_; +use PHPUnit\PhpParser\Node\Stmt\If_; +use PHPUnit\PhpParser\Node\Stmt\While_; +use PHPUnit\PhpParser\NodeVisitorAbstract; +final class CyclomaticComplexityCalculatingVisitor extends NodeVisitorAbstract { - public function map(\PHPUnit\SebastianBergmann\CodeCoverage\Filter $filter, \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage $configuration) : void + /** + * @var int + */ + private $cyclomaticComplexity = 1; + public function enterNode(Node $node) : void { - foreach ($configuration->directories() as $directory) { - $filter->includeDirectory($directory->path(), $directory->suffix(), $directory->prefix()); - } - foreach ($configuration->files() as $file) { - $filter->includeFile($file->path()); - } - foreach ($configuration->excludeDirectories() as $directory) { - $filter->excludeDirectory($directory->path(), $directory->suffix(), $directory->prefix()); - } - foreach ($configuration->excludeFiles() as $file) { - $filter->excludeFile($file->path()); + /* @noinspection GetClassMissUseInspection */ + switch (get_class($node)) { + case BooleanAnd::class: + case BooleanOr::class: + case Case_::class: + case Catch_::class: + case ElseIf_::class: + case For_::class: + case Foreach_::class: + case If_::class: + case LogicalAnd::class: + case LogicalOr::class: + case Ternary::class: + case While_::class: + $this->cyclomaticComplexity++; } } + public function cyclomaticComplexity() : int + { + return $this->cyclomaticComplexity; + } } +Object Reflector + +Copyright (c) 2017-2020, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\LinesOfCode; -use RuntimeException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Exception extends \RuntimeException implements \PHPUnit\Exception +use InvalidArgumentException; +final class NegativeValueException extends InvalidArgumentException implements Exception { } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\LinesOfCode; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable +use LogicException; +final class IllogicalValuesException extends LogicException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -final class IniSetting +namespace PHPUnit\SebastianBergmann\LinesOfCode; + +final class RuntimeException extends \RuntimeException implements Exception { - /** - * @var string - */ - private $name; - /** - * @var string - */ - private $value; - public function __construct(string $name, string $value) - { - $this->name = $name; - $this->value = $value; - } - public function name() : string - { - return $this->name; - } - public function value() : string - { - return $this->value; - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\LinesOfCode; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable +use Throwable; +interface Exception extends Throwable +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -final class Constant +namespace PHPUnit\SebastianBergmann\LinesOfCode; + +use function substr_count; +use PHPUnit\PhpParser\Error; +use PHPUnit\PhpParser\Lexer; +use PHPUnit\PhpParser\Node; +use PHPUnit\PhpParser\NodeTraverser; +use PHPUnit\PhpParser\Parser; +use PHPUnit\PhpParser\ParserFactory; +final class Counter { /** - * @var string + * @throws RuntimeException */ - private $name; + public function countInSourceFile(string $sourceFile) : LinesOfCode + { + return $this->countInSourceString(\file_get_contents($sourceFile)); + } /** - * @var mixed + * @throws RuntimeException */ - private $value; - public function __construct(string $name, $value) + public function countInSourceString(string $source) : LinesOfCode { - $this->name = $name; - $this->value = $value; + $linesOfCode = substr_count($source, "\n"); + if ($linesOfCode === 0 && !empty($source)) { + $linesOfCode = 1; + } + try { + $nodes = $this->parser()->parse($source); + \assert($nodes !== null); + return $this->countInAbstractSyntaxTree($linesOfCode, $nodes); + // @codeCoverageIgnoreStart + } catch (Error $error) { + throw new RuntimeException($error->getMessage(), (int) $error->getCode(), $error); + } + // @codeCoverageIgnoreEnd } - public function name() : string + /** + * @param Node[] $nodes + * + * @throws RuntimeException + */ + public function countInAbstractSyntaxTree(int $linesOfCode, array $nodes) : LinesOfCode { - return $this->name; + $traverser = new NodeTraverser(); + $visitor = new LineCountingVisitor($linesOfCode); + $traverser->addVisitor($visitor); + try { + /* @noinspection UnusedFunctionResultInspection */ + $traverser->traverse($nodes); + // @codeCoverageIgnoreStart + } catch (Error $error) { + throw new RuntimeException($error->getMessage(), (int) $error->getCode(), $error); + } + // @codeCoverageIgnoreEnd + return $visitor->result(); } - public function value() + private function parser() : Parser { - return $this->value; + return (new ParserFactory())->create(ParserFactory::PREFER_PHP7, new Lexer()); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\LinesOfCode; -use function count; -use function iterator_count; -use Countable; -use Iterator; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable */ -final class VariableCollectionIterator implements \Countable, \Iterator +final class LinesOfCode { /** - * @var Variable[] + * @var int */ - private $variables; + private $linesOfCode; /** * @var int */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) - { - $this->variables = $variables->asArray(); - } - public function count() : int + private $commentLinesOfCode; + /** + * @var int + */ + private $nonCommentLinesOfCode; + /** + * @var int + */ + private $logicalLinesOfCode; + /** + * @throws IllogicalValuesException + * @throws NegativeValueException + */ + public function __construct(int $linesOfCode, int $commentLinesOfCode, int $nonCommentLinesOfCode, int $logicalLinesOfCode) { - return \iterator_count($this); + if ($linesOfCode < 0) { + throw new NegativeValueException('$linesOfCode must not be negative'); + } + if ($commentLinesOfCode < 0) { + throw new NegativeValueException('$commentLinesOfCode must not be negative'); + } + if ($nonCommentLinesOfCode < 0) { + throw new NegativeValueException('$nonCommentLinesOfCode must not be negative'); + } + if ($logicalLinesOfCode < 0) { + throw new NegativeValueException('$logicalLinesOfCode must not be negative'); + } + if ($linesOfCode - $commentLinesOfCode !== $nonCommentLinesOfCode) { + throw new IllogicalValuesException('$linesOfCode !== $commentLinesOfCode + $nonCommentLinesOfCode'); + } + $this->linesOfCode = $linesOfCode; + $this->commentLinesOfCode = $commentLinesOfCode; + $this->nonCommentLinesOfCode = $nonCommentLinesOfCode; + $this->logicalLinesOfCode = $logicalLinesOfCode; } - public function rewind() : void + public function linesOfCode() : int { - $this->position = 0; + return $this->linesOfCode; } - public function valid() : bool + public function commentLinesOfCode() : int { - return $this->position < \count($this->variables); + return $this->commentLinesOfCode; } - public function key() : int + public function nonCommentLinesOfCode() : int { - return $this->position; + return $this->nonCommentLinesOfCode; } - public function current() : \PHPUnit\TextUI\XmlConfiguration\Variable + public function logicalLinesOfCode() : int { - return $this->variables[$this->position]; + return $this->logicalLinesOfCode; } - public function next() : void + public function plus(self $other) : self { - $this->position++; + return new self($this->linesOfCode() + $other->linesOfCode(), $this->commentLinesOfCode() + $other->commentLinesOfCode(), $this->nonCommentLinesOfCode() + $other->nonCommentLinesOfCode(), $this->logicalLinesOfCode() + $other->logicalLinesOfCode()); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\LinesOfCode; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class Variable +use function array_merge; +use function array_unique; +use function count; +use PHPUnit\PhpParser\Comment; +use PHPUnit\PhpParser\Node; +use PHPUnit\PhpParser\Node\Expr; +use PHPUnit\PhpParser\NodeVisitorAbstract; +final class LineCountingVisitor extends NodeVisitorAbstract { /** - * @var string + * @var int */ - private $name; + private $linesOfCode; /** - * @var mixed + * @var Comment[] */ - private $value; + private $comments = []; /** - * @var bool + * @var int[] */ - private $force; - public function __construct(string $name, $value, bool $force) + private $linesWithStatements = []; + public function __construct(int $linesOfCode) { - $this->name = $name; - $this->value = $value; - $this->force = $force; + $this->linesOfCode = $linesOfCode; } - public function name() : string + public function enterNode(Node $node) : void { - return $this->name; + $this->comments = array_merge($this->comments, $node->getComments()); + if (!$node instanceof Expr) { + return; + } + $this->linesWithStatements[] = $node->getStartLine(); } - public function value() + public function result() : LinesOfCode { - return $this->value; + $commentLinesOfCode = 0; + foreach ($this->comments() as $comment) { + $commentLinesOfCode += $comment->getEndLine() - $comment->getStartLine() + 1; + } + return new LinesOfCode($this->linesOfCode, $commentLinesOfCode, $this->linesOfCode - $commentLinesOfCode, count(array_unique($this->linesWithStatements))); } - public function force() : bool + /** + * @return Comment[] + */ + private function comments() : array { - return $this->force; + $comments = []; + foreach ($this->comments as $comment) { + $comments[$comment->getStartLine() . '_' . $comment->getStartTokenPos() . '_' . $comment->getEndLine() . '_' . $comment->getEndTokenPos()] = $comment; + } + return $comments; } } +sebastian/lines-of-code + +Copyright (c) 2020, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +phpunit/phpunit: 9.5.10 +doctrine/instantiator: 1.4.0 +myclabs/deep-copy: 1.10.2 +nikic/php-parser: v4.13.0 +phar-io/manifest: 2.0.3 +phar-io/version: 3.1.0 +phpdocumentor/reflection-common: 2.2.0 +phpdocumentor/reflection-docblock: 5.2.2 +phpdocumentor/type-resolver: 1.5.0 +phpspec/prophecy: 1.14.0 +phpunit/php-code-coverage: 9.2.7 +phpunit/php-file-iterator: 3.0.5 +phpunit/php-invoker: 3.1.1 +phpunit/php-text-template: 2.0.4 +phpunit/php-timer: 5.0.3 +sebastian/cli-parser: 1.0.1 +sebastian/code-unit: 1.0.8 +sebastian/code-unit-reverse-lookup: 2.0.3 +sebastian/comparator: 4.0.6 +sebastian/complexity: 2.0.2 +sebastian/diff: 4.0.4 +sebastian/environment: 5.1.3 +sebastian/exporter: 4.0.3 +sebastian/global-state: 5.0.3 +sebastian/lines-of-code: 1.0.3 +sebastian/object-enumerator: 4.0.4 +sebastian/object-reflector: 2.0.4 +sebastian/recursion-context: 4.0.4 +sebastian/resource-operations: 3.0.3 +sebastian/type: 2.3.4 +sebastian/version: 3.0.2 +symfony/polyfill-ctype: v1.23.0 +theseer/tokenizer: 1.2.1 +webmozart/assert: 1.10.0 - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\Doctrine\Instantiator\Exception; -use function count; -use function iterator_count; -use Countable; -use Iterator; +use Exception; +use ReflectionClass; +use UnexpectedValueException as BaseUnexpectedValueException; +use function sprintf; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * Exception for given parameters causing invalid/unexpected state on instantiation */ -final class IniSettingCollectionIterator implements \Countable, \Iterator +class UnexpectedValueException extends BaseUnexpectedValueException implements ExceptionInterface { /** - * @var IniSetting[] - */ - private $iniSettings; - /** - * @var int + * @template T of object + * @phpstan-param ReflectionClass $reflectionClass */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\IniSettingCollection $iniSettings) - { - $this->iniSettings = $iniSettings->asArray(); - } - public function count() : int + public static function fromSerializationTriggeredException(ReflectionClass $reflectionClass, Exception $exception) : self { - return \iterator_count($this); + return new self(sprintf('An exception was raised while trying to instantiate an instance of "%s" via un-serialization', $reflectionClass->getName()), 0, $exception); } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < \count($this->iniSettings); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\IniSetting - { - return $this->iniSettings[$this->position]; - } - public function next() : void + /** + * @template T of object + * @phpstan-param ReflectionClass $reflectionClass + */ + public static function fromUncleanUnSerialization(ReflectionClass $reflectionClass, string $errorString, int $errorCode, string $errorFile, int $errorLine) : self { - $this->position++; + return new self(sprintf('Could not produce an instance of "%s" via un-serialization, since an error was triggered ' . 'in file "%s" at line "%d"', $reflectionClass->getName(), $errorFile, $errorLine), 0, new Exception($errorString, $errorCode)); } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. +namespace PHPUnit\Doctrine\Instantiator\Exception; + +use Throwable; +/** + * Base exception marker interface for the instantiator component */ -namespace PHPUnit\TextUI\XmlConfiguration; +interface ExceptionInterface extends Throwable +{ +} + $reflectionClass */ - private $getVariables; + public static function fromAbstractClass(ReflectionClass $reflectionClass) : self + { + return new self(sprintf('The provided class "%s" is abstract, and can not be instantiated', $reflectionClass->getName())); + } +} +includePaths = $includePaths; - $this->iniSettings = $iniSettings; - $this->constants = $constants; - $this->globalVariables = $globalVariables; - $this->envVariables = $envVariables; - $this->postVariables = $postVariables; - $this->getVariables = $getVariables; - $this->cookieVariables = $cookieVariables; - $this->serverVariables = $serverVariables; - $this->filesVariables = $filesVariables; - $this->requestVariables = $requestVariables; - } - public function includePaths() : \PHPUnit\TextUI\XmlConfiguration\DirectoryCollection - { - return $this->includePaths; - } - public function iniSettings() : \PHPUnit\TextUI\XmlConfiguration\IniSettingCollection - { - return $this->iniSettings; - } - public function constants() : \PHPUnit\TextUI\XmlConfiguration\ConstantCollection + public function instantiate($className) { - return $this->constants; + if (isset(self::$cachedCloneables[$className])) { + return clone self::$cachedCloneables[$className]; + } + if (isset(self::$cachedInstantiators[$className])) { + $factory = self::$cachedInstantiators[$className]; + return $factory(); + } + return $this->buildAndCacheFromFactory($className); } - public function globalVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + /** + * Builds the requested object and caches it in static properties for performance + * + * @return object + * + * @template T of object + * @phpstan-param class-string $className + * + * @phpstan-return T + */ + private function buildAndCacheFromFactory(string $className) { - return $this->globalVariables; + $factory = self::$cachedInstantiators[$className] = $this->buildFactory($className); + $instance = $factory(); + if ($this->isSafeToClone(new ReflectionClass($instance))) { + self::$cachedCloneables[$className] = clone $instance; + } + return $instance; } - public function envVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + /** + * Builds a callable capable of instantiating the given $className without + * invoking its constructor. + * + * @throws InvalidArgumentException + * @throws UnexpectedValueException + * @throws ReflectionException + * + * @template T of object + * @phpstan-param class-string $className + * + * @phpstan-return callable(): T + */ + private function buildFactory(string $className) : callable { - return $this->envVariables; + $reflectionClass = $this->getReflectionClass($className); + if ($this->isInstantiableViaReflection($reflectionClass)) { + return [$reflectionClass, 'newInstanceWithoutConstructor']; + } + $serializedString = sprintf('%s:%d:"%s":0:{}', is_subclass_of($className, Serializable::class) ? self::SERIALIZATION_FORMAT_USE_UNSERIALIZER : self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER, strlen($className), $className); + $this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString); + return static function () use($serializedString) { + return unserialize($serializedString); + }; } - public function postVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + /** + * @throws InvalidArgumentException + * @throws ReflectionException + * + * @template T of object + * @phpstan-param class-string $className + * + * @phpstan-return ReflectionClass + */ + private function getReflectionClass(string $className) : ReflectionClass { - return $this->postVariables; + if (!class_exists($className)) { + throw InvalidArgumentException::fromNonExistingClass($className); + } + $reflection = new ReflectionClass($className); + if ($reflection->isAbstract()) { + throw InvalidArgumentException::fromAbstractClass($reflection); + } + return $reflection; } - public function getVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + /** + * @throws UnexpectedValueException + * + * @template T of object + * @phpstan-param ReflectionClass $reflectionClass + */ + private function checkIfUnSerializationIsSupported(ReflectionClass $reflectionClass, string $serializedString) : void { - return $this->getVariables; + set_error_handler(static function (int $code, string $message, string $file, int $line) use($reflectionClass, &$error) : bool { + $error = UnexpectedValueException::fromUncleanUnSerialization($reflectionClass, $message, $code, $file, $line); + return \true; + }); + try { + $this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString); + } finally { + restore_error_handler(); + } + if ($error) { + throw $error; + } } - public function cookieVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + /** + * @throws UnexpectedValueException + * + * @template T of object + * @phpstan-param ReflectionClass $reflectionClass + */ + private function attemptInstantiationViaUnSerialization(ReflectionClass $reflectionClass, string $serializedString) : void { - return $this->cookieVariables; + try { + unserialize($serializedString); + } catch (Exception $exception) { + throw UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $exception); + } } - public function serverVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + /** + * @template T of object + * @phpstan-param ReflectionClass $reflectionClass + */ + private function isInstantiableViaReflection(ReflectionClass $reflectionClass) : bool { - return $this->serverVariables; + return !($this->hasInternalAncestors($reflectionClass) && $reflectionClass->isFinal()); } - public function filesVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + /** + * Verifies whether the given class is to be considered internal + * + * @template T of object + * @phpstan-param ReflectionClass $reflectionClass + */ + private function hasInternalAncestors(ReflectionClass $reflectionClass) : bool { - return $this->filesVariables; + do { + if ($reflectionClass->isInternal()) { + return \true; + } + $reflectionClass = $reflectionClass->getParentClass(); + } while ($reflectionClass); + return \false; } - public function requestVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection + /** + * Checks if a class is cloneable + * + * Classes implementing `__clone` cannot be safely cloned, as that may cause side-effects. + * + * @template T of object + * @phpstan-param ReflectionClass $reflectionClass + */ + private function isSafeToClone(ReflectionClass $reflectionClass) : bool { - return $this->requestVariables; + return $reflectionClass->isCloneable() && !$reflectionClass->hasMethod('__clone') && !$reflectionClass->isSubclassOf(ArrayIterator::class); } } $className + */ + public function instantiate($className); +} +Copyright (c) 2014 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\ResourceOperations; -use function count; -use function iterator_count; -use Countable; -use Iterator; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ConstantCollectionIterator implements \Countable, \Iterator +final class ResourceOperations { /** - * @var Constant[] - */ - private $constants; - /** - * @var int + * @return string[] */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\ConstantCollection $constants) - { - $this->constants = $constants->asArray(); - } - public function count() : int - { - return \iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < \count($this->constants); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\Constant - { - return $this->constants[$this->position]; - } - public function next() : void + public static function getFunctions() : array { - $this->position++; + return ['Directory::close', 'Directory::read', 'Directory::rewind', 'DirectoryIterator::openFile', 'FilesystemIterator::openFile', 'Gmagick::readimagefile', 'HttpResponse::getRequestBodyStream', 'HttpResponse::getStream', 'HttpResponse::setStream', 'Imagick::pingImageFile', 'Imagick::readImageFile', 'Imagick::writeImageFile', 'Imagick::writeImagesFile', 'MongoGridFSCursor::__construct', 'MongoGridFSFile::getResource', 'MysqlndUhConnection::stmtInit', 'MysqlndUhConnection::storeResult', 'MysqlndUhConnection::useResult', 'PDF_activate_item', 'PDF_add_launchlink', 'PDF_add_locallink', 'PDF_add_nameddest', 'PDF_add_note', 'PDF_add_pdflink', 'PDF_add_table_cell', 'PDF_add_textflow', 'PDF_add_thumbnail', 'PDF_add_weblink', 'PDF_arc', 'PDF_arcn', 'PDF_attach_file', 'PDF_begin_document', 'PDF_begin_font', 'PDF_begin_glyph', 'PDF_begin_item', 'PDF_begin_layer', 'PDF_begin_page', 'PDF_begin_page_ext', 'PDF_begin_pattern', 'PDF_begin_template', 'PDF_begin_template_ext', 'PDF_circle', 'PDF_clip', 'PDF_close', 'PDF_close_image', 'PDF_close_pdi', 'PDF_close_pdi_page', 'PDF_closepath', 'PDF_closepath_fill_stroke', 'PDF_closepath_stroke', 'PDF_concat', 'PDF_continue_text', 'PDF_create_3dview', 'PDF_create_action', 'PDF_create_annotation', 'PDF_create_bookmark', 'PDF_create_field', 'PDF_create_fieldgroup', 'PDF_create_gstate', 'PDF_create_pvf', 'PDF_create_textflow', 'PDF_curveto', 'PDF_define_layer', 'PDF_delete', 'PDF_delete_pvf', 'PDF_delete_table', 'PDF_delete_textflow', 'PDF_encoding_set_char', 'PDF_end_document', 'PDF_end_font', 'PDF_end_glyph', 'PDF_end_item', 'PDF_end_layer', 'PDF_end_page', 'PDF_end_page_ext', 'PDF_end_pattern', 'PDF_end_template', 'PDF_endpath', 'PDF_fill', 'PDF_fill_imageblock', 'PDF_fill_pdfblock', 'PDF_fill_stroke', 'PDF_fill_textblock', 'PDF_findfont', 'PDF_fit_image', 'PDF_fit_pdi_page', 'PDF_fit_table', 'PDF_fit_textflow', 'PDF_fit_textline', 'PDF_get_apiname', 'PDF_get_buffer', 'PDF_get_errmsg', 'PDF_get_errnum', 'PDF_get_parameter', 'PDF_get_pdi_parameter', 'PDF_get_pdi_value', 'PDF_get_value', 'PDF_info_font', 'PDF_info_matchbox', 'PDF_info_table', 'PDF_info_textflow', 'PDF_info_textline', 'PDF_initgraphics', 'PDF_lineto', 'PDF_load_3ddata', 'PDF_load_font', 'PDF_load_iccprofile', 'PDF_load_image', 'PDF_makespotcolor', 'PDF_moveto', 'PDF_new', 'PDF_open_ccitt', 'PDF_open_file', 'PDF_open_image', 'PDF_open_image_file', 'PDF_open_memory_image', 'PDF_open_pdi', 'PDF_open_pdi_document', 'PDF_open_pdi_page', 'PDF_pcos_get_number', 'PDF_pcos_get_stream', 'PDF_pcos_get_string', 'PDF_place_image', 'PDF_place_pdi_page', 'PDF_process_pdi', 'PDF_rect', 'PDF_restore', 'PDF_resume_page', 'PDF_rotate', 'PDF_save', 'PDF_scale', 'PDF_set_border_color', 'PDF_set_border_dash', 'PDF_set_border_style', 'PDF_set_gstate', 'PDF_set_info', 'PDF_set_layer_dependency', 'PDF_set_parameter', 'PDF_set_text_pos', 'PDF_set_value', 'PDF_setcolor', 'PDF_setdash', 'PDF_setdashpattern', 'PDF_setflat', 'PDF_setfont', 'PDF_setgray', 'PDF_setgray_fill', 'PDF_setgray_stroke', 'PDF_setlinecap', 'PDF_setlinejoin', 'PDF_setlinewidth', 'PDF_setmatrix', 'PDF_setmiterlimit', 'PDF_setrgbcolor', 'PDF_setrgbcolor_fill', 'PDF_setrgbcolor_stroke', 'PDF_shading', 'PDF_shading_pattern', 'PDF_shfill', 'PDF_show', 'PDF_show_boxed', 'PDF_show_xy', 'PDF_skew', 'PDF_stringwidth', 'PDF_stroke', 'PDF_suspend_page', 'PDF_translate', 'PDF_utf16_to_utf8', 'PDF_utf32_to_utf16', 'PDF_utf8_to_utf16', 'PDO::pgsqlLOBOpen', 'RarEntry::getStream', 'SQLite3::openBlob', 'SWFMovie::saveToFile', 'SplFileInfo::openFile', 'SplFileObject::openFile', 'SplTempFileObject::openFile', 'V8Js::compileString', 'V8Js::executeScript', 'Vtiful\\Kernel\\Excel::setColumn', 'Vtiful\\Kernel\\Excel::setRow', 'Vtiful\\Kernel\\Format::align', 'Vtiful\\Kernel\\Format::bold', 'Vtiful\\Kernel\\Format::italic', 'Vtiful\\Kernel\\Format::underline', 'XMLWriter::openMemory', 'XMLWriter::openURI', 'ZipArchive::getStream', 'Zookeeper::setLogStream', 'apc_bin_dumpfile', 'apc_bin_loadfile', 'bbcode_add_element', 'bbcode_add_smiley', 'bbcode_create', 'bbcode_destroy', 'bbcode_parse', 'bbcode_set_arg_parser', 'bbcode_set_flags', 'bcompiler_read', 'bcompiler_write_class', 'bcompiler_write_constant', 'bcompiler_write_exe_footer', 'bcompiler_write_file', 'bcompiler_write_footer', 'bcompiler_write_function', 'bcompiler_write_functions_from_file', 'bcompiler_write_header', 'bcompiler_write_included_filename', 'bzclose', 'bzerrno', 'bzerror', 'bzerrstr', 'bzflush', 'bzopen', 'bzread', 'bzwrite', 'cairo_surface_write_to_png', 'closedir', 'copy', 'crack_closedict', 'crack_opendict', 'cubrid_bind', 'cubrid_close_prepare', 'cubrid_close_request', 'cubrid_col_get', 'cubrid_col_size', 'cubrid_column_names', 'cubrid_column_types', 'cubrid_commit', 'cubrid_connect', 'cubrid_connect_with_url', 'cubrid_current_oid', 'cubrid_db_parameter', 'cubrid_disconnect', 'cubrid_drop', 'cubrid_fetch', 'cubrid_free_result', 'cubrid_get', 'cubrid_get_autocommit', 'cubrid_get_charset', 'cubrid_get_class_name', 'cubrid_get_db_parameter', 'cubrid_get_query_timeout', 'cubrid_get_server_info', 'cubrid_insert_id', 'cubrid_is_instance', 'cubrid_lob2_bind', 'cubrid_lob2_close', 'cubrid_lob2_export', 'cubrid_lob2_import', 'cubrid_lob2_new', 'cubrid_lob2_read', 'cubrid_lob2_seek', 'cubrid_lob2_seek64', 'cubrid_lob2_size', 'cubrid_lob2_size64', 'cubrid_lob2_tell', 'cubrid_lob2_tell64', 'cubrid_lob2_write', 'cubrid_lob_export', 'cubrid_lob_get', 'cubrid_lob_send', 'cubrid_lob_size', 'cubrid_lock_read', 'cubrid_lock_write', 'cubrid_move_cursor', 'cubrid_next_result', 'cubrid_num_cols', 'cubrid_num_rows', 'cubrid_pconnect', 'cubrid_pconnect_with_url', 'cubrid_prepare', 'cubrid_put', 'cubrid_query', 'cubrid_rollback', 'cubrid_schema', 'cubrid_seq_add', 'cubrid_seq_drop', 'cubrid_seq_insert', 'cubrid_seq_put', 'cubrid_set_add', 'cubrid_set_autocommit', 'cubrid_set_db_parameter', 'cubrid_set_drop', 'cubrid_set_query_timeout', 'cubrid_unbuffered_query', 'curl_close', 'curl_copy_handle', 'curl_errno', 'curl_error', 'curl_escape', 'curl_exec', 'curl_getinfo', 'curl_multi_add_handle', 'curl_multi_close', 'curl_multi_errno', 'curl_multi_exec', 'curl_multi_getcontent', 'curl_multi_info_read', 'curl_multi_remove_handle', 'curl_multi_select', 'curl_multi_setopt', 'curl_pause', 'curl_reset', 'curl_setopt', 'curl_setopt_array', 'curl_share_close', 'curl_share_errno', 'curl_share_init', 'curl_share_setopt', 'curl_unescape', 'cyrus_authenticate', 'cyrus_bind', 'cyrus_close', 'cyrus_connect', 'cyrus_query', 'cyrus_unbind', 'db2_autocommit', 'db2_bind_param', 'db2_client_info', 'db2_close', 'db2_column_privileges', 'db2_columns', 'db2_commit', 'db2_conn_error', 'db2_conn_errormsg', 'db2_connect', 'db2_cursor_type', 'db2_exec', 'db2_execute', 'db2_fetch_array', 'db2_fetch_assoc', 'db2_fetch_both', 'db2_fetch_object', 'db2_fetch_row', 'db2_field_display_size', 'db2_field_name', 'db2_field_num', 'db2_field_precision', 'db2_field_scale', 'db2_field_type', 'db2_field_width', 'db2_foreign_keys', 'db2_free_result', 'db2_free_stmt', 'db2_get_option', 'db2_last_insert_id', 'db2_lob_read', 'db2_next_result', 'db2_num_fields', 'db2_num_rows', 'db2_pclose', 'db2_pconnect', 'db2_prepare', 'db2_primary_keys', 'db2_procedure_columns', 'db2_procedures', 'db2_result', 'db2_rollback', 'db2_server_info', 'db2_set_option', 'db2_special_columns', 'db2_statistics', 'db2_stmt_error', 'db2_stmt_errormsg', 'db2_table_privileges', 'db2_tables', 'dba_close', 'dba_delete', 'dba_exists', 'dba_fetch', 'dba_firstkey', 'dba_insert', 'dba_nextkey', 'dba_open', 'dba_optimize', 'dba_popen', 'dba_replace', 'dba_sync', 'dbplus_add', 'dbplus_aql', 'dbplus_close', 'dbplus_curr', 'dbplus_find', 'dbplus_first', 'dbplus_flush', 'dbplus_freelock', 'dbplus_freerlocks', 'dbplus_getlock', 'dbplus_getunique', 'dbplus_info', 'dbplus_last', 'dbplus_lockrel', 'dbplus_next', 'dbplus_open', 'dbplus_prev', 'dbplus_rchperm', 'dbplus_rcreate', 'dbplus_rcrtexact', 'dbplus_rcrtlike', 'dbplus_restorepos', 'dbplus_rkeys', 'dbplus_ropen', 'dbplus_rquery', 'dbplus_rrename', 'dbplus_rsecindex', 'dbplus_runlink', 'dbplus_rzap', 'dbplus_savepos', 'dbplus_setindex', 'dbplus_setindexbynumber', 'dbplus_sql', 'dbplus_tremove', 'dbplus_undo', 'dbplus_undoprepare', 'dbplus_unlockrel', 'dbplus_unselect', 'dbplus_update', 'dbplus_xlockrel', 'dbplus_xunlockrel', 'deflate_add', 'dio_close', 'dio_fcntl', 'dio_open', 'dio_read', 'dio_seek', 'dio_stat', 'dio_tcsetattr', 'dio_truncate', 'dio_write', 'dir', 'eio_busy', 'eio_cancel', 'eio_chmod', 'eio_chown', 'eio_close', 'eio_custom', 'eio_dup2', 'eio_fallocate', 'eio_fchmod', 'eio_fchown', 'eio_fdatasync', 'eio_fstat', 'eio_fstatvfs', 'eio_fsync', 'eio_ftruncate', 'eio_futime', 'eio_get_last_error', 'eio_grp', 'eio_grp_add', 'eio_grp_cancel', 'eio_grp_limit', 'eio_link', 'eio_lstat', 'eio_mkdir', 'eio_mknod', 'eio_nop', 'eio_open', 'eio_read', 'eio_readahead', 'eio_readdir', 'eio_readlink', 'eio_realpath', 'eio_rename', 'eio_rmdir', 'eio_seek', 'eio_sendfile', 'eio_stat', 'eio_statvfs', 'eio_symlink', 'eio_sync', 'eio_sync_file_range', 'eio_syncfs', 'eio_truncate', 'eio_unlink', 'eio_utime', 'eio_write', 'enchant_broker_describe', 'enchant_broker_dict_exists', 'enchant_broker_free', 'enchant_broker_free_dict', 'enchant_broker_get_dict_path', 'enchant_broker_get_error', 'enchant_broker_init', 'enchant_broker_list_dicts', 'enchant_broker_request_dict', 'enchant_broker_request_pwl_dict', 'enchant_broker_set_dict_path', 'enchant_broker_set_ordering', 'enchant_dict_add_to_personal', 'enchant_dict_add_to_session', 'enchant_dict_check', 'enchant_dict_describe', 'enchant_dict_get_error', 'enchant_dict_is_in_session', 'enchant_dict_quick_check', 'enchant_dict_store_replacement', 'enchant_dict_suggest', 'event_add', 'event_base_free', 'event_base_loop', 'event_base_loopbreak', 'event_base_loopexit', 'event_base_new', 'event_base_priority_init', 'event_base_reinit', 'event_base_set', 'event_buffer_base_set', 'event_buffer_disable', 'event_buffer_enable', 'event_buffer_fd_set', 'event_buffer_free', 'event_buffer_new', 'event_buffer_priority_set', 'event_buffer_read', 'event_buffer_set_callback', 'event_buffer_timeout_set', 'event_buffer_watermark_set', 'event_buffer_write', 'event_del', 'event_free', 'event_new', 'event_priority_set', 'event_set', 'event_timer_add', 'event_timer_del', 'event_timer_pending', 'event_timer_set', 'expect_expectl', 'expect_popen', 'fam_cancel_monitor', 'fam_close', 'fam_monitor_collection', 'fam_monitor_directory', 'fam_monitor_file', 'fam_next_event', 'fam_open', 'fam_pending', 'fam_resume_monitor', 'fam_suspend_monitor', 'fann_cascadetrain_on_data', 'fann_cascadetrain_on_file', 'fann_clear_scaling_params', 'fann_copy', 'fann_create_from_file', 'fann_create_shortcut_array', 'fann_create_standard', 'fann_create_standard_array', 'fann_create_train', 'fann_create_train_from_callback', 'fann_descale_input', 'fann_descale_output', 'fann_descale_train', 'fann_destroy', 'fann_destroy_train', 'fann_duplicate_train_data', 'fann_get_MSE', 'fann_get_activation_function', 'fann_get_activation_steepness', 'fann_get_bias_array', 'fann_get_bit_fail', 'fann_get_bit_fail_limit', 'fann_get_cascade_activation_functions', 'fann_get_cascade_activation_functions_count', 'fann_get_cascade_activation_steepnesses', 'fann_get_cascade_activation_steepnesses_count', 'fann_get_cascade_candidate_change_fraction', 'fann_get_cascade_candidate_limit', 'fann_get_cascade_candidate_stagnation_epochs', 'fann_get_cascade_max_cand_epochs', 'fann_get_cascade_max_out_epochs', 'fann_get_cascade_min_cand_epochs', 'fann_get_cascade_min_out_epochs', 'fann_get_cascade_num_candidate_groups', 'fann_get_cascade_num_candidates', 'fann_get_cascade_output_change_fraction', 'fann_get_cascade_output_stagnation_epochs', 'fann_get_cascade_weight_multiplier', 'fann_get_connection_array', 'fann_get_connection_rate', 'fann_get_errno', 'fann_get_errstr', 'fann_get_layer_array', 'fann_get_learning_momentum', 'fann_get_learning_rate', 'fann_get_network_type', 'fann_get_num_input', 'fann_get_num_layers', 'fann_get_num_output', 'fann_get_quickprop_decay', 'fann_get_quickprop_mu', 'fann_get_rprop_decrease_factor', 'fann_get_rprop_delta_max', 'fann_get_rprop_delta_min', 'fann_get_rprop_delta_zero', 'fann_get_rprop_increase_factor', 'fann_get_sarprop_step_error_shift', 'fann_get_sarprop_step_error_threshold_factor', 'fann_get_sarprop_temperature', 'fann_get_sarprop_weight_decay_shift', 'fann_get_total_connections', 'fann_get_total_neurons', 'fann_get_train_error_function', 'fann_get_train_stop_function', 'fann_get_training_algorithm', 'fann_init_weights', 'fann_length_train_data', 'fann_merge_train_data', 'fann_num_input_train_data', 'fann_num_output_train_data', 'fann_randomize_weights', 'fann_read_train_from_file', 'fann_reset_errno', 'fann_reset_errstr', 'fann_run', 'fann_save', 'fann_save_train', 'fann_scale_input', 'fann_scale_input_train_data', 'fann_scale_output', 'fann_scale_output_train_data', 'fann_scale_train', 'fann_scale_train_data', 'fann_set_activation_function', 'fann_set_activation_function_hidden', 'fann_set_activation_function_layer', 'fann_set_activation_function_output', 'fann_set_activation_steepness', 'fann_set_activation_steepness_hidden', 'fann_set_activation_steepness_layer', 'fann_set_activation_steepness_output', 'fann_set_bit_fail_limit', 'fann_set_callback', 'fann_set_cascade_activation_functions', 'fann_set_cascade_activation_steepnesses', 'fann_set_cascade_candidate_change_fraction', 'fann_set_cascade_candidate_limit', 'fann_set_cascade_candidate_stagnation_epochs', 'fann_set_cascade_max_cand_epochs', 'fann_set_cascade_max_out_epochs', 'fann_set_cascade_min_cand_epochs', 'fann_set_cascade_min_out_epochs', 'fann_set_cascade_num_candidate_groups', 'fann_set_cascade_output_change_fraction', 'fann_set_cascade_output_stagnation_epochs', 'fann_set_cascade_weight_multiplier', 'fann_set_error_log', 'fann_set_input_scaling_params', 'fann_set_learning_momentum', 'fann_set_learning_rate', 'fann_set_output_scaling_params', 'fann_set_quickprop_decay', 'fann_set_quickprop_mu', 'fann_set_rprop_decrease_factor', 'fann_set_rprop_delta_max', 'fann_set_rprop_delta_min', 'fann_set_rprop_delta_zero', 'fann_set_rprop_increase_factor', 'fann_set_sarprop_step_error_shift', 'fann_set_sarprop_step_error_threshold_factor', 'fann_set_sarprop_temperature', 'fann_set_sarprop_weight_decay_shift', 'fann_set_scaling_params', 'fann_set_train_error_function', 'fann_set_train_stop_function', 'fann_set_training_algorithm', 'fann_set_weight', 'fann_set_weight_array', 'fann_shuffle_train_data', 'fann_subset_train_data', 'fann_test', 'fann_test_data', 'fann_train', 'fann_train_epoch', 'fann_train_on_data', 'fann_train_on_file', 'fbsql_affected_rows', 'fbsql_autocommit', 'fbsql_blob_size', 'fbsql_change_user', 'fbsql_clob_size', 'fbsql_close', 'fbsql_commit', 'fbsql_connect', 'fbsql_create_blob', 'fbsql_create_clob', 'fbsql_create_db', 'fbsql_data_seek', 'fbsql_database', 'fbsql_database_password', 'fbsql_db_query', 'fbsql_db_status', 'fbsql_drop_db', 'fbsql_errno', 'fbsql_error', 'fbsql_fetch_array', 'fbsql_fetch_assoc', 'fbsql_fetch_field', 'fbsql_fetch_lengths', 'fbsql_fetch_object', 'fbsql_fetch_row', 'fbsql_field_flags', 'fbsql_field_len', 'fbsql_field_name', 'fbsql_field_seek', 'fbsql_field_table', 'fbsql_field_type', 'fbsql_free_result', 'fbsql_get_autostart_info', 'fbsql_hostname', 'fbsql_insert_id', 'fbsql_list_dbs', 'fbsql_list_fields', 'fbsql_list_tables', 'fbsql_next_result', 'fbsql_num_fields', 'fbsql_num_rows', 'fbsql_password', 'fbsql_pconnect', 'fbsql_query', 'fbsql_read_blob', 'fbsql_read_clob', 'fbsql_result', 'fbsql_rollback', 'fbsql_rows_fetched', 'fbsql_select_db', 'fbsql_set_characterset', 'fbsql_set_lob_mode', 'fbsql_set_password', 'fbsql_set_transaction', 'fbsql_start_db', 'fbsql_stop_db', 'fbsql_table_name', 'fbsql_username', 'fclose', 'fdf_add_doc_javascript', 'fdf_add_template', 'fdf_close', 'fdf_create', 'fdf_enum_values', 'fdf_get_ap', 'fdf_get_attachment', 'fdf_get_encoding', 'fdf_get_file', 'fdf_get_flags', 'fdf_get_opt', 'fdf_get_status', 'fdf_get_value', 'fdf_get_version', 'fdf_next_field_name', 'fdf_open', 'fdf_open_string', 'fdf_remove_item', 'fdf_save', 'fdf_save_string', 'fdf_set_ap', 'fdf_set_encoding', 'fdf_set_file', 'fdf_set_flags', 'fdf_set_javascript_action', 'fdf_set_on_import_javascript', 'fdf_set_opt', 'fdf_set_status', 'fdf_set_submit_form_action', 'fdf_set_target_frame', 'fdf_set_value', 'fdf_set_version', 'feof', 'fflush', 'ffmpeg_frame::__construct', 'ffmpeg_frame::toGDImage', 'fgetc', 'fgetcsv', 'fgets', 'fgetss', 'file', 'file_get_contents', 'file_put_contents', 'finfo::buffer', 'finfo::file', 'finfo_buffer', 'finfo_close', 'finfo_file', 'finfo_open', 'finfo_set_flags', 'flock', 'fopen', 'fpassthru', 'fprintf', 'fputcsv', 'fputs', 'fread', 'fscanf', 'fseek', 'fstat', 'ftell', 'ftp_alloc', 'ftp_append', 'ftp_cdup', 'ftp_chdir', 'ftp_chmod', 'ftp_close', 'ftp_delete', 'ftp_exec', 'ftp_fget', 'ftp_fput', 'ftp_get', 'ftp_get_option', 'ftp_login', 'ftp_mdtm', 'ftp_mkdir', 'ftp_mlsd', 'ftp_nb_continue', 'ftp_nb_fget', 'ftp_nb_fput', 'ftp_nb_get', 'ftp_nb_put', 'ftp_nlist', 'ftp_pasv', 'ftp_put', 'ftp_pwd', 'ftp_quit', 'ftp_raw', 'ftp_rawlist', 'ftp_rename', 'ftp_rmdir', 'ftp_set_option', 'ftp_site', 'ftp_size', 'ftp_systype', 'ftruncate', 'fwrite', 'get_resource_type', 'gmp_div', 'gnupg::init', 'gnupg_adddecryptkey', 'gnupg_addencryptkey', 'gnupg_addsignkey', 'gnupg_cleardecryptkeys', 'gnupg_clearencryptkeys', 'gnupg_clearsignkeys', 'gnupg_decrypt', 'gnupg_decryptverify', 'gnupg_encrypt', 'gnupg_encryptsign', 'gnupg_export', 'gnupg_geterror', 'gnupg_getprotocol', 'gnupg_import', 'gnupg_init', 'gnupg_keyinfo', 'gnupg_setarmor', 'gnupg_seterrormode', 'gnupg_setsignmode', 'gnupg_sign', 'gnupg_verify', 'gupnp_context_get_host_ip', 'gupnp_context_get_port', 'gupnp_context_get_subscription_timeout', 'gupnp_context_host_path', 'gupnp_context_new', 'gupnp_context_set_subscription_timeout', 'gupnp_context_timeout_add', 'gupnp_context_unhost_path', 'gupnp_control_point_browse_start', 'gupnp_control_point_browse_stop', 'gupnp_control_point_callback_set', 'gupnp_control_point_new', 'gupnp_device_action_callback_set', 'gupnp_device_info_get', 'gupnp_device_info_get_service', 'gupnp_root_device_get_available', 'gupnp_root_device_get_relative_location', 'gupnp_root_device_new', 'gupnp_root_device_set_available', 'gupnp_root_device_start', 'gupnp_root_device_stop', 'gupnp_service_action_get', 'gupnp_service_action_return', 'gupnp_service_action_return_error', 'gupnp_service_action_set', 'gupnp_service_freeze_notify', 'gupnp_service_info_get', 'gupnp_service_info_get_introspection', 'gupnp_service_introspection_get_state_variable', 'gupnp_service_notify', 'gupnp_service_proxy_action_get', 'gupnp_service_proxy_action_set', 'gupnp_service_proxy_add_notify', 'gupnp_service_proxy_callback_set', 'gupnp_service_proxy_get_subscribed', 'gupnp_service_proxy_remove_notify', 'gupnp_service_proxy_send_action', 'gupnp_service_proxy_set_subscribed', 'gupnp_service_thaw_notify', 'gzclose', 'gzeof', 'gzgetc', 'gzgets', 'gzgetss', 'gzpassthru', 'gzputs', 'gzread', 'gzrewind', 'gzseek', 'gztell', 'gzwrite', 'hash_update_stream', 'http\\Env\\Response::send', 'http_get_request_body_stream', 'ibase_add_user', 'ibase_affected_rows', 'ibase_backup', 'ibase_blob_add', 'ibase_blob_cancel', 'ibase_blob_close', 'ibase_blob_create', 'ibase_blob_get', 'ibase_blob_open', 'ibase_close', 'ibase_commit', 'ibase_commit_ret', 'ibase_connect', 'ibase_db_info', 'ibase_delete_user', 'ibase_drop_db', 'ibase_execute', 'ibase_fetch_assoc', 'ibase_fetch_object', 'ibase_fetch_row', 'ibase_field_info', 'ibase_free_event_handler', 'ibase_free_query', 'ibase_free_result', 'ibase_gen_id', 'ibase_maintain_db', 'ibase_modify_user', 'ibase_name_result', 'ibase_num_fields', 'ibase_num_params', 'ibase_param_info', 'ibase_pconnect', 'ibase_prepare', 'ibase_query', 'ibase_restore', 'ibase_rollback', 'ibase_rollback_ret', 'ibase_server_info', 'ibase_service_attach', 'ibase_service_detach', 'ibase_set_event_handler', 'ibase_trans', 'ifx_affected_rows', 'ifx_close', 'ifx_connect', 'ifx_do', 'ifx_error', 'ifx_fetch_row', 'ifx_fieldproperties', 'ifx_fieldtypes', 'ifx_free_result', 'ifx_getsqlca', 'ifx_htmltbl_result', 'ifx_num_fields', 'ifx_num_rows', 'ifx_pconnect', 'ifx_prepare', 'ifx_query', 'image2wbmp', 'imageaffine', 'imagealphablending', 'imageantialias', 'imagearc', 'imagebmp', 'imagechar', 'imagecharup', 'imagecolorallocate', 'imagecolorallocatealpha', 'imagecolorat', 'imagecolorclosest', 'imagecolorclosestalpha', 'imagecolorclosesthwb', 'imagecolordeallocate', 'imagecolorexact', 'imagecolorexactalpha', 'imagecolormatch', 'imagecolorresolve', 'imagecolorresolvealpha', 'imagecolorset', 'imagecolorsforindex', 'imagecolorstotal', 'imagecolortransparent', 'imageconvolution', 'imagecopy', 'imagecopymerge', 'imagecopymergegray', 'imagecopyresampled', 'imagecopyresized', 'imagecrop', 'imagecropauto', 'imagedashedline', 'imagedestroy', 'imageellipse', 'imagefill', 'imagefilledarc', 'imagefilledellipse', 'imagefilledpolygon', 'imagefilledrectangle', 'imagefilltoborder', 'imagefilter', 'imageflip', 'imagefttext', 'imagegammacorrect', 'imagegd', 'imagegd2', 'imagegetclip', 'imagegif', 'imagegrabscreen', 'imagegrabwindow', 'imageinterlace', 'imageistruecolor', 'imagejpeg', 'imagelayereffect', 'imageline', 'imageopenpolygon', 'imagepalettecopy', 'imagepalettetotruecolor', 'imagepng', 'imagepolygon', 'imagepsencodefont', 'imagepsextendfont', 'imagepsfreefont', 'imagepsloadfont', 'imagepsslantfont', 'imagepstext', 'imagerectangle', 'imageresolution', 'imagerotate', 'imagesavealpha', 'imagescale', 'imagesetbrush', 'imagesetclip', 'imagesetinterpolation', 'imagesetpixel', 'imagesetstyle', 'imagesetthickness', 'imagesettile', 'imagestring', 'imagestringup', 'imagesx', 'imagesy', 'imagetruecolortopalette', 'imagettftext', 'imagewbmp', 'imagewebp', 'imagexbm', 'imap_append', 'imap_body', 'imap_bodystruct', 'imap_check', 'imap_clearflag_full', 'imap_close', 'imap_create', 'imap_createmailbox', 'imap_delete', 'imap_deletemailbox', 'imap_expunge', 'imap_fetch_overview', 'imap_fetchbody', 'imap_fetchheader', 'imap_fetchmime', 'imap_fetchstructure', 'imap_fetchtext', 'imap_gc', 'imap_get_quota', 'imap_get_quotaroot', 'imap_getacl', 'imap_getmailboxes', 'imap_getsubscribed', 'imap_header', 'imap_headerinfo', 'imap_headers', 'imap_list', 'imap_listmailbox', 'imap_listscan', 'imap_listsubscribed', 'imap_lsub', 'imap_mail_copy', 'imap_mail_move', 'imap_mailboxmsginfo', 'imap_msgno', 'imap_num_msg', 'imap_num_recent', 'imap_ping', 'imap_rename', 'imap_renamemailbox', 'imap_reopen', 'imap_savebody', 'imap_scan', 'imap_scanmailbox', 'imap_search', 'imap_set_quota', 'imap_setacl', 'imap_setflag_full', 'imap_sort', 'imap_status', 'imap_subscribe', 'imap_thread', 'imap_uid', 'imap_undelete', 'imap_unsubscribe', 'inflate_add', 'inflate_get_read_len', 'inflate_get_status', 'ingres_autocommit', 'ingres_autocommit_state', 'ingres_charset', 'ingres_close', 'ingres_commit', 'ingres_connect', 'ingres_cursor', 'ingres_errno', 'ingres_error', 'ingres_errsqlstate', 'ingres_escape_string', 'ingres_execute', 'ingres_fetch_array', 'ingres_fetch_assoc', 'ingres_fetch_object', 'ingres_fetch_proc_return', 'ingres_fetch_row', 'ingres_field_length', 'ingres_field_name', 'ingres_field_nullable', 'ingres_field_precision', 'ingres_field_scale', 'ingres_field_type', 'ingres_free_result', 'ingres_next_error', 'ingres_num_fields', 'ingres_num_rows', 'ingres_pconnect', 'ingres_prepare', 'ingres_query', 'ingres_result_seek', 'ingres_rollback', 'ingres_set_environment', 'ingres_unbuffered_query', 'inotify_add_watch', 'inotify_init', 'inotify_queue_len', 'inotify_read', 'inotify_rm_watch', 'kadm5_chpass_principal', 'kadm5_create_principal', 'kadm5_delete_principal', 'kadm5_destroy', 'kadm5_flush', 'kadm5_get_policies', 'kadm5_get_principal', 'kadm5_get_principals', 'kadm5_init_with_password', 'kadm5_modify_principal', 'ldap_add', 'ldap_bind', 'ldap_close', 'ldap_compare', 'ldap_control_paged_result', 'ldap_control_paged_result_response', 'ldap_count_entries', 'ldap_delete', 'ldap_errno', 'ldap_error', 'ldap_exop', 'ldap_exop_passwd', 'ldap_exop_refresh', 'ldap_exop_whoami', 'ldap_first_attribute', 'ldap_first_entry', 'ldap_first_reference', 'ldap_free_result', 'ldap_get_attributes', 'ldap_get_dn', 'ldap_get_entries', 'ldap_get_option', 'ldap_get_values', 'ldap_get_values_len', 'ldap_mod_add', 'ldap_mod_del', 'ldap_mod_replace', 'ldap_modify', 'ldap_modify_batch', 'ldap_next_attribute', 'ldap_next_entry', 'ldap_next_reference', 'ldap_parse_exop', 'ldap_parse_reference', 'ldap_parse_result', 'ldap_rename', 'ldap_sasl_bind', 'ldap_set_option', 'ldap_set_rebind_proc', 'ldap_sort', 'ldap_start_tls', 'ldap_unbind', 'libxml_set_streams_context', 'm_checkstatus', 'm_completeauthorizations', 'm_connect', 'm_connectionerror', 'm_deletetrans', 'm_destroyconn', 'm_getcell', 'm_getcellbynum', 'm_getcommadelimited', 'm_getheader', 'm_initconn', 'm_iscommadelimited', 'm_maxconntimeout', 'm_monitor', 'm_numcolumns', 'm_numrows', 'm_parsecommadelimited', 'm_responsekeys', 'm_responseparam', 'm_returnstatus', 'm_setblocking', 'm_setdropfile', 'm_setip', 'm_setssl', 'm_setssl_cafile', 'm_setssl_files', 'm_settimeout', 'm_transactionssent', 'm_transinqueue', 'm_transkeyval', 'm_transnew', 'm_transsend', 'm_validateidentifier', 'm_verifyconnection', 'm_verifysslcert', 'mailparse_determine_best_xfer_encoding', 'mailparse_msg_create', 'mailparse_msg_extract_part', 'mailparse_msg_extract_part_file', 'mailparse_msg_extract_whole_part_file', 'mailparse_msg_free', 'mailparse_msg_get_part', 'mailparse_msg_get_part_data', 'mailparse_msg_get_structure', 'mailparse_msg_parse', 'mailparse_msg_parse_file', 'mailparse_stream_encode', 'mailparse_uudecode_all', 'maxdb::use_result', 'maxdb_affected_rows', 'maxdb_connect', 'maxdb_disable_rpl_parse', 'maxdb_dump_debug_info', 'maxdb_embedded_connect', 'maxdb_enable_reads_from_master', 'maxdb_enable_rpl_parse', 'maxdb_errno', 'maxdb_error', 'maxdb_fetch_lengths', 'maxdb_field_tell', 'maxdb_get_host_info', 'maxdb_get_proto_info', 'maxdb_get_server_info', 'maxdb_get_server_version', 'maxdb_info', 'maxdb_init', 'maxdb_insert_id', 'maxdb_master_query', 'maxdb_more_results', 'maxdb_next_result', 'maxdb_num_fields', 'maxdb_num_rows', 'maxdb_rpl_parse_enabled', 'maxdb_rpl_probe', 'maxdb_select_db', 'maxdb_sqlstate', 'maxdb_stmt::result_metadata', 'maxdb_stmt_affected_rows', 'maxdb_stmt_errno', 'maxdb_stmt_error', 'maxdb_stmt_num_rows', 'maxdb_stmt_param_count', 'maxdb_stmt_result_metadata', 'maxdb_stmt_sqlstate', 'maxdb_thread_id', 'maxdb_use_result', 'maxdb_warning_count', 'mcrypt_enc_get_algorithms_name', 'mcrypt_enc_get_block_size', 'mcrypt_enc_get_iv_size', 'mcrypt_enc_get_key_size', 'mcrypt_enc_get_modes_name', 'mcrypt_enc_get_supported_key_sizes', 'mcrypt_enc_is_block_algorithm', 'mcrypt_enc_is_block_algorithm_mode', 'mcrypt_enc_is_block_mode', 'mcrypt_enc_self_test', 'mcrypt_generic', 'mcrypt_generic_deinit', 'mcrypt_generic_end', 'mcrypt_generic_init', 'mcrypt_module_close', 'mcrypt_module_open', 'mdecrypt_generic', 'mkdir', 'mqseries_back', 'mqseries_begin', 'mqseries_close', 'mqseries_cmit', 'mqseries_conn', 'mqseries_connx', 'mqseries_disc', 'mqseries_get', 'mqseries_inq', 'mqseries_open', 'mqseries_put', 'mqseries_put1', 'mqseries_set', 'msg_get_queue', 'msg_receive', 'msg_remove_queue', 'msg_send', 'msg_set_queue', 'msg_stat_queue', 'msql_affected_rows', 'msql_close', 'msql_connect', 'msql_create_db', 'msql_data_seek', 'msql_db_query', 'msql_drop_db', 'msql_fetch_array', 'msql_fetch_field', 'msql_fetch_object', 'msql_fetch_row', 'msql_field_flags', 'msql_field_len', 'msql_field_name', 'msql_field_seek', 'msql_field_table', 'msql_field_type', 'msql_free_result', 'msql_list_dbs', 'msql_list_fields', 'msql_list_tables', 'msql_num_fields', 'msql_num_rows', 'msql_pconnect', 'msql_query', 'msql_result', 'msql_select_db', 'mssql_bind', 'mssql_close', 'mssql_connect', 'mssql_data_seek', 'mssql_execute', 'mssql_fetch_array', 'mssql_fetch_assoc', 'mssql_fetch_batch', 'mssql_fetch_field', 'mssql_fetch_object', 'mssql_fetch_row', 'mssql_field_length', 'mssql_field_name', 'mssql_field_seek', 'mssql_field_type', 'mssql_free_result', 'mssql_free_statement', 'mssql_init', 'mssql_next_result', 'mssql_num_fields', 'mssql_num_rows', 'mssql_pconnect', 'mssql_query', 'mssql_result', 'mssql_rows_affected', 'mssql_select_db', 'mysql_affected_rows', 'mysql_client_encoding', 'mysql_close', 'mysql_connect', 'mysql_create_db', 'mysql_data_seek', 'mysql_db_name', 'mysql_db_query', 'mysql_drop_db', 'mysql_errno', 'mysql_error', 'mysql_fetch_array', 'mysql_fetch_assoc', 'mysql_fetch_field', 'mysql_fetch_lengths', 'mysql_fetch_object', 'mysql_fetch_row', 'mysql_field_flags', 'mysql_field_len', 'mysql_field_name', 'mysql_field_seek', 'mysql_field_table', 'mysql_field_type', 'mysql_free_result', 'mysql_get_host_info', 'mysql_get_proto_info', 'mysql_get_server_info', 'mysql_info', 'mysql_insert_id', 'mysql_list_dbs', 'mysql_list_fields', 'mysql_list_processes', 'mysql_list_tables', 'mysql_num_fields', 'mysql_num_rows', 'mysql_pconnect', 'mysql_ping', 'mysql_query', 'mysql_real_escape_string', 'mysql_result', 'mysql_select_db', 'mysql_set_charset', 'mysql_stat', 'mysql_tablename', 'mysql_thread_id', 'mysql_unbuffered_query', 'mysqlnd_uh_convert_to_mysqlnd', 'ncurses_bottom_panel', 'ncurses_del_panel', 'ncurses_delwin', 'ncurses_getmaxyx', 'ncurses_getyx', 'ncurses_hide_panel', 'ncurses_keypad', 'ncurses_meta', 'ncurses_move_panel', 'ncurses_mvwaddstr', 'ncurses_new_panel', 'ncurses_newpad', 'ncurses_newwin', 'ncurses_panel_above', 'ncurses_panel_below', 'ncurses_panel_window', 'ncurses_pnoutrefresh', 'ncurses_prefresh', 'ncurses_replace_panel', 'ncurses_show_panel', 'ncurses_top_panel', 'ncurses_waddch', 'ncurses_waddstr', 'ncurses_wattroff', 'ncurses_wattron', 'ncurses_wattrset', 'ncurses_wborder', 'ncurses_wclear', 'ncurses_wcolor_set', 'ncurses_werase', 'ncurses_wgetch', 'ncurses_whline', 'ncurses_wmouse_trafo', 'ncurses_wmove', 'ncurses_wnoutrefresh', 'ncurses_wrefresh', 'ncurses_wstandend', 'ncurses_wstandout', 'ncurses_wvline', 'newt_button', 'newt_button_bar', 'newt_checkbox', 'newt_checkbox_get_value', 'newt_checkbox_set_flags', 'newt_checkbox_set_value', 'newt_checkbox_tree', 'newt_checkbox_tree_add_item', 'newt_checkbox_tree_find_item', 'newt_checkbox_tree_get_current', 'newt_checkbox_tree_get_entry_value', 'newt_checkbox_tree_get_multi_selection', 'newt_checkbox_tree_get_selection', 'newt_checkbox_tree_multi', 'newt_checkbox_tree_set_current', 'newt_checkbox_tree_set_entry', 'newt_checkbox_tree_set_entry_value', 'newt_checkbox_tree_set_width', 'newt_compact_button', 'newt_component_add_callback', 'newt_component_takes_focus', 'newt_create_grid', 'newt_draw_form', 'newt_entry', 'newt_entry_get_value', 'newt_entry_set', 'newt_entry_set_filter', 'newt_entry_set_flags', 'newt_form', 'newt_form_add_component', 'newt_form_add_components', 'newt_form_add_hot_key', 'newt_form_destroy', 'newt_form_get_current', 'newt_form_run', 'newt_form_set_background', 'newt_form_set_height', 'newt_form_set_size', 'newt_form_set_timer', 'newt_form_set_width', 'newt_form_watch_fd', 'newt_grid_add_components_to_form', 'newt_grid_basic_window', 'newt_grid_free', 'newt_grid_get_size', 'newt_grid_h_close_stacked', 'newt_grid_h_stacked', 'newt_grid_place', 'newt_grid_set_field', 'newt_grid_simple_window', 'newt_grid_v_close_stacked', 'newt_grid_v_stacked', 'newt_grid_wrapped_window', 'newt_grid_wrapped_window_at', 'newt_label', 'newt_label_set_text', 'newt_listbox', 'newt_listbox_append_entry', 'newt_listbox_clear', 'newt_listbox_clear_selection', 'newt_listbox_delete_entry', 'newt_listbox_get_current', 'newt_listbox_get_selection', 'newt_listbox_insert_entry', 'newt_listbox_item_count', 'newt_listbox_select_item', 'newt_listbox_set_current', 'newt_listbox_set_current_by_key', 'newt_listbox_set_data', 'newt_listbox_set_entry', 'newt_listbox_set_width', 'newt_listitem', 'newt_listitem_get_data', 'newt_listitem_set', 'newt_radio_get_current', 'newt_radiobutton', 'newt_run_form', 'newt_scale', 'newt_scale_set', 'newt_scrollbar_set', 'newt_textbox', 'newt_textbox_get_num_lines', 'newt_textbox_reflowed', 'newt_textbox_set_height', 'newt_textbox_set_text', 'newt_vertical_scrollbar', 'oci_bind_array_by_name', 'oci_bind_by_name', 'oci_cancel', 'oci_close', 'oci_commit', 'oci_connect', 'oci_define_by_name', 'oci_error', 'oci_execute', 'oci_fetch', 'oci_fetch_all', 'oci_fetch_array', 'oci_fetch_assoc', 'oci_fetch_object', 'oci_fetch_row', 'oci_field_is_null', 'oci_field_name', 'oci_field_precision', 'oci_field_scale', 'oci_field_size', 'oci_field_type', 'oci_field_type_raw', 'oci_free_cursor', 'oci_free_statement', 'oci_get_implicit_resultset', 'oci_new_collection', 'oci_new_connect', 'oci_new_cursor', 'oci_new_descriptor', 'oci_num_fields', 'oci_num_rows', 'oci_parse', 'oci_pconnect', 'oci_register_taf_callback', 'oci_result', 'oci_rollback', 'oci_server_version', 'oci_set_action', 'oci_set_client_identifier', 'oci_set_client_info', 'oci_set_module_name', 'oci_set_prefetch', 'oci_statement_type', 'oci_unregister_taf_callback', 'odbc_autocommit', 'odbc_close', 'odbc_columnprivileges', 'odbc_columns', 'odbc_commit', 'odbc_connect', 'odbc_cursor', 'odbc_data_source', 'odbc_do', 'odbc_error', 'odbc_errormsg', 'odbc_exec', 'odbc_execute', 'odbc_fetch_array', 'odbc_fetch_into', 'odbc_fetch_row', 'odbc_field_len', 'odbc_field_name', 'odbc_field_num', 'odbc_field_precision', 'odbc_field_scale', 'odbc_field_type', 'odbc_foreignkeys', 'odbc_free_result', 'odbc_gettypeinfo', 'odbc_next_result', 'odbc_num_fields', 'odbc_num_rows', 'odbc_pconnect', 'odbc_prepare', 'odbc_primarykeys', 'odbc_procedurecolumns', 'odbc_procedures', 'odbc_result', 'odbc_result_all', 'odbc_rollback', 'odbc_setoption', 'odbc_specialcolumns', 'odbc_statistics', 'odbc_tableprivileges', 'odbc_tables', 'openal_buffer_create', 'openal_buffer_data', 'openal_buffer_destroy', 'openal_buffer_get', 'openal_buffer_loadwav', 'openal_context_create', 'openal_context_current', 'openal_context_destroy', 'openal_context_process', 'openal_context_suspend', 'openal_device_close', 'openal_device_open', 'openal_source_create', 'openal_source_destroy', 'openal_source_get', 'openal_source_pause', 'openal_source_play', 'openal_source_rewind', 'openal_source_set', 'openal_source_stop', 'openal_stream', 'opendir', 'openssl_csr_new', 'openssl_dh_compute_key', 'openssl_free_key', 'openssl_pkey_export', 'openssl_pkey_free', 'openssl_pkey_get_details', 'openssl_spki_new', 'openssl_x509_free', 'pclose', 'pfsockopen', 'pg_affected_rows', 'pg_cancel_query', 'pg_client_encoding', 'pg_close', 'pg_connect_poll', 'pg_connection_busy', 'pg_connection_reset', 'pg_connection_status', 'pg_consume_input', 'pg_convert', 'pg_copy_from', 'pg_copy_to', 'pg_dbname', 'pg_delete', 'pg_end_copy', 'pg_escape_bytea', 'pg_escape_identifier', 'pg_escape_literal', 'pg_escape_string', 'pg_execute', 'pg_fetch_all', 'pg_fetch_all_columns', 'pg_fetch_array', 'pg_fetch_assoc', 'pg_fetch_row', 'pg_field_name', 'pg_field_num', 'pg_field_size', 'pg_field_table', 'pg_field_type', 'pg_field_type_oid', 'pg_flush', 'pg_free_result', 'pg_get_notify', 'pg_get_pid', 'pg_get_result', 'pg_host', 'pg_insert', 'pg_last_error', 'pg_last_notice', 'pg_last_oid', 'pg_lo_close', 'pg_lo_create', 'pg_lo_export', 'pg_lo_import', 'pg_lo_open', 'pg_lo_read', 'pg_lo_read_all', 'pg_lo_seek', 'pg_lo_tell', 'pg_lo_truncate', 'pg_lo_unlink', 'pg_lo_write', 'pg_meta_data', 'pg_num_fields', 'pg_num_rows', 'pg_options', 'pg_parameter_status', 'pg_ping', 'pg_port', 'pg_prepare', 'pg_put_line', 'pg_query', 'pg_query_params', 'pg_result_error', 'pg_result_error_field', 'pg_result_seek', 'pg_result_status', 'pg_select', 'pg_send_execute', 'pg_send_prepare', 'pg_send_query', 'pg_send_query_params', 'pg_set_client_encoding', 'pg_set_error_verbosity', 'pg_socket', 'pg_trace', 'pg_transaction_status', 'pg_tty', 'pg_untrace', 'pg_update', 'pg_version', 'php_user_filter::filter', 'proc_close', 'proc_get_status', 'proc_terminate', 'ps_add_bookmark', 'ps_add_launchlink', 'ps_add_locallink', 'ps_add_note', 'ps_add_pdflink', 'ps_add_weblink', 'ps_arc', 'ps_arcn', 'ps_begin_page', 'ps_begin_pattern', 'ps_begin_template', 'ps_circle', 'ps_clip', 'ps_close', 'ps_close_image', 'ps_closepath', 'ps_closepath_stroke', 'ps_continue_text', 'ps_curveto', 'ps_delete', 'ps_end_page', 'ps_end_pattern', 'ps_end_template', 'ps_fill', 'ps_fill_stroke', 'ps_findfont', 'ps_get_buffer', 'ps_get_parameter', 'ps_get_value', 'ps_hyphenate', 'ps_include_file', 'ps_lineto', 'ps_makespotcolor', 'ps_moveto', 'ps_new', 'ps_open_file', 'ps_open_image', 'ps_open_image_file', 'ps_open_memory_image', 'ps_place_image', 'ps_rect', 'ps_restore', 'ps_rotate', 'ps_save', 'ps_scale', 'ps_set_border_color', 'ps_set_border_dash', 'ps_set_border_style', 'ps_set_info', 'ps_set_parameter', 'ps_set_text_pos', 'ps_set_value', 'ps_setcolor', 'ps_setdash', 'ps_setflat', 'ps_setfont', 'ps_setgray', 'ps_setlinecap', 'ps_setlinejoin', 'ps_setlinewidth', 'ps_setmiterlimit', 'ps_setoverprintmode', 'ps_setpolydash', 'ps_shading', 'ps_shading_pattern', 'ps_shfill', 'ps_show', 'ps_show2', 'ps_show_boxed', 'ps_show_xy', 'ps_show_xy2', 'ps_string_geometry', 'ps_stringwidth', 'ps_stroke', 'ps_symbol', 'ps_symbol_name', 'ps_symbol_width', 'ps_translate', 'px_close', 'px_create_fp', 'px_date2string', 'px_delete', 'px_delete_record', 'px_get_field', 'px_get_info', 'px_get_parameter', 'px_get_record', 'px_get_schema', 'px_get_value', 'px_insert_record', 'px_new', 'px_numfields', 'px_numrecords', 'px_open_fp', 'px_put_record', 'px_retrieve_record', 'px_set_blob_file', 'px_set_parameter', 'px_set_tablename', 'px_set_targetencoding', 'px_set_value', 'px_timestamp2string', 'px_update_record', 'radius_acct_open', 'radius_add_server', 'radius_auth_open', 'radius_close', 'radius_config', 'radius_create_request', 'radius_demangle', 'radius_demangle_mppe_key', 'radius_get_attr', 'radius_put_addr', 'radius_put_attr', 'radius_put_int', 'radius_put_string', 'radius_put_vendor_addr', 'radius_put_vendor_attr', 'radius_put_vendor_int', 'radius_put_vendor_string', 'radius_request_authenticator', 'radius_salt_encrypt_attr', 'radius_send_request', 'radius_server_secret', 'radius_strerror', 'readdir', 'readfile', 'recode_file', 'rename', 'rewind', 'rewinddir', 'rmdir', 'rpm_close', 'rpm_get_tag', 'rpm_open', 'sapi_windows_vt100_support', 'scandir', 'sem_acquire', 'sem_get', 'sem_release', 'sem_remove', 'set_file_buffer', 'shm_attach', 'shm_detach', 'shm_get_var', 'shm_has_var', 'shm_put_var', 'shm_remove', 'shm_remove_var', 'shmop_close', 'shmop_delete', 'shmop_open', 'shmop_read', 'shmop_size', 'shmop_write', 'socket_accept', 'socket_addrinfo_bind', 'socket_addrinfo_connect', 'socket_addrinfo_explain', 'socket_bind', 'socket_clear_error', 'socket_close', 'socket_connect', 'socket_export_stream', 'socket_get_option', 'socket_get_status', 'socket_getopt', 'socket_getpeername', 'socket_getsockname', 'socket_import_stream', 'socket_last_error', 'socket_listen', 'socket_read', 'socket_recv', 'socket_recvfrom', 'socket_recvmsg', 'socket_send', 'socket_sendmsg', 'socket_sendto', 'socket_set_block', 'socket_set_blocking', 'socket_set_nonblock', 'socket_set_option', 'socket_set_timeout', 'socket_shutdown', 'socket_write', 'sqlite_close', 'sqlite_fetch_string', 'sqlite_has_more', 'sqlite_open', 'sqlite_popen', 'sqlsrv_begin_transaction', 'sqlsrv_cancel', 'sqlsrv_client_info', 'sqlsrv_close', 'sqlsrv_commit', 'sqlsrv_connect', 'sqlsrv_execute', 'sqlsrv_fetch', 'sqlsrv_fetch_array', 'sqlsrv_fetch_object', 'sqlsrv_field_metadata', 'sqlsrv_free_stmt', 'sqlsrv_get_field', 'sqlsrv_has_rows', 'sqlsrv_next_result', 'sqlsrv_num_fields', 'sqlsrv_num_rows', 'sqlsrv_prepare', 'sqlsrv_query', 'sqlsrv_rollback', 'sqlsrv_rows_affected', 'sqlsrv_send_stream_data', 'sqlsrv_server_info', 'ssh2_auth_agent', 'ssh2_auth_hostbased_file', 'ssh2_auth_none', 'ssh2_auth_password', 'ssh2_auth_pubkey_file', 'ssh2_disconnect', 'ssh2_exec', 'ssh2_fetch_stream', 'ssh2_fingerprint', 'ssh2_methods_negotiated', 'ssh2_publickey_add', 'ssh2_publickey_init', 'ssh2_publickey_list', 'ssh2_publickey_remove', 'ssh2_scp_recv', 'ssh2_scp_send', 'ssh2_sftp', 'ssh2_sftp_chmod', 'ssh2_sftp_lstat', 'ssh2_sftp_mkdir', 'ssh2_sftp_readlink', 'ssh2_sftp_realpath', 'ssh2_sftp_rename', 'ssh2_sftp_rmdir', 'ssh2_sftp_stat', 'ssh2_sftp_symlink', 'ssh2_sftp_unlink', 'ssh2_shell', 'ssh2_tunnel', 'stomp_connect', 'streamWrapper::stream_cast', 'stream_bucket_append', 'stream_bucket_make_writeable', 'stream_bucket_new', 'stream_bucket_prepend', 'stream_context_create', 'stream_context_get_default', 'stream_context_get_options', 'stream_context_get_params', 'stream_context_set_default', 'stream_context_set_params', 'stream_copy_to_stream', 'stream_encoding', 'stream_filter_append', 'stream_filter_prepend', 'stream_filter_remove', 'stream_get_contents', 'stream_get_line', 'stream_get_meta_data', 'stream_isatty', 'stream_set_blocking', 'stream_set_chunk_size', 'stream_set_read_buffer', 'stream_set_timeout', 'stream_set_write_buffer', 'stream_socket_accept', 'stream_socket_client', 'stream_socket_enable_crypto', 'stream_socket_get_name', 'stream_socket_recvfrom', 'stream_socket_sendto', 'stream_socket_server', 'stream_socket_shutdown', 'stream_supports_lock', 'svn_fs_abort_txn', 'svn_fs_apply_text', 'svn_fs_begin_txn2', 'svn_fs_change_node_prop', 'svn_fs_check_path', 'svn_fs_contents_changed', 'svn_fs_copy', 'svn_fs_delete', 'svn_fs_dir_entries', 'svn_fs_file_contents', 'svn_fs_file_length', 'svn_fs_is_dir', 'svn_fs_is_file', 'svn_fs_make_dir', 'svn_fs_make_file', 'svn_fs_node_created_rev', 'svn_fs_node_prop', 'svn_fs_props_changed', 'svn_fs_revision_prop', 'svn_fs_revision_root', 'svn_fs_txn_root', 'svn_fs_youngest_rev', 'svn_repos_create', 'svn_repos_fs', 'svn_repos_fs_begin_txn_for_commit', 'svn_repos_fs_commit_txn', 'svn_repos_open', 'sybase_affected_rows', 'sybase_close', 'sybase_connect', 'sybase_data_seek', 'sybase_fetch_array', 'sybase_fetch_assoc', 'sybase_fetch_field', 'sybase_fetch_object', 'sybase_fetch_row', 'sybase_field_seek', 'sybase_free_result', 'sybase_num_fields', 'sybase_num_rows', 'sybase_pconnect', 'sybase_query', 'sybase_result', 'sybase_select_db', 'sybase_set_message_handler', 'sybase_unbuffered_query', 'tmpfile', 'udm_add_search_limit', 'udm_alloc_agent', 'udm_alloc_agent_array', 'udm_cat_list', 'udm_cat_path', 'udm_check_charset', 'udm_clear_search_limits', 'udm_crc32', 'udm_errno', 'udm_error', 'udm_find', 'udm_free_agent', 'udm_free_res', 'udm_get_doc_count', 'udm_get_res_field', 'udm_get_res_param', 'udm_hash32', 'udm_load_ispell_data', 'udm_set_agent_param', 'unlink', 'vfprintf', 'w32api_init_dtype', 'wddx_add_vars', 'wddx_packet_end', 'wddx_packet_start', 'xml_get_current_byte_index', 'xml_get_current_column_number', 'xml_get_current_line_number', 'xml_get_error_code', 'xml_parse', 'xml_parse_into_struct', 'xml_parser_create', 'xml_parser_create_ns', 'xml_parser_free', 'xml_parser_get_option', 'xml_parser_set_option', 'xml_set_character_data_handler', 'xml_set_default_handler', 'xml_set_element_handler', 'xml_set_end_namespace_decl_handler', 'xml_set_external_entity_ref_handler', 'xml_set_notation_decl_handler', 'xml_set_object', 'xml_set_processing_instruction_handler', 'xml_set_start_namespace_decl_handler', 'xml_set_unparsed_entity_decl_handler', 'xmlrpc_server_add_introspection_data', 'xmlrpc_server_call_method', 'xmlrpc_server_create', 'xmlrpc_server_destroy', 'xmlrpc_server_register_introspection_callback', 'xmlrpc_server_register_method', 'xmlwriter_end_attribute', 'xmlwriter_end_cdata', 'xmlwriter_end_comment', 'xmlwriter_end_document', 'xmlwriter_end_dtd', 'xmlwriter_end_dtd_attlist', 'xmlwriter_end_dtd_element', 'xmlwriter_end_dtd_entity', 'xmlwriter_end_element', 'xmlwriter_end_pi', 'xmlwriter_flush', 'xmlwriter_full_end_element', 'xmlwriter_open_memory', 'xmlwriter_open_uri', 'xmlwriter_output_memory', 'xmlwriter_set_indent', 'xmlwriter_set_indent_string', 'xmlwriter_start_attribute', 'xmlwriter_start_attribute_ns', 'xmlwriter_start_cdata', 'xmlwriter_start_comment', 'xmlwriter_start_document', 'xmlwriter_start_dtd', 'xmlwriter_start_dtd_attlist', 'xmlwriter_start_dtd_element', 'xmlwriter_start_dtd_entity', 'xmlwriter_start_element', 'xmlwriter_start_element_ns', 'xmlwriter_start_pi', 'xmlwriter_text', 'xmlwriter_write_attribute', 'xmlwriter_write_attribute_ns', 'xmlwriter_write_cdata', 'xmlwriter_write_comment', 'xmlwriter_write_dtd', 'xmlwriter_write_dtd_attlist', 'xmlwriter_write_dtd_element', 'xmlwriter_write_dtd_entity', 'xmlwriter_write_element', 'xmlwriter_write_element_ns', 'xmlwriter_write_pi', 'xmlwriter_write_raw', 'xslt_create', 'yaz_addinfo', 'yaz_ccl_conf', 'yaz_ccl_parse', 'yaz_close', 'yaz_database', 'yaz_element', 'yaz_errno', 'yaz_error', 'yaz_es', 'yaz_es_result', 'yaz_get_option', 'yaz_hits', 'yaz_itemorder', 'yaz_present', 'yaz_range', 'yaz_record', 'yaz_scan', 'yaz_scan_result', 'yaz_schema', 'yaz_search', 'yaz_sort', 'yaz_syntax', 'zip_close', 'zip_entry_close', 'zip_entry_compressedsize', 'zip_entry_compressionmethod', 'zip_entry_filesize', 'zip_entry_name', 'zip_entry_open', 'zip_entry_read', 'zip_open', 'zip_read']; } } +Resource Operations + +Copyright (c) 2015-2020, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeUnit; -use function count; -use Countable; -use IteratorAggregate; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit * @psalm-immutable */ -final class IniSettingCollection implements \Countable, \IteratorAggregate +final class FunctionUnit extends CodeUnit { /** - * @var IniSetting[] - */ - private $iniSettings; - /** - * @param IniSetting[] $iniSettings - */ - public static function fromArray(array $iniSettings) : self - { - return new self(...$iniSettings); - } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\IniSetting ...$iniSettings) - { - $this->iniSettings = $iniSettings; - } - /** - * @return IniSetting[] + * @psalm-assert-if-true FunctionUnit $this */ - public function asArray() : array - { - return $this->iniSettings; - } - public function count() : int - { - return \count($this->iniSettings); - } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\IniSettingCollectionIterator + public function isFunction() : bool { - return new \PHPUnit\TextUI\XmlConfiguration\IniSettingCollectionIterator($this); + return \true; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeUnit; -use function count; -use Countable; -use IteratorAggregate; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class ConstantCollection implements \Countable, \IteratorAggregate +use Iterator; +final class CodeUnitCollectionIterator implements Iterator { /** - * @var Constant[] + * @psalm-var list */ - private $constants; + private $codeUnits; /** - * @param Constant[] $constants + * @var int */ - public static function fromArray(array $constants) : self + private $position = 0; + public function __construct(CodeUnitCollection $collection) { - return new self(...$constants); + $this->codeUnits = $collection->asArray(); } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\Constant ...$constants) + public function rewind() : void { - $this->constants = $constants; + $this->position = 0; } - /** - * @return Constant[] - */ - public function asArray() : array + public function valid() : bool { - return $this->constants; + return isset($this->codeUnits[$this->position]); } - public function count() : int + public function key() : int { - return \count($this->constants); + return $this->position; } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\ConstantCollectionIterator + public function current() : CodeUnit { - return new \PHPUnit\TextUI\XmlConfiguration\ConstantCollectionIterator($this); + return $this->codeUnits[$this->position]; + } + public function next() : void + { + $this->position++; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeUnit; -use function count; -use Countable; -use IteratorAggregate; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class VariableCollection implements \Countable, \IteratorAggregate +use function array_keys; +use function array_merge; +use function array_unique; +use function array_values; +use function class_exists; +use function explode; +use function function_exists; +use function interface_exists; +use function ksort; +use function method_exists; +use function sort; +use function sprintf; +use function str_replace; +use function strpos; +use function trait_exists; +use ReflectionClass; +use ReflectionFunction; +use ReflectionMethod; +final class Mapper { /** - * @var Variable[] + * @psalm-return array> */ - private $variables; + public function codeUnitsToSourceLines(CodeUnitCollection $codeUnits) : array + { + $result = []; + foreach ($codeUnits as $codeUnit) { + $sourceFileName = $codeUnit->sourceFileName(); + if (!isset($result[$sourceFileName])) { + $result[$sourceFileName] = []; + } + $result[$sourceFileName] = array_merge($result[$sourceFileName], $codeUnit->sourceLines()); + } + foreach (array_keys($result) as $sourceFileName) { + $result[$sourceFileName] = array_values(array_unique($result[$sourceFileName])); + sort($result[$sourceFileName]); + } + ksort($result); + return $result; + } /** - * @param Variable[] $variables + * @throws InvalidCodeUnitException + * @throws ReflectionException */ - public static function fromArray(array $variables) : self + public function stringToCodeUnits(string $unit) : CodeUnitCollection { - return new self(...$variables); + if (strpos($unit, '::') !== \false) { + [$firstPart, $secondPart] = explode('::', $unit); + if (empty($firstPart) && $this->isUserDefinedFunction($secondPart)) { + return CodeUnitCollection::fromList(CodeUnit::forFunction($secondPart)); + } + if ($this->isUserDefinedClass($firstPart)) { + if ($secondPart === '') { + return $this->publicMethodsOfClass($firstPart); + } + if ($secondPart === '') { + return $this->protectedAndPrivateMethodsOfClass($firstPart); + } + if ($secondPart === '') { + return $this->protectedMethodsOfClass($firstPart); + } + if ($secondPart === '') { + return $this->publicAndPrivateMethodsOfClass($firstPart); + } + if ($secondPart === '') { + return $this->privateMethodsOfClass($firstPart); + } + if ($secondPart === '') { + return $this->publicAndProtectedMethodsOfClass($firstPart); + } + if ($this->isUserDefinedMethod($firstPart, $secondPart)) { + return CodeUnitCollection::fromList(CodeUnit::forClassMethod($firstPart, $secondPart)); + } + } + if ($this->isUserDefinedInterface($firstPart)) { + return CodeUnitCollection::fromList(CodeUnit::forInterfaceMethod($firstPart, $secondPart)); + } + if ($this->isUserDefinedTrait($firstPart)) { + return CodeUnitCollection::fromList(CodeUnit::forTraitMethod($firstPart, $secondPart)); + } + } else { + if ($this->isUserDefinedClass($unit)) { + $units = [CodeUnit::forClass($unit)]; + foreach ($this->reflectorForClass($unit)->getTraits() as $trait) { + if (!$trait->isUserDefined()) { + // @codeCoverageIgnoreStart + continue; + // @codeCoverageIgnoreEnd + } + $units[] = CodeUnit::forTrait($trait->getName()); + } + return CodeUnitCollection::fromArray($units); + } + if ($this->isUserDefinedInterface($unit)) { + return CodeUnitCollection::fromList(CodeUnit::forInterface($unit)); + } + if ($this->isUserDefinedTrait($unit)) { + return CodeUnitCollection::fromList(CodeUnit::forTrait($unit)); + } + if ($this->isUserDefinedFunction($unit)) { + return CodeUnitCollection::fromList(CodeUnit::forFunction($unit)); + } + $unit = str_replace('', '', $unit); + if ($this->isUserDefinedClass($unit)) { + return $this->classAndParentClassesAndTraits($unit); + } + } + throw new InvalidCodeUnitException(sprintf('"%s" is not a valid code unit', $unit)); } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\Variable ...$variables) + /** + * @psalm-param class-string $className + * + * @throws ReflectionException + */ + private function publicMethodsOfClass(string $className) : CodeUnitCollection { - $this->variables = $variables; + return $this->methodsOfClass($className, ReflectionMethod::IS_PUBLIC); } /** - * @return Variable[] + * @psalm-param class-string $className + * + * @throws ReflectionException */ - public function asArray() : array + private function publicAndProtectedMethodsOfClass(string $className) : CodeUnitCollection { - return $this->variables; + return $this->methodsOfClass($className, ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED); } - public function count() : int + /** + * @psalm-param class-string $className + * + * @throws ReflectionException + */ + private function publicAndPrivateMethodsOfClass(string $className) : CodeUnitCollection { - return \count($this->variables); + return $this->methodsOfClass($className, ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PRIVATE); } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\VariableCollectionIterator + /** + * @psalm-param class-string $className + * + * @throws ReflectionException + */ + private function protectedMethodsOfClass(string $className) : CodeUnitCollection { - return new \PHPUnit\TextUI\XmlConfiguration\VariableCollectionIterator($this); + return $this->methodsOfClass($className, ReflectionMethod::IS_PROTECTED); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use const PATH_SEPARATOR; -use function constant; -use function define; -use function defined; -use function getenv; -use function implode; -use function ini_get; -use function ini_set; -use function putenv; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class PhpHandler -{ - public function handle(\PHPUnit\TextUI\XmlConfiguration\Php $configuration) : void + /** + * @psalm-param class-string $className + * + * @throws ReflectionException + */ + private function protectedAndPrivateMethodsOfClass(string $className) : CodeUnitCollection { - $this->handleIncludePaths($configuration->includePaths()); - $this->handleIniSettings($configuration->iniSettings()); - $this->handleConstants($configuration->constants()); - $this->handleGlobalVariables($configuration->globalVariables()); - $this->handleServerVariables($configuration->serverVariables()); - $this->handleEnvVariables($configuration->envVariables()); - $this->handleVariables('_POST', $configuration->postVariables()); - $this->handleVariables('_GET', $configuration->getVariables()); - $this->handleVariables('_COOKIE', $configuration->cookieVariables()); - $this->handleVariables('_FILES', $configuration->filesVariables()); - $this->handleVariables('_REQUEST', $configuration->requestVariables()); + return $this->methodsOfClass($className, ReflectionMethod::IS_PROTECTED | ReflectionMethod::IS_PRIVATE); } - private function handleIncludePaths(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection $includePaths) : void + /** + * @psalm-param class-string $className + * + * @throws ReflectionException + */ + private function privateMethodsOfClass(string $className) : CodeUnitCollection { - if (!$includePaths->isEmpty()) { - $includePathsAsStrings = []; - foreach ($includePaths as $includePath) { - $includePathsAsStrings[] = $includePath->path(); + return $this->methodsOfClass($className, ReflectionMethod::IS_PRIVATE); + } + /** + * @psalm-param class-string $className + * + * @throws ReflectionException + */ + private function methodsOfClass(string $className, int $filter) : CodeUnitCollection + { + $units = []; + foreach ($this->reflectorForClass($className)->getMethods($filter) as $method) { + if (!$method->isUserDefined()) { + continue; } - \ini_set('include_path', \implode(\PATH_SEPARATOR, $includePathsAsStrings) . \PATH_SEPARATOR . \ini_get('include_path')); + $units[] = CodeUnit::forClassMethod($className, $method->getName()); } + return CodeUnitCollection::fromArray($units); } - private function handleIniSettings(\PHPUnit\TextUI\XmlConfiguration\IniSettingCollection $iniSettings) : void + /** + * @psalm-param class-string $className + * + * @throws ReflectionException + */ + private function classAndParentClassesAndTraits(string $className) : CodeUnitCollection { - foreach ($iniSettings as $iniSetting) { - $value = $iniSetting->value(); - if (\defined($value)) { - $value = (string) \constant($value); + $units = [CodeUnit::forClass($className)]; + $reflector = $this->reflectorForClass($className); + foreach ($this->reflectorForClass($className)->getTraits() as $trait) { + if (!$trait->isUserDefined()) { + // @codeCoverageIgnoreStart + continue; + // @codeCoverageIgnoreEnd + } + $units[] = CodeUnit::forTrait($trait->getName()); + } + while ($reflector = $reflector->getParentClass()) { + if (!$reflector->isUserDefined()) { + break; + } + $units[] = CodeUnit::forClass($reflector->getName()); + foreach ($reflector->getTraits() as $trait) { + if (!$trait->isUserDefined()) { + // @codeCoverageIgnoreStart + continue; + // @codeCoverageIgnoreEnd + } + $units[] = CodeUnit::forTrait($trait->getName()); } - \ini_set($iniSetting->name(), $value); } + return CodeUnitCollection::fromArray($units); } - private function handleConstants(\PHPUnit\TextUI\XmlConfiguration\ConstantCollection $constants) : void + /** + * @psalm-param class-string $className + * + * @throws ReflectionException + */ + private function reflectorForClass(string $className) : ReflectionClass { - foreach ($constants as $constant) { - if (!\defined($constant->name())) { - \define($constant->name(), $constant->value()); - } + try { + return new ReflectionClass($className); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); } + // @codeCoverageIgnoreEnd } - private function handleGlobalVariables(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void + /** + * @throws ReflectionException + */ + private function isUserDefinedFunction(string $functionName) : bool { - foreach ($variables as $variable) { - $GLOBALS[$variable->name()] = $variable->value(); + if (!function_exists($functionName)) { + return \false; + } + try { + return (new ReflectionFunction($functionName))->isUserDefined(); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); } + // @codeCoverageIgnoreEnd } - private function handleServerVariables(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void + /** + * @throws ReflectionException + */ + private function isUserDefinedClass(string $className) : bool { - foreach ($variables as $variable) { - $_SERVER[$variable->name()] = $variable->value(); + if (!class_exists($className)) { + return \false; } + try { + return (new ReflectionClass($className))->isUserDefined(); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd } - private function handleVariables(string $target, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void + /** + * @throws ReflectionException + */ + private function isUserDefinedInterface(string $interfaceName) : bool { - foreach ($variables as $variable) { - $GLOBALS[$target][$variable->name()] = $variable->value(); + if (!interface_exists($interfaceName)) { + return \false; + } + try { + return (new ReflectionClass($interfaceName))->isUserDefined(); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); } + // @codeCoverageIgnoreEnd } - private function handleEnvVariables(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void + /** + * @throws ReflectionException + */ + private function isUserDefinedTrait(string $traitName) : bool { - foreach ($variables as $variable) { - $name = $variable->name(); - $value = $variable->value(); - $force = $variable->force(); - if ($force || \getenv($name) === \false) { - \putenv("{$name}={$value}"); - } - $value = \getenv($name); - if ($force || !isset($_ENV[$name])) { - $_ENV[$name] = $value; - } + if (!trait_exists($traitName)) { + return \false; + } + try { + return (new ReflectionClass($traitName))->isUserDefined(); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + /** + * @throws ReflectionException + */ + private function isUserDefinedMethod(string $className, string $methodName) : bool + { + if (!class_exists($className)) { + // @codeCoverageIgnoreStart + return \false; + // @codeCoverageIgnoreEnd + } + if (!method_exists($className, $methodName)) { + // @codeCoverageIgnoreStart + return \false; + // @codeCoverageIgnoreEnd + } + try { + return (new ReflectionMethod($className, $methodName))->isUserDefined(); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); } + // @codeCoverageIgnoreEnd + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeUnit; + +/** + * @psalm-immutable + */ +final class InterfaceUnit extends CodeUnit +{ + /** + * @psalm-assert-if-true InterfaceUnit $this + */ + public function isInterface() : bool + { + return \true; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeUnit; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage; -use PHPUnit\TextUI\XmlConfiguration\Logging\Logging; -use PHPUnit\Util\Xml\ValidationResult; +use function range; +use function sprintf; +use ReflectionClass; +use ReflectionFunction; +use ReflectionMethod; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit * @psalm-immutable */ -final class Configuration +abstract class CodeUnit { /** * @var string */ - private $filename; + private $name; /** - * @var ValidationResult + * @var string */ - private $validationResult; + private $sourceFileName; /** - * @var ExtensionCollection + * @var array + * @psalm-var list */ - private $extensions; + private $sourceLines; /** - * @var CodeCoverage + * @psalm-param class-string $className + * + * @throws InvalidCodeUnitException + * @throws ReflectionException */ - private $codeCoverage; + public static function forClass(string $className) : ClassUnit + { + self::ensureUserDefinedClass($className); + $reflector = self::reflectorForClass($className); + return new ClassUnit($className, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); + } /** - * @var Groups + * @psalm-param class-string $className + * + * @throws InvalidCodeUnitException + * @throws ReflectionException */ - private $groups; + public static function forClassMethod(string $className, string $methodName) : ClassMethodUnit + { + self::ensureUserDefinedClass($className); + $reflector = self::reflectorForClassMethod($className, $methodName); + return new ClassMethodUnit($className . '::' . $methodName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); + } /** - * @var Groups + * @psalm-param class-string $interfaceName + * + * @throws InvalidCodeUnitException + * @throws ReflectionException */ - private $testdoxGroups; + public static function forInterface(string $interfaceName) : InterfaceUnit + { + self::ensureUserDefinedInterface($interfaceName); + $reflector = self::reflectorForClass($interfaceName); + return new InterfaceUnit($interfaceName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); + } /** - * @var ExtensionCollection + * @psalm-param class-string $interfaceName + * + * @throws InvalidCodeUnitException + * @throws ReflectionException */ - private $listeners; + public static function forInterfaceMethod(string $interfaceName, string $methodName) : InterfaceMethodUnit + { + self::ensureUserDefinedInterface($interfaceName); + $reflector = self::reflectorForClassMethod($interfaceName, $methodName); + return new InterfaceMethodUnit($interfaceName . '::' . $methodName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); + } /** - * @var Logging + * @psalm-param class-string $traitName + * + * @throws InvalidCodeUnitException + * @throws ReflectionException */ - private $logging; + public static function forTrait(string $traitName) : TraitUnit + { + self::ensureUserDefinedTrait($traitName); + $reflector = self::reflectorForClass($traitName); + return new TraitUnit($traitName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); + } /** - * @var Php + * @psalm-param class-string $traitName + * + * @throws InvalidCodeUnitException + * @throws ReflectionException */ - private $php; + public static function forTraitMethod(string $traitName, string $methodName) : TraitMethodUnit + { + self::ensureUserDefinedTrait($traitName); + $reflector = self::reflectorForClassMethod($traitName, $methodName); + return new TraitMethodUnit($traitName . '::' . $methodName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); + } /** - * @var PHPUnit + * @psalm-param callable-string $functionName + * + * @throws InvalidCodeUnitException + * @throws ReflectionException */ - private $phpunit; + public static function forFunction(string $functionName) : FunctionUnit + { + $reflector = self::reflectorForFunction($functionName); + if (!$reflector->isUserDefined()) { + throw new InvalidCodeUnitException(sprintf('"%s" is not a user-defined function', $functionName)); + } + return new FunctionUnit($functionName, $reflector->getFileName(), range($reflector->getStartLine(), $reflector->getEndLine())); + } /** - * @var TestSuiteCollection + * @psalm-param list $sourceLines */ - private $testSuite; - public function __construct(string $filename, \PHPUnit\Util\Xml\ValidationResult $validationResult, \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection $extensions, \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage $codeCoverage, \PHPUnit\TextUI\XmlConfiguration\Groups $groups, \PHPUnit\TextUI\XmlConfiguration\Groups $testdoxGroups, \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection $listeners, \PHPUnit\TextUI\XmlConfiguration\Logging\Logging $logging, \PHPUnit\TextUI\XmlConfiguration\Php $php, \PHPUnit\TextUI\XmlConfiguration\PHPUnit $phpunit, \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection $testSuite) + private function __construct(string $name, string $sourceFileName, array $sourceLines) { - $this->filename = $filename; - $this->validationResult = $validationResult; - $this->extensions = $extensions; - $this->codeCoverage = $codeCoverage; - $this->groups = $groups; - $this->testdoxGroups = $testdoxGroups; - $this->listeners = $listeners; - $this->logging = $logging; - $this->php = $php; - $this->phpunit = $phpunit; - $this->testSuite = $testSuite; + $this->name = $name; + $this->sourceFileName = $sourceFileName; + $this->sourceLines = $sourceLines; } - public function filename() : string + public function name() : string { - return $this->filename; + return $this->name; } - public function hasValidationErrors() : bool + public function sourceFileName() : string { - return $this->validationResult->hasValidationErrors(); + return $this->sourceFileName; } - public function validationErrors() : string + /** + * @psalm-return list + */ + public function sourceLines() : array { - return $this->validationResult->asString(); + return $this->sourceLines; } - public function extensions() : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection + public function isClass() : bool { - return $this->extensions; + return \false; } - public function codeCoverage() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage + public function isClassMethod() : bool { - return $this->codeCoverage; + return \false; } - public function groups() : \PHPUnit\TextUI\XmlConfiguration\Groups + public function isInterface() : bool { - return $this->groups; + return \false; } - public function testdoxGroups() : \PHPUnit\TextUI\XmlConfiguration\Groups + public function isInterfaceMethod() : bool { - return $this->testdoxGroups; + return \false; } - public function listeners() : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection + public function isTrait() : bool { - return $this->listeners; + return \false; } - public function logging() : \PHPUnit\TextUI\XmlConfiguration\Logging\Logging + public function isTraitMethod() : bool { - return $this->logging; + return \false; } - public function php() : \PHPUnit\TextUI\XmlConfiguration\Php + public function isFunction() : bool { - return $this->php; + return \false; } - public function phpunit() : \PHPUnit\TextUI\XmlConfiguration\PHPUnit + /** + * @psalm-param class-string $className + * + * @throws InvalidCodeUnitException + */ + private static function ensureUserDefinedClass(string $className) : void { - return $this->phpunit; + try { + $reflector = new ReflectionClass($className); + if ($reflector->isInterface()) { + throw new InvalidCodeUnitException(sprintf('"%s" is an interface and not a class', $className)); + } + if ($reflector->isTrait()) { + throw new InvalidCodeUnitException(sprintf('"%s" is a trait and not a class', $className)); + } + if (!$reflector->isUserDefined()) { + throw new InvalidCodeUnitException(sprintf('"%s" is not a user-defined class', $className)); + } + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd } - public function testSuite() : \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection + /** + * @psalm-param class-string $interfaceName + * + * @throws InvalidCodeUnitException + */ + private static function ensureUserDefinedInterface(string $interfaceName) : void { - return $this->testSuite; + try { + $reflector = new ReflectionClass($interfaceName); + if (!$reflector->isInterface()) { + throw new InvalidCodeUnitException(sprintf('"%s" is not an interface', $interfaceName)); + } + if (!$reflector->isUserDefined()) { + throw new InvalidCodeUnitException(sprintf('"%s" is not a user-defined interface', $interfaceName)); + } + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + /** + * @psalm-param class-string $traitName + * + * @throws InvalidCodeUnitException + */ + private static function ensureUserDefinedTrait(string $traitName) : void + { + try { + $reflector = new ReflectionClass($traitName); + if (!$reflector->isTrait()) { + throw new InvalidCodeUnitException(sprintf('"%s" is not a trait', $traitName)); + } + // @codeCoverageIgnoreStart + if (!$reflector->isUserDefined()) { + throw new InvalidCodeUnitException(sprintf('"%s" is not a user-defined trait', $traitName)); + } + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + /** + * @psalm-param class-string $className + * + * @throws ReflectionException + */ + private static function reflectorForClass(string $className) : ReflectionClass + { + try { + return new ReflectionClass($className); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + /** + * @psalm-param class-string $className + * + * @throws ReflectionException + */ + private static function reflectorForClassMethod(string $className, string $methodName) : ReflectionMethod + { + try { + return new ReflectionMethod($className, $methodName); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + /** + * @psalm-param callable-string $functionName + * + * @throws ReflectionException + */ + private static function reflectorForFunction(string $functionName) : ReflectionFunction + { + try { + return new ReflectionFunction($functionName); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeUnit; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit * @psalm-immutable */ -final class TestSuite +final class TraitUnit extends CodeUnit { /** - * @var string - */ - private $name; - /** - * @var TestDirectoryCollection - */ - private $directories; - /** - * @var TestFileCollection - */ - private $files; - /** - * @var FileCollection + * @psalm-assert-if-true TraitUnit $this */ - private $exclude; - public function __construct(string $name, \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection $directories, \PHPUnit\TextUI\XmlConfiguration\TestFileCollection $files, \PHPUnit\TextUI\XmlConfiguration\FileCollection $exclude) - { - $this->name = $name; - $this->directories = $directories; - $this->files = $files; - $this->exclude = $exclude; - } - public function name() : string - { - return $this->name; - } - public function directories() : \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection - { - return $this->directories; - } - public function files() : \PHPUnit\TextUI\XmlConfiguration\TestFileCollection - { - return $this->files; - } - public function exclude() : \PHPUnit\TextUI\XmlConfiguration\FileCollection + public function isTrait() : bool { - return $this->exclude; + return \true; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeUnit; -use function count; -use function iterator_count; -use Countable; -use Iterator; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable */ -final class TestFileCollectionIterator implements \Countable, \Iterator +final class InterfaceMethodUnit extends CodeUnit { /** - * @var TestFile[] - */ - private $files; - /** - * @var int + * @psalm-assert-if-true InterfaceMethod $this */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\TestFileCollection $files) - { - $this->files = $files->asArray(); - } - public function count() : int - { - return \iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < \count($this->files); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\TestFile - { - return $this->files[$this->position]; - } - public function next() : void + public function isInterfaceMethod() : bool { - $this->position++; + return \true; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeUnit; -use PHPUnit\Util\VersionComparisonOperator; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit * @psalm-immutable */ -final class TestFile +final class ClassMethodUnit extends CodeUnit { /** - * @var string - */ - private $path; - /** - * @var string - */ - private $phpVersion; - /** - * @var VersionComparisonOperator + * @psalm-assert-if-true ClassMethodUnit $this */ - private $phpVersionOperator; - public function __construct(string $path, string $phpVersion, \PHPUnit\Util\VersionComparisonOperator $phpVersionOperator) - { - $this->path = $path; - $this->phpVersion = $phpVersion; - $this->phpVersionOperator = $phpVersionOperator; - } - public function path() : string - { - return $this->path; - } - public function phpVersion() : string - { - return $this->phpVersion; - } - public function phpVersionOperator() : \PHPUnit\Util\VersionComparisonOperator + public function isClassMethod() : bool { - return $this->phpVersionOperator; + return \true; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeUnit; -use function count; -use Countable; -use IteratorAggregate; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class TestFileCollection implements \Countable, \IteratorAggregate +use RuntimeException; +final class NoTraitException extends RuntimeException implements Exception { - /** - * @var TestFile[] - */ - private $files; - /** - * @param TestFile[] $files - */ - public static function fromArray(array $files) : self - { - return new self(...$files); - } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\TestFile ...$files) - { - $this->files = $files; - } - /** - * @return TestFile[] - */ - public function asArray() : array - { - return $this->files; - } - public function count() : int - { - return \count($this->files); - } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\TestFileCollectionIterator - { - return new \PHPUnit\TextUI\XmlConfiguration\TestFileCollectionIterator($this); - } - public function isEmpty() : bool - { - return $this->count() === 0; - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeUnit; -use function count; -use function iterator_count; -use Countable; -use Iterator; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestSuiteCollectionIterator implements \Countable, \Iterator +use RuntimeException; +final class ReflectionException extends RuntimeException implements Exception { - /** - * @var TestSuite[] - */ - private $testSuites; - /** - * @var int - */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection $testSuites) - { - $this->testSuites = $testSuites->asArray(); - } - public function count() : int - { - return \iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < \count($this->testSuites); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\TestSuite - { - return $this->testSuites[$this->position]; - } - public function next() : void - { - $this->position++; - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeUnit; -use function count; -use Countable; -use IteratorAggregate; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class TestDirectoryCollection implements \Countable, \IteratorAggregate +use Throwable; +interface Exception extends Throwable +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeUnit; + +use RuntimeException; +final class InvalidCodeUnitException extends RuntimeException implements Exception { - /** - * @var TestDirectory[] - */ - private $directories; - /** - * @param TestDirectory[] $directories - */ - public static function fromArray(array $directories) : self - { - return new self(...$directories); - } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\TestDirectory ...$directories) - { - $this->directories = $directories; - } - /** - * @return TestDirectory[] - */ - public function asArray() : array - { - return $this->directories; - } - public function count() : int - { - return \count($this->directories); - } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollectionIterator - { - return new \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollectionIterator($this); - } - public function isEmpty() : bool - { - return $this->count() === 0; - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeUnit; -use const PHP_VERSION; -use function explode; -use function in_array; -use function version_compare; -use PHPUnit\Framework\TestSuite as TestSuiteObject; -use PHPUnit\SebastianBergmann\FileIterator\Facade; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable */ -final class TestSuiteMapper +final class ClassUnit extends CodeUnit { - public function map(\PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection $configuration, string $filter) : \PHPUnit\Framework\TestSuite + /** + * @psalm-assert-if-true ClassUnit $this + */ + public function isClass() : bool { - $filterAsArray = $filter ? \explode(',', $filter) : []; - $result = new \PHPUnit\Framework\TestSuite(); - foreach ($configuration as $testSuiteConfiguration) { - if (!empty($filterAsArray) && !\in_array($testSuiteConfiguration->name(), $filterAsArray, \true)) { - continue; - } - $testSuite = new \PHPUnit\Framework\TestSuite($testSuiteConfiguration->name()); - $testSuiteEmpty = \true; - foreach ($testSuiteConfiguration->directories() as $directory) { - if (!\version_compare(\PHP_VERSION, $directory->phpVersion(), $directory->phpVersionOperator()->asString())) { - continue; - } - $exclude = []; - foreach ($testSuiteConfiguration->exclude()->asArray() as $file) { - $exclude[] = $file->path(); - } - $testSuite->addTestFiles((new \PHPUnit\SebastianBergmann\FileIterator\Facade())->getFilesAsArray($directory->path(), $directory->suffix(), $directory->prefix(), $exclude)); - $testSuiteEmpty = \false; - } - foreach ($testSuiteConfiguration->files() as $file) { - if (!\version_compare(\PHP_VERSION, $file->phpVersion(), $file->phpVersionOperator()->asString())) { - continue; - } - $testSuite->addTestFile($file->path()); - $testSuiteEmpty = \false; - } - if (!$testSuiteEmpty) { - $result->addTest($testSuite); - } - } - return $result; + return \true; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeUnit; -use function count; -use function iterator_count; -use Countable; -use Iterator; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable */ -final class TestDirectoryCollectionIterator implements \Countable, \Iterator +final class TraitMethodUnit extends CodeUnit { /** - * @var TestDirectory[] - */ - private $directories; - /** - * @var int + * @psalm-assert-if-true TraitMethodUnit $this */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection $directories) - { - $this->directories = $directories->asArray(); - } - public function count() : int - { - return \iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < \count($this->directories); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\TestDirectory - { - return $this->directories[$this->position]; - } - public function next() : void + public function isTraitMethod() : bool { - $this->position++; + return \true; } } +sebastian/code-unit + +Copyright (c) 2020, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeUnit; +use function array_merge; use function count; use Countable; use IteratorAggregate; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class TestSuiteCollection implements \Countable, \IteratorAggregate +final class CodeUnitCollection implements Countable, IteratorAggregate { /** - * @var TestSuite[] + * @psalm-var list */ - private $testSuites; + private $codeUnits = []; /** - * @param TestSuite[] $testSuites + * @psalm-param list $items */ - public static function fromArray(array $testSuites) : self + public static function fromArray(array $items) : self { - return new self(...$testSuites); + $collection = new self(); + foreach ($items as $item) { + $collection->add($item); + } + return $collection; } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\TestSuite ...$testSuites) + public static function fromList(CodeUnit ...$items) : self + { + return self::fromArray($items); + } + private function __construct() { - $this->testSuites = $testSuites; } /** - * @return TestSuite[] + * @psalm-return list */ public function asArray() : array { - return $this->testSuites; + return $this->codeUnits; } - public function count() : int + public function getIterator() : CodeUnitCollectionIterator { - return \count($this->testSuites); + return new CodeUnitCollectionIterator($this); } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollectionIterator + public function count() : int { - return new \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollectionIterator($this); + return count($this->codeUnits); } public function isEmpty() : bool { - return $this->count() === 0; + return empty($this->codeUnits); } + public function mergeWith(self $other) : self + { + return self::fromArray(array_merge($this->asArray(), $other->asArray())); + } + private function add(CodeUnit $item) : void + { + $this->codeUnits[] = $item; + } +} +"$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"]) + ); } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; -use PHPUnit\Util\VersionComparisonOperator; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable +use RuntimeException; +use PHPUnit\SebastianBergmann\CodeCoverage\Exception; +final class WrongXdebugVersionException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -final class TestDirectory +namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; + +use RuntimeException; +use PHPUnit\SebastianBergmann\CodeCoverage\Exception; +final class PhpdbgNotAvailableException extends RuntimeException implements Exception { - /** - * @var string - */ - private $path; - /** - * @var string - */ - private $prefix; - /** - * @var string - */ - private $suffix; - /** - * @var string - */ - private $phpVersion; - /** - * @var VersionComparisonOperator - */ - private $phpVersionOperator; - public function __construct(string $path, string $prefix, string $suffix, string $phpVersion, \PHPUnit\Util\VersionComparisonOperator $phpVersionOperator) - { - $this->path = $path; - $this->prefix = $prefix; - $this->suffix = $suffix; - $this->phpVersion = $phpVersion; - $this->phpVersionOperator = $phpVersionOperator; - } - public function path() : string - { - return $this->path; - } - public function prefix() : string - { - return $this->prefix; - } - public function suffix() : string - { - return $this->suffix; - } - public function phpVersion() : string - { - return $this->phpVersion; - } - public function phpVersionOperator() : \PHPUnit\Util\VersionComparisonOperator + public function __construct() { - return $this->phpVersionOperator; + parent::__construct('The PHPDBG SAPI is not available'); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage; -use function sprintf; -use PHPUnit\Util\Xml\Exception as XmlException; -use PHPUnit\Util\Xml\Loader as XmlLoader; -use PHPUnit\Util\Xml\SchemaDetector; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Migrator +use RuntimeException; +final class NoCodeCoverageDriverAvailableException extends RuntimeException implements Exception { - /** - * @throws MigrationBuilderException - * @throws MigrationException - * @throws Exception - * @throws XmlException - */ - public function migrate(string $filename) : string + public function __construct() { - $origin = (new \PHPUnit\Util\Xml\SchemaDetector())->detect($filename); - if (!$origin->detected()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception(\sprintf('"%s" is not a valid PHPUnit XML configuration file that can be migrated', $filename)); - } - $configurationDocument = (new \PHPUnit\Util\Xml\Loader())->loadFile($filename, \false, \true, \true); - foreach ((new \PHPUnit\TextUI\XmlConfiguration\MigrationBuilder())->build($origin->version()) as $migration) { - $migration->migrate($configurationDocument); - } - $configurationDocument->formatOutput = \true; - $configurationDocument->preserveWhiteSpace = \false; - return $configurationDocument->saveXML(); + parent::__construct('No code coverage driver available'); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage; use RuntimeException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MigrationBuilderException extends \RuntimeException implements \PHPUnit\Exception +final class XmlException extends RuntimeException implements Exception { } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage; -use DOMDocument; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MoveAttributesFromFilterWhitelistToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration +use RuntimeException; +final class ReportAlreadyFinalizedException extends RuntimeException implements Exception { - /** - * @throws MigrationException - */ - public function migrate(\DOMDocument $document) : void + public function __construct() { - $whitelist = $document->getElementsByTagName('whitelist')->item(0); - if (!$whitelist) { - return; - } - $coverage = $document->getElementsByTagName('coverage')->item(0); - if (!$coverage instanceof \DOMElement) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); - } - $map = ['addUncoveredFilesFromWhitelist' => 'includeUncoveredFiles', 'processUncoveredFilesFromWhitelist' => 'processUncoveredFiles']; - foreach ($map as $old => $new) { - if (!$whitelist->hasAttribute($old)) { - continue; - } - $coverage->setAttribute($new, $whitelist->getAttribute($old)); - $whitelist->removeAttribute($old); - } + parent::__construct('The code coverage report has already been finalized'); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface Migration +use RuntimeException; +use PHPUnit\SebastianBergmann\CodeCoverage\Exception; +final class XdebugNotAvailableException extends RuntimeException implements Exception { - public function migrate(\DOMDocument $document) : void; + public function __construct() + { + parent::__construct('The Xdebug extension is not available'); + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; use function sprintf; -use DOMDocument; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class RemoveEmptyFilter implements \PHPUnit\TextUI\XmlConfiguration\Migration +use RuntimeException; +use PHPUnit\SebastianBergmann\CodeCoverage\Exception; +final class WriteOperationFailedException extends RuntimeException implements Exception { - /** - * @throws MigrationException - */ - public function migrate(\DOMDocument $document) : void - { - $whitelist = $document->getElementsByTagName('whitelist')->item(0); - if ($whitelist instanceof \DOMElement) { - $this->ensureEmpty($whitelist); - $whitelist->parentNode->removeChild($whitelist); - } - $filter = $document->getElementsByTagName('filter')->item(0); - if ($filter instanceof \DOMElement) { - $this->ensureEmpty($filter); - $filter->parentNode->removeChild($filter); - } - } - /** - * @throws MigrationException - */ - private function ensureEmpty(\DOMElement $element) : void + public function __construct(string $path) { - if ($element->attributes->length > 0) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException(\sprintf('%s element has unexpected attributes', $element->nodeName)); - } - if ($element->getElementsByTagName('*')->length > 0) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException(\sprintf('%s element has unexpected children', $element->nodeName)); - } + parent::__construct(sprintf('Cannot write to "%s"', $path)); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage; -use DOMDocument; -use DOMElement; -use PHPUnit\Util\Xml\SnapshotNodeList; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class RemoveLogTypes implements \PHPUnit\TextUI\XmlConfiguration\Migration +use RuntimeException; +final class DirectoryCouldNotBeCreatedException extends RuntimeException implements Exception { - public function migrate(\DOMDocument $document) : void - { - $logging = $document->getElementsByTagName('logging')->item(0); - if (!$logging instanceof \DOMElement) { - return; - } - foreach (\PHPUnit\Util\Xml\SnapshotNodeList::fromNodeList($logging->getElementsByTagName('log')) as $logNode) { - switch ($logNode->getAttribute('type')) { - case 'json': - case 'tap': - $logging->removeChild($logNode); - } - } - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class CoverageCloverToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +use RuntimeException; +final class DeadCodeDetectionNotSupportedException extends RuntimeException implements Exception { - protected function forType() : string - { - return 'coverage-clover'; - } - protected function toReportFormat(\DOMElement $logNode) : \DOMElement - { - $clover = $logNode->ownerDocument->createElement('clover'); - $clover->setAttribute('outputFile', $logNode->getAttribute('target')); - return $clover; - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class CoverageCrap4jToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +use RuntimeException; +use PHPUnit\SebastianBergmann\CodeCoverage\Exception; +final class Xdebug2NotEnabledException extends RuntimeException implements Exception { - protected function forType() : string - { - return 'coverage-crap4j'; - } - protected function toReportFormat(\DOMElement $logNode) : \DOMElement + public function __construct() { - $crap4j = $logNode->ownerDocument->createElement('crap4j'); - $crap4j->setAttribute('outputFile', $logNode->getAttribute('target')); - $this->migrateAttributes($logNode, $crap4j, ['threshold']); - return $crap4j; + parent::__construct('xdebug.coverage_enable=On has to be set'); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage; -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class IntroduceCoverageElement implements \PHPUnit\TextUI\XmlConfiguration\Migration +use RuntimeException; +final class TestIdMissingException extends RuntimeException implements Exception { - public function migrate(\DOMDocument $document) : void + public function __construct() { - $coverage = $document->createElement('coverage'); - $document->documentElement->insertBefore($coverage, $document->documentElement->firstChild); + parent::__construct('Test ID is missing'); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class CoverageHtmlToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +use RuntimeException; +final class ReflectionException extends RuntimeException implements Exception { - protected function forType() : string - { - return 'coverage-html'; - } - protected function toReportFormat(\DOMElement $logNode) : \DOMElement - { - $html = $logNode->ownerDocument->createElement('html'); - $html->setAttribute('outputDirectory', $logNode->getAttribute('target')); - $this->migrateAttributes($logNode, $html, ['lowUpperBound', 'highLowerBound']); - return $html; - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class CoveragePhpToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +use RuntimeException; +use PHPUnit\SebastianBergmann\CodeCoverage\Exception; +final class Xdebug3NotEnabledException extends RuntimeException implements Exception { - protected function forType() : string - { - return 'coverage-php'; - } - protected function toReportFormat(\DOMElement $logNode) : \DOMElement + public function __construct() { - $php = $logNode->ownerDocument->createElement('php'); - $php->setAttribute('outputFile', $logNode->getAttribute('target')); - return $php; + parent::__construct('XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set'); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage; -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit +use RuntimeException; +final class NoCodeCoverageDriverWithPathCoverageSupportAvailableException extends RuntimeException implements Exception +{ + public function __construct() + { + parent::__construct('No code coverage driver with path coverage support available'); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -final class UpdateSchemaLocationTo93 implements \PHPUnit\TextUI\XmlConfiguration\Migration +namespace PHPUnit\SebastianBergmann\CodeCoverage; + +use Throwable; +interface Exception extends Throwable { - public function migrate(\DOMDocument $document) : void - { - $document->documentElement->setAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation', 'https://schema.phpunit.de/9.3/phpunit.xsd'); - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; -use function sprintf; -use DOMDocument; -use DOMElement; -use DOMXPath; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -abstract class LogToReportMigration implements \PHPUnit\TextUI\XmlConfiguration\Migration +use RuntimeException; +use PHPUnit\SebastianBergmann\CodeCoverage\Exception; +final class PcovNotAvailableException extends RuntimeException implements Exception { - /** - * @throws MigrationException - */ - public function migrate(\DOMDocument $document) : void - { - $coverage = $document->getElementsByTagName('coverage')->item(0); - if (!$coverage instanceof \DOMElement) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); - } - $logNode = $this->findLogNode($document); - if ($logNode === null) { - return; - } - $reportChild = $this->toReportFormat($logNode); - $report = $coverage->getElementsByTagName('report')->item(0); - if ($report === null) { - $report = $coverage->appendChild($document->createElement('report')); - } - $report->appendChild($reportChild); - $logNode->parentNode->removeChild($logNode); - } - protected function migrateAttributes(\DOMElement $src, \DOMElement $dest, array $attributes) : void - { - foreach ($attributes as $attr) { - if (!$src->hasAttribute($attr)) { - continue; - } - $dest->setAttribute($attr, $src->getAttribute($attr)); - $src->removeAttribute($attr); - } - } - protected abstract function forType() : string; - protected abstract function toReportFormat(\DOMElement $logNode) : \DOMElement; - private function findLogNode(\DOMDocument $document) : ?\DOMElement + public function __construct() { - $logNode = (new \DOMXPath($document))->query(\sprintf('//logging/log[@type="%s"]', $this->forType()))->item(0); - if (!$logNode instanceof \DOMElement) { - return null; - } - return $logNode; + parent::__construct('The PCOV extension is not available'); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; -use DOMDocument; -use DOMElement; -use PHPUnit\Util\Xml\SnapshotNodeList; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MoveWhitelistExcludesToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration +use function sprintf; +use RuntimeException; +use PHPUnit\SebastianBergmann\CodeCoverage\Exception; +final class PathExistsButIsNotDirectoryException extends RuntimeException implements Exception { - /** - * @throws MigrationException - */ - public function migrate(\DOMDocument $document) : void + public function __construct(string $path) { - $whitelist = $document->getElementsByTagName('whitelist')->item(0); - if ($whitelist === null) { - return; - } - $excludeNodes = \PHPUnit\Util\Xml\SnapshotNodeList::fromNodeList($whitelist->getElementsByTagName('exclude')); - if ($excludeNodes->count() === 0) { - return; - } - $coverage = $document->getElementsByTagName('coverage')->item(0); - if (!$coverage instanceof \DOMElement) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); - } - $targetExclude = $coverage->getElementsByTagName('exclude')->item(0); - if ($targetExclude === null) { - $targetExclude = $coverage->appendChild($document->createElement('exclude')); - } - foreach ($excludeNodes as $excludeNode) { - \assert($excludeNode instanceof \DOMElement); - foreach (\PHPUnit\Util\Xml\SnapshotNodeList::fromNodeList($excludeNode->childNodes) as $child) { - if (!$child instanceof \DOMElement || !\in_array($child->nodeName, ['directory', 'file'], \true)) { - continue; - } - $targetExclude->appendChild($child); - } - if ($excludeNode->getElementsByTagName('*')->count() !== 0) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Dangling child elements in exclude found.'); - } - $whitelist->removeChild($excludeNode); - } + parent::__construct(sprintf('"%s" exists but is not a directory', $path)); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class CoverageXmlToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +use RuntimeException; +final class StaticAnalysisCacheNotConfiguredException extends RuntimeException implements Exception { - protected function forType() : string - { - return 'coverage-xml'; - } - protected function toReportFormat(\DOMElement $logNode) : \DOMElement - { - $xml = $logNode->ownerDocument->createElement('xml'); - $xml->setAttribute('outputDirectory', $logNode->getAttribute('target')); - return $xml; - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage; -use DOMDocument; -use DOMElement; -use PHPUnit\Util\Xml\SnapshotNodeList; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MoveWhitelistDirectoriesToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration +use RuntimeException; +final class UnintentionallyCoveredCodeException extends RuntimeException implements Exception { /** - * @throws MigrationException + * @var array */ - public function migrate(\DOMDocument $document) : void + private $unintentionallyCoveredUnits; + public function __construct(array $unintentionallyCoveredUnits) { - $whitelist = $document->getElementsByTagName('whitelist')->item(0); - if ($whitelist === null) { - return; - } - $coverage = $document->getElementsByTagName('coverage')->item(0); - if (!$coverage instanceof \DOMElement) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); - } - $include = $document->createElement('include'); - $coverage->appendChild($include); - foreach (\PHPUnit\Util\Xml\SnapshotNodeList::fromNodeList($whitelist->childNodes) as $child) { - if (!$child instanceof \DOMElement || $child->nodeName !== 'directory') { - continue; - } - $include->appendChild($child); + $this->unintentionallyCoveredUnits = $unintentionallyCoveredUnits; + parent::__construct($this->toString()); + } + public function getUnintentionallyCoveredUnits() : array + { + return $this->unintentionallyCoveredUnits; + } + private function toString() : string + { + $message = ''; + foreach ($this->unintentionallyCoveredUnits as $unit) { + $message .= '- ' . $unit . "\n"; } + return $message; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage; -use DOMDocument; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ConvertLogTypes implements \PHPUnit\TextUI\XmlConfiguration\Migration +use RuntimeException; +final class BranchAndPathCoverageNotSupportedException extends RuntimeException implements Exception { - public function migrate(\DOMDocument $document) : void - { - $logging = $document->getElementsByTagName('logging')->item(0); - if (!$logging instanceof \DOMElement) { - return; - } - $types = ['junit' => 'junit', 'teamcity' => 'teamcity', 'testdox-html' => 'testdoxHtml', 'testdox-text' => 'testdoxText', 'testdox-xml' => 'testdoxXml', 'plain' => 'text']; - $logNodes = []; - foreach ($logging->getElementsByTagName('log') as $logNode) { - if (!isset($types[$logNode->getAttribute('type')])) { - continue; - } - $logNodes[] = $logNode; - } - foreach ($logNodes as $oldNode) { - $newLogNode = $document->createElement($types[$oldNode->getAttribute('type')]); - $newLogNode->setAttribute('outputFile', $oldNode->getAttribute('target')); - $logging->replaceChild($newLogNode, $oldNode); - } - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage; -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class RemoveCacheTokensAttribute implements \PHPUnit\TextUI\XmlConfiguration\Migration +final class InvalidArgumentException extends \InvalidArgumentException implements Exception { - public function migrate(\DOMDocument $document) : void - { - $root = $document->documentElement; - if ($root->hasAttribute('cacheTokens')) { - $root->removeAttribute('cacheTokens'); - } - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage; -use DOMDocument; -use DOMElement; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MoveAttributesFromRootToCoverage implements \PHPUnit\TextUI\XmlConfiguration\Migration +use RuntimeException; +final class ParserException extends RuntimeException implements Exception { - /** - * @throws MigrationException - */ - public function migrate(\DOMDocument $document) : void - { - $map = ['disableCodeCoverageIgnore' => 'disableCodeCoverageIgnore', 'ignoreDeprecatedCodeUnitsFromCodeCoverage' => 'ignoreDeprecatedCodeUnits']; - $root = $document->documentElement; - $coverage = $document->getElementsByTagName('coverage')->item(0); - if (!$coverage instanceof \DOMElement) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); - } - foreach ($map as $old => $new) { - if (!$root->hasAttribute($old)) { - continue; - } - $coverage->setAttribute($new, $root->getAttribute($old)); - $root->removeAttribute($old); - } - } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage; -use DOMElement; +use function sprintf; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -final class CoverageTextToReport extends \PHPUnit\TextUI\XmlConfiguration\LogToReportMigration +final class Percentage { - protected function forType() : string + /** + * @var float + */ + private $fraction; + /** + * @var float + */ + private $total; + public static function fromFractionAndTotal(float $fraction, float $total) : self { - return 'coverage-text'; + return new self($fraction, $total); } - protected function toReportFormat(\DOMElement $logNode) : \DOMElement + private function __construct(float $fraction, float $total) { - $text = $logNode->ownerDocument->createElement('text'); - $text->setAttribute('outputFile', $logNode->getAttribute('target')); - $this->migrateAttributes($logNode, $text, ['showUncoveredFiles', 'showOnlySummary']); - return $text; + $this->fraction = $fraction; + $this->total = $total; + } + public function asFloat() : float + { + if ($this->total > 0) { + return $this->fraction / $this->total * 100; + } + return 100.0; + } + public function asString() : string + { + if ($this->total > 0) { + return sprintf('%01.2F%%', $this->asFloat()); + } + return ''; + } + public function asFixedWidthString() : string + { + if ($this->total > 0) { + return sprintf('%6.2F%%', $this->asFloat()); + } + return ''; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage; -use RuntimeException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MigrationException extends \RuntimeException implements \PHPUnit\Exception +use function array_keys; +use function is_file; +use function realpath; +use function strpos; +use PHPUnit\SebastianBergmann\FileIterator\Facade as FileIteratorFacade; +final class Filter { + /** + * @psalm-var array + */ + private $files = []; + /** + * @psalm-var array + */ + private $isFileCache = []; + public function includeDirectory(string $directory, string $suffix = '.php', string $prefix = '') : void + { + foreach ((new FileIteratorFacade())->getFilesAsArray($directory, $suffix, $prefix) as $file) { + $this->includeFile($file); + } + } + /** + * @psalm-param list $files + */ + public function includeFiles(array $filenames) : void + { + foreach ($filenames as $filename) { + $this->includeFile($filename); + } + } + public function includeFile(string $filename) : void + { + $filename = realpath($filename); + if (!$filename) { + return; + } + $this->files[$filename] = \true; + } + public function excludeDirectory(string $directory, string $suffix = '.php', string $prefix = '') : void + { + foreach ((new FileIteratorFacade())->getFilesAsArray($directory, $suffix, $prefix) as $file) { + $this->excludeFile($file); + } + } + public function excludeFile(string $filename) : void + { + $filename = realpath($filename); + if (!$filename || !isset($this->files[$filename])) { + return; + } + unset($this->files[$filename]); + } + public function isFile(string $filename) : bool + { + if (isset($this->isFileCache[$filename])) { + return $this->isFileCache[$filename]; + } + if ($filename === '-' || strpos($filename, 'vfs://') === 0 || strpos($filename, 'xdebug://debug-eval') !== \false || strpos($filename, 'eval()\'d code') !== \false || strpos($filename, 'runtime-created function') !== \false || strpos($filename, 'runkit created function') !== \false || strpos($filename, 'assert code') !== \false || strpos($filename, 'regexp code') !== \false || strpos($filename, 'Standard input code') !== \false) { + $isFile = \false; + } else { + $isFile = is_file($filename); + } + $this->isFileCache[$filename] = $isFile; + return $isFile; + } + public function isExcluded(string $filename) : bool + { + if (!$this->isFile($filename)) { + return \true; + } + return !isset($this->files[$filename]); + } + /** + * @psalm-return list + */ + public function files() : array + { + return array_keys($this->files); + } + public function isEmpty() : bool + { + return empty($this->files); + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; -use function array_key_exists; -use function sprintf; -use function version_compare; +use function extension_loaded; +use function phpversion; +use PHPUnit\SebastianBergmann\CodeCoverage\Filter; +use PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -final class MigrationBuilder +final class PcovDriver extends Driver { - private const AVAILABLE_MIGRATIONS = ['8.5' => [\PHPUnit\TextUI\XmlConfiguration\RemoveLogTypes::class], '9.2' => [\PHPUnit\TextUI\XmlConfiguration\RemoveCacheTokensAttribute::class, \PHPUnit\TextUI\XmlConfiguration\IntroduceCoverageElement::class, \PHPUnit\TextUI\XmlConfiguration\MoveAttributesFromRootToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveAttributesFromFilterWhitelistToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveWhitelistDirectoriesToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveWhitelistExcludesToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\RemoveEmptyFilter::class, \PHPUnit\TextUI\XmlConfiguration\CoverageCloverToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageCrap4jToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageHtmlToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoveragePhpToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageTextToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageXmlToReport::class, \PHPUnit\TextUI\XmlConfiguration\ConvertLogTypes::class, \PHPUnit\TextUI\XmlConfiguration\UpdateSchemaLocationTo93::class]]; /** - * @throws MigrationBuilderException + * @var Filter */ - public function build(string $fromVersion) : array + private $filter; + /** + * @throws PcovNotAvailableException + */ + public function __construct(Filter $filter) { - if (!\array_key_exists($fromVersion, self::AVAILABLE_MIGRATIONS)) { - throw new \PHPUnit\TextUI\XmlConfiguration\MigrationBuilderException(\sprintf('Migration from schema version %s is not supported', $fromVersion)); + if (!extension_loaded('pcov')) { + throw new PcovNotAvailableException(); } - $stack = []; - foreach (self::AVAILABLE_MIGRATIONS as $version => $migrations) { - if (\version_compare($version, $fromVersion, '<')) { - continue; - } - foreach ($migrations as $migration) { - $stack[] = new $migration(); - } - } - return $stack; + $this->filter = $filter; + } + public function start() : void + { + \pcov\start(); + } + public function stop() : RawCodeCoverageData + { + \pcov\stop(); + $collect = \pcov\collect(\pcov\inclusive, !$this->filter->isEmpty() ? $this->filter->files() : \pcov\waiting()); + \pcov\clear(); + return RawCodeCoverageData::fromXdebugWithoutPathCoverage($collect); + } + public function nameAndVersion() : string + { + return 'PCOV ' . phpversion('pcov'); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; +use function sprintf; +use PHPUnit\SebastianBergmann\CodeCoverage\BranchAndPathCoverageNotSupportedException; +use PHPUnit\SebastianBergmann\CodeCoverage\DeadCodeDetectionNotSupportedException; +use PHPUnit\SebastianBergmann\CodeCoverage\Filter; +use PHPUnit\SebastianBergmann\CodeCoverage\NoCodeCoverageDriverAvailableException; +use PHPUnit\SebastianBergmann\CodeCoverage\NoCodeCoverageDriverWithPathCoverageSupportAvailableException; +use PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -final class Extension +abstract class Driver { /** - * @var string - * @psalm-var class-string + * @var int + * + * @see http://xdebug.org/docs/code_coverage */ - private $className; + public const LINE_NOT_EXECUTABLE = -2; /** - * @var string + * @var int + * + * @see http://xdebug.org/docs/code_coverage */ - private $sourceFile; + public const LINE_NOT_EXECUTED = -1; /** - * @var array + * @var int + * + * @see http://xdebug.org/docs/code_coverage */ - private $arguments; + public const LINE_EXECUTED = 1; /** - * @psalm-param class-string $className + * @var int + * + * @see http://xdebug.org/docs/code_coverage */ - public function __construct(string $className, string $sourceFile, array $arguments) + public const BRANCH_NOT_HIT = 0; + /** + * @var int + * + * @see http://xdebug.org/docs/code_coverage + */ + public const BRANCH_HIT = 1; + /** + * @var bool + */ + private $collectBranchAndPathCoverage = \false; + /** + * @var bool + */ + private $detectDeadCode = \false; + /** + * @throws NoCodeCoverageDriverAvailableException + * @throws PcovNotAvailableException + * @throws PhpdbgNotAvailableException + * @throws Xdebug2NotEnabledException + * @throws Xdebug3NotEnabledException + * @throws XdebugNotAvailableException + * + * @deprecated Use DriverSelector::forLineCoverage() instead + */ + public static function forLineCoverage(Filter $filter) : self { - $this->className = $className; - $this->sourceFile = $sourceFile; - $this->arguments = $arguments; + return (new Selector())->forLineCoverage($filter); } /** - * @psalm-return class-string + * @throws NoCodeCoverageDriverWithPathCoverageSupportAvailableException + * @throws Xdebug2NotEnabledException + * @throws Xdebug3NotEnabledException + * @throws XdebugNotAvailableException + * + * @deprecated Use DriverSelector::forLineAndPathCoverage() instead */ - public function className() : string + public static function forLineAndPathCoverage(Filter $filter) : self { - return $this->className; + return (new Selector())->forLineAndPathCoverage($filter); } - public function hasSourceFile() : bool + public function canCollectBranchAndPathCoverage() : bool { - return $this->sourceFile !== ''; + return \false; } - public function sourceFile() : string + public function collectsBranchAndPathCoverage() : bool { - return $this->sourceFile; + return $this->collectBranchAndPathCoverage; + } + /** + * @throws BranchAndPathCoverageNotSupportedException + */ + public function enableBranchAndPathCoverage() : void + { + if (!$this->canCollectBranchAndPathCoverage()) { + throw new BranchAndPathCoverageNotSupportedException(sprintf('%s does not support branch and path coverage', $this->nameAndVersion())); + } + $this->collectBranchAndPathCoverage = \true; + } + public function disableBranchAndPathCoverage() : void + { + $this->collectBranchAndPathCoverage = \false; + } + public function canDetectDeadCode() : bool + { + return \false; + } + public function detectsDeadCode() : bool + { + return $this->detectDeadCode; } - public function hasArguments() : bool + /** + * @throws DeadCodeDetectionNotSupportedException + */ + public function enableDeadCodeDetection() : void { - return !empty($this->arguments); + if (!$this->canDetectDeadCode()) { + throw new DeadCodeDetectionNotSupportedException(sprintf('%s does not support dead code detection', $this->nameAndVersion())); + } + $this->detectDeadCode = \true; } - public function arguments() : array + public function disableDeadCodeDetection() : void { - return $this->arguments; + $this->detectDeadCode = \false; } + public abstract function nameAndVersion() : string; + public abstract function start() : void; + public abstract function stop() : RawCodeCoverageData; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; -use IteratorAggregate; +use const XDEBUG_CC_BRANCH_CHECK; +use const XDEBUG_CC_DEAD_CODE; +use const XDEBUG_CC_UNUSED; +use const XDEBUG_FILTER_CODE_COVERAGE; +use const PHPUnit\XDEBUG_PATH_INCLUDE; +use function explode; +use function extension_loaded; +use function getenv; +use function in_array; +use function ini_get; +use function phpversion; +use function sprintf; +use function version_compare; +use function xdebug_get_code_coverage; +use function xdebug_set_filter; +use function xdebug_start_code_coverage; +use function xdebug_stop_code_coverage; +use PHPUnit\SebastianBergmann\CodeCoverage\Filter; +use PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -final class ExtensionCollection implements \IteratorAggregate +final class Xdebug3Driver extends Driver { /** - * @var Extension[] - */ - private $extensions; - /** - * @param Extension[] $extensions + * @throws WrongXdebugVersionException + * @throws Xdebug3NotEnabledException + * @throws XdebugNotAvailableException */ - public static function fromArray(array $extensions) : self + public function __construct(Filter $filter) { - return new self(...$extensions); + if (!extension_loaded('xdebug')) { + throw new XdebugNotAvailableException(); + } + if (version_compare(phpversion('xdebug'), '3', '<')) { + throw new WrongXdebugVersionException(sprintf('This driver requires Xdebug 3 but version %s is loaded', phpversion('xdebug'))); + } + $mode = getenv('XDEBUG_MODE'); + if ($mode === \false || $mode === '') { + $mode = ini_get('xdebug.mode'); + } + if ($mode === \false || !in_array('coverage', explode(',', $mode), \true)) { + throw new Xdebug3NotEnabledException(); + } + if (!$filter->isEmpty()) { + xdebug_set_filter(\XDEBUG_FILTER_CODE_COVERAGE, \XDEBUG_PATH_INCLUDE, $filter->files()); + } } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\Extension ...$extensions) + public function canCollectBranchAndPathCoverage() : bool { - $this->extensions = $extensions; + return \true; } - /** - * @return Extension[] - */ - public function asArray() : array + public function canDetectDeadCode() : bool { - return $this->extensions; + return \true; } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollectionIterator + public function start() : void { - return new \PHPUnit\TextUI\XmlConfiguration\ExtensionCollectionIterator($this); + $flags = \XDEBUG_CC_UNUSED; + if ($this->detectsDeadCode() || $this->collectsBranchAndPathCoverage()) { + $flags |= \XDEBUG_CC_DEAD_CODE; + } + if ($this->collectsBranchAndPathCoverage()) { + $flags |= \XDEBUG_CC_BRANCH_CHECK; + } + xdebug_start_code_coverage($flags); + } + public function stop() : RawCodeCoverageData + { + $data = xdebug_get_code_coverage(); + xdebug_stop_code_coverage(); + if ($this->collectsBranchAndPathCoverage()) { + return RawCodeCoverageData::fromXdebugWithPathCoverage($data); + } + return RawCodeCoverageData::fromXdebugWithoutPathCoverage($data); + } + public function nameAndVersion() : string + { + return 'Xdebug ' . phpversion('xdebug'); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; -use function count; -use function iterator_count; -use Countable; -use Iterator; +use const XDEBUG_CC_BRANCH_CHECK; +use const XDEBUG_CC_DEAD_CODE; +use const XDEBUG_CC_UNUSED; +use const XDEBUG_FILTER_CODE_COVERAGE; +use const PHPUnit\XDEBUG_PATH_INCLUDE; +use const XDEBUG_PATH_WHITELIST; +use function defined; +use function extension_loaded; +use function ini_get; +use function phpversion; +use function sprintf; +use function version_compare; +use function xdebug_get_code_coverage; +use function xdebug_set_filter; +use function xdebug_start_code_coverage; +use function xdebug_stop_code_coverage; +use PHPUnit\SebastianBergmann\CodeCoverage\Filter; +use PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -final class ExtensionCollectionIterator implements \Countable, \Iterator +final class Xdebug2Driver extends Driver { /** - * @var Extension[] + * @var bool */ - private $extensions; + private $pathCoverageIsMixedCoverage; /** - * @var int + * @throws WrongXdebugVersionException + * @throws Xdebug2NotEnabledException + * @throws XdebugNotAvailableException */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\ExtensionCollection $extensions) - { - $this->extensions = $extensions->asArray(); - } - public function count() : int + public function __construct(Filter $filter) { - return \iterator_count($this); + if (!extension_loaded('xdebug')) { + throw new XdebugNotAvailableException(); + } + if (version_compare(phpversion('xdebug'), '3', '>=')) { + throw new WrongXdebugVersionException(sprintf('This driver requires Xdebug 2 but version %s is loaded', phpversion('xdebug'))); + } + if (!ini_get('xdebug.coverage_enable')) { + throw new Xdebug2NotEnabledException(); + } + if (!$filter->isEmpty()) { + if (defined('XDEBUG_PATH_WHITELIST')) { + $listType = \XDEBUG_PATH_WHITELIST; + } else { + $listType = \XDEBUG_PATH_INCLUDE; + } + xdebug_set_filter(\XDEBUG_FILTER_CODE_COVERAGE, $listType, $filter->files()); + } + $this->pathCoverageIsMixedCoverage = version_compare(phpversion('xdebug'), '2.9.6', '<'); } - public function rewind() : void + public function canCollectBranchAndPathCoverage() : bool { - $this->position = 0; + return \true; } - public function valid() : bool + public function canDetectDeadCode() : bool { - return $this->position < \count($this->extensions); + return \true; } - public function key() : int + public function start() : void { - return $this->position; + $flags = \XDEBUG_CC_UNUSED; + if ($this->detectsDeadCode() || $this->collectsBranchAndPathCoverage()) { + $flags |= \XDEBUG_CC_DEAD_CODE; + } + if ($this->collectsBranchAndPathCoverage()) { + $flags |= \XDEBUG_CC_BRANCH_CHECK; + } + xdebug_start_code_coverage($flags); } - public function current() : \PHPUnit\TextUI\XmlConfiguration\Extension + public function stop() : RawCodeCoverageData { - return $this->extensions[$this->position]; + $data = xdebug_get_code_coverage(); + xdebug_stop_code_coverage(); + if ($this->collectsBranchAndPathCoverage()) { + if ($this->pathCoverageIsMixedCoverage) { + return RawCodeCoverageData::fromXdebugWithMixedCoverage($data); + } + return RawCodeCoverageData::fromXdebugWithPathCoverage($data); + } + return RawCodeCoverageData::fromXdebugWithoutPathCoverage($data); } - public function next() : void + public function nameAndVersion() : string { - $this->position++; + return 'Xdebug ' . phpversion('xdebug'); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; +use const PHP_SAPI; +use const PHP_VERSION; +use function array_diff; +use function array_keys; +use function array_merge; +use function get_included_files; +use function phpdbg_end_oplog; +use function phpdbg_get_executable; +use function phpdbg_start_oplog; +use PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -final class PHPUnit +final class PhpdbgDriver extends Driver { /** - * @var bool - */ - private $cacheResult; - /** - * @var ?string - */ - private $cacheResultFile; - /** - * @var int|string - */ - private $columns; - /** - * @var string - */ - private $colors; - /** - * @var bool - */ - private $stderr; - /** - * @var bool - */ - private $noInteraction; - /** - * @var bool - */ - private $verbose; - /** - * @var bool - */ - private $reverseDefectList; - /** - * @var bool - */ - private $convertDeprecationsToExceptions; - /** - * @var bool - */ - private $convertErrorsToExceptions; - /** - * @var bool - */ - private $convertNoticesToExceptions; - /** - * @var bool - */ - private $convertWarningsToExceptions; - /** - * @var bool - */ - private $forceCoversAnnotation; - /** - * @var ?string - */ - private $bootstrap; - /** - * @var bool - */ - private $processIsolation; - /** - * @var bool - */ - private $failOnEmptyTestSuite; - /** - * @var bool - */ - private $failOnIncomplete; - /** - * @var bool - */ - private $failOnRisky; - /** - * @var bool - */ - private $failOnSkipped; - /** - * @var bool - */ - private $failOnWarning; - /** - * @var bool - */ - private $stopOnDefect; - /** - * @var bool - */ - private $stopOnError; - /** - * @var bool - */ - private $stopOnFailure; - /** - * @var bool - */ - private $stopOnWarning; - /** - * @var bool - */ - private $stopOnIncomplete; - /** - * @var bool - */ - private $stopOnRisky; - /** - * @var bool - */ - private $stopOnSkipped; - /** - * @var ?string - */ - private $extensionsDirectory; - /** - * @var ?string - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - */ - private $testSuiteLoaderClass; - /** - * @var ?string - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - */ - private $testSuiteLoaderFile; - /** - * @var ?string - */ - private $printerClass; - /** - * @var ?string - */ - private $printerFile; - /** - * @var bool - */ - private $beStrictAboutChangesToGlobalState; - /** - * @var bool - */ - private $beStrictAboutOutputDuringTests; - /** - * @var bool - */ - private $beStrictAboutResourceUsageDuringSmallTests; - /** - * @var bool - */ - private $beStrictAboutTestsThatDoNotTestAnything; - /** - * @var bool - */ - private $beStrictAboutTodoAnnotatedTests; - /** - * @var bool - */ - private $beStrictAboutCoversAnnotation; - /** - * @var bool - */ - private $enforceTimeLimit; - /** - * @var int - */ - private $defaultTimeLimit; - /** - * @var int - */ - private $timeoutForSmallTests; - /** - * @var int - */ - private $timeoutForMediumTests; - /** - * @var int - */ - private $timeoutForLargeTests; - /** - * @var ?string - */ - private $defaultTestSuite; - /** - * @var int - */ - private $executionOrder; - /** - * @var bool - */ - private $resolveDependencies; - /** - * @var bool - */ - private $defectsFirst; - /** - * @var bool - */ - private $backupGlobals; - /** - * @var bool - */ - private $backupStaticAttributes; - /** - * @var bool - */ - private $registerMockObjectsFromTestArgumentsRecursively; - /** - * @var bool - */ - private $conflictBetweenPrinterClassAndTestdox; - public function __construct(bool $cacheResult, ?string $cacheResultFile, $columns, string $colors, bool $stderr, bool $noInteraction, bool $verbose, bool $reverseDefectList, bool $convertDeprecationsToExceptions, bool $convertErrorsToExceptions, bool $convertNoticesToExceptions, bool $convertWarningsToExceptions, bool $forceCoversAnnotation, ?string $bootstrap, bool $processIsolation, bool $failOnEmptyTestSuite, bool $failOnIncomplete, bool $failOnRisky, bool $failOnSkipped, bool $failOnWarning, bool $stopOnDefect, bool $stopOnError, bool $stopOnFailure, bool $stopOnWarning, bool $stopOnIncomplete, bool $stopOnRisky, bool $stopOnSkipped, ?string $extensionsDirectory, ?string $testSuiteLoaderClass, ?string $testSuiteLoaderFile, ?string $printerClass, ?string $printerFile, bool $beStrictAboutChangesToGlobalState, bool $beStrictAboutOutputDuringTests, bool $beStrictAboutResourceUsageDuringSmallTests, bool $beStrictAboutTestsThatDoNotTestAnything, bool $beStrictAboutTodoAnnotatedTests, bool $beStrictAboutCoversAnnotation, bool $enforceTimeLimit, int $defaultTimeLimit, int $timeoutForSmallTests, int $timeoutForMediumTests, int $timeoutForLargeTests, ?string $defaultTestSuite, int $executionOrder, bool $resolveDependencies, bool $defectsFirst, bool $backupGlobals, bool $backupStaticAttributes, bool $registerMockObjectsFromTestArgumentsRecursively, bool $conflictBetweenPrinterClassAndTestdox) - { - $this->cacheResult = $cacheResult; - $this->cacheResultFile = $cacheResultFile; - $this->columns = $columns; - $this->colors = $colors; - $this->stderr = $stderr; - $this->noInteraction = $noInteraction; - $this->verbose = $verbose; - $this->reverseDefectList = $reverseDefectList; - $this->convertDeprecationsToExceptions = $convertDeprecationsToExceptions; - $this->convertErrorsToExceptions = $convertErrorsToExceptions; - $this->convertNoticesToExceptions = $convertNoticesToExceptions; - $this->convertWarningsToExceptions = $convertWarningsToExceptions; - $this->forceCoversAnnotation = $forceCoversAnnotation; - $this->bootstrap = $bootstrap; - $this->processIsolation = $processIsolation; - $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; - $this->failOnIncomplete = $failOnIncomplete; - $this->failOnRisky = $failOnRisky; - $this->failOnSkipped = $failOnSkipped; - $this->failOnWarning = $failOnWarning; - $this->stopOnDefect = $stopOnDefect; - $this->stopOnError = $stopOnError; - $this->stopOnFailure = $stopOnFailure; - $this->stopOnWarning = $stopOnWarning; - $this->stopOnIncomplete = $stopOnIncomplete; - $this->stopOnRisky = $stopOnRisky; - $this->stopOnSkipped = $stopOnSkipped; - $this->extensionsDirectory = $extensionsDirectory; - $this->testSuiteLoaderClass = $testSuiteLoaderClass; - $this->testSuiteLoaderFile = $testSuiteLoaderFile; - $this->printerClass = $printerClass; - $this->printerFile = $printerFile; - $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; - $this->beStrictAboutOutputDuringTests = $beStrictAboutOutputDuringTests; - $this->beStrictAboutResourceUsageDuringSmallTests = $beStrictAboutResourceUsageDuringSmallTests; - $this->beStrictAboutTestsThatDoNotTestAnything = $beStrictAboutTestsThatDoNotTestAnything; - $this->beStrictAboutTodoAnnotatedTests = $beStrictAboutTodoAnnotatedTests; - $this->beStrictAboutCoversAnnotation = $beStrictAboutCoversAnnotation; - $this->enforceTimeLimit = $enforceTimeLimit; - $this->defaultTimeLimit = $defaultTimeLimit; - $this->timeoutForSmallTests = $timeoutForSmallTests; - $this->timeoutForMediumTests = $timeoutForMediumTests; - $this->timeoutForLargeTests = $timeoutForLargeTests; - $this->defaultTestSuite = $defaultTestSuite; - $this->executionOrder = $executionOrder; - $this->resolveDependencies = $resolveDependencies; - $this->defectsFirst = $defectsFirst; - $this->backupGlobals = $backupGlobals; - $this->backupStaticAttributes = $backupStaticAttributes; - $this->registerMockObjectsFromTestArgumentsRecursively = $registerMockObjectsFromTestArgumentsRecursively; - $this->conflictBetweenPrinterClassAndTestdox = $conflictBetweenPrinterClassAndTestdox; - } - public function cacheResult() : bool - { - return $this->cacheResult; - } - /** - * @psalm-assert-if-true !null $this->cacheResultFile - */ - public function hasCacheResultFile() : bool - { - return $this->cacheResultFile !== null; - } - /** - * @throws Exception + * @throws PhpdbgNotAvailableException */ - public function cacheResultFile() : string + public function __construct() { - if (!$this->hasCacheResultFile()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Cache result file is not configured'); + if (\PHP_SAPI !== 'phpdbg') { + throw new PhpdbgNotAvailableException(); } - return (string) $this->cacheResultFile; - } - public function columns() - { - return $this->columns; - } - public function colors() : string - { - return $this->colors; - } - public function stderr() : bool - { - return $this->stderr; - } - public function noInteraction() : bool - { - return $this->noInteraction; - } - public function verbose() : bool - { - return $this->verbose; - } - public function reverseDefectList() : bool - { - return $this->reverseDefectList; - } - public function convertDeprecationsToExceptions() : bool - { - return $this->convertDeprecationsToExceptions; } - public function convertErrorsToExceptions() : bool + public function start() : void { - return $this->convertErrorsToExceptions; + phpdbg_start_oplog(); } - public function convertNoticesToExceptions() : bool + public function stop() : RawCodeCoverageData { - return $this->convertNoticesToExceptions; + static $fetchedLines = []; + $dbgData = phpdbg_end_oplog(); + if ($fetchedLines === []) { + $sourceLines = phpdbg_get_executable(); + } else { + $newFiles = array_diff(get_included_files(), array_keys($fetchedLines)); + $sourceLines = []; + if ($newFiles) { + $sourceLines = phpdbg_get_executable(['files' => $newFiles]); + } + } + foreach ($sourceLines as $file => $lines) { + foreach ($lines as $lineNo => $numExecuted) { + $sourceLines[$file][$lineNo] = self::LINE_NOT_EXECUTED; + } + } + $fetchedLines = array_merge($fetchedLines, $sourceLines); + return RawCodeCoverageData::fromXdebugWithoutPathCoverage($this->detectExecutedLines($fetchedLines, $dbgData)); } - public function convertWarningsToExceptions() : bool + public function nameAndVersion() : string { - return $this->convertWarningsToExceptions; + return 'PHPDBG ' . \PHP_VERSION; } - public function forceCoversAnnotation() : bool + private function detectExecutedLines(array $sourceLines, array $dbgData) : array { - return $this->forceCoversAnnotation; + foreach ($dbgData as $file => $coveredLines) { + foreach ($coveredLines as $lineNo => $numExecuted) { + // phpdbg also reports $lineNo=0 when e.g. exceptions get thrown. + // make sure we only mark lines executed which are actually executable. + if (isset($sourceLines[$file][$lineNo])) { + $sourceLines[$file][$lineNo] = self::LINE_EXECUTED; + } + } + } + return $sourceLines; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Driver; + +use function phpversion; +use function version_compare; +use PHPUnit\SebastianBergmann\CodeCoverage\Filter; +use PHPUnit\SebastianBergmann\CodeCoverage\NoCodeCoverageDriverAvailableException; +use PHPUnit\SebastianBergmann\CodeCoverage\NoCodeCoverageDriverWithPathCoverageSupportAvailableException; +use PHPUnit\SebastianBergmann\Environment\Runtime; +final class Selector +{ /** - * @psalm-assert-if-true !null $this->bootstrap + * @throws NoCodeCoverageDriverAvailableException + * @throws PcovNotAvailableException + * @throws PhpdbgNotAvailableException + * @throws Xdebug2NotEnabledException + * @throws Xdebug3NotEnabledException + * @throws XdebugNotAvailableException */ - public function hasBootstrap() : bool + public function forLineCoverage(Filter $filter) : Driver { - return $this->bootstrap !== null; + $runtime = new Runtime(); + if ($runtime->hasPHPDBGCodeCoverage()) { + return new PhpdbgDriver(); + } + if ($runtime->hasPCOV()) { + return new PcovDriver($filter); + } + if ($runtime->hasXdebug()) { + if (version_compare(phpversion('xdebug'), '3', '>=')) { + $driver = new Xdebug3Driver($filter); + } else { + $driver = new Xdebug2Driver($filter); + } + $driver->enableDeadCodeDetection(); + return $driver; + } + throw new NoCodeCoverageDriverAvailableException(); } /** - * @throws Exception + * @throws NoCodeCoverageDriverWithPathCoverageSupportAvailableException + * @throws Xdebug2NotEnabledException + * @throws Xdebug3NotEnabledException + * @throws XdebugNotAvailableException */ - public function bootstrap() : string + public function forLineAndPathCoverage(Filter $filter) : Driver { - if (!$this->hasBootstrap()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Bootstrap script is not configured'); + if ((new Runtime())->hasXdebug()) { + if (version_compare(phpversion('xdebug'), '3', '>=')) { + $driver = new Xdebug3Driver($filter); + } else { + $driver = new Xdebug2Driver($filter); + } + $driver->enableDeadCodeDetection(); + $driver->enableBranchAndPathCoverage(); + return $driver; } - return (string) $this->bootstrap; - } - public function processIsolation() : bool - { - return $this->processIsolation; + throw new NoCodeCoverageDriverWithPathCoverageSupportAvailableException(); } - public function failOnEmptyTestSuite() : bool +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; + +use PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class CachingCoveredFileAnalyser extends Cache implements CoveredFileAnalyser +{ + /** + * @var CoveredFileAnalyser + */ + private $coveredFileAnalyser; + /** + * @var array + */ + private $cache = []; + public function __construct(string $directory, CoveredFileAnalyser $coveredFileAnalyser) { - return $this->failOnEmptyTestSuite; + parent::__construct($directory); + $this->coveredFileAnalyser = $coveredFileAnalyser; } - public function failOnIncomplete() : bool + public function classesIn(string $filename) : array { - return $this->failOnIncomplete; + if (!isset($this->cache[$filename])) { + $this->process($filename); + } + return $this->cache[$filename]['classesIn']; } - public function failOnRisky() : bool + public function traitsIn(string $filename) : array { - return $this->failOnRisky; + if (!isset($this->cache[$filename])) { + $this->process($filename); + } + return $this->cache[$filename]['traitsIn']; } - public function failOnSkipped() : bool + public function functionsIn(string $filename) : array { - return $this->failOnSkipped; + if (!isset($this->cache[$filename])) { + $this->process($filename); + } + return $this->cache[$filename]['functionsIn']; } - public function failOnWarning() : bool + public function linesOfCodeFor(string $filename) : LinesOfCode { - return $this->failOnWarning; + if (!isset($this->cache[$filename])) { + $this->process($filename); + } + return $this->cache[$filename]['linesOfCodeFor']; } - public function stopOnDefect() : bool + public function ignoredLinesFor(string $filename) : array { - return $this->stopOnDefect; + if (!isset($this->cache[$filename])) { + $this->process($filename); + } + return $this->cache[$filename]['ignoredLinesFor']; } - public function stopOnError() : bool + public function process(string $filename) : void { - return $this->stopOnError; + if ($this->has($filename, __CLASS__)) { + $this->cache[$filename] = $this->read($filename, __CLASS__, [LinesOfCode::class]); + return; + } + $this->cache[$filename] = ['classesIn' => $this->coveredFileAnalyser->classesIn($filename), 'traitsIn' => $this->coveredFileAnalyser->traitsIn($filename), 'functionsIn' => $this->coveredFileAnalyser->functionsIn($filename), 'linesOfCodeFor' => $this->coveredFileAnalyser->linesOfCodeFor($filename), 'ignoredLinesFor' => $this->coveredFileAnalyser->ignoredLinesFor($filename)]; + $this->write($filename, __CLASS__, $this->cache[$filename]); } - public function stopOnFailure() : bool +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +interface UncoveredFileAnalyser +{ + public function executableLinesIn(string $filename) : array; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; + +use function array_unique; +use function assert; +use function file_get_contents; +use function is_array; +use function sprintf; +use function substr_count; +use function token_get_all; +use function trim; +use PHPUnit\PhpParser\Error; +use PHPUnit\PhpParser\Lexer; +use PHPUnit\PhpParser\NodeTraverser; +use PHPUnit\PhpParser\NodeVisitor\NameResolver; +use PHPUnit\PhpParser\NodeVisitor\ParentConnectingVisitor; +use PHPUnit\PhpParser\ParserFactory; +use PHPUnit\SebastianBergmann\CodeCoverage\ParserException; +use PHPUnit\SebastianBergmann\LinesOfCode\LineCountingVisitor; +use PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class ParsingCoveredFileAnalyser implements CoveredFileAnalyser +{ + /** + * @var array + */ + private $classes = []; + /** + * @var array + */ + private $traits = []; + /** + * @var array + */ + private $functions = []; + /** + * @var LinesOfCode[] + */ + private $linesOfCode = []; + /** + * @var array + */ + private $ignoredLines = []; + /** + * @var bool + */ + private $useAnnotationsForIgnoringCode; + /** + * @var bool + */ + private $ignoreDeprecatedCode; + public function __construct(bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode) { - return $this->stopOnFailure; + $this->useAnnotationsForIgnoringCode = $useAnnotationsForIgnoringCode; + $this->ignoreDeprecatedCode = $ignoreDeprecatedCode; } - public function stopOnWarning() : bool + public function classesIn(string $filename) : array { - return $this->stopOnWarning; + $this->analyse($filename); + return $this->classes[$filename]; } - public function stopOnIncomplete() : bool + public function traitsIn(string $filename) : array { - return $this->stopOnIncomplete; + $this->analyse($filename); + return $this->traits[$filename]; } - public function stopOnRisky() : bool + public function functionsIn(string $filename) : array { - return $this->stopOnRisky; + $this->analyse($filename); + return $this->functions[$filename]; } - public function stopOnSkipped() : bool + public function linesOfCodeFor(string $filename) : LinesOfCode { - return $this->stopOnSkipped; + $this->analyse($filename); + return $this->linesOfCode[$filename]; } - /** - * @psalm-assert-if-true !null $this->extensionsDirectory - */ - public function hasExtensionsDirectory() : bool + public function ignoredLinesFor(string $filename) : array { - return $this->extensionsDirectory !== null; + $this->analyse($filename); + return $this->ignoredLines[$filename]; } /** - * @throws Exception + * @throws ParserException */ - public function extensionsDirectory() : string + private function analyse(string $filename) : void { - if (!$this->hasExtensionsDirectory()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Extensions directory is not configured'); + if (isset($this->classes[$filename])) { + return; } - return (string) $this->extensionsDirectory; + $source = file_get_contents($filename); + $linesOfCode = substr_count($source, "\n"); + if ($linesOfCode === 0 && !empty($source)) { + $linesOfCode = 1; + } + $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7, new Lexer()); + try { + $nodes = $parser->parse($source); + assert($nodes !== null); + $traverser = new NodeTraverser(); + $codeUnitFindingVisitor = new CodeUnitFindingVisitor(); + $lineCountingVisitor = new LineCountingVisitor($linesOfCode); + $ignoredLinesFindingVisitor = new IgnoredLinesFindingVisitor($this->useAnnotationsForIgnoringCode, $this->ignoreDeprecatedCode); + $traverser->addVisitor(new NameResolver()); + $traverser->addVisitor(new ParentConnectingVisitor()); + $traverser->addVisitor($codeUnitFindingVisitor); + $traverser->addVisitor($lineCountingVisitor); + $traverser->addVisitor($ignoredLinesFindingVisitor); + /* @noinspection UnusedFunctionResultInspection */ + $traverser->traverse($nodes); + // @codeCoverageIgnoreStart + } catch (Error $error) { + throw new ParserException(sprintf('Cannot parse %s: %s', $filename, $error->getMessage()), (int) $error->getCode(), $error); + } + // @codeCoverageIgnoreEnd + $this->classes[$filename] = $codeUnitFindingVisitor->classes(); + $this->traits[$filename] = $codeUnitFindingVisitor->traits(); + $this->functions[$filename] = $codeUnitFindingVisitor->functions(); + $this->linesOfCode[$filename] = $lineCountingVisitor->result(); + $this->ignoredLines[$filename] = []; + $this->findLinesIgnoredByLineBasedAnnotations($filename, $source, $this->useAnnotationsForIgnoringCode); + $this->ignoredLines[$filename] = array_unique(\array_merge($this->ignoredLines[$filename], $ignoredLinesFindingVisitor->ignoredLines())); + \sort($this->ignoredLines[$filename]); } - /** - * @psalm-assert-if-true !null $this->testSuiteLoaderClass - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - */ - public function hasTestSuiteLoaderClass() : bool + private function findLinesIgnoredByLineBasedAnnotations(string $filename, string $source, bool $useAnnotationsForIgnoringCode) : void { - return $this->testSuiteLoaderClass !== null; + $ignore = \false; + $stop = \false; + foreach (token_get_all($source) as $token) { + if (!is_array($token)) { + continue; + } + switch ($token[0]) { + case \T_COMMENT: + case \T_DOC_COMMENT: + if (!$useAnnotationsForIgnoringCode) { + break; + } + $comment = trim($token[1]); + if ($comment === '// @codeCoverageIgnore' || $comment === '//@codeCoverageIgnore') { + $ignore = \true; + $stop = \true; + } elseif ($comment === '// @codeCoverageIgnoreStart' || $comment === '//@codeCoverageIgnoreStart') { + $ignore = \true; + } elseif ($comment === '// @codeCoverageIgnoreEnd' || $comment === '//@codeCoverageIgnoreEnd') { + $stop = \true; + } + break; + } + if ($ignore) { + $this->ignoredLines[$filename][] = $token[2]; + if ($stop) { + $ignore = \false; + $stop = \false; + } + } + } } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; + +use function array_unique; +use function sort; +use PHPUnit\PhpParser\Node; +use PHPUnit\PhpParser\Node\Stmt\Break_; +use PHPUnit\PhpParser\Node\Stmt\Case_; +use PHPUnit\PhpParser\Node\Stmt\Catch_; +use PHPUnit\PhpParser\Node\Stmt\Continue_; +use PHPUnit\PhpParser\Node\Stmt\Do_; +use PHPUnit\PhpParser\Node\Stmt\Echo_; +use PHPUnit\PhpParser\Node\Stmt\Else_; +use PHPUnit\PhpParser\Node\Stmt\ElseIf_; +use PHPUnit\PhpParser\Node\Stmt\Expression; +use PHPUnit\PhpParser\Node\Stmt\Finally_; +use PHPUnit\PhpParser\Node\Stmt\For_; +use PHPUnit\PhpParser\Node\Stmt\Foreach_; +use PHPUnit\PhpParser\Node\Stmt\Goto_; +use PHPUnit\PhpParser\Node\Stmt\If_; +use PHPUnit\PhpParser\Node\Stmt\Return_; +use PHPUnit\PhpParser\Node\Stmt\Switch_; +use PHPUnit\PhpParser\Node\Stmt\Throw_; +use PHPUnit\PhpParser\Node\Stmt\TryCatch; +use PHPUnit\PhpParser\Node\Stmt\Unset_; +use PHPUnit\PhpParser\Node\Stmt\While_; +use PHPUnit\PhpParser\NodeVisitorAbstract; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract +{ /** - * @throws Exception - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 + * @psalm-var list */ - public function testSuiteLoaderClass() : string + private $executableLines = []; + public function enterNode(Node $node) : void { - if (!$this->hasTestSuiteLoaderClass()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('TestSuiteLoader class is not configured'); + if (!$this->isExecutable($node)) { + return; } - return (string) $this->testSuiteLoaderClass; + $this->executableLines[] = $node->getStartLine(); } /** - * @psalm-assert-if-true !null $this->testSuiteLoaderFile - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 + * @psalm-return list */ - public function hasTestSuiteLoaderFile() : bool + public function executableLines() : array { - return $this->testSuiteLoaderFile !== null; + $executableLines = array_unique($this->executableLines); + sort($executableLines); + return $executableLines; } - /** - * @throws Exception - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - */ - public function testSuiteLoaderFile() : string + private function isExecutable(Node $node) : bool { - if (!$this->hasTestSuiteLoaderFile()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('TestSuiteLoader sourcecode file is not configured'); - } - return (string) $this->testSuiteLoaderFile; + return $node instanceof Break_ || $node instanceof Case_ || $node instanceof Catch_ || $node instanceof Continue_ || $node instanceof Do_ || $node instanceof Echo_ || $node instanceof ElseIf_ || $node instanceof Else_ || $node instanceof Expression || $node instanceof Finally_ || $node instanceof Foreach_ || $node instanceof For_ || $node instanceof Goto_ || $node instanceof If_ || $node instanceof Return_ || $node instanceof Switch_ || $node instanceof Throw_ || $node instanceof TryCatch || $node instanceof Unset_ || $node instanceof While_; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; + +use const DIRECTORY_SEPARATOR; +use function file_get_contents; +use function file_put_contents; +use function filemtime; +use function hash; +use function is_file; +use function serialize; +use function unserialize; +use PHPUnit\SebastianBergmann\CodeCoverage\Directory; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +abstract class Cache +{ /** - * @psalm-assert-if-true !null $this->printerClass + * @var string */ - public function hasPrinterClass() : bool + private $directory; + public function __construct(string $directory) { - return $this->printerClass !== null; + Directory::create($directory); + $this->directory = $directory; } - /** - * @throws Exception - */ - public function printerClass() : string + protected function has(string $filename, string $key) : bool { - if (!$this->hasPrinterClass()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('ResultPrinter class is not configured'); + $cacheFile = $this->cacheFile($filename, $key); + if (!is_file($cacheFile)) { + return \false; } - return (string) $this->printerClass; + if (filemtime($cacheFile) < filemtime($filename)) { + return \false; + } + return \true; } /** - * @psalm-assert-if-true !null $this->printerFile + * @psalm-param list $allowedClasses + * + * @return mixed */ - public function hasPrinterFile() : bool + protected function read(string $filename, string $key, array $allowedClasses = []) { - return $this->printerFile !== null; + $options = ['allowed_classes' => \false]; + if (!empty($allowedClasses)) { + $options = ['allowed_classes' => $allowedClasses]; + } + return unserialize(file_get_contents($this->cacheFile($filename, $key)), $options); } /** - * @throws Exception + * @param mixed $data */ - public function printerFile() : string - { - if (!$this->hasPrinterFile()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('ResultPrinter sourcecode file is not configured'); - } - return (string) $this->printerFile; - } - public function beStrictAboutChangesToGlobalState() : bool - { - return $this->beStrictAboutChangesToGlobalState; - } - public function beStrictAboutOutputDuringTests() : bool - { - return $this->beStrictAboutOutputDuringTests; - } - public function beStrictAboutResourceUsageDuringSmallTests() : bool - { - return $this->beStrictAboutResourceUsageDuringSmallTests; - } - public function beStrictAboutTestsThatDoNotTestAnything() : bool - { - return $this->beStrictAboutTestsThatDoNotTestAnything; - } - public function beStrictAboutTodoAnnotatedTests() : bool - { - return $this->beStrictAboutTodoAnnotatedTests; - } - public function beStrictAboutCoversAnnotation() : bool + protected function write(string $filename, string $key, $data) : void { - return $this->beStrictAboutCoversAnnotation; + file_put_contents($this->cacheFile($filename, $key), serialize($data)); } - public function enforceTimeLimit() : bool + private function cacheFile(string $filename, string $key) : string { - return $this->enforceTimeLimit; + return $this->directory . \DIRECTORY_SEPARATOR . hash('sha256', $filename . $key); } - public function defaultTimeLimit() : int +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; + +use function implode; +use function rtrim; +use function trim; +use PHPUnit\PhpParser\Node; +use PHPUnit\PhpParser\Node\Identifier; +use PHPUnit\PhpParser\Node\Name; +use PHPUnit\PhpParser\Node\NullableType; +use PHPUnit\PhpParser\Node\Stmt\Class_; +use PHPUnit\PhpParser\Node\Stmt\ClassMethod; +use PHPUnit\PhpParser\Node\Stmt\Function_; +use PHPUnit\PhpParser\Node\Stmt\Interface_; +use PHPUnit\PhpParser\Node\Stmt\Trait_; +use PHPUnit\PhpParser\Node\UnionType; +use PHPUnit\PhpParser\NodeTraverser; +use PHPUnit\PhpParser\NodeVisitorAbstract; +use PHPUnit\SebastianBergmann\Complexity\CyclomaticComplexityCalculatingVisitor; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class CodeUnitFindingVisitor extends NodeVisitorAbstract +{ + /** + * @var array + */ + private $classes = []; + /** + * @var array + */ + private $traits = []; + /** + * @var array + */ + private $functions = []; + public function enterNode(Node $node) { - return $this->defaultTimeLimit; + if ($node instanceof Class_) { + if ($node->isAnonymous()) { + return; + } + $this->processClass($node); + } + if ($node instanceof Trait_) { + $this->processTrait($node); + } + if (!$node instanceof ClassMethod && !$node instanceof Function_) { + return null; + } + if ($node instanceof ClassMethod) { + $parentNode = $node->getAttribute('parent'); + if ($parentNode instanceof Class_ && $parentNode->isAnonymous()) { + return; + } + $this->processMethod($node); + return; + } + $this->processFunction($node); } - public function timeoutForSmallTests() : int + public function classes() : array { - return $this->timeoutForSmallTests; + return $this->classes; } - public function timeoutForMediumTests() : int + public function traits() : array { - return $this->timeoutForMediumTests; + return $this->traits; } - public function timeoutForLargeTests() : int + public function functions() : array { - return $this->timeoutForLargeTests; + return $this->functions; } /** - * @psalm-assert-if-true !null $this->defaultTestSuite + * @psalm-param ClassMethod|Function_ $node */ - public function hasDefaultTestSuite() : bool + private function cyclomaticComplexity(Node $node) : int { - return $this->defaultTestSuite !== null; + \assert($node instanceof ClassMethod || $node instanceof Function_); + $nodes = $node->getStmts(); + if ($nodes === null) { + return 0; + } + $traverser = new NodeTraverser(); + $cyclomaticComplexityCalculatingVisitor = new CyclomaticComplexityCalculatingVisitor(); + $traverser->addVisitor($cyclomaticComplexityCalculatingVisitor); + /* @noinspection UnusedFunctionResultInspection */ + $traverser->traverse($nodes); + return $cyclomaticComplexityCalculatingVisitor->cyclomaticComplexity(); } /** - * @throws Exception + * @psalm-param ClassMethod|Function_ $node */ - public function defaultTestSuite() : string + private function signature(Node $node) : string { - if (!$this->hasDefaultTestSuite()) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Default test suite is not configured'); + \assert($node instanceof ClassMethod || $node instanceof Function_); + $signature = ($node->returnsByRef() ? '&' : '') . $node->name->toString() . '('; + $parameters = []; + foreach ($node->getParams() as $parameter) { + \assert(isset($parameter->var->name)); + $parameterAsString = ''; + if ($parameter->type !== null) { + $parameterAsString = $this->type($parameter->type) . ' '; + } + $parameterAsString .= '$' . $parameter->var->name; + /* @todo Handle default values */ + $parameters[] = $parameterAsString; } - return (string) $this->defaultTestSuite; + $signature .= implode(', ', $parameters) . ')'; + $returnType = $node->getReturnType(); + if ($returnType !== null) { + $signature .= ': ' . $this->type($returnType); + } + return $signature; } - public function executionOrder() : int + /** + * @psalm-param Identifier|Name|NullableType|UnionType $type + */ + private function type(Node $type) : string { - return $this->executionOrder; + \assert($type instanceof Identifier || $type instanceof Name || $type instanceof NullableType || $type instanceof UnionType); + if ($type instanceof NullableType) { + return '?' . $type->type; + } + if ($type instanceof UnionType) { + $types = []; + foreach ($type->types as $_type) { + $types[] = $_type->toString(); + } + return implode('|', $types); + } + return $type->toString(); } - public function resolveDependencies() : bool + private function visibility(ClassMethod $node) : string { - return $this->resolveDependencies; + if ($node->isPrivate()) { + return 'private'; + } + if ($node->isProtected()) { + return 'protected'; + } + return 'public'; } - public function defectsFirst() : bool + private function processClass(Class_ $node) : void { - return $this->defectsFirst; + $name = $node->name->toString(); + $namespacedName = $node->namespacedName->toString(); + $this->classes[$namespacedName] = ['name' => $name, 'namespacedName' => $namespacedName, 'namespace' => $this->namespace($namespacedName, $name), 'startLine' => $node->getStartLine(), 'endLine' => $node->getEndLine(), 'methods' => []]; } - public function backupGlobals() : bool + private function processTrait(Trait_ $node) : void { - return $this->backupGlobals; + $name = $node->name->toString(); + $namespacedName = $node->namespacedName->toString(); + $this->traits[$namespacedName] = ['name' => $name, 'namespacedName' => $namespacedName, 'namespace' => $this->namespace($namespacedName, $name), 'startLine' => $node->getStartLine(), 'endLine' => $node->getEndLine(), 'methods' => []]; } - public function backupStaticAttributes() : bool + private function processMethod(ClassMethod $node) : void { - return $this->backupStaticAttributes; + $parentNode = $node->getAttribute('parent'); + if ($parentNode instanceof Interface_) { + return; + } + \assert($parentNode instanceof Class_ || $parentNode instanceof Trait_); + \assert(isset($parentNode->name)); + \assert(isset($parentNode->namespacedName)); + \assert($parentNode->namespacedName instanceof Name); + $parentName = $parentNode->name->toString(); + $parentNamespacedName = $parentNode->namespacedName->toString(); + if ($parentNode instanceof Class_) { + $storage =& $this->classes; + } else { + $storage =& $this->traits; + } + if (!isset($storage[$parentNamespacedName])) { + $storage[$parentNamespacedName] = ['name' => $parentName, 'namespacedName' => $parentNamespacedName, 'namespace' => $this->namespace($parentNamespacedName, $parentName), 'startLine' => $parentNode->getStartLine(), 'endLine' => $parentNode->getEndLine(), 'methods' => []]; + } + $storage[$parentNamespacedName]['methods'][$node->name->toString()] = ['methodName' => $node->name->toString(), 'signature' => $this->signature($node), 'visibility' => $this->visibility($node), 'startLine' => $node->getStartLine(), 'endLine' => $node->getEndLine(), 'ccn' => $this->cyclomaticComplexity($node)]; } - public function registerMockObjectsFromTestArgumentsRecursively() : bool + private function processFunction(Function_ $node) : void { - return $this->registerMockObjectsFromTestArgumentsRecursively; + \assert(isset($node->name)); + \assert(isset($node->namespacedName)); + \assert($node->namespacedName instanceof Name); + $name = $node->name->toString(); + $namespacedName = $node->namespacedName->toString(); + $this->functions[$namespacedName] = ['name' => $name, 'namespacedName' => $namespacedName, 'namespace' => $this->namespace($namespacedName, $name), 'signature' => $this->signature($node), 'startLine' => $node->getStartLine(), 'endLine' => $node->getEndLine(), 'ccn' => $this->cyclomaticComplexity($node)]; } - public function conflictBetweenPrinterClassAndTestdox() : bool + private function namespace(string $namespacedName, string $name) : string { - return $this->conflictBetweenPrinterClassAndTestdox; + return trim(rtrim($namespacedName, $name), '\\'); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; -use function class_exists; -use function sprintf; -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\TestListener; -use PHPUnit\Runner\Hook; -use ReflectionClass; -use ReflectionException; +use PHPUnit\PhpParser\Error; +use PHPUnit\PhpParser\Lexer; +use PHPUnit\PhpParser\NodeTraverser; +use PHPUnit\PhpParser\ParserFactory; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -final class ExtensionHandler +final class ParsingUncoveredFileAnalyser implements UncoveredFileAnalyser { - public function createHookInstance(\PHPUnit\TextUI\XmlConfiguration\Extension $extension) : \PHPUnit\Runner\Hook - { - $object = $this->createInstance($extension); - if (!$object instanceof \PHPUnit\Runner\Hook) { - throw new \PHPUnit\Framework\Exception(\sprintf('Class "%s" does not implement a PHPUnit\\Runner\\Hook interface', $extension->className())); - } - return $object; - } - public function createTestListenerInstance(\PHPUnit\TextUI\XmlConfiguration\Extension $extension) : \PHPUnit\Framework\TestListener - { - $object = $this->createInstance($extension); - if (!$object instanceof \PHPUnit\Framework\TestListener) { - throw new \PHPUnit\Framework\Exception(\sprintf('Class "%s" does not implement the PHPUnit\\Framework\\TestListener interface', $extension->className())); - } - return $object; - } - private function createInstance(\PHPUnit\TextUI\XmlConfiguration\Extension $extension) : object + public function executableLinesIn(string $filename) : array { - $this->ensureClassExists($extension); + $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7, new Lexer()); try { - $reflector = new \ReflectionClass($extension->className()); - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - if (!$extension->hasArguments()) { - return $reflector->newInstance(); - } - return $reflector->newInstanceArgs($extension->arguments()); - } - /** - * @throws Exception - */ - private function ensureClassExists(\PHPUnit\TextUI\XmlConfiguration\Extension $extension) : void - { - if (\class_exists($extension->className(), \false)) { - return; - } - if ($extension->hasSourceFile()) { - /** @noinspection PhpIncludeInspection */ - require_once $extension->sourceFile(); - } - if (!\class_exists($extension->className())) { - throw new \PHPUnit\Framework\Exception(\sprintf('Class "%s" does not exist', $extension->className())); + $nodes = $parser->parse(\file_get_contents($filename)); + \assert($nodes !== null); + $traverser = new NodeTraverser(); + $visitor = new ExecutableLinesFindingVisitor(); + $traverser->addVisitor($visitor); + /* @noinspection UnusedFunctionResultInspection */ + $traverser->traverse($nodes); + return $visitor->executableLines(); + // @codeCoverageIgnoreStart + } catch (Error $error) { } + // @codeCoverageIgnoreEnd + return []; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -final class Group +final class CachingUncoveredFileAnalyser extends Cache implements UncoveredFileAnalyser { /** - * @var string + * @var UncoveredFileAnalyser */ - private $name; - public function __construct(string $name) + private $uncoveredFileAnalyser; + public function __construct(string $directory, UncoveredFileAnalyser $uncoveredFileAnalyser) { - $this->name = $name; + parent::__construct($directory); + $this->uncoveredFileAnalyser = $uncoveredFileAnalyser; } - public function name() : string + public function executableLinesIn(string $filename) : array { - return $this->name; + if ($this->has($filename, __METHOD__)) { + return $this->read($filename, __METHOD__); + } + $data = $this->uncoveredFileAnalyser->executableLinesIn($filename); + $this->write($filename, __METHOD__, $data); + return $data; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; -use function count; -use function iterator_count; -use Countable; -use Iterator; +use PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -final class GroupCollectionIterator implements \Countable, \Iterator +interface CoveredFileAnalyser { - /** - * @var Group[] - */ - private $groups; - /** - * @var int - */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\GroupCollection $groups) - { - $this->groups = $groups->asArray(); - } - public function count() : int - { - return \iterator_count($this); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < \count($this->groups); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\TextUI\XmlConfiguration\Group - { - return $this->groups[$this->position]; - } - public function next() : void - { - $this->position++; - } + public function classesIn(string $filename) : array; + public function traitsIn(string $filename) : array; + public function functionsIn(string $filename) : array; + public function linesOfCodeFor(string $filename) : LinesOfCode; + public function ignoredLinesFor(string $filename) : array; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; -use IteratorAggregate; +use function array_merge; +use function range; +use function strpos; +use PHPUnit\PhpParser\Node; +use PHPUnit\PhpParser\Node\Stmt\Class_; +use PHPUnit\PhpParser\Node\Stmt\ClassMethod; +use PHPUnit\PhpParser\Node\Stmt\Function_; +use PHPUnit\PhpParser\Node\Stmt\Interface_; +use PHPUnit\PhpParser\Node\Stmt\Trait_; +use PHPUnit\PhpParser\NodeTraverser; +use PHPUnit\PhpParser\NodeVisitorAbstract; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -final class GroupCollection implements \IteratorAggregate +final class IgnoredLinesFindingVisitor extends NodeVisitorAbstract { /** - * @var Group[] + * @psalm-var list */ - private $groups; + private $ignoredLines = []; /** - * @param Group[] $groups + * @var bool */ - public static function fromArray(array $groups) : self + private $useAnnotationsForIgnoringCode; + /** + * @var bool + */ + private $ignoreDeprecated; + public function __construct(bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecated) { - return new self(...$groups); + $this->useAnnotationsForIgnoringCode = $useAnnotationsForIgnoringCode; + $this->ignoreDeprecated = $ignoreDeprecated; } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\Group ...$groups) + public function enterNode(Node $node) : ?int { - $this->groups = $groups; + if (!$node instanceof Class_ && !$node instanceof Trait_ && !$node instanceof Interface_ && !$node instanceof ClassMethod && !$node instanceof Function_) { + return null; + } + if ($node instanceof Class_ && $node->isAnonymous()) { + return null; + } + // Workaround for https://bugs.xdebug.org/view.php?id=1798 + if ($node instanceof Class_ || $node instanceof Trait_ || $node instanceof Interface_) { + $this->ignoredLines[] = $node->getStartLine(); + } + if (!$this->useAnnotationsForIgnoringCode) { + return null; + } + if ($node instanceof Interface_) { + return null; + } + $docComment = $node->getDocComment(); + if ($docComment === null) { + return null; + } + if (strpos($docComment->getText(), '@codeCoverageIgnore') !== \false) { + $this->ignoredLines = array_merge($this->ignoredLines, range($node->getStartLine(), $node->getEndLine())); + } + if ($this->ignoreDeprecated && strpos($docComment->getText(), '@deprecated') !== \false) { + $this->ignoredLines = array_merge($this->ignoredLines, range($node->getStartLine(), $node->getEndLine())); + } + if ($node instanceof ClassMethod || $node instanceof Function_) { + return NodeTraverser::DONT_TRAVERSE_CHILDREN; + } + return null; } /** - * @return Group[] + * @psalm-return list */ - public function asArray() : array + public function ignoredLines() : array { - return $this->groups; + return $this->ignoredLines; } - /** - * @return string[] - */ - public function asArrayOfStrings() : array +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis; + +use PHPUnit\SebastianBergmann\CodeCoverage\Filter; +final class CacheWarmer +{ + public function warmCache(string $cacheDirectory, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode, Filter $filter) : void { - $result = []; - foreach ($this->groups as $group) { - $result[] = $group->name(); + $coveredFileAnalyser = new CachingCoveredFileAnalyser($cacheDirectory, new ParsingCoveredFileAnalyser($useAnnotationsForIgnoringCode, $ignoreDeprecatedCode)); + $uncoveredFileAnalyser = new CachingUncoveredFileAnalyser($cacheDirectory, new ParsingUncoveredFileAnalyser()); + foreach ($filter->files() as $file) { + $coveredFileAnalyser->process($file); + /* @noinspection UnusedFunctionResultInspection */ + $uncoveredFileAnalyser->executableLinesIn($file); } - return $result; - } - public function isEmpty() : bool - { - return empty($this->groups); } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\GroupCollectionIterator +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage; + +use function dirname; +use PHPUnit\SebastianBergmann\Version as VersionId; +final class Version +{ + /** + * @var string + */ + private static $version; + public static function id() : string { - return new \PHPUnit\TextUI\XmlConfiguration\GroupCollectionIterator($this); + if (self::$version === null) { + self::$version = (new VersionId('9.2.7', dirname(__DIR__)))->getVersion(); + } + return self::$version; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\XmlConfiguration; +namespace PHPUnit\SebastianBergmann\CodeCoverage; +use function array_diff; +use function array_diff_key; +use function array_flip; +use function array_keys; +use function array_merge; +use function array_unique; +use function array_values; +use function count; +use function explode; +use function get_class; +use function is_array; +use function is_file; +use function sort; +use PHPUnit\Framework\TestCase; +use PHPUnit\Runner\PhptTestCase; +use PHPUnit\Util\Test; +use ReflectionClass; +use PHPUnit\SebastianBergmann\CodeCoverage\Driver\Driver; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\Builder; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory; +use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CachingCoveredFileAnalyser; +use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CachingUncoveredFileAnalyser; +use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CoveredFileAnalyser; +use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingCoveredFileAnalyser; +use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingUncoveredFileAnalyser; +use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\UncoveredFileAnalyser; +use PHPUnit\SebastianBergmann\CodeUnitReverseLookup\Wizard; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable + * Provides collection functionality for PHP code coverage information. */ -final class Groups +final class CodeCoverage { + private const UNCOVERED_FILES = 'UNCOVERED_FILES'; /** - * @var GroupCollection + * @var Driver */ - private $include; + private $driver; /** - * @var GroupCollection + * @var Filter */ - private $exclude; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\GroupCollection $include, \PHPUnit\TextUI\XmlConfiguration\GroupCollection $exclude) - { - $this->include = $include; - $this->exclude = $exclude; - } - public function hasInclude() : bool - { - return !$this->include->isEmpty(); - } - public function include() : \PHPUnit\TextUI\XmlConfiguration\GroupCollection + private $filter; + /** + * @var Wizard + */ + private $wizard; + /** + * @var bool + */ + private $checkForUnintentionallyCoveredCode = \false; + /** + * @var bool + */ + private $includeUncoveredFiles = \true; + /** + * @var bool + */ + private $processUncoveredFiles = \false; + /** + * @var bool + */ + private $ignoreDeprecatedCode = \false; + /** + * @var PhptTestCase|string|TestCase + */ + private $currentId; + /** + * Code coverage data. + * + * @var ProcessedCodeCoverageData + */ + private $data; + /** + * @var bool + */ + private $useAnnotationsForIgnoringCode = \true; + /** + * Test data. + * + * @var array + */ + private $tests = []; + /** + * @psalm-var list + */ + private $parentClassesExcludedFromUnintentionallyCoveredCodeCheck = []; + /** + * @var ?CoveredFileAnalyser + */ + private $coveredFileAnalyser; + /** + * @var ?UncoveredFileAnalyser + */ + private $uncoveredFileAnalyser; + /** + * @var ?string + */ + private $cacheDirectory; + public function __construct(Driver $driver, Filter $filter) { - return $this->include; + $this->driver = $driver; + $this->filter = $filter; + $this->data = new ProcessedCodeCoverageData(); + $this->wizard = new Wizard(); } - public function hasExclude() : bool + /** + * Returns the code coverage information as a graph of node objects. + */ + public function getReport() : Directory { - return !$this->exclude->isEmpty(); + return (new Builder($this->coveredFileAnalyser()))->build($this); } - public function exclude() : \PHPUnit\TextUI\XmlConfiguration\GroupCollection + /** + * Clears collected code coverage data. + */ + public function clear() : void { - return $this->exclude; + $this->currentId = null; + $this->data = new ProcessedCodeCoverageData(); + $this->tests = []; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function count; -use Countable; -use IteratorAggregate; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class FileCollection implements \Countable, \IteratorAggregate -{ /** - * @var File[] + * Returns the filter object used. */ - private $files; + public function filter() : Filter + { + return $this->filter; + } /** - * @param File[] $files + * Returns the collected code coverage data. */ - public static function fromArray(array $files) : self + public function getData(bool $raw = \false) : ProcessedCodeCoverageData { - return new self(...$files); + if (!$raw) { + if ($this->processUncoveredFiles) { + $this->processUncoveredFilesFromFilter(); + } elseif ($this->includeUncoveredFiles) { + $this->addUncoveredFilesFromFilter(); + } + } + return $this->data; } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\File ...$files) + /** + * Sets the coverage data. + */ + public function setData(ProcessedCodeCoverageData $data) : void { - $this->files = $files; + $this->data = $data; } /** - * @return File[] + * Returns the test data. */ - public function asArray() : array + public function getTests() : array { - return $this->files; + return $this->tests; } - public function count() : int + /** + * Sets the test data. + */ + public function setTests(array $tests) : void { - return \count($this->files); + $this->tests = $tests; } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\FileCollectionIterator + /** + * Start collection of code coverage information. + * + * @param PhptTestCase|string|TestCase $id + */ + public function start($id, bool $clear = \false) : void { - return new \PHPUnit\TextUI\XmlConfiguration\FileCollectionIterator($this); + if ($clear) { + $this->clear(); + } + $this->currentId = $id; + $this->driver->start(); } - public function isEmpty() : bool + /** + * Stop collection of code coverage information. + * + * @param array|false $linesToBeCovered + */ + public function stop(bool $append = \true, $linesToBeCovered = [], array $linesToBeUsed = []) : RawCodeCoverageData { - return $this->count() === 0; + if (!is_array($linesToBeCovered) && $linesToBeCovered !== \false) { + throw new InvalidArgumentException('$linesToBeCovered must be an array or false'); + } + $data = $this->driver->stop(); + $this->append($data, null, $append, $linesToBeCovered, $linesToBeUsed); + $this->currentId = null; + return $data; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function count; -use function iterator_count; -use Countable; -use Iterator; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class FileCollectionIterator implements \Countable, \Iterator -{ /** - * @var File[] + * Appends code coverage data. + * + * @param PhptTestCase|string|TestCase $id + * @param array|false $linesToBeCovered + * + * @throws ReflectionException + * @throws TestIdMissingException + * @throws UnintentionallyCoveredCodeException */ - private $files; + public function append(RawCodeCoverageData $rawData, $id = null, bool $append = \true, $linesToBeCovered = [], array $linesToBeUsed = []) : void + { + if ($id === null) { + $id = $this->currentId; + } + if ($id === null) { + throw new TestIdMissingException(); + } + $this->applyFilter($rawData); + if ($this->useAnnotationsForIgnoringCode) { + $this->applyIgnoredLinesFilter($rawData); + } + $this->data->initializeUnseenData($rawData); + if (!$append) { + return; + } + if ($id !== self::UNCOVERED_FILES) { + $this->applyCoversAnnotationFilter($rawData, $linesToBeCovered, $linesToBeUsed); + if (empty($rawData->lineCoverage())) { + return; + } + $size = 'unknown'; + $status = -1; + $fromTestcase = \false; + if ($id instanceof TestCase) { + $fromTestcase = \true; + $_size = $id->getSize(); + if ($_size === Test::SMALL) { + $size = 'small'; + } elseif ($_size === Test::MEDIUM) { + $size = 'medium'; + } elseif ($_size === Test::LARGE) { + $size = 'large'; + } + $status = $id->getStatus(); + $id = get_class($id) . '::' . $id->getName(); + } elseif ($id instanceof PhptTestCase) { + $fromTestcase = \true; + $size = 'large'; + $id = $id->getName(); + } + $this->tests[$id] = ['size' => $size, 'status' => $status, 'fromTestcase' => $fromTestcase]; + $this->data->markCodeAsExecutedByTestCase($id, $rawData); + } + } /** - * @var int + * Merges the data from another instance. */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\FileCollection $files) + public function merge(self $that) : void { - $this->files = $files->asArray(); + $this->filter->includeFiles($that->filter()->files()); + $this->data->merge($that->data); + $this->tests = array_merge($this->tests, $that->getTests()); } - public function count() : int + public function enableCheckForUnintentionallyCoveredCode() : void { - return \iterator_count($this); + $this->checkForUnintentionallyCoveredCode = \true; } - public function rewind() : void + public function disableCheckForUnintentionallyCoveredCode() : void { - $this->position = 0; + $this->checkForUnintentionallyCoveredCode = \false; } - public function valid() : bool + public function includeUncoveredFiles() : void { - return $this->position < \count($this->files); + $this->includeUncoveredFiles = \true; } - public function key() : int + public function excludeUncoveredFiles() : void { - return $this->position; + $this->includeUncoveredFiles = \false; } - public function current() : \PHPUnit\TextUI\XmlConfiguration\File + public function processUncoveredFiles() : void { - return $this->files[$this->position]; + $this->processUncoveredFiles = \true; } - public function next() : void + public function doNotProcessUncoveredFiles() : void { - $this->position++; + $this->processUncoveredFiles = \false; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class Directory -{ - /** - * @var string - */ - private $path; - public function __construct(string $path) + public function enableAnnotationsForIgnoringCode() : void { - $this->path = $path; + $this->useAnnotationsForIgnoringCode = \true; } - public function path() : string + public function disableAnnotationsForIgnoringCode() : void { - return $this->path; + $this->useAnnotationsForIgnoringCode = \false; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function count; -use Countable; -use IteratorAggregate; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class DirectoryCollection implements \Countable, \IteratorAggregate -{ - /** - * @var Directory[] - */ - private $directories; - /** - * @param Directory[] $directories - */ - public static function fromArray(array $directories) : self + public function ignoreDeprecatedCode() : void { - return new self(...$directories); + $this->ignoreDeprecatedCode = \true; } - private function __construct(\PHPUnit\TextUI\XmlConfiguration\Directory ...$directories) + public function doNotIgnoreDeprecatedCode() : void { - $this->directories = $directories; + $this->ignoreDeprecatedCode = \false; } /** - * @return Directory[] + * @psalm-assert-if-true !null $this->cacheDirectory */ - public function asArray() : array - { - return $this->directories; - } - public function count() : int + public function cachesStaticAnalysis() : bool { - return \count($this->directories); + return $this->cacheDirectory !== null; } - public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\DirectoryCollectionIterator + public function cacheStaticAnalysis(string $directory) : void { - return new \PHPUnit\TextUI\XmlConfiguration\DirectoryCollectionIterator($this); + $this->cacheDirectory = $directory; } - public function isEmpty() : bool + public function doNotCacheStaticAnalysis() : void { - return $this->count() === 0; + $this->cacheDirectory = null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use function count; -use function iterator_count; -use Countable; -use Iterator; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class DirectoryCollectionIterator implements \Countable, \Iterator -{ /** - * @var Directory[] + * @throws StaticAnalysisCacheNotConfiguredException */ - private $directories; + public function cacheDirectory() : string + { + if (!$this->cachesStaticAnalysis()) { + throw new StaticAnalysisCacheNotConfiguredException('The static analysis cache is not configured'); + } + return $this->cacheDirectory; + } /** - * @var int + * @psalm-param class-string $className */ - private $position; - public function __construct(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection $directories) + public function excludeSubclassesOfThisClassFromUnintentionallyCoveredCodeCheck(string $className) : void { - $this->directories = $directories->asArray(); + $this->parentClassesExcludedFromUnintentionallyCoveredCodeCheck[] = $className; } - public function count() : int + public function enableBranchAndPathCoverage() : void { - return \iterator_count($this); + $this->driver->enableBranchAndPathCoverage(); } - public function rewind() : void + public function disableBranchAndPathCoverage() : void { - $this->position = 0; + $this->driver->disableBranchAndPathCoverage(); } - public function valid() : bool + public function collectsBranchAndPathCoverage() : bool { - return $this->position < \count($this->directories); + return $this->driver->collectsBranchAndPathCoverage(); } - public function key() : int + public function detectsDeadCode() : bool { - return $this->position; + return $this->driver->detectsDeadCode(); } - public function current() : \PHPUnit\TextUI\XmlConfiguration\Directory + /** + * Applies the @covers annotation filtering. + * + * @param array|false $linesToBeCovered + * + * @throws ReflectionException + * @throws UnintentionallyCoveredCodeException + */ + private function applyCoversAnnotationFilter(RawCodeCoverageData $rawData, $linesToBeCovered, array $linesToBeUsed) : void { - return $this->directories[$this->position]; + if ($linesToBeCovered === \false) { + $rawData->clear(); + return; + } + if (empty($linesToBeCovered)) { + return; + } + if ($this->checkForUnintentionallyCoveredCode && (!$this->currentId instanceof TestCase || !$this->currentId->isMedium() && !$this->currentId->isLarge())) { + $this->performUnintentionallyCoveredCodeCheck($rawData, $linesToBeCovered, $linesToBeUsed); + } + $rawLineData = $rawData->lineCoverage(); + $filesWithNoCoverage = array_diff_key($rawLineData, $linesToBeCovered); + foreach (array_keys($filesWithNoCoverage) as $fileWithNoCoverage) { + $rawData->removeCoverageDataForFile($fileWithNoCoverage); + } + if (is_array($linesToBeCovered)) { + foreach ($linesToBeCovered as $fileToBeCovered => $includedLines) { + $rawData->keepCoverageDataOnlyForLines($fileToBeCovered, $includedLines); + } + } } - public function next() : void + private function applyFilter(RawCodeCoverageData $data) : void { - $this->position++; + if ($this->filter->isEmpty()) { + return; + } + foreach (array_keys($data->lineCoverage()) as $filename) { + if ($this->filter->isExcluded($filename)) { + $data->removeCoverageDataForFile($filename); + } + } } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class File -{ - /** - * @var string - */ - private $path; - public function __construct(string $path) + private function applyIgnoredLinesFilter(RawCodeCoverageData $data) : void { - $this->path = $path; + foreach (array_keys($data->lineCoverage()) as $filename) { + if (!$this->filter->isFile($filename)) { + continue; + } + $data->removeCoverageDataForLines($filename, $this->coveredFileAnalyser()->ignoredLinesFor($filename)); + } } - public function path() : string + /** + * @throws UnintentionallyCoveredCodeException + */ + private function addUncoveredFilesFromFilter() : void { - return $this->path; + $uncoveredFiles = array_diff($this->filter->files(), $this->data->coveredFiles()); + foreach ($uncoveredFiles as $uncoveredFile) { + if (is_file($uncoveredFile)) { + $this->append(RawCodeCoverageData::fromUncoveredFile($uncoveredFile, $this->uncoveredFileAnalyser()), self::UNCOVERED_FILES); + } + } } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use const DIRECTORY_SEPARATOR; -use const PHP_VERSION; -use function assert; -use function defined; -use function dirname; -use function explode; -use function is_file; -use function is_numeric; -use function preg_match; -use function stream_resolve_include_path; -use function strlen; -use function strpos; -use function strtolower; -use function substr; -use function trim; -use DOMDocument; -use DOMElement; -use DOMNodeList; -use DOMXPath; -use PHPUnit\Runner\TestSuiteSorter; -use PHPUnit\Runner\Version; -use PHPUnit\TextUI\DefaultResultPrinter; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\Directory as FilterDirectory; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection as FilterDirectoryCollection; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Clover; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Cobertura; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Crap4j; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Html as CodeCoverageHtml; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Php as CodeCoveragePhp; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Text as CodeCoverageText; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Xml as CodeCoverageXml; -use PHPUnit\TextUI\XmlConfiguration\Logging\Junit; -use PHPUnit\TextUI\XmlConfiguration\Logging\Logging; -use PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity; -use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Html as TestDoxHtml; -use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Text as TestDoxText; -use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Xml as TestDoxXml; -use PHPUnit\TextUI\XmlConfiguration\Logging\Text; -use PHPUnit\TextUI\XmlConfiguration\TestSuite as TestSuiteConfiguration; -use PHPUnit\Util\TestDox\CliTestDoxPrinter; -use PHPUnit\Util\VersionComparisonOperator; -use PHPUnit\Util\Xml; -use PHPUnit\Util\Xml\Exception as XmlException; -use PHPUnit\Util\Xml\Loader as XmlLoader; -use PHPUnit\Util\Xml\SchemaFinder; -use PHPUnit\Util\Xml\Validator; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Loader -{ /** - * @throws Exception + * @throws UnintentionallyCoveredCodeException */ - public function load(string $filename) : \PHPUnit\TextUI\XmlConfiguration\Configuration + private function processUncoveredFilesFromFilter() : void { - try { - $document = (new \PHPUnit\Util\Xml\Loader())->loadFile($filename, \false, \true, \true); - } catch (\PHPUnit\Util\Xml\Exception $e) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - $xpath = new \DOMXPath($document); - try { - $xsdFilename = (new \PHPUnit\Util\Xml\SchemaFinder())->find(\PHPUnit\Runner\Version::series()); - } catch (\PHPUnit\Util\Xml\Exception $e) { - throw new \PHPUnit\TextUI\XmlConfiguration\Exception($e->getMessage(), (int) $e->getCode(), $e); + $uncoveredFiles = array_diff($this->filter->files(), $this->data->coveredFiles()); + $this->driver->start(); + foreach ($uncoveredFiles as $uncoveredFile) { + if (is_file($uncoveredFile)) { + include_once $uncoveredFile; + } } - return new \PHPUnit\TextUI\XmlConfiguration\Configuration($filename, (new \PHPUnit\Util\Xml\Validator())->validate($document, $xsdFilename), $this->extensions($filename, $xpath), $this->codeCoverage($filename, $xpath, $document), $this->groups($xpath), $this->testdoxGroups($xpath), $this->listeners($filename, $xpath), $this->logging($filename, $xpath), $this->php($filename, $xpath), $this->phpunit($filename, $document), $this->testSuite($filename, $xpath)); + $this->append($this->driver->stop(), self::UNCOVERED_FILES); } - public function logging(string $filename, \DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\Logging\Logging + /** + * @throws ReflectionException + * @throws UnintentionallyCoveredCodeException + */ + private function performUnintentionallyCoveredCodeCheck(RawCodeCoverageData $data, array $linesToBeCovered, array $linesToBeUsed) : void { - if ($xpath->query('logging/log')->length !== 0) { - return $this->legacyLogging($filename, $xpath); - } - $junit = null; - $element = $this->element($xpath, 'logging/junit'); - if ($element) { - $junit = new \PHPUnit\TextUI\XmlConfiguration\Logging\Junit(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $text = null; - $element = $this->element($xpath, 'logging/text'); - if ($element) { - $text = new \PHPUnit\TextUI\XmlConfiguration\Logging\Text(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + $allowedLines = $this->getAllowedLines($linesToBeCovered, $linesToBeUsed); + $unintentionallyCoveredUnits = []; + foreach ($data->lineCoverage() as $file => $_data) { + foreach ($_data as $line => $flag) { + if ($flag === 1 && !isset($allowedLines[$file][$line])) { + $unintentionallyCoveredUnits[] = $this->wizard->lookup($file, $line); + } + } } - $teamCity = null; - $element = $this->element($xpath, 'logging/teamcity'); - if ($element) { - $teamCity = new \PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + $unintentionallyCoveredUnits = $this->processUnintentionallyCoveredUnits($unintentionallyCoveredUnits); + if (!empty($unintentionallyCoveredUnits)) { + throw new UnintentionallyCoveredCodeException($unintentionallyCoveredUnits); } - $testDoxHtml = null; - $element = $this->element($xpath, 'logging/testdoxHtml'); - if ($element) { - $testDoxHtml = new \PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Html(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed) : array + { + $allowedLines = []; + foreach (array_keys($linesToBeCovered) as $file) { + if (!isset($allowedLines[$file])) { + $allowedLines[$file] = []; + } + $allowedLines[$file] = array_merge($allowedLines[$file], $linesToBeCovered[$file]); } - $testDoxText = null; - $element = $this->element($xpath, 'logging/testdoxText'); - if ($element) { - $testDoxText = new \PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Text(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + foreach (array_keys($linesToBeUsed) as $file) { + if (!isset($allowedLines[$file])) { + $allowedLines[$file] = []; + } + $allowedLines[$file] = array_merge($allowedLines[$file], $linesToBeUsed[$file]); } - $testDoxXml = null; - $element = $this->element($xpath, 'logging/testdoxXml'); - if ($element) { - $testDoxXml = new \PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Xml(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + foreach (array_keys($allowedLines) as $file) { + $allowedLines[$file] = array_flip(array_unique($allowedLines[$file])); } - return new \PHPUnit\TextUI\XmlConfiguration\Logging\Logging($junit, $text, $teamCity, $testDoxHtml, $testDoxText, $testDoxXml); + return $allowedLines; } - public function legacyLogging(string $filename, \DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\Logging\Logging + /** + * @throws ReflectionException + */ + private function processUnintentionallyCoveredUnits(array $unintentionallyCoveredUnits) : array { - $junit = null; - $teamCity = null; - $testDoxHtml = null; - $testDoxText = null; - $testDoxXml = null; - $text = null; - foreach ($xpath->query('logging/log') as $log) { - \assert($log instanceof \DOMElement); - $type = (string) $log->getAttribute('type'); - $target = (string) $log->getAttribute('target'); - if (!$target) { + $unintentionallyCoveredUnits = array_unique($unintentionallyCoveredUnits); + sort($unintentionallyCoveredUnits); + foreach (array_keys($unintentionallyCoveredUnits) as $k => $v) { + $unit = explode('::', $unintentionallyCoveredUnits[$k]); + if (count($unit) !== 2) { continue; } - $target = $this->toAbsolutePath($filename, $target); - switch ($type) { - case 'plain': - $text = new \PHPUnit\TextUI\XmlConfiguration\Logging\Text(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'junit': - $junit = new \PHPUnit\TextUI\XmlConfiguration\Logging\Junit(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'teamcity': - $teamCity = new \PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'testdox-html': - $testDoxHtml = new \PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Html(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'testdox-text': - $testDoxText = new \PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Text(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'testdox-xml': - $testDoxXml = new \PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Xml(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; + try { + $class = new ReflectionClass($unit[0]); + foreach ($this->parentClassesExcludedFromUnintentionallyCoveredCodeCheck as $parentClass) { + if ($class->isSubclassOf($parentClass)) { + unset($unintentionallyCoveredUnits[$k]); + break; + } + } + } catch (\ReflectionException $e) { + throw new ReflectionException($e->getMessage(), (int) $e->getCode(), $e); } } - return new \PHPUnit\TextUI\XmlConfiguration\Logging\Logging($junit, $text, $teamCity, $testDoxHtml, $testDoxText, $testDoxXml); + return array_values($unintentionallyCoveredUnits); } - private function extensions(string $filename, \DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection + private function coveredFileAnalyser() : CoveredFileAnalyser { - $extensions = []; - foreach ($xpath->query('extensions/extension') as $extension) { - \assert($extension instanceof \DOMElement); - $extensions[] = $this->getElementConfigurationParameters($filename, $extension); + if ($this->coveredFileAnalyser !== null) { + return $this->coveredFileAnalyser; } - return \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection::fromArray($extensions); + $this->coveredFileAnalyser = new ParsingCoveredFileAnalyser($this->useAnnotationsForIgnoringCode, $this->ignoreDeprecatedCode); + if ($this->cachesStaticAnalysis()) { + $this->coveredFileAnalyser = new CachingCoveredFileAnalyser($this->cacheDirectory, $this->coveredFileAnalyser); + } + return $this->coveredFileAnalyser; } - private function getElementConfigurationParameters(string $filename, \DOMElement $element) : \PHPUnit\TextUI\XmlConfiguration\Extension + private function uncoveredFileAnalyser() : UncoveredFileAnalyser { - /** @psalm-var class-string $class */ - $class = (string) $element->getAttribute('class'); - $file = ''; - $arguments = $this->getConfigurationArguments($filename, $element->childNodes); - if ($element->getAttribute('file')) { - $file = $this->toAbsolutePath($filename, (string) $element->getAttribute('file'), \true); + if ($this->uncoveredFileAnalyser !== null) { + return $this->uncoveredFileAnalyser; } - return new \PHPUnit\TextUI\XmlConfiguration\Extension($class, $file, $arguments); + $this->uncoveredFileAnalyser = new ParsingUncoveredFileAnalyser(); + if ($this->cachesStaticAnalysis()) { + $this->uncoveredFileAnalyser = new CachingUncoveredFileAnalyser($this->cacheDirectory, $this->uncoveredFileAnalyser); + } + return $this->uncoveredFileAnalyser; } - private function toAbsolutePath(string $filename, string $path, bool $useIncludePath = \false) : string +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage; + +use function array_key_exists; +use function array_keys; +use function array_merge; +use function array_unique; +use function count; +use function is_array; +use function ksort; +use PHPUnit\SebastianBergmann\CodeCoverage\Driver\Driver; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class ProcessedCodeCoverageData +{ + /** + * Line coverage data. + * An array of filenames, each having an array of linenumbers, each executable line having an array of testcase ids. + * + * @var array + */ + private $lineCoverage = []; + /** + * Function coverage data. + * Maintains base format of raw data (@see https://xdebug.org/docs/code_coverage), but each 'hit' entry is an array + * of testcase ids. + * + * @var array + */ + private $functionCoverage = []; + public function initializeUnseenData(RawCodeCoverageData $rawData) : void { - $path = \trim($path); - if (\strpos($path, '/') === 0) { - return $path; + foreach ($rawData->lineCoverage() as $file => $lines) { + if (!isset($this->lineCoverage[$file])) { + $this->lineCoverage[$file] = []; + foreach ($lines as $k => $v) { + $this->lineCoverage[$file][$k] = $v === Driver::LINE_NOT_EXECUTABLE ? null : []; + } + } } - // Matches the following on Windows: - // - \\NetworkComputer\Path - // - \\.\D: - // - \\.\c: - // - C:\Windows - // - C:\windows - // - C:/windows - // - c:/windows - if (\defined('PHP_WINDOWS_VERSION_BUILD') && ($path[0] === '\\' || \strlen($path) >= 3 && \preg_match('#^[A-Z]\\:[/\\\\]#i', \substr($path, 0, 3)))) { - return $path; + foreach ($rawData->functionCoverage() as $file => $functions) { + foreach ($functions as $functionName => $functionData) { + if (isset($this->functionCoverage[$file][$functionName])) { + $this->initPreviouslySeenFunction($file, $functionName, $functionData); + } else { + $this->initPreviouslyUnseenFunction($file, $functionName, $functionData); + } + } } - if (\strpos($path, '://') !== \false) { - return $path; + } + public function markCodeAsExecutedByTestCase(string $testCaseId, RawCodeCoverageData $executedCode) : void + { + foreach ($executedCode->lineCoverage() as $file => $lines) { + foreach ($lines as $k => $v) { + if ($v === Driver::LINE_EXECUTED) { + $this->lineCoverage[$file][$k][] = $testCaseId; + } + } } - $file = \dirname($filename) . \DIRECTORY_SEPARATOR . $path; - if ($useIncludePath && !\is_file($file)) { - $includePathFile = \stream_resolve_include_path($path); - if ($includePathFile) { - $file = $includePathFile; + foreach ($executedCode->functionCoverage() as $file => $functions) { + foreach ($functions as $functionName => $functionData) { + foreach ($functionData['branches'] as $branchId => $branchData) { + if ($branchData['hit'] === Driver::BRANCH_HIT) { + $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'][] = $testCaseId; + } + } + foreach ($functionData['paths'] as $pathId => $pathData) { + if ($pathData['hit'] === Driver::BRANCH_HIT) { + $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'][] = $testCaseId; + } + } } } - return $file; } - private function getConfigurationArguments(string $filename, \DOMNodeList $nodes) : array + public function setLineCoverage(array $lineCoverage) : void { - $arguments = []; - if ($nodes->length === 0) { - return $arguments; + $this->lineCoverage = $lineCoverage; + } + public function lineCoverage() : array + { + ksort($this->lineCoverage); + return $this->lineCoverage; + } + public function setFunctionCoverage(array $functionCoverage) : void + { + $this->functionCoverage = $functionCoverage; + } + public function functionCoverage() : array + { + ksort($this->functionCoverage); + return $this->functionCoverage; + } + public function coveredFiles() : array + { + ksort($this->lineCoverage); + return array_keys($this->lineCoverage); + } + public function renameFile(string $oldFile, string $newFile) : void + { + $this->lineCoverage[$newFile] = $this->lineCoverage[$oldFile]; + if (isset($this->functionCoverage[$oldFile])) { + $this->functionCoverage[$newFile] = $this->functionCoverage[$oldFile]; } - foreach ($nodes as $node) { - if (!$node instanceof \DOMElement) { + unset($this->lineCoverage[$oldFile], $this->functionCoverage[$oldFile]); + } + public function merge(self $newData) : void + { + foreach ($newData->lineCoverage as $file => $lines) { + if (!isset($this->lineCoverage[$file])) { + $this->lineCoverage[$file] = $lines; continue; } - if ($node->tagName !== 'arguments') { + // we should compare the lines if any of two contains data + $compareLineNumbers = array_unique(array_merge(array_keys($this->lineCoverage[$file]), array_keys($newData->lineCoverage[$file]))); + foreach ($compareLineNumbers as $line) { + $thatPriority = $this->priorityForLine($newData->lineCoverage[$file], $line); + $thisPriority = $this->priorityForLine($this->lineCoverage[$file], $line); + if ($thatPriority > $thisPriority) { + $this->lineCoverage[$file][$line] = $newData->lineCoverage[$file][$line]; + } elseif ($thatPriority === $thisPriority && is_array($this->lineCoverage[$file][$line])) { + $this->lineCoverage[$file][$line] = array_unique(array_merge($this->lineCoverage[$file][$line], $newData->lineCoverage[$file][$line])); + } + } + } + foreach ($newData->functionCoverage as $file => $functions) { + if (!isset($this->functionCoverage[$file])) { + $this->functionCoverage[$file] = $functions; continue; } - foreach ($node->childNodes as $argument) { - if (!$argument instanceof \DOMElement) { - continue; - } - if ($argument->tagName === 'file' || $argument->tagName === 'directory') { - $arguments[] = $this->toAbsolutePath($filename, (string) $argument->textContent); + foreach ($functions as $functionName => $functionData) { + if (isset($this->functionCoverage[$file][$functionName])) { + $this->initPreviouslySeenFunction($file, $functionName, $functionData); } else { - $arguments[] = \PHPUnit\Util\Xml::xmlToVariable($argument); + $this->initPreviouslyUnseenFunction($file, $functionName, $functionData); + } + foreach ($functionData['branches'] as $branchId => $branchData) { + $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = array_unique(array_merge($this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'], $branchData['hit'])); + } + foreach ($functionData['paths'] as $pathId => $pathData) { + $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = array_unique(array_merge($this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'], $pathData['hit'])); } } } - return $arguments; } - private function codeCoverage(string $filename, \DOMXPath $xpath, \DOMDocument $document) : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage + /** + * Determine the priority for a line. + * + * 1 = the line is not set + * 2 = the line has not been tested + * 3 = the line is dead code + * 4 = the line has been tested + * + * During a merge, a higher number is better. + */ + private function priorityForLine(array $data, int $line) : int { - if ($xpath->query('filter/whitelist')->length !== 0) { - return $this->legacyCodeCoverage($filename, $xpath, $document); - } - $cacheDirectory = null; - $pathCoverage = \false; - $includeUncoveredFiles = \true; - $processUncoveredFiles = \false; - $ignoreDeprecatedCodeUnits = \false; - $disableCodeCoverageIgnore = \false; - $element = $this->element($xpath, 'coverage'); - if ($element) { - $cacheDirectory = $this->getStringAttribute($element, 'cacheDirectory'); - if ($cacheDirectory !== null) { - $cacheDirectory = new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, $cacheDirectory)); - } - $pathCoverage = $this->getBooleanAttribute($element, 'pathCoverage', \false); - $includeUncoveredFiles = $this->getBooleanAttribute($element, 'includeUncoveredFiles', \true); - $processUncoveredFiles = $this->getBooleanAttribute($element, 'processUncoveredFiles', \false); - $ignoreDeprecatedCodeUnits = $this->getBooleanAttribute($element, 'ignoreDeprecatedCodeUnits', \false); - $disableCodeCoverageIgnore = $this->getBooleanAttribute($element, 'disableCodeCoverageIgnore', \false); - } - $clover = null; - $element = $this->element($xpath, 'coverage/report/clover'); - if ($element) { - $clover = new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Clover(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $cobertura = null; - $element = $this->element($xpath, 'coverage/report/cobertura'); - if ($element) { - $cobertura = new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Cobertura(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); - } - $crap4j = null; - $element = $this->element($xpath, 'coverage/report/crap4j'); - if ($element) { - $crap4j = new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Crap4j(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile'))), $this->getIntegerAttribute($element, 'threshold', 30)); + if (!array_key_exists($line, $data)) { + return 1; } - $html = null; - $element = $this->element($xpath, 'coverage/report/html'); - if ($element) { - $html = new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Html(new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputDirectory'))), $this->getIntegerAttribute($element, 'lowUpperBound', 50), $this->getIntegerAttribute($element, 'highLowerBound', 90)); + if (is_array($data[$line]) && count($data[$line]) === 0) { + return 2; } - $php = null; - $element = $this->element($xpath, 'coverage/report/php'); - if ($element) { - $php = new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Php(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + if ($data[$line] === null) { + return 3; } - $text = null; - $element = $this->element($xpath, 'coverage/report/text'); - if ($element) { - $text = new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Text(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile'))), $this->getBooleanAttribute($element, 'showUncoveredFiles', \false), $this->getBooleanAttribute($element, 'showOnlySummary', \false)); + return 4; + } + /** + * For a function we have never seen before, copy all data over and simply init the 'hit' array. + */ + private function initPreviouslyUnseenFunction(string $file, string $functionName, array $functionData) : void + { + $this->functionCoverage[$file][$functionName] = $functionData; + foreach (array_keys($functionData['branches']) as $branchId) { + $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = []; } - $xml = null; - $element = $this->element($xpath, 'coverage/report/xml'); - if ($element) { - $xml = new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Xml(new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputDirectory')))); + foreach (array_keys($functionData['paths']) as $pathId) { + $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = []; } - return new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage($cacheDirectory, $this->readFilterDirectories($filename, $xpath, 'coverage/include/directory'), $this->readFilterFiles($filename, $xpath, 'coverage/include/file'), $this->readFilterDirectories($filename, $xpath, 'coverage/exclude/directory'), $this->readFilterFiles($filename, $xpath, 'coverage/exclude/file'), $pathCoverage, $includeUncoveredFiles, $processUncoveredFiles, $ignoreDeprecatedCodeUnits, $disableCodeCoverageIgnore, $clover, $cobertura, $crap4j, $html, $php, $text, $xml); } /** - * @deprecated + * For a function we have seen before, only copy over and init the 'hit' array for any unseen branches and paths. + * Techniques such as mocking and where the contents of a file are different vary during tests (e.g. compiling + * containers) mean that the functions inside a file cannot be relied upon to be static. */ - private function legacyCodeCoverage(string $filename, \DOMXPath $xpath, \DOMDocument $document) : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage + private function initPreviouslySeenFunction(string $file, string $functionName, array $functionData) : void { - $ignoreDeprecatedCodeUnits = $this->getBooleanAttribute($document->documentElement, 'ignoreDeprecatedCodeUnitsFromCodeCoverage', \false); - $disableCodeCoverageIgnore = $this->getBooleanAttribute($document->documentElement, 'disableCodeCoverageIgnore', \false); - $includeUncoveredFiles = \true; - $processUncoveredFiles = \false; - $element = $this->element($xpath, 'filter/whitelist'); - if ($element) { - if ($element->hasAttribute('addUncoveredFilesFromWhitelist')) { - $includeUncoveredFiles = (bool) $this->getBoolean((string) $element->getAttribute('addUncoveredFilesFromWhitelist'), \true); + foreach ($functionData['branches'] as $branchId => $branchData) { + if (!isset($this->functionCoverage[$file][$functionName]['branches'][$branchId])) { + $this->functionCoverage[$file][$functionName]['branches'][$branchId] = $branchData; + $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = []; } - if ($element->hasAttribute('processUncoveredFilesFromWhitelist')) { - $processUncoveredFiles = (bool) $this->getBoolean((string) $element->getAttribute('processUncoveredFilesFromWhitelist'), \false); + } + foreach ($functionData['paths'] as $pathId => $pathData) { + if (!isset($this->functionCoverage[$file][$functionName]['paths'][$pathId])) { + $this->functionCoverage[$file][$functionName]['paths'][$pathId] = $pathData; + $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = []; } } - $clover = null; - $cobertura = null; - $crap4j = null; - $html = null; - $php = null; - $text = null; - $xml = null; - foreach ($xpath->query('logging/log') as $log) { - \assert($log instanceof \DOMElement); - $type = (string) $log->getAttribute('type'); - $target = (string) $log->getAttribute('target'); - if (!$target) { + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report; + +use function date; +use function dirname; +use function file_put_contents; +use function htmlspecialchars; +use function is_string; +use function round; +use DOMDocument; +use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; +use PHPUnit\SebastianBergmann\CodeCoverage\Directory; +use PHPUnit\SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\File; +final class Crap4j +{ + /** + * @var int + */ + private $threshold; + public function __construct(int $threshold = 30) + { + $this->threshold = $threshold; + } + /** + * @throws WriteOperationFailedException + */ + public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null) : string + { + $document = new DOMDocument('1.0', 'UTF-8'); + $document->formatOutput = \true; + $root = $document->createElement('crap_result'); + $document->appendChild($root); + $project = $document->createElement('project', is_string($name) ? $name : ''); + $root->appendChild($project); + $root->appendChild($document->createElement('timestamp', date('Y-m-d H:i:s'))); + $stats = $document->createElement('stats'); + $methodsNode = $document->createElement('methods'); + $report = $coverage->getReport(); + unset($coverage); + $fullMethodCount = 0; + $fullCrapMethodCount = 0; + $fullCrapLoad = 0; + $fullCrap = 0; + foreach ($report as $item) { + $namespace = 'global'; + if (!$item instanceof File) { continue; } - $target = $this->toAbsolutePath($filename, $target); - switch ($type) { - case 'coverage-clover': - $clover = new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Clover(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'coverage-cobertura': - $cobertura = new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Cobertura(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'coverage-crap4j': - $crap4j = new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Crap4j(new \PHPUnit\TextUI\XmlConfiguration\File($target), $this->getIntegerAttribute($log, 'threshold', 30)); - break; - case 'coverage-html': - $html = new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Html(new \PHPUnit\TextUI\XmlConfiguration\Directory($target), $this->getIntegerAttribute($log, 'lowUpperBound', 50), $this->getIntegerAttribute($log, 'highLowerBound', 90)); - break; - case 'coverage-php': - $php = new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Php(new \PHPUnit\TextUI\XmlConfiguration\File($target)); - break; - case 'coverage-text': - $text = new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Text(new \PHPUnit\TextUI\XmlConfiguration\File($target), $this->getBooleanAttribute($log, 'showUncoveredFiles', \false), $this->getBooleanAttribute($log, 'showOnlySummary', \false)); - break; - case 'coverage-xml': - $xml = new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Xml(new \PHPUnit\TextUI\XmlConfiguration\Directory($target)); - break; + $file = $document->createElement('file'); + $file->setAttribute('name', $item->pathAsString()); + $classes = $item->classesAndTraits(); + foreach ($classes as $className => $class) { + foreach ($class['methods'] as $methodName => $method) { + $crapLoad = $this->crapLoad((float) $method['crap'], $method['ccn'], $method['coverage']); + $fullCrap += $method['crap']; + $fullCrapLoad += $crapLoad; + $fullMethodCount++; + if ($method['crap'] >= $this->threshold) { + $fullCrapMethodCount++; + } + $methodNode = $document->createElement('method'); + if (!empty($class['namespace'])) { + $namespace = $class['namespace']; + } + $methodNode->appendChild($document->createElement('package', $namespace)); + $methodNode->appendChild($document->createElement('className', $className)); + $methodNode->appendChild($document->createElement('methodName', $methodName)); + $methodNode->appendChild($document->createElement('methodSignature', htmlspecialchars($method['signature']))); + $methodNode->appendChild($document->createElement('fullMethod', htmlspecialchars($method['signature']))); + $methodNode->appendChild($document->createElement('crap', (string) $this->roundValue((float) $method['crap']))); + $methodNode->appendChild($document->createElement('complexity', (string) $method['ccn'])); + $methodNode->appendChild($document->createElement('coverage', (string) $this->roundValue($method['coverage']))); + $methodNode->appendChild($document->createElement('crapLoad', (string) round($crapLoad))); + $methodsNode->appendChild($methodNode); + } + } + } + $stats->appendChild($document->createElement('name', 'Method Crap Stats')); + $stats->appendChild($document->createElement('methodCount', (string) $fullMethodCount)); + $stats->appendChild($document->createElement('crapMethodCount', (string) $fullCrapMethodCount)); + $stats->appendChild($document->createElement('crapLoad', (string) round($fullCrapLoad))); + $stats->appendChild($document->createElement('totalCrap', (string) $fullCrap)); + $crapMethodPercent = 0; + if ($fullMethodCount > 0) { + $crapMethodPercent = $this->roundValue(100 * $fullCrapMethodCount / $fullMethodCount); + } + $stats->appendChild($document->createElement('crapMethodPercent', (string) $crapMethodPercent)); + $root->appendChild($stats); + $root->appendChild($methodsNode); + $buffer = $document->saveXML(); + if ($target !== null) { + Directory::create(dirname($target)); + if (@file_put_contents($target, $buffer) === \false) { + throw new WriteOperationFailedException($target); } } - return new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage(null, $this->readFilterDirectories($filename, $xpath, 'filter/whitelist/directory'), $this->readFilterFiles($filename, $xpath, 'filter/whitelist/file'), $this->readFilterDirectories($filename, $xpath, 'filter/whitelist/exclude/directory'), $this->readFilterFiles($filename, $xpath, 'filter/whitelist/exclude/file'), \false, $includeUncoveredFiles, $processUncoveredFiles, $ignoreDeprecatedCodeUnits, $disableCodeCoverageIgnore, $clover, $cobertura, $crap4j, $html, $php, $text, $xml); + return $buffer; + } + private function crapLoad(float $crapValue, int $cyclomaticComplexity, float $coveragePercent) : float + { + $crapLoad = 0; + if ($crapValue >= $this->threshold) { + $crapLoad += $cyclomaticComplexity * (1.0 - $coveragePercent / 100); + $crapLoad += $cyclomaticComplexity / $this->threshold; + } + return $crapLoad; + } + private function roundValue(float $value) : float + { + return round($value, 2); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report; + +use const PHP_EOL; +use function array_map; +use function date; +use function ksort; +use function max; +use function sprintf; +use function str_pad; +use function strlen; +use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\File; +use PHPUnit\SebastianBergmann\CodeCoverage\Percentage; +final class Text +{ + /** + * @var string + */ + private const COLOR_GREEN = "\33[30;42m"; + /** + * @var string + */ + private const COLOR_YELLOW = "\33[30;43m"; + /** + * @var string + */ + private const COLOR_RED = "\33[37;41m"; + /** + * @var string + */ + private const COLOR_HEADER = "\33[1;37;40m"; + /** + * @var string + */ + private const COLOR_RESET = "\33[0m"; + /** + * @var string + */ + private const COLOR_EOL = "\33[2K"; + /** + * @var int + */ + private $lowUpperBound; + /** + * @var int + */ + private $highLowerBound; + /** + * @var bool + */ + private $showUncoveredFiles; /** - * If $value is 'false' or 'true', this returns the value that $value represents. - * Otherwise, returns $default, which may be a string in rare cases. - * - * @see \PHPUnit\TextUI\XmlConfigurationTest::testPHPConfigurationIsReadCorrectly - * - * @param bool|string $default - * - * @return bool|string + * @var bool */ - private function getBoolean(string $value, $default) + private $showOnlySummary; + public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, bool $showUncoveredFiles = \false, bool $showOnlySummary = \false) { - if (\strtolower($value) === 'false') { - return \false; - } - if (\strtolower($value) === 'true') { - return \true; - } - return $default; + $this->lowUpperBound = $lowUpperBound; + $this->highLowerBound = $highLowerBound; + $this->showUncoveredFiles = $showUncoveredFiles; + $this->showOnlySummary = $showOnlySummary; } - private function readFilterDirectories(string $filename, \DOMXPath $xpath, string $query) : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection + public function process(CodeCoverage $coverage, bool $showColors = \false) : string { - $directories = []; - foreach ($xpath->query($query) as $directoryNode) { - \assert($directoryNode instanceof \DOMElement); - $directoryPath = (string) $directoryNode->textContent; - if (!$directoryPath) { + $hasBranchCoverage = !empty($coverage->getData(\true)->functionCoverage()); + $output = \PHP_EOL . \PHP_EOL; + $report = $coverage->getReport(); + $colors = ['header' => '', 'classes' => '', 'methods' => '', 'lines' => '', 'branches' => '', 'paths' => '', 'reset' => '', 'eol' => '']; + if ($showColors) { + $colors['classes'] = $this->coverageColor($report->numberOfTestedClassesAndTraits(), $report->numberOfClassesAndTraits()); + $colors['methods'] = $this->coverageColor($report->numberOfTestedMethods(), $report->numberOfMethods()); + $colors['lines'] = $this->coverageColor($report->numberOfExecutedLines(), $report->numberOfExecutableLines()); + $colors['branches'] = $this->coverageColor($report->numberOfExecutedBranches(), $report->numberOfExecutableBranches()); + $colors['paths'] = $this->coverageColor($report->numberOfExecutedPaths(), $report->numberOfExecutablePaths()); + $colors['reset'] = self::COLOR_RESET; + $colors['header'] = self::COLOR_HEADER; + $colors['eol'] = self::COLOR_EOL; + } + $classes = sprintf(' Classes: %6s (%d/%d)', Percentage::fromFractionAndTotal($report->numberOfTestedClassesAndTraits(), $report->numberOfClassesAndTraits())->asString(), $report->numberOfTestedClassesAndTraits(), $report->numberOfClassesAndTraits()); + $methods = sprintf(' Methods: %6s (%d/%d)', Percentage::fromFractionAndTotal($report->numberOfTestedMethods(), $report->numberOfMethods())->asString(), $report->numberOfTestedMethods(), $report->numberOfMethods()); + $paths = ''; + $branches = ''; + if ($hasBranchCoverage) { + $paths = sprintf(' Paths: %6s (%d/%d)', Percentage::fromFractionAndTotal($report->numberOfExecutedPaths(), $report->numberOfExecutablePaths())->asString(), $report->numberOfExecutedPaths(), $report->numberOfExecutablePaths()); + $branches = sprintf(' Branches: %6s (%d/%d)', Percentage::fromFractionAndTotal($report->numberOfExecutedBranches(), $report->numberOfExecutableBranches())->asString(), $report->numberOfExecutedBranches(), $report->numberOfExecutableBranches()); + } + $lines = sprintf(' Lines: %6s (%d/%d)', Percentage::fromFractionAndTotal($report->numberOfExecutedLines(), $report->numberOfExecutableLines())->asString(), $report->numberOfExecutedLines(), $report->numberOfExecutableLines()); + $padding = max(array_map('strlen', [$classes, $methods, $lines])); + if ($this->showOnlySummary) { + $title = 'Code Coverage Report Summary:'; + $padding = max($padding, strlen($title)); + $output .= $this->format($colors['header'], $padding, $title); + } else { + $date = date(' Y-m-d H:i:s'); + $title = 'Code Coverage Report:'; + $output .= $this->format($colors['header'], $padding, $title); + $output .= $this->format($colors['header'], $padding, $date); + $output .= $this->format($colors['header'], $padding, ''); + $output .= $this->format($colors['header'], $padding, ' Summary:'); + } + $output .= $this->format($colors['classes'], $padding, $classes); + $output .= $this->format($colors['methods'], $padding, $methods); + if ($hasBranchCoverage) { + $output .= $this->format($colors['paths'], $padding, $paths); + $output .= $this->format($colors['branches'], $padding, $branches); + } + $output .= $this->format($colors['lines'], $padding, $lines); + if ($this->showOnlySummary) { + return $output . \PHP_EOL; + } + $classCoverage = []; + foreach ($report as $item) { + if (!$item instanceof File) { continue; } - $directories[] = new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\Directory($this->toAbsolutePath($filename, $directoryPath), $directoryNode->hasAttribute('prefix') ? (string) $directoryNode->getAttribute('prefix') : '', $directoryNode->hasAttribute('suffix') ? (string) $directoryNode->getAttribute('suffix') : '.php', $directoryNode->hasAttribute('group') ? (string) $directoryNode->getAttribute('group') : 'DEFAULT'); + $classes = $item->classesAndTraits(); + foreach ($classes as $className => $class) { + $classExecutableLines = 0; + $classExecutedLines = 0; + $classExecutableBranches = 0; + $classExecutedBranches = 0; + $classExecutablePaths = 0; + $classExecutedPaths = 0; + $coveredMethods = 0; + $classMethods = 0; + foreach ($class['methods'] as $method) { + if ($method['executableLines'] == 0) { + continue; + } + $classMethods++; + $classExecutableLines += $method['executableLines']; + $classExecutedLines += $method['executedLines']; + $classExecutableBranches += $method['executableBranches']; + $classExecutedBranches += $method['executedBranches']; + $classExecutablePaths += $method['executablePaths']; + $classExecutedPaths += $method['executedPaths']; + if ($method['coverage'] == 100) { + $coveredMethods++; + } + } + $classCoverage[$className] = ['namespace' => $class['namespace'], 'className' => $className, 'methodsCovered' => $coveredMethods, 'methodCount' => $classMethods, 'statementsCovered' => $classExecutedLines, 'statementCount' => $classExecutableLines, 'branchesCovered' => $classExecutedBranches, 'branchesCount' => $classExecutableBranches, 'pathsCovered' => $classExecutedPaths, 'pathsCount' => $classExecutablePaths]; + } } - return \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection::fromArray($directories); - } - private function readFilterFiles(string $filename, \DOMXPath $xpath, string $query) : \PHPUnit\TextUI\XmlConfiguration\FileCollection - { - $files = []; - foreach ($xpath->query($query) as $file) { - $filePath = (string) $file->textContent; - if ($filePath) { - $files[] = new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, $filePath)); + ksort($classCoverage); + $methodColor = ''; + $pathsColor = ''; + $branchesColor = ''; + $linesColor = ''; + $resetColor = ''; + foreach ($classCoverage as $fullQualifiedPath => $classInfo) { + if ($this->showUncoveredFiles || $classInfo['statementsCovered'] != 0) { + if ($showColors) { + $methodColor = $this->coverageColor($classInfo['methodsCovered'], $classInfo['methodCount']); + $pathsColor = $this->coverageColor($classInfo['pathsCovered'], $classInfo['pathsCount']); + $branchesColor = $this->coverageColor($classInfo['branchesCovered'], $classInfo['branchesCount']); + $linesColor = $this->coverageColor($classInfo['statementsCovered'], $classInfo['statementCount']); + $resetColor = $colors['reset']; + } + $output .= \PHP_EOL . $fullQualifiedPath . \PHP_EOL . ' ' . $methodColor . 'Methods: ' . $this->printCoverageCounts($classInfo['methodsCovered'], $classInfo['methodCount'], 2) . $resetColor . ' '; + if ($hasBranchCoverage) { + $output .= ' ' . $pathsColor . 'Paths: ' . $this->printCoverageCounts($classInfo['pathsCovered'], $classInfo['pathsCount'], 3) . $resetColor . ' ' . ' ' . $branchesColor . 'Branches: ' . $this->printCoverageCounts($classInfo['branchesCovered'], $classInfo['branchesCount'], 3) . $resetColor . ' '; + } + $output .= ' ' . $linesColor . 'Lines: ' . $this->printCoverageCounts($classInfo['statementsCovered'], $classInfo['statementCount'], 3) . $resetColor; } } - return \PHPUnit\TextUI\XmlConfiguration\FileCollection::fromArray($files); + return $output . \PHP_EOL; } - private function groups(\DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\Groups + private function coverageColor(int $numberOfCoveredElements, int $totalNumberOfElements) : string { - return $this->parseGroupConfiguration($xpath, 'groups'); + $coverage = Percentage::fromFractionAndTotal($numberOfCoveredElements, $totalNumberOfElements); + if ($coverage->asFloat() >= $this->highLowerBound) { + return self::COLOR_GREEN; + } + if ($coverage->asFloat() > $this->lowUpperBound) { + return self::COLOR_YELLOW; + } + return self::COLOR_RED; } - private function testdoxGroups(\DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\Groups + private function printCoverageCounts(int $numberOfCoveredElements, int $totalNumberOfElements, int $precision) : string { - return $this->parseGroupConfiguration($xpath, 'testdoxGroups'); + $format = '%' . $precision . 's'; + return Percentage::fromFractionAndTotal($numberOfCoveredElements, $totalNumberOfElements)->asFixedWidthString() . ' (' . sprintf($format, $numberOfCoveredElements) . '/' . sprintf($format, $totalNumberOfElements) . ')'; } - private function parseGroupConfiguration(\DOMXPath $xpath, string $root) : \PHPUnit\TextUI\XmlConfiguration\Groups + /** + * @param false|string $string + */ + private function format(string $color, int $padding, $string) : string { - $include = []; - $exclude = []; - foreach ($xpath->query($root . '/include/group') as $group) { - $include[] = new \PHPUnit\TextUI\XmlConfiguration\Group((string) $group->textContent); - } - foreach ($xpath->query($root . '/exclude/group') as $group) { - $exclude[] = new \PHPUnit\TextUI\XmlConfiguration\Group((string) $group->textContent); - } - return new \PHPUnit\TextUI\XmlConfiguration\Groups(\PHPUnit\TextUI\XmlConfiguration\GroupCollection::fromArray($include), \PHPUnit\TextUI\XmlConfiguration\GroupCollection::fromArray($exclude)); + $reset = $color ? self::COLOR_RESET : ''; + return $color . str_pad((string) $string, $padding) . $reset . \PHP_EOL; } - private function listeners(string $filename, \DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Html; + +use const DIRECTORY_SEPARATOR; +use function copy; +use function date; +use function dirname; +use function substr; +use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; +use PHPUnit\SebastianBergmann\CodeCoverage\Directory as DirectoryUtil; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; +final class Facade +{ + /** + * @var string + */ + private $templatePath; + /** + * @var string + */ + private $generator; + /** + * @var int + */ + private $lowUpperBound; + /** + * @var int + */ + private $highLowerBound; + public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, string $generator = '') { - $listeners = []; - foreach ($xpath->query('listeners/listener') as $listener) { - \assert($listener instanceof \DOMElement); - $listeners[] = $this->getElementConfigurationParameters($filename, $listener); - } - return \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection::fromArray($listeners); + $this->generator = $generator; + $this->highLowerBound = $highLowerBound; + $this->lowUpperBound = $lowUpperBound; + $this->templatePath = __DIR__ . '/Renderer/Template/'; } - private function getBooleanAttribute(\DOMElement $element, string $attribute, bool $default) : bool + public function process(CodeCoverage $coverage, string $target) : void { - if (!$element->hasAttribute($attribute)) { - return $default; + $target = $this->directory($target); + $report = $coverage->getReport(); + $date = date('D M j G:i:s T Y'); + $dashboard = new Dashboard($this->templatePath, $this->generator, $date, $this->lowUpperBound, $this->highLowerBound, $coverage->collectsBranchAndPathCoverage()); + $directory = new Directory($this->templatePath, $this->generator, $date, $this->lowUpperBound, $this->highLowerBound, $coverage->collectsBranchAndPathCoverage()); + $file = new File($this->templatePath, $this->generator, $date, $this->lowUpperBound, $this->highLowerBound, $coverage->collectsBranchAndPathCoverage()); + $directory->render($report, $target . 'index.html'); + $dashboard->render($report, $target . 'dashboard.html'); + foreach ($report as $node) { + $id = $node->id(); + if ($node instanceof DirectoryNode) { + DirectoryUtil::create($target . $id); + $directory->render($node, $target . $id . '/index.html'); + $dashboard->render($node, $target . $id . '/dashboard.html'); + } else { + $dir = dirname($target . $id); + DirectoryUtil::create($dir); + $file->render($node, $target . $id); + } } - return (bool) $this->getBoolean((string) $element->getAttribute($attribute), \false); + $this->copyFiles($target); } - private function getIntegerAttribute(\DOMElement $element, string $attribute, int $default) : int + private function copyFiles(string $target) : void { - if (!$element->hasAttribute($attribute)) { - return $default; - } - return $this->getInteger((string) $element->getAttribute($attribute), $default); + $dir = $this->directory($target . '_css'); + copy($this->templatePath . 'css/bootstrap.min.css', $dir . 'bootstrap.min.css'); + copy($this->templatePath . 'css/nv.d3.min.css', $dir . 'nv.d3.min.css'); + copy($this->templatePath . 'css/style.css', $dir . 'style.css'); + copy($this->templatePath . 'css/custom.css', $dir . 'custom.css'); + copy($this->templatePath . 'css/octicons.css', $dir . 'octicons.css'); + $dir = $this->directory($target . '_icons'); + copy($this->templatePath . 'icons/file-code.svg', $dir . 'file-code.svg'); + copy($this->templatePath . 'icons/file-directory.svg', $dir . 'file-directory.svg'); + $dir = $this->directory($target . '_js'); + copy($this->templatePath . 'js/bootstrap.min.js', $dir . 'bootstrap.min.js'); + copy($this->templatePath . 'js/popper.min.js', $dir . 'popper.min.js'); + copy($this->templatePath . 'js/d3.min.js', $dir . 'd3.min.js'); + copy($this->templatePath . 'js/jquery.min.js', $dir . 'jquery.min.js'); + copy($this->templatePath . 'js/nv.d3.min.js', $dir . 'nv.d3.min.js'); + copy($this->templatePath . 'js/file.js', $dir . 'file.js'); } - private function getStringAttribute(\DOMElement $element, string $attribute) : ?string + private function directory(string $directory) : string { - if (!$element->hasAttribute($attribute)) { - return null; + if (substr($directory, -1, 1) != \DIRECTORY_SEPARATOR) { + $directory .= \DIRECTORY_SEPARATOR; } - return (string) $element->getAttribute($attribute); + DirectoryUtil::create($directory); + return $directory; } - private function getInteger(string $value, int $default) : int +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Html; + +use function array_pop; +use function count; +use function sprintf; +use function str_repeat; +use function substr_count; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\File as FileNode; +use PHPUnit\SebastianBergmann\CodeCoverage\Version; +use PHPUnit\SebastianBergmann\Environment\Runtime; +use PHPUnit\SebastianBergmann\Template\Template; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +abstract class Renderer +{ + /** + * @var string + */ + protected $templatePath; + /** + * @var string + */ + protected $generator; + /** + * @var string + */ + protected $date; + /** + * @var int + */ + protected $lowUpperBound; + /** + * @var int + */ + protected $highLowerBound; + /** + * @var bool + */ + protected $hasBranchCoverage; + /** + * @var string + */ + protected $version; + public function __construct(string $templatePath, string $generator, string $date, int $lowUpperBound, int $highLowerBound, bool $hasBranchCoverage) { - if (\is_numeric($value)) { - return (int) $value; - } - return $default; + $this->templatePath = $templatePath; + $this->generator = $generator; + $this->date = $date; + $this->lowUpperBound = $lowUpperBound; + $this->highLowerBound = $highLowerBound; + $this->version = Version::id(); + $this->hasBranchCoverage = $hasBranchCoverage; } - private function php(string $filename, \DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\Php + protected function renderItemTemplate(Template $template, array $data) : string { - $includePaths = []; - foreach ($xpath->query('php/includePath') as $includePath) { - $path = (string) $includePath->textContent; - if ($path) { - $includePaths[] = new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, $path)); - } + $numSeparator = ' / '; + if (isset($data['numClasses']) && $data['numClasses'] > 0) { + $classesLevel = $this->colorLevel($data['testedClassesPercent']); + $classesNumber = $data['numTestedClasses'] . $numSeparator . $data['numClasses']; + $classesBar = $this->coverageBar($data['testedClassesPercent']); + } else { + $classesLevel = ''; + $classesNumber = '0' . $numSeparator . '0'; + $classesBar = ''; + $data['testedClassesPercentAsString'] = 'n/a'; } - $iniSettings = []; - foreach ($xpath->query('php/ini') as $ini) { - \assert($ini instanceof \DOMElement); - $iniSettings[] = new \PHPUnit\TextUI\XmlConfiguration\IniSetting((string) $ini->getAttribute('name'), (string) $ini->getAttribute('value')); + if ($data['numMethods'] > 0) { + $methodsLevel = $this->colorLevel($data['testedMethodsPercent']); + $methodsNumber = $data['numTestedMethods'] . $numSeparator . $data['numMethods']; + $methodsBar = $this->coverageBar($data['testedMethodsPercent']); + } else { + $methodsLevel = ''; + $methodsNumber = '0' . $numSeparator . '0'; + $methodsBar = ''; + $data['testedMethodsPercentAsString'] = 'n/a'; } - $constants = []; - foreach ($xpath->query('php/const') as $const) { - \assert($const instanceof \DOMElement); - $value = (string) $const->getAttribute('value'); - $constants[] = new \PHPUnit\TextUI\XmlConfiguration\Constant((string) $const->getAttribute('name'), $this->getBoolean($value, $value)); + if ($data['numExecutableLines'] > 0) { + $linesLevel = $this->colorLevel($data['linesExecutedPercent']); + $linesNumber = $data['numExecutedLines'] . $numSeparator . $data['numExecutableLines']; + $linesBar = $this->coverageBar($data['linesExecutedPercent']); + } else { + $linesLevel = ''; + $linesNumber = '0' . $numSeparator . '0'; + $linesBar = ''; + $data['linesExecutedPercentAsString'] = 'n/a'; } - $variables = ['var' => [], 'env' => [], 'post' => [], 'get' => [], 'cookie' => [], 'server' => [], 'files' => [], 'request' => []]; - foreach (['var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) { - foreach ($xpath->query('php/' . $array) as $var) { - \assert($var instanceof \DOMElement); - $name = (string) $var->getAttribute('name'); - $value = (string) $var->getAttribute('value'); - $force = \false; - $verbatim = \false; - if ($var->hasAttribute('force')) { - $force = (bool) $this->getBoolean($var->getAttribute('force'), \false); - } - if ($var->hasAttribute('verbatim')) { - $verbatim = $this->getBoolean($var->getAttribute('verbatim'), \false); - } - if (!$verbatim) { - $value = $this->getBoolean($value, $value); - } - $variables[$array][] = new \PHPUnit\TextUI\XmlConfiguration\Variable($name, $value, $force); - } + if ($data['numExecutablePaths'] > 0) { + $pathsLevel = $this->colorLevel($data['pathsExecutedPercent']); + $pathsNumber = $data['numExecutedPaths'] . $numSeparator . $data['numExecutablePaths']; + $pathsBar = $this->coverageBar($data['pathsExecutedPercent']); + } else { + $pathsLevel = ''; + $pathsNumber = '0' . $numSeparator . '0'; + $pathsBar = ''; + $data['pathsExecutedPercentAsString'] = 'n/a'; } - return new \PHPUnit\TextUI\XmlConfiguration\Php(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection::fromArray($includePaths), \PHPUnit\TextUI\XmlConfiguration\IniSettingCollection::fromArray($iniSettings), \PHPUnit\TextUI\XmlConfiguration\ConstantCollection::fromArray($constants), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['var']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['env']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['post']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['get']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['cookie']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['server']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['files']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['request'])); + if ($data['numExecutableBranches'] > 0) { + $branchesLevel = $this->colorLevel($data['branchesExecutedPercent']); + $branchesNumber = $data['numExecutedBranches'] . $numSeparator . $data['numExecutableBranches']; + $branchesBar = $this->coverageBar($data['branchesExecutedPercent']); + } else { + $branchesLevel = ''; + $branchesNumber = '0' . $numSeparator . '0'; + $branchesBar = ''; + $data['branchesExecutedPercentAsString'] = 'n/a'; + } + $template->setVar(['icon' => $data['icon'] ?? '', 'crap' => $data['crap'] ?? '', 'name' => $data['name'], 'lines_bar' => $linesBar, 'lines_executed_percent' => $data['linesExecutedPercentAsString'], 'lines_level' => $linesLevel, 'lines_number' => $linesNumber, 'paths_bar' => $pathsBar, 'paths_executed_percent' => $data['pathsExecutedPercentAsString'], 'paths_level' => $pathsLevel, 'paths_number' => $pathsNumber, 'branches_bar' => $branchesBar, 'branches_executed_percent' => $data['branchesExecutedPercentAsString'], 'branches_level' => $branchesLevel, 'branches_number' => $branchesNumber, 'methods_bar' => $methodsBar, 'methods_tested_percent' => $data['testedMethodsPercentAsString'], 'methods_level' => $methodsLevel, 'methods_number' => $methodsNumber, 'classes_bar' => $classesBar, 'classes_tested_percent' => $data['testedClassesPercentAsString'] ?? '', 'classes_level' => $classesLevel, 'classes_number' => $classesNumber]); + return $template->render(); } - private function phpunit(string $filename, \DOMDocument $document) : \PHPUnit\TextUI\XmlConfiguration\PHPUnit + protected function setCommonTemplateVariables(Template $template, AbstractNode $node) : void { - $executionOrder = \PHPUnit\Runner\TestSuiteSorter::ORDER_DEFAULT; - $defectsFirst = \false; - $resolveDependencies = $this->getBooleanAttribute($document->documentElement, 'resolveDependencies', \true); - if ($document->documentElement->hasAttribute('executionOrder')) { - foreach (\explode(',', $document->documentElement->getAttribute('executionOrder')) as $order) { - switch ($order) { - case 'default': - $executionOrder = \PHPUnit\Runner\TestSuiteSorter::ORDER_DEFAULT; - $defectsFirst = \false; - $resolveDependencies = \true; - break; - case 'depends': - $resolveDependencies = \true; - break; - case 'no-depends': - $resolveDependencies = \false; - break; - case 'defects': - $defectsFirst = \true; - break; - case 'duration': - $executionOrder = \PHPUnit\Runner\TestSuiteSorter::ORDER_DURATION; - break; - case 'random': - $executionOrder = \PHPUnit\Runner\TestSuiteSorter::ORDER_RANDOMIZED; - break; - case 'reverse': - $executionOrder = \PHPUnit\Runner\TestSuiteSorter::ORDER_REVERSED; - break; - case 'size': - $executionOrder = \PHPUnit\Runner\TestSuiteSorter::ORDER_SIZE; - break; - } - } - } - $printerClass = $this->getStringAttribute($document->documentElement, 'printerClass'); - $testdox = $this->getBooleanAttribute($document->documentElement, 'testdox', \false); - $conflictBetweenPrinterClassAndTestdox = \false; - if ($testdox) { - if ($printerClass !== null) { - $conflictBetweenPrinterClassAndTestdox = \true; - } - $printerClass = \PHPUnit\Util\TestDox\CliTestDoxPrinter::class; - } - $cacheResultFile = $this->getStringAttribute($document->documentElement, 'cacheResultFile'); - if ($cacheResultFile !== null) { - $cacheResultFile = $this->toAbsolutePath($filename, $cacheResultFile); - } - $bootstrap = $this->getStringAttribute($document->documentElement, 'bootstrap'); - if ($bootstrap !== null) { - $bootstrap = $this->toAbsolutePath($filename, $bootstrap); - } - $extensionsDirectory = $this->getStringAttribute($document->documentElement, 'extensionsDirectory'); - if ($extensionsDirectory !== null) { - $extensionsDirectory = $this->toAbsolutePath($filename, $extensionsDirectory); - } - $testSuiteLoaderFile = $this->getStringAttribute($document->documentElement, 'testSuiteLoaderFile'); - if ($testSuiteLoaderFile !== null) { - $testSuiteLoaderFile = $this->toAbsolutePath($filename, $testSuiteLoaderFile); - } - $printerFile = $this->getStringAttribute($document->documentElement, 'printerFile'); - if ($printerFile !== null) { - $printerFile = $this->toAbsolutePath($filename, $printerFile); - } - return new \PHPUnit\TextUI\XmlConfiguration\PHPUnit($this->getBooleanAttribute($document->documentElement, 'cacheResult', \true), $cacheResultFile, $this->getColumns($document), $this->getColors($document), $this->getBooleanAttribute($document->documentElement, 'stderr', \false), $this->getBooleanAttribute($document->documentElement, 'noInteraction', \false), $this->getBooleanAttribute($document->documentElement, 'verbose', \false), $this->getBooleanAttribute($document->documentElement, 'reverseDefectList', \false), $this->getBooleanAttribute($document->documentElement, 'convertDeprecationsToExceptions', \true), $this->getBooleanAttribute($document->documentElement, 'convertErrorsToExceptions', \true), $this->getBooleanAttribute($document->documentElement, 'convertNoticesToExceptions', \true), $this->getBooleanAttribute($document->documentElement, 'convertWarningsToExceptions', \true), $this->getBooleanAttribute($document->documentElement, 'forceCoversAnnotation', \false), $bootstrap, $this->getBooleanAttribute($document->documentElement, 'processIsolation', \false), $this->getBooleanAttribute($document->documentElement, 'failOnEmptyTestSuite', \false), $this->getBooleanAttribute($document->documentElement, 'failOnIncomplete', \false), $this->getBooleanAttribute($document->documentElement, 'failOnRisky', \false), $this->getBooleanAttribute($document->documentElement, 'failOnSkipped', \false), $this->getBooleanAttribute($document->documentElement, 'failOnWarning', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnDefect', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnError', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnFailure', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnWarning', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnIncomplete', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnRisky', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnSkipped', \false), $extensionsDirectory, $this->getStringAttribute($document->documentElement, 'testSuiteLoaderClass'), $testSuiteLoaderFile, $printerClass, $printerFile, $this->getBooleanAttribute($document->documentElement, 'beStrictAboutChangesToGlobalState', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutOutputDuringTests', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutResourceUsageDuringSmallTests', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutTestsThatDoNotTestAnything', \true), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutTodoAnnotatedTests', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutCoversAnnotation', \false), $this->getBooleanAttribute($document->documentElement, 'enforceTimeLimit', \false), $this->getIntegerAttribute($document->documentElement, 'defaultTimeLimit', 1), $this->getIntegerAttribute($document->documentElement, 'timeoutForSmallTests', 1), $this->getIntegerAttribute($document->documentElement, 'timeoutForMediumTests', 10), $this->getIntegerAttribute($document->documentElement, 'timeoutForLargeTests', 60), $this->getStringAttribute($document->documentElement, 'defaultTestSuite'), $executionOrder, $resolveDependencies, $defectsFirst, $this->getBooleanAttribute($document->documentElement, 'backupGlobals', \false), $this->getBooleanAttribute($document->documentElement, 'backupStaticAttributes', \false), $this->getBooleanAttribute($document->documentElement, 'registerMockObjectsFromTestArgumentsRecursively', \false), $conflictBetweenPrinterClassAndTestdox); + $template->setVar(['id' => $node->id(), 'full_path' => $node->pathAsString(), 'path_to_root' => $this->pathToRoot($node), 'breadcrumbs' => $this->breadcrumbs($node), 'date' => $this->date, 'version' => $this->version, 'runtime' => $this->runtimeString(), 'generator' => $this->generator, 'low_upper_bound' => $this->lowUpperBound, 'high_lower_bound' => $this->highLowerBound]); } - private function getColors(\DOMDocument $document) : string + protected function breadcrumbs(AbstractNode $node) : string { - $colors = \PHPUnit\TextUI\DefaultResultPrinter::COLOR_DEFAULT; - if ($document->documentElement->hasAttribute('colors')) { - /* only allow boolean for compatibility with previous versions - 'always' only allowed from command line */ - if ($this->getBoolean($document->documentElement->getAttribute('colors'), \false)) { - $colors = \PHPUnit\TextUI\DefaultResultPrinter::COLOR_AUTO; + $breadcrumbs = ''; + $path = $node->pathAsArray(); + $pathToRoot = []; + $max = count($path); + if ($node instanceof FileNode) { + $max--; + } + for ($i = 0; $i < $max; $i++) { + $pathToRoot[] = str_repeat('../', $i); + } + foreach ($path as $step) { + if ($step !== $node) { + $breadcrumbs .= $this->inactiveBreadcrumb($step, array_pop($pathToRoot)); } else { - $colors = \PHPUnit\TextUI\DefaultResultPrinter::COLOR_NEVER; + $breadcrumbs .= $this->activeBreadcrumb($step); } } - return $colors; + return $breadcrumbs; } - /** - * @return int|string - */ - private function getColumns(\DOMDocument $document) + protected function activeBreadcrumb(AbstractNode $node) : string { - $columns = 80; - if ($document->documentElement->hasAttribute('columns')) { - $columns = (string) $document->documentElement->getAttribute('columns'); - if ($columns !== 'max') { - $columns = $this->getInteger($columns, 80); - } + $buffer = sprintf(' ' . "\n", $node->name()); + if ($node instanceof DirectoryNode) { + $buffer .= ' ' . "\n"; } - return $columns; + return $buffer; } - private function testSuite(string $filename, \DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection + protected function inactiveBreadcrumb(AbstractNode $node, string $pathToRoot) : string { - $testSuites = []; - foreach ($this->getTestSuiteElements($xpath) as $element) { - $exclude = []; - foreach ($element->getElementsByTagName('exclude') as $excludeNode) { - $excludeFile = (string) $excludeNode->textContent; - if ($excludeFile) { - $exclude[] = new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, $excludeFile)); - } - } - $directories = []; - foreach ($element->getElementsByTagName('directory') as $directoryNode) { - \assert($directoryNode instanceof \DOMElement); - $directory = (string) $directoryNode->textContent; - if (empty($directory)) { - continue; - } - $prefix = ''; - if ($directoryNode->hasAttribute('prefix')) { - $prefix = (string) $directoryNode->getAttribute('prefix'); - } - $suffix = 'Test.php'; - if ($directoryNode->hasAttribute('suffix')) { - $suffix = (string) $directoryNode->getAttribute('suffix'); - } - $phpVersion = \PHP_VERSION; - if ($directoryNode->hasAttribute('phpVersion')) { - $phpVersion = (string) $directoryNode->getAttribute('phpVersion'); - } - $phpVersionOperator = new \PHPUnit\Util\VersionComparisonOperator('>='); - if ($directoryNode->hasAttribute('phpVersionOperator')) { - $phpVersionOperator = new \PHPUnit\Util\VersionComparisonOperator((string) $directoryNode->getAttribute('phpVersionOperator')); - } - $directories[] = new \PHPUnit\TextUI\XmlConfiguration\TestDirectory($this->toAbsolutePath($filename, $directory), $prefix, $suffix, $phpVersion, $phpVersionOperator); - } - $files = []; - foreach ($element->getElementsByTagName('file') as $fileNode) { - \assert($fileNode instanceof \DOMElement); - $file = (string) $fileNode->textContent; - if (empty($file)) { - continue; - } - $phpVersion = \PHP_VERSION; - if ($fileNode->hasAttribute('phpVersion')) { - $phpVersion = (string) $fileNode->getAttribute('phpVersion'); - } - $phpVersionOperator = new \PHPUnit\Util\VersionComparisonOperator('>='); - if ($fileNode->hasAttribute('phpVersionOperator')) { - $phpVersionOperator = new \PHPUnit\Util\VersionComparisonOperator((string) $fileNode->getAttribute('phpVersionOperator')); - } - $files[] = new \PHPUnit\TextUI\XmlConfiguration\TestFile($this->toAbsolutePath($filename, $file), $phpVersion, $phpVersionOperator); - } - $testSuites[] = new \PHPUnit\TextUI\XmlConfiguration\TestSuite((string) $element->getAttribute('name'), \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection::fromArray($directories), \PHPUnit\TextUI\XmlConfiguration\TestFileCollection::fromArray($files), \PHPUnit\TextUI\XmlConfiguration\FileCollection::fromArray($exclude)); + return sprintf(' ' . "\n", $pathToRoot, $node->name()); + } + protected function pathToRoot(AbstractNode $node) : string + { + $id = $node->id(); + $depth = substr_count($id, '/'); + if ($id !== 'index' && $node instanceof DirectoryNode) { + $depth++; } - return \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection::fromArray($testSuites); + return str_repeat('../', $depth); } - /** - * @return DOMElement[] - */ - private function getTestSuiteElements(\DOMXPath $xpath) : array + protected function coverageBar(float $percent) : string { - /** @var DOMElement[] $elements */ - $elements = []; - $testSuiteNodes = $xpath->query('testsuites/testsuite'); - if ($testSuiteNodes->length === 0) { - $testSuiteNodes = $xpath->query('testsuite'); + $level = $this->colorLevel($percent); + $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'coverage_bar_branch.html' : 'coverage_bar.html'); + $template = new Template($templateName, '{{', '}}'); + $template->setVar(['level' => $level, 'percent' => sprintf('%.2F', $percent)]); + return $template->render(); + } + protected function colorLevel(float $percent) : string + { + if ($percent <= $this->lowUpperBound) { + return 'danger'; } - if ($testSuiteNodes->length === 1) { - $element = $testSuiteNodes->item(0); - \assert($element instanceof \DOMElement); - $elements[] = $element; - } else { - foreach ($testSuiteNodes as $testSuiteNode) { - \assert($testSuiteNode instanceof \DOMElement); - $elements[] = $testSuiteNode; - } + if ($percent > $this->lowUpperBound && $percent < $this->highLowerBound) { + return 'warning'; } - return $elements; + return 'success'; } - private function element(\DOMXPath $xpath, string $element) : ?\DOMElement + private function runtimeString() : string { - $nodes = $xpath->query($element); - if ($nodes->length === 1) { - $node = $nodes->item(0); - \assert($node instanceof \DOMElement); - return $node; + $runtime = new Runtime(); + return sprintf('%s %s', $runtime->getVendorUrl(), $runtime->getName(), $runtime->getVersion()); + } +} + + + + + Code Coverage for {{full_path}} + + + + + + + +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      + + + + + + + + + + + + + + +{{items}} + +
       
      Code Coverage
       
      Classes and Traits
      Functions and Methods
      Lines
      +
      +{{lines}} +{{structure}} + +
      + + + + + + +
      +
      + {{percent}}% covered ({{level}}) +
      +
      + + + + + Dashboard for {{full_path}} + + + + + + + +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +
      +
      +

      Coverage Distribution

      +
      + +
      +
      +
      +

      Complexity

      +
      + +
      +
      +
      +
      +
      +

      Insufficient Coverage

      +
      + + + + + + + + +{{insufficient_coverage_classes}} + +
      ClassCoverage
      +
      +
      +
      +

      Project Risks

      +
      + + + + + + + + +{{project_risks_classes}} + +
      ClassCRAP
      +
      +
      +
      +
      +
      +

      Methods

      +
      +
      +
      +
      +

      Coverage Distribution

      +
      + +
      +
      +
      +

      Complexity

      +
      + +
      +
      +
      +
      +
      +

      Insufficient Coverage

      +
      + + + + + + + + +{{insufficient_coverage_methods}} + +
      MethodCoverage
      +
      +
      +
      +

      Project Risks

      +
      + + + + + + + + +{{project_risks_methods}} + +
      MethodCRAP
      +
      +
      +
      + +
      + + + + + + +
      +

      Paths

      +

      + Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not + necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once. + Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement + always has an else as part of its logical flow even if you didn't write one. +

      +{{paths}} + + + + + Code Coverage for {{full_path}} + + + + + + + +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      + + + + + + + + + + + + + + +{{items}} + +
       
      Code Coverage
       
      Lines
      Functions and Methods
      Classes and Traits
      +
      +
      +
      +

      Legend

      +

      + Low: 0% to {{low_upper_bound}}% + Medium: {{low_upper_bound}}% to {{high_lower_bound}}% + High: {{high_lower_bound}}% to 100% +

      +

      + Generated by php-code-coverage {{version}} using {{runtime}}{{generator}} at {{date}}. +

      +
      +
      + + +/* nvd3 version 1.8.1 (https://github.com/novus/nvd3) 2015-06-15 */ +!function(){var a={};a.dev=!1,a.tooltip=a.tooltip||{},a.utils=a.utils||{},a.models=a.models||{},a.charts={},a.logs={},a.dom={},a.dispatch=d3.dispatch("render_start","render_end"),Function.prototype.bind||(Function.prototype.bind=function(a){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var b=Array.prototype.slice.call(arguments,1),c=this,d=function(){},e=function(){return c.apply(this instanceof d&&a?this:a,b.concat(Array.prototype.slice.call(arguments)))};return d.prototype=this.prototype,e.prototype=new d,e}),a.dev&&(a.dispatch.on("render_start",function(){a.logs.startTime=+new Date}),a.dispatch.on("render_end",function(){a.logs.endTime=+new Date,a.logs.totalTime=a.logs.endTime-a.logs.startTime,a.log("total",a.logs.totalTime)})),a.log=function(){if(a.dev&&window.console&&console.log&&console.log.apply)console.log.apply(console,arguments);else if(a.dev&&window.console&&"function"==typeof console.log&&Function.prototype.bind){var b=Function.prototype.bind.call(console.log,console);b.apply(console,arguments)}return arguments[arguments.length-1]},a.deprecated=function(a,b){console&&console.warn&&console.warn("nvd3 warning: `"+a+"` has been deprecated. ",b||"")},a.render=function(b){b=b||1,a.render.active=!0,a.dispatch.render_start();var c=function(){for(var d,e,f=0;b>f&&(e=a.render.queue[f]);f++)d=e.generate(),typeof e.callback==typeof Function&&e.callback(d);a.render.queue.splice(0,f),a.render.queue.length?setTimeout(c):(a.dispatch.render_end(),a.render.active=!1)};setTimeout(c)},a.render.active=!1,a.render.queue=[],a.addGraph=function(b){typeof arguments[0]==typeof Function&&(b={generate:arguments[0],callback:arguments[1]}),a.render.queue.push(b),a.render.active||a.render()},"undefined"!=typeof module&&"undefined"!=typeof exports&&(module.exports=a),"undefined"!=typeof window&&(window.nv=a),a.dom.write=function(a){return void 0!==window.fastdom?fastdom.write(a):a()},a.dom.read=function(a){return void 0!==window.fastdom?fastdom.read(a):a()},a.interactiveGuideline=function(){"use strict";function b(l){l.each(function(l){function m(){var a=d3.mouse(this),d=a[0],e=a[1],i=!0,j=!1;if(k&&(d=d3.event.offsetX,e=d3.event.offsetY,"svg"!==d3.event.target.tagName&&(i=!1),d3.event.target.className.baseVal.match("nv-legend")&&(j=!0)),i&&(d-=f.left,e-=f.top),0>d||0>e||d>o||e>p||d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement||j){if(k&&d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement&&(void 0===d3.event.relatedTarget.className||d3.event.relatedTarget.className.match(c.nvPointerEventsClass)))return;return h.elementMouseout({mouseX:d,mouseY:e}),b.renderGuideLine(null),void c.hidden(!0)}c.hidden(!1);var l=g.invert(d);h.elementMousemove({mouseX:d,mouseY:e,pointXValue:l}),"dblclick"===d3.event.type&&h.elementDblclick({mouseX:d,mouseY:e,pointXValue:l}),"click"===d3.event.type&&h.elementClick({mouseX:d,mouseY:e,pointXValue:l})}var n=d3.select(this),o=d||960,p=e||400,q=n.selectAll("g.nv-wrap.nv-interactiveLineLayer").data([l]),r=q.enter().append("g").attr("class"," nv-wrap nv-interactiveLineLayer");r.append("g").attr("class","nv-interactiveGuideLine"),j&&(j.on("touchmove",m).on("mousemove",m,!0).on("mouseout",m,!0).on("dblclick",m).on("click",m),b.guideLine=null,b.renderGuideLine=function(c){i&&(b.guideLine&&b.guideLine.attr("x1")===c||a.dom.write(function(){var b=q.select(".nv-interactiveGuideLine").selectAll("line").data(null!=c?[a.utils.NaNtoZero(c)]:[],String);b.enter().append("line").attr("class","nv-guideline").attr("x1",function(a){return a}).attr("x2",function(a){return a}).attr("y1",p).attr("y2",0),b.exit().remove()}))})})}var c=a.models.tooltip();c.duration(0).hideDelay(0)._isInteractiveLayer(!0).hidden(!1);var d=null,e=null,f={left:0,top:0},g=d3.scale.linear(),h=d3.dispatch("elementMousemove","elementMouseout","elementClick","elementDblclick"),i=!0,j=null,k="ActiveXObject"in window;return b.dispatch=h,b.tooltip=c,b.margin=function(a){return arguments.length?(f.top="undefined"!=typeof a.top?a.top:f.top,f.left="undefined"!=typeof a.left?a.left:f.left,b):f},b.width=function(a){return arguments.length?(d=a,b):d},b.height=function(a){return arguments.length?(e=a,b):e},b.xScale=function(a){return arguments.length?(g=a,b):g},b.showGuideLine=function(a){return arguments.length?(i=a,b):i},b.svgContainer=function(a){return arguments.length?(j=a,b):j},b},a.interactiveBisect=function(a,b,c){"use strict";if(!(a instanceof Array))return null;var d;d="function"!=typeof c?function(a){return a.x}:c;var e=function(a,b){return d(a)-b},f=d3.bisector(e).left,g=d3.max([0,f(a,b)-1]),h=d(a[g]);if("undefined"==typeof h&&(h=g),h===b)return g;var i=d3.min([g+1,a.length-1]),j=d(a[i]);return"undefined"==typeof j&&(j=i),Math.abs(j-b)>=Math.abs(h-b)?g:i},a.nearestValueIndex=function(a,b,c){"use strict";var d=1/0,e=null;return a.forEach(function(a,f){var g=Math.abs(b-a);null!=a&&d>=g&&c>g&&(d=g,e=f)}),e},function(){"use strict";a.models.tooltip=function(){function b(){if(k){var a=d3.select(k);"svg"!==a.node().tagName&&(a=a.select("svg"));var b=a.node()?a.attr("viewBox"):null;if(b){b=b.split(" ");var c=parseInt(a.style("width"),10)/b[2];p.left=p.left*c,p.top=p.top*c}}}function c(){if(!n){var a;a=k?k:document.body,n=d3.select(a).append("div").attr("class","nvtooltip "+(j?j:"xy-tooltip")).attr("id",v),n.style("top",0).style("left",0),n.style("opacity",0),n.selectAll("div, table, td, tr").classed(w,!0),n.classed(w,!0),o=n.node()}}function d(){if(r&&B(e)){b();var f=p.left,g=null!==i?i:p.top;return a.dom.write(function(){c();var b=A(e);b&&(o.innerHTML=b),k&&u?a.dom.read(function(){var a=k.getElementsByTagName("svg")[0],b={left:0,top:0};if(a){var c=a.getBoundingClientRect(),d=k.getBoundingClientRect(),e=c.top;if(0>e){var i=k.getBoundingClientRect();e=Math.abs(e)>i.height?0:e}b.top=Math.abs(e-d.top),b.left=Math.abs(c.left-d.left)}f+=k.offsetLeft+b.left-2*k.scrollLeft,g+=k.offsetTop+b.top-2*k.scrollTop,h&&h>0&&(g=Math.floor(g/h)*h),C([f,g])}):C([f,g])}),d}}var e=null,f="w",g=25,h=0,i=null,j=null,k=null,l=!0,m=400,n=null,o=null,p={left:null,top:null},q={left:0,top:0},r=!0,s=100,t=!0,u=!1,v="nvtooltip-"+Math.floor(1e5*Math.random()),w="nv-pointer-events-none",x=function(a){return a},y=function(a){return a},z=function(a){return a},A=function(a){if(null===a)return"";var b=d3.select(document.createElement("table"));if(t){var c=b.selectAll("thead").data([a]).enter().append("thead");c.append("tr").append("td").attr("colspan",3).append("strong").classed("x-value",!0).html(y(a.value))}var d=b.selectAll("tbody").data([a]).enter().append("tbody"),e=d.selectAll("tr").data(function(a){return a.series}).enter().append("tr").classed("highlight",function(a){return a.highlight});e.append("td").classed("legend-color-guide",!0).append("div").style("background-color",function(a){return a.color}),e.append("td").classed("key",!0).html(function(a,b){return z(a.key,b)}),e.append("td").classed("value",!0).html(function(a,b){return x(a.value,b)}),e.selectAll("td").each(function(a){if(a.highlight){var b=d3.scale.linear().domain([0,1]).range(["#fff",a.color]),c=.6;d3.select(this).style("border-bottom-color",b(c)).style("border-top-color",b(c))}});var f=b.node().outerHTML;return void 0!==a.footer&&(f+=""),f},B=function(a){if(a&&a.series){if(a.series instanceof Array)return!!a.series.length;if(a.series instanceof Object)return a.series=[a.series],!0}return!1},C=function(b){o&&a.dom.read(function(){var c,d,e=parseInt(o.offsetHeight,10),h=parseInt(o.offsetWidth,10),i=a.utils.windowSize().width,j=a.utils.windowSize().height,k=window.pageYOffset,p=window.pageXOffset;j=window.innerWidth>=document.body.scrollWidth?j:j-16,i=window.innerHeight>=document.body.scrollHeight?i:i-16;var r,t,u=function(a){var b=d;do isNaN(a.offsetTop)||(b+=a.offsetTop),a=a.offsetParent;while(a);return b},v=function(a){var b=c;do isNaN(a.offsetLeft)||(b+=a.offsetLeft),a=a.offsetParent;while(a);return b};switch(f){case"e":c=b[0]-h-g,d=b[1]-e/2,r=v(o),t=u(o),p>r&&(c=b[0]+g>p?b[0]+g:p-r+c),k>t&&(d=k-t+d),t+e>k+j&&(d=k+j-t+d-e);break;case"w":c=b[0]+g,d=b[1]-e/2,r=v(o),t=u(o),r+h>i&&(c=b[0]-h-g),k>t&&(d=k+5),t+e>k+j&&(d=k+j-t+d-e);break;case"n":c=b[0]-h/2-5,d=b[1]+g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),t+e>k+j&&(d=k+j-t+d-e);break;case"s":c=b[0]-h/2,d=b[1]-e-g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),k>t&&(d=k);break;case"none":c=b[0],d=b[1]-g,r=v(o),t=u(o)}c-=q.left,d-=q.top;var w=o.getBoundingClientRect(),k=window.pageYOffset||document.documentElement.scrollTop,p=window.pageXOffset||document.documentElement.scrollLeft,x="translate("+(w.left+p)+"px, "+(w.top+k)+"px)",y="translate("+c+"px, "+d+"px)",z=d3.interpolateString(x,y),A=n.style("opacity")<.1;l?n.transition().delay(m).duration(0).style("opacity",0):n.interrupt().transition().duration(A?0:s).styleTween("transform",function(){return z},"important").style("-webkit-transform",y).style("opacity",1)})};return d.nvPointerEventsClass=w,d.options=a.utils.optionsFunc.bind(d),d._options=Object.create({},{duration:{get:function(){return s},set:function(a){s=a}},gravity:{get:function(){return f},set:function(a){f=a}},distance:{get:function(){return g},set:function(a){g=a}},snapDistance:{get:function(){return h},set:function(a){h=a}},classes:{get:function(){return j},set:function(a){j=a}},chartContainer:{get:function(){return k},set:function(a){k=a}},fixedTop:{get:function(){return i},set:function(a){i=a}},enabled:{get:function(){return r},set:function(a){r=a}},hideDelay:{get:function(){return m},set:function(a){m=a}},contentGenerator:{get:function(){return A},set:function(a){A=a}},valueFormatter:{get:function(){return x},set:function(a){x=a}},headerFormatter:{get:function(){return y},set:function(a){y=a}},keyFormatter:{get:function(){return z},set:function(a){z=a}},headerEnabled:{get:function(){return t},set:function(a){t=a}},_isInteractiveLayer:{get:function(){return u},set:function(a){u=!!a}},position:{get:function(){return p},set:function(a){p.left=void 0!==a.left?a.left:p.left,p.top=void 0!==a.top?a.top:p.top}},offset:{get:function(){return q},set:function(a){q.left=void 0!==a.left?a.left:q.left,q.top=void 0!==a.top?a.top:q.top}},hidden:{get:function(){return l},set:function(a){l!=a&&(l=!!a,d())}},data:{get:function(){return e},set:function(a){a.point&&(a.value=a.point.x,a.series=a.series||{},a.series.value=a.point.y,a.series.color=a.point.color||a.series.color),e=a}},tooltipElem:{get:function(){return o},set:function(){}},id:{get:function(){return v},set:function(){}}}),a.utils.initOptions(d),d}}(),a.utils.windowSize=function(){var a={width:640,height:480};return window.innerWidth&&window.innerHeight?(a.width=window.innerWidth,a.height=window.innerHeight,a):"CSS1Compat"==document.compatMode&&document.documentElement&&document.documentElement.offsetWidth?(a.width=document.documentElement.offsetWidth,a.height=document.documentElement.offsetHeight,a):document.body&&document.body.offsetWidth?(a.width=document.body.offsetWidth,a.height=document.body.offsetHeight,a):a},a.utils.windowResize=function(b){return window.addEventListener?window.addEventListener("resize",b):a.log("ERROR: Failed to bind to window.resize with: ",b),{callback:b,clear:function(){window.removeEventListener("resize",b)}}},a.utils.getColor=function(b){if(void 0===b)return a.utils.defaultColor();if(Array.isArray(b)){var c=d3.scale.ordinal().range(b);return function(a,b){var d=void 0===b?a:b;return a.color||c(d)}}return b},a.utils.defaultColor=function(){return a.utils.getColor(d3.scale.category20().range())},a.utils.customTheme=function(a,b,c){b=b||function(a){return a.key},c=c||d3.scale.category20().range();var d=c.length;return function(e){var f=b(e);return"function"==typeof a[f]?a[f]():void 0!==a[f]?a[f]:(d||(d=c.length),d-=1,c[d])}},a.utils.pjax=function(b,c){var d=function(d){d3.html(d,function(d){var e=d3.select(c).node();e.parentNode.replaceChild(d3.select(d).select(c).node(),e),a.utils.pjax(b,c)})};d3.selectAll(b).on("click",function(){history.pushState(this.href,this.textContent,this.href),d(this.href),d3.event.preventDefault()}),d3.select(window).on("popstate",function(){d3.event.state&&d(d3.event.state)})},a.utils.calcApproxTextWidth=function(a){if("function"==typeof a.style&&"function"==typeof a.text){var b=parseInt(a.style("font-size").replace("px",""),10),c=a.text().length;return c*b*.5}return 0},a.utils.NaNtoZero=function(a){return"number"!=typeof a||isNaN(a)||null===a||1/0===a||a===-1/0?0:a},d3.selection.prototype.watchTransition=function(a){var b=[this].concat([].slice.call(arguments,1));return a.transition.apply(a,b)},a.utils.renderWatch=function(b,c){if(!(this instanceof a.utils.renderWatch))return new a.utils.renderWatch(b,c);var d=void 0!==c?c:250,e=[],f=this;this.models=function(a){return a=[].slice.call(arguments,0),a.forEach(function(a){a.__rendered=!1,function(a){a.dispatch.on("renderEnd",function(){a.__rendered=!0,f.renderEnd("model")})}(a),e.indexOf(a)<0&&e.push(a)}),this},this.reset=function(a){void 0!==a&&(d=a),e=[]},this.transition=function(a,b,c){if(b=arguments.length>1?[].slice.call(arguments,1):[],c=b.length>1?b.pop():void 0!==d?d:250,a.__rendered=!1,e.indexOf(a)<0&&e.push(a),0===c)return a.__rendered=!0,a.delay=function(){return this},a.duration=function(){return this},a;a.__rendered=0===a.length?!0:a.every(function(a){return!a.length})?!0:!1;var g=0;return a.transition().duration(c).each(function(){++g}).each("end",function(){0===--g&&(a.__rendered=!0,f.renderEnd.apply(this,b))})},this.renderEnd=function(){e.every(function(a){return a.__rendered})&&(e.forEach(function(a){a.__rendered=!1}),b.renderEnd.apply(this,arguments))}},a.utils.deepExtend=function(b){var c=arguments.length>1?[].slice.call(arguments,1):[];c.forEach(function(c){for(var d in c){var e=b[d]instanceof Array,f="object"==typeof b[d],g="object"==typeof c[d];f&&!e&&g?a.utils.deepExtend(b[d],c[d]):b[d]=c[d]}})},a.utils.state=function(){if(!(this instanceof a.utils.state))return new a.utils.state;var b={},c=function(){},d=function(){return{}},e=null,f=null;this.dispatch=d3.dispatch("change","set"),this.dispatch.on("set",function(a){c(a,!0)}),this.getter=function(a){return d=a,this},this.setter=function(a,b){return b||(b=function(){}),c=function(c,d){a(c),d&&b()},this},this.init=function(b){e=e||{},a.utils.deepExtend(e,b)};var g=function(){var a=d();if(JSON.stringify(a)===JSON.stringify(b))return!1;for(var c in a)void 0===b[c]&&(b[c]={}),b[c]=a[c],f=!0;return!0};this.update=function(){e&&(c(e,!1),e=null),g.call(this)&&this.dispatch.change(b)}},a.utils.optionsFunc=function(a){return a&&d3.map(a).forEach(function(a,b){"function"==typeof this[a]&&this[a](b)}.bind(this)),this},a.utils.calcTicksX=function(b,c){var d=1,e=0;for(e;ed?f:d}return a.log("Requested number of ticks: ",b),a.log("Calculated max values to be: ",d),b=b>d?b=d-1:b,b=1>b?1:b,b=Math.floor(b),a.log("Calculating tick count as: ",b),b},a.utils.calcTicksY=function(b,c){return a.utils.calcTicksX(b,c)},a.utils.initOption=function(a,b){a._calls&&a._calls[b]?a[b]=a._calls[b]:(a[b]=function(c){return arguments.length?(a._overrides[b]=!0,a._options[b]=c,a):a._options[b]},a["_"+b]=function(c){return arguments.length?(a._overrides[b]||(a._options[b]=c),a):a._options[b]})},a.utils.initOptions=function(b){b._overrides=b._overrides||{};var c=Object.getOwnPropertyNames(b._options||{}),d=Object.getOwnPropertyNames(b._calls||{});c=c.concat(d);for(var e in c)a.utils.initOption(b,c[e])},a.utils.inheritOptionsD3=function(a,b,c){a._d3options=c.concat(a._d3options||[]),c.unshift(b),c.unshift(a),d3.rebind.apply(this,c)},a.utils.arrayUnique=function(a){return a.sort().filter(function(b,c){return!c||b!=a[c-1]})},a.utils.symbolMap=d3.map(),a.utils.symbol=function(){function b(b,e){var f=c.call(this,b,e),g=d.call(this,b,e);return-1!==d3.svg.symbolTypes.indexOf(f)?d3.svg.symbol().type(f).size(g)():a.utils.symbolMap.get(f)(g)}var c,d=64;return b.type=function(a){return arguments.length?(c=d3.functor(a),b):c},b.size=function(a){return arguments.length?(d=d3.functor(a),b):d},b},a.utils.inheritOptions=function(b,c){var d=Object.getOwnPropertyNames(c._options||{}),e=Object.getOwnPropertyNames(c._calls||{}),f=c._inherited||[],g=c._d3options||[],h=d.concat(e).concat(f).concat(g);h.unshift(c),h.unshift(b),d3.rebind.apply(this,h),b._inherited=a.utils.arrayUnique(d.concat(e).concat(f).concat(d).concat(b._inherited||[])),b._d3options=a.utils.arrayUnique(g.concat(b._d3options||[]))},a.utils.initSVG=function(a){a.classed({"nvd3-svg":!0})},a.utils.sanitizeHeight=function(a,b){return a||parseInt(b.style("height"),10)||400},a.utils.sanitizeWidth=function(a,b){return a||parseInt(b.style("width"),10)||960},a.utils.availableHeight=function(b,c,d){return a.utils.sanitizeHeight(b,c)-d.top-d.bottom},a.utils.availableWidth=function(b,c,d){return a.utils.sanitizeWidth(b,c)-d.left-d.right},a.utils.noData=function(b,c){var d=b.options(),e=d.margin(),f=d.noData(),g=null==f?["No Data Available."]:[f],h=a.utils.availableHeight(d.height(),c,e),i=a.utils.availableWidth(d.width(),c,e),j=e.left+i/2,k=e.top+h/2;c.selectAll("g").remove();var l=c.selectAll(".nv-noData").data(g);l.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),l.attr("x",j).attr("y",k).text(function(a){return a})},a.models.axis=function(){"use strict";function b(g){return s.reset(),g.each(function(b){var g=d3.select(this);a.utils.initSVG(g);var p=g.selectAll("g.nv-wrap.nv-axis").data([b]),q=p.enter().append("g").attr("class","nvd3 nv-wrap nv-axis"),t=(q.append("g"),p.select("g"));null!==n?c.ticks(n):("top"==c.orient()||"bottom"==c.orient())&&c.ticks(Math.abs(d.range()[1]-d.range()[0])/100),t.watchTransition(s,"axis").call(c),r=r||c.scale();var u=c.tickFormat();null==u&&(u=r.tickFormat());var v=t.selectAll("text.nv-axislabel").data([h||null]);v.exit().remove();var w,x,y;switch(c.orient()){case"top":v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",0).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b))+",0)"}).select("text").attr("dy","-0.5em").attr("y",-c.tickPadding()).attr("text-anchor","middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max top").attr("transform",function(b,c){return"translate("+a.utils.NaNtoZero(d.range()[c])+",0)"}));break;case"bottom":w=o+36;var z=30,A=0,B=t.selectAll("g").select("text"),C="";if(j%360){B.each(function(){var a=this.getBoundingClientRect(),b=a.width;A=a.height,b>z&&(z=b)}),C="rotate("+j+" 0,"+(A/2+c.tickPadding())+")";var D=Math.abs(Math.sin(j*Math.PI/180));w=(D?D*z:z)+30,B.attr("transform",C).style("text-anchor",j%360>0?"start":"end")}v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",w).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data([d.domain()[0],d.domain()[d.domain().length-1]]),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"}).select("text").attr("dy",".71em").attr("y",c.tickPadding()).attr("transform",C).style("text-anchor",j?j%360>0?"start":"end":"middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max bottom").attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"})),l&&B.attr("transform",function(a,b){return"translate(0,"+(b%2==0?"0":"12")+")"});break;case"right":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"begin").attr("transform",k?"rotate(90)":"").attr("y",k?-Math.max(e.right,f)+12:-10).attr("x",k?d3.max(d.range())/2:c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(d(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",c.tickPadding()).style("text-anchor","start").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1));break;case"left":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"end").attr("transform",k?"rotate(-90)":"").attr("y",k?-Math.max(e.left,f)+25-(o||0):-10).attr("x",k?-d3.max(d.range())/2:-c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(r(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",-c.tickPadding()).attr("text-anchor","end").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1))}if(v.text(function(a){return a}),!i||"left"!==c.orient()&&"right"!==c.orient()||(t.selectAll("g").each(function(a){d3.select(this).select("text").attr("opacity",1),(d(a)d.range()[0]-10)&&((a>1e-10||-1e-10>a)&&d3.select(this).attr("opacity",0),d3.select(this).select("text").attr("opacity",0))}),d.domain()[0]==d.domain()[1]&&0==d.domain()[0]&&p.selectAll("g.nv-axisMaxMin").style("opacity",function(a,b){return b?0:1})),i&&("top"===c.orient()||"bottom"===c.orient())){var E=[];p.selectAll("g.nv-axisMaxMin").each(function(a,b){try{E.push(b?d(a)-this.getBoundingClientRect().width-4:d(a)+this.getBoundingClientRect().width+4)}catch(c){E.push(b?d(a)-4:d(a)+4)}}),t.selectAll("g").each(function(a){(d(a)E[1])&&(a>1e-10||-1e-10>a?d3.select(this).remove():d3.select(this).select("text").remove())})}t.selectAll(".tick").filter(function(a){return!parseFloat(Math.round(1e5*a)/1e6)&&void 0!==a}).classed("zero",!0),r=d.copy()}),s.renderEnd("axis immediate"),b}var c=d3.svg.axis(),d=d3.scale.linear(),e={top:0,right:0,bottom:0,left:0},f=75,g=60,h=null,i=!0,j=0,k=!0,l=!1,m=!1,n=null,o=0,p=250,q=d3.dispatch("renderEnd");c.scale(d).orient("bottom").tickFormat(function(a){return a});var r,s=a.utils.renderWatch(q,p);return b.axis=c,b.dispatch=q,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{axisLabelDistance:{get:function(){return o},set:function(a){o=a}},staggerLabels:{get:function(){return l},set:function(a){l=a}},rotateLabels:{get:function(){return j},set:function(a){j=a}},rotateYLabel:{get:function(){return k},set:function(a){k=a}},showMaxMin:{get:function(){return i},set:function(a){i=a}},axisLabel:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return g},set:function(a){g=a}},ticks:{get:function(){return n},set:function(a){n=a}},width:{get:function(){return f},set:function(a){f=a}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},duration:{get:function(){return p},set:function(a){p=a,s.reset(p)}},scale:{get:function(){return d},set:function(e){d=e,c.scale(d),m="function"==typeof d.rangeBands,a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"])}}}),a.utils.initOptions(b),a.utils.inheritOptionsD3(b,c,["orient","tickValues","tickSubdivide","tickSize","tickPadding","tickFormat"]),a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"]),b},a.models.boxPlot=function(){"use strict";function b(l){return v.reset(),l.each(function(b){var l=j-i.left-i.right,p=k-i.top-i.bottom;r=d3.select(this),a.utils.initSVG(r),m.domain(c||b.map(function(a,b){return o(a,b)})).rangeBands(e||[0,l],.1);var w=[];if(!d){var x=d3.min(b.map(function(a){var b=[];return b.push(a.values.Q1),a.values.hasOwnProperty("whisker_low")&&null!==a.values.whisker_low&&b.push(a.values.whisker_low),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.min(b)})),y=d3.max(b.map(function(a){var b=[];return b.push(a.values.Q3),a.values.hasOwnProperty("whisker_high")&&null!==a.values.whisker_high&&b.push(a.values.whisker_high),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.max(b)}));w=[x,y]}n.domain(d||w),n.range(f||[p,0]),g=g||m,h=h||n.copy().range([n(0),n(0)]);{var z=r.selectAll("g.nv-wrap").data([b]);z.enter().append("g").attr("class","nvd3 nv-wrap")}z.attr("transform","translate("+i.left+","+i.top+")");var A=z.selectAll(".nv-boxplot").data(function(a){return a}),B=A.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);A.attr("class","nv-boxplot").attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}).classed("hover",function(a){return a.hover}),A.watchTransition(v,"nv-boxplot: boxplots").style("stroke-opacity",1).style("fill-opacity",.75).delay(function(a,c){return c*t/b.length}).attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}),A.exit().remove(),B.each(function(a,b){var c=d3.select(this);["low","high"].forEach(function(d){a.values.hasOwnProperty("whisker_"+d)&&null!==a.values["whisker_"+d]&&(c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-whisker nv-boxplot-"+d),c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-tick nv-boxplot-"+d))})});var C=A.selectAll(".nv-boxplot-outlier").data(function(a){return a.values.hasOwnProperty("outliers")&&null!==a.values.outliers?a.values.outliers:[]});C.enter().append("circle").style("fill",function(a,b,c){return q(a,c)}).style("stroke",function(a,b,c){return q(a,c)}).on("mouseover",function(a,b,c){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:a,color:q(a,c)},e:d3.event})}).on("mouseout",function(a,b,c){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:a,color:q(a,c)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),C.attr("class","nv-boxplot-outlier"),C.watchTransition(v,"nv-boxplot: nv-boxplot-outlier").attr("cx",.45*m.rangeBand()).attr("cy",function(a){return n(a)}).attr("r","3"),C.exit().remove();var D=function(){return null===u?.9*m.rangeBand():Math.min(75,.9*m.rangeBand())},E=function(){return.45*m.rangeBand()-D()/2},F=function(){return.45*m.rangeBand()+D()/2};["low","high"].forEach(function(a){var b="low"===a?"Q1":"Q3";A.select("line.nv-boxplot-whisker.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",.45*m.rangeBand()).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",.45*m.rangeBand()).attr("y2",function(a){return n(a.values[b])}),A.select("line.nv-boxplot-tick.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",E).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",F).attr("y2",function(b){return n(b.values["whisker_"+a])})}),["low","high"].forEach(function(a){B.selectAll(".nv-boxplot-"+a).on("mouseover",function(b,c,d){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mouseout",function(b,c,d){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})})}),B.append("rect").attr("class","nv-boxplot-box").on("mouseover",function(a,b){d3.select(this).classed("hover",!0),s.elementMouseover({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),s.elementMouseout({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),A.select("rect.nv-boxplot-box").watchTransition(v,"nv-boxplot: boxes").attr("y",function(a){return n(a.values.Q3)}).attr("width",D).attr("x",E).attr("height",function(a){return Math.abs(n(a.values.Q3)-n(a.values.Q1))||1}).style("fill",function(a,b){return a.color||q(a,b)}).style("stroke",function(a,b){return a.color||q(a,b)}),B.append("line").attr("class","nv-boxplot-median"),A.select("line.nv-boxplot-median").watchTransition(v,"nv-boxplot: boxplots line").attr("x1",E).attr("y1",function(a){return n(a.values.Q2)}).attr("x2",F).attr("y2",function(a){return n(a.values.Q2)}),g=m.copy(),h=n.copy()}),v.renderEnd("nv-boxplot immediate"),b}var c,d,e,f,g,h,i={top:0,right:0,bottom:0,left:0},j=960,k=500,l=Math.floor(1e4*Math.random()),m=d3.scale.ordinal(),n=d3.scale.linear(),o=function(a){return a.x},p=function(a){return a.y},q=a.utils.defaultColor(),r=null,s=d3.dispatch("elementMouseover","elementMouseout","elementMousemove","renderEnd"),t=250,u=null,v=a.utils.renderWatch(s,t);return b.dispatch=s,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},maxBoxWidth:{get:function(){return u},set:function(a){u=a}},x:{get:function(){return o},set:function(a){o=a}},y:{get:function(){return p},set:function(a){p=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return l},set:function(a){l=a}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}},duration:{get:function(){return t},set:function(a){t=a,v.reset(t)}}}),a.utils.initOptions(b),b},a.models.boxPlotChart=function(){"use strict";function b(k){return t.reset(),t.models(e),l&&t.models(f),m&&t.models(g),k.each(function(k){var p=d3.select(this);a.utils.initSVG(p);var t=(i||parseInt(p.style("width"))||960)-h.left-h.right,u=(j||parseInt(p.style("height"))||400)-h.top-h.bottom;if(b.update=function(){r.beforeUpdate(),p.transition().duration(s).call(b)},b.container=this,!(k&&k.length&&k.filter(function(a){return a.values.hasOwnProperty("Q1")&&a.values.hasOwnProperty("Q2")&&a.values.hasOwnProperty("Q3")}).length)){var v=p.selectAll(".nv-noData").data([q]);return v.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),v.attr("x",h.left+t/2).attr("y",h.top+u/2).text(function(a){return a}),b}p.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var w=p.selectAll("g.nv-wrap.nv-boxPlotWithAxes").data([k]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-boxPlotWithAxes").append("g"),y=x.append("defs"),z=w.select("g"); +x.append("g").attr("class","nv-x nv-axis"),x.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),x.append("g").attr("class","nv-barsWrap"),z.attr("transform","translate("+h.left+","+h.top+")"),n&&z.select(".nv-y.nv-axis").attr("transform","translate("+t+",0)"),e.width(t).height(u);var A=z.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));if(A.transition().call(e),y.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),z.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(o?2:1)).attr("height",16).attr("x",-c.rangeBand()/(o?1:2)),l){f.scale(c).ticks(a.utils.calcTicksX(t/100,k)).tickSize(-u,0),z.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),z.select(".nv-x.nv-axis").call(f);var B=z.select(".nv-x.nv-axis").selectAll("g");o&&B.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}m&&(g.scale(d).ticks(Math.floor(u/36)).tickSize(-t,0),z.select(".nv-y.nv-axis").call(g)),z.select(".nv-zeroLine line").attr("x1",0).attr("x2",t).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("nv-boxplot chart immediate"),b}var c,d,e=a.models.boxPlot(),f=a.models.axis(),g=a.models.axis(),h={top:15,right:10,bottom:50,left:60},i=null,j=null,k=a.utils.getColor(),l=!0,m=!0,n=!1,o=!1,p=a.models.tooltip(),q="No Data Available.",r=d3.dispatch("tooltipShow","tooltipHide","beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(n?"right":"left").tickFormat(d3.format(",.1f")),p.duration(0);var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){p.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(a){p.data(a).hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){p.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.boxplot=e,b.xAxis=f,b.yAxis=g,b.tooltip=p,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},staggerLabels:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return l},set:function(a){l=a}},showYAxis:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return tooltips},set:function(a){tooltips=a}},tooltipContent:{get:function(){return p},set:function(a){p=a}},noData:{get:function(){return q},set:function(a){q=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!==a.top?a.top:h.top,h.right=void 0!==a.right?a.right:h.right,h.bottom=void 0!==a.bottom?a.bottom:h.bottom,h.left=void 0!==a.left?a.left:h.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}},rightAlignYAxis:{get:function(){return n},set:function(a){n=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.bullet=function(){"use strict";function b(d){return d.each(function(b,d){var p=m-c.left-c.right,s=n-c.top-c.bottom;o=d3.select(this),a.utils.initSVG(o);{var t=f.call(this,b,d).slice().sort(d3.descending),u=g.call(this,b,d).slice().sort(d3.descending),v=h.call(this,b,d).slice().sort(d3.descending),w=i.call(this,b,d).slice(),x=j.call(this,b,d).slice(),y=k.call(this,b,d).slice(),z=d3.scale.linear().domain(d3.extent(d3.merge([l,t]))).range(e?[p,0]:[0,p]);this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range())}this.__chart__=z;var A=d3.min(t),B=d3.max(t),C=t[1],D=o.selectAll("g.nv-wrap.nv-bullet").data([b]),E=D.enter().append("g").attr("class","nvd3 nv-wrap nv-bullet"),F=E.append("g"),G=D.select("g");F.append("rect").attr("class","nv-range nv-rangeMax"),F.append("rect").attr("class","nv-range nv-rangeAvg"),F.append("rect").attr("class","nv-range nv-rangeMin"),F.append("rect").attr("class","nv-measure"),D.attr("transform","translate("+c.left+","+c.top+")");var H=function(a){return Math.abs(z(a)-z(0))},I=function(a){return z(0>a?a:0)};G.select("rect.nv-rangeMax").attr("height",s).attr("width",H(B>0?B:A)).attr("x",I(B>0?B:A)).datum(B>0?B:A),G.select("rect.nv-rangeAvg").attr("height",s).attr("width",H(C)).attr("x",I(C)).datum(C),G.select("rect.nv-rangeMin").attr("height",s).attr("width",H(B)).attr("x",I(B)).attr("width",H(B>0?A:B)).attr("x",I(B>0?A:B)).datum(B>0?A:B),G.select("rect.nv-measure").style("fill",q).attr("height",s/3).attr("y",s/3).attr("width",0>v?z(0)-z(v[0]):z(v[0])-z(0)).attr("x",I(v)).on("mouseover",function(){r.elementMouseover({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mouseout",function(){r.elementMouseout({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})});var J=s/6,K=u.map(function(a,b){return{value:a,label:x[b]}});F.selectAll("path.nv-markerTriangle").data(K).enter().append("path").attr("class","nv-markerTriangle").attr("transform",function(a){return"translate("+z(a.value)+","+s/2+")"}).attr("d","M0,"+J+"L"+J+","+-J+" "+-J+","+-J+"Z").on("mouseover",function(a){r.elementMouseover({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill"),pos:[z(a.value),s/2]})}).on("mousemove",function(a){r.elementMousemove({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a){r.elementMouseout({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}),D.selectAll(".nv-range").on("mouseover",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseover({value:a,label:c,color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseout({value:a,label:c,color:d3.select(this).style("fill")})})}),b}var c={top:0,right:0,bottom:0,left:0},d="left",e=!1,f=function(a){return a.ranges},g=function(a){return a.markers?a.markers:[0]},h=function(a){return a.measures},i=function(a){return a.rangeLabels?a.rangeLabels:[]},j=function(a){return a.markerLabels?a.markerLabels:[]},k=function(a){return a.measureLabels?a.measureLabels:[]},l=[0],m=380,n=30,o=null,p=null,q=a.utils.getColor(["#1f77b4"]),r=d3.dispatch("elementMouseover","elementMouseout","elementMousemove");return b.dispatch=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return f},set:function(a){f=a}},markers:{get:function(){return g},set:function(a){g=a}},measures:{get:function(){return h},set:function(a){h=a}},forceX:{get:function(){return l},set:function(a){l=a}},width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},tickFormat:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},orient:{get:function(){return d},set:function(a){d=a,e="right"==d||"bottom"==d}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.bulletChart=function(){"use strict";function b(d){return d.each(function(e,o){var p=d3.select(this);a.utils.initSVG(p);var q=a.utils.availableWidth(k,p,g),r=l-g.top-g.bottom;if(b.update=function(){b(d)},b.container=this,!e||!h.call(this,e,o))return a.utils.noData(b,p),b;p.selectAll(".nv-noData").remove();var s=h.call(this,e,o).slice().sort(d3.descending),t=i.call(this,e,o).slice().sort(d3.descending),u=j.call(this,e,o).slice().sort(d3.descending),v=p.selectAll("g.nv-wrap.nv-bulletChart").data([e]),w=v.enter().append("g").attr("class","nvd3 nv-wrap nv-bulletChart"),x=w.append("g"),y=v.select("g");x.append("g").attr("class","nv-bulletWrap"),x.append("g").attr("class","nv-titles"),v.attr("transform","translate("+g.left+","+g.top+")");var z=d3.scale.linear().domain([0,Math.max(s[0],t[0],u[0])]).range(f?[q,0]:[0,q]),A=this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range());this.__chart__=z;var B=x.select(".nv-titles").append("g").attr("text-anchor","end").attr("transform","translate(-6,"+(l-g.top-g.bottom)/2+")");B.append("text").attr("class","nv-title").text(function(a){return a.title}),B.append("text").attr("class","nv-subtitle").attr("dy","1em").text(function(a){return a.subtitle}),c.width(q).height(r);var C=y.select(".nv-bulletWrap");d3.transition(C).call(c);var D=m||z.tickFormat(q/100),E=y.selectAll("g.nv-tick").data(z.ticks(n?n:q/50),function(a){return this.textContent||D(a)}),F=E.enter().append("g").attr("class","nv-tick").attr("transform",function(a){return"translate("+A(a)+",0)"}).style("opacity",1e-6);F.append("line").attr("y1",r).attr("y2",7*r/6),F.append("text").attr("text-anchor","middle").attr("dy","1em").attr("y",7*r/6).text(D);var G=d3.transition(E).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1);G.select("line").attr("y1",r).attr("y2",7*r/6),G.select("text").attr("y",7*r/6),d3.transition(E.exit()).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1e-6).remove()}),d3.timer.flush(),b}var c=a.models.bullet(),d=a.models.tooltip(),e="left",f=!1,g={top:5,right:40,bottom:20,left:120},h=function(a){return a.ranges},i=function(a){return a.markers?a.markers:[0]},j=function(a){return a.measures},k=null,l=55,m=null,n=null,o=null,p=d3.dispatch("tooltipShow","tooltipHide");return d.duration(0).headerEnabled(!1),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.label,value:a.value,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.bullet=c,b.dispatch=p,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return h},set:function(a){h=a}},markers:{get:function(){return i},set:function(a){i=a}},measures:{get:function(){return j},set:function(a){j=a}},width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},tickFormat:{get:function(){return m},set:function(a){m=a}},ticks:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return o},set:function(a){o=a}},tooltips:{get:function(){return d.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),d.enabled(!!b)}},tooltipContent:{get:function(){return d.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),d.contentGenerator(b)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},orient:{get:function(){return e},set:function(a){e=a,f="right"==e||"bottom"==e}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.candlestickBar=function(){"use strict";function b(x){return x.each(function(b){c=d3.select(this);var x=a.utils.availableWidth(i,c,h),y=a.utils.availableHeight(j,c,h);a.utils.initSVG(c);var A=x/b[0].values.length*.45;l.domain(d||d3.extent(b[0].values.map(n).concat(t))),l.range(v?f||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:f||[5+A/2,x-A/2-5]),m.domain(e||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(g||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var B=d3.select(this).selectAll("g.nv-wrap.nv-candlestickBar").data([b[0].values]),C=B.enter().append("g").attr("class","nvd3 nv-wrap nv-candlestickBar"),D=C.append("defs"),E=C.append("g"),F=B.select("g");E.append("g").attr("class","nv-ticks"),B.attr("transform","translate("+h.left+","+h.top+")"),c.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:k})}),D.append("clipPath").attr("id","nv-chart-clip-path-"+k).append("rect"),B.select("#nv-chart-clip-path-"+k+" rect").attr("width",x).attr("height",y),F.attr("clip-path",w?"url(#nv-chart-clip-path-"+k+")":"");var G=B.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});G.exit().remove();{var H=G.enter().append("g").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b});H.append("line").attr("class","nv-candlestick-lines").attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),H.append("rect").attr("class","nv-candlestick-rects nv-bars").attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}c.selectAll(".nv-candlestick-lines").transition().attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),c.selectAll(".nv-candlestick-rects").transition().attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}),b}var c,d,e,f,g,h={top:0,right:0,bottom:0,left:0},i=null,j=null,k=Math.floor(1e4*Math.random()),l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,d){b.clearHighlights(),c.select(".nv-candlestickBar .nv-tick-0-"+a).classed("hover",d)},b.clearHighlights=function(){c.select(".nv-candlestickBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return k},set:function(a){k=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!=a.top?a.top:h.top,h.right=void 0!=a.right?a.right:h.right,h.bottom=void 0!=a.bottom?a.bottom:h.bottom,h.left=void 0!=a.left?a.left:h.left}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.cumulativeLineChart=function(){"use strict";function b(l){return H.reset(),H.models(f),r&&H.models(g),s&&H.models(h),l.each(function(l){function A(){d3.select(b.container).style("cursor","ew-resize")}function E(){G.x=d3.event.x,G.i=Math.round(F.invert(G.x)),K()}function H(){d3.select(b.container).style("cursor","auto"),y.index=G.i,C.stateChange(y)}function K(){bb.data([G]);var a=b.duration();b.duration(0),b.update(),b.duration(a)}var L=d3.select(this);a.utils.initSVG(L),L.classed("nv-chart-"+x,!0);var M=this,N=a.utils.availableWidth(o,L,m),O=a.utils.availableHeight(p,L,m);if(b.update=function(){0===D?L.call(b):L.transition().duration(D).call(b)},b.container=this,y.setter(J(l),b.update).getter(I(l)).update(),y.disabled=l.map(function(a){return!!a.disabled}),!z){var P;z={};for(P in y)z[P]=y[P]instanceof Array?y[P].slice(0):y[P]}var Q=d3.behavior.drag().on("dragstart",A).on("drag",E).on("dragend",H);if(!(l&&l.length&&l.filter(function(a){return a.values.length}).length))return a.utils.noData(b,L),b;if(L.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale(),w)f.yDomain(null);else{var R=l.filter(function(a){return!a.disabled}).map(function(a){var b=d3.extent(a.values,f.y());return b[0]<-.95&&(b[0]=-.95),[(b[0]-b[1])/(1+b[1]),(b[1]-b[0])/(1+b[0])]}),S=[d3.min(R,function(a){return a[0]}),d3.max(R,function(a){return a[1]})];f.yDomain(S)}F.domain([0,l[0].values.length-1]).range([0,N]).clamp(!0);var l=c(G.i,l),T=v?"none":"all",U=L.selectAll("g.nv-wrap.nv-cumulativeLine").data([l]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-cumulativeLine").append("g"),W=U.select("g");if(V.append("g").attr("class","nv-interactive"),V.append("g").attr("class","nv-x nv-axis").style("pointer-events","none"),V.append("g").attr("class","nv-y nv-axis"),V.append("g").attr("class","nv-background"),V.append("g").attr("class","nv-linesWrap").style("pointer-events",T),V.append("g").attr("class","nv-avgLinesWrap").style("pointer-events","none"),V.append("g").attr("class","nv-legendWrap"),V.append("g").attr("class","nv-controlsWrap"),q&&(i.width(N),W.select(".nv-legendWrap").datum(l).call(i),m.top!=i.height()&&(m.top=i.height(),O=a.utils.availableHeight(p,L,m)),W.select(".nv-legendWrap").attr("transform","translate(0,"+-m.top+")")),u){var X=[{key:"Re-scale y-axis",disabled:!w}];j.width(140).color(["#444","#444","#444"]).rightAlign(!1).margin({top:5,right:0,bottom:5,left:20}),W.select(".nv-controlsWrap").datum(X).attr("transform","translate(0,"+-m.top+")").call(j)}U.attr("transform","translate("+m.left+","+m.top+")"),t&&W.select(".nv-y.nv-axis").attr("transform","translate("+N+",0)");var Y=l.filter(function(a){return a.tempDisabled});U.select(".tempDisabled").remove(),Y.length&&U.append("text").attr("class","tempDisabled").attr("x",N/2).attr("y","-.71em").style("text-anchor","end").text(Y.map(function(a){return a.key}).join(", ")+" values cannot be calculated for this time period."),v&&(k.width(N).height(O).margin({left:m.left,top:m.top}).svgContainer(L).xScale(d),U.select(".nv-interactive").call(k)),V.select(".nv-background").append("rect"),W.select(".nv-background rect").attr("width",N).attr("height",O),f.y(function(a){return a.display.y}).width(N).height(O).color(l.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!l[b].disabled&&!l[b].tempDisabled}));var Z=W.select(".nv-linesWrap").datum(l.filter(function(a){return!a.disabled&&!a.tempDisabled}));Z.call(f),l.forEach(function(a,b){a.seriesIndex=b});var $=l.filter(function(a){return!a.disabled&&!!B(a)}),_=W.select(".nv-avgLinesWrap").selectAll("line").data($,function(a){return a.key}),ab=function(a){var b=e(B(a));return 0>b?0:b>O?O:b};_.enter().append("line").style("stroke-width",2).style("stroke-dasharray","10,10").style("stroke",function(a){return f.color()(a,a.seriesIndex)}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.style("stroke-opacity",function(a){var b=e(B(a));return 0>b||b>O?0:1}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.exit().remove();var bb=Z.selectAll(".nv-indexLine").data([G]);bb.enter().append("rect").attr("class","nv-indexLine").attr("width",3).attr("x",-2).attr("fill","red").attr("fill-opacity",.5).style("pointer-events","all").call(Q),bb.attr("transform",function(a){return"translate("+F(a.i)+",0)"}).attr("height",O),r&&(g.scale(d)._ticks(a.utils.calcTicksX(N/70,l)).tickSize(-O,0),W.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),W.select(".nv-x.nv-axis").call(g)),s&&(h.scale(e)._ticks(a.utils.calcTicksY(O/36,l)).tickSize(-N,0),W.select(".nv-y.nv-axis").call(h)),W.select(".nv-background rect").on("click",function(){G.x=d3.mouse(this)[0],G.i=Math.round(F.invert(G.x)),y.index=G.i,C.stateChange(y),K()}),f.dispatch.on("elementClick",function(a){G.i=a.pointIndex,G.x=F(G.i),y.index=G.i,C.stateChange(y),K()}),j.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,w=!a.disabled,y.rescaleY=w,C.stateChange(y),b.update()}),i.dispatch.on("stateChange",function(a){for(var c in a)y[c]=a[c];C.stateChange(y),b.update()}),k.dispatch.on("elementMousemove",function(c){f.clearHighlights();var d,e,i,j=[];if(l.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g,h){e=a.interactiveBisect(g.values,c.pointXValue,b.x()),f.highlightPoint(h,e,!0);var k=g.values[e];"undefined"!=typeof k&&("undefined"==typeof d&&(d=k),"undefined"==typeof i&&(i=b.xScale()(b.x()(k,e))),j.push({key:g.key,value:b.y()(k,e),color:n(g,g.seriesIndex)}))}),j.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(j.map(function(a){return a.value}),o,q);null!==r&&(j[r].highlight=!0)}var s=g.tickFormat()(b.x()(d,e),e);k.tooltip.position({left:i+m.left,top:c.mouseY+m.top}).chartContainer(M.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:s,series:j})(),k.renderGuideLine(i)}),k.dispatch.on("elementMouseout",function(){f.clearHighlights()}),C.on("changeState",function(a){"undefined"!=typeof a.disabled&&(l.forEach(function(b,c){b.disabled=a.disabled[c]}),y.disabled=a.disabled),"undefined"!=typeof a.index&&(G.i=a.index,G.x=F(G.i),y.index=a.index,bb.data([G])),"undefined"!=typeof a.rescaleY&&(w=a.rescaleY),b.update()})}),H.renderEnd("cumulativeLineChart immediate"),b}function c(a,b){return K||(K=f.y()),b.map(function(b){if(!b.values)return b;var c=b.values[a];if(null==c)return b;var d=K(c,a);return-.95>d&&!E?(b.tempDisabled=!0,b):(b.tempDisabled=!1,b.values=b.values.map(function(a,b){return a.display={y:(K(a,b)-d)/(1+d)},a}),b)})}var d,e,f=a.models.line(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.models.legend(),k=a.interactiveGuideline(),l=a.models.tooltip(),m={top:30,right:30,bottom:50,left:60},n=a.utils.defaultColor(),o=null,p=null,q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=!0,x=f.id(),y=a.utils.state(),z=null,A=null,B=function(a){return a.average},C=d3.dispatch("stateChange","changeState","renderEnd"),D=250,E=!1;y.index=0,y.rescaleY=w,g.orient("bottom").tickPadding(7),h.orient(t?"right":"left"),l.valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)}),j.updateState(!1);var F=d3.scale.linear(),G={i:0,x:0},H=a.utils.renderWatch(C,D),I=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),index:G.i,rescaleY:w}}},J=function(a){return function(b){void 0!==b.index&&(G.i=b.index),void 0!==b.rescaleY&&(w=b.rescaleY),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};f.dispatch.on("elementMouseover.tooltip",function(a){var c={x:b.x()(a.point),y:b.y()(a.point),color:a.point.color};a.point=c,l.data(a).position(a.pos).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){l.hidden(!0)});var K=null;return b.dispatch=C,b.lines=f,b.legend=i,b.controls=j,b.xAxis=g,b.yAxis=h,b.interactiveLayer=k,b.state=y,b.tooltip=l,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return o},set:function(a){o=a}},height:{get:function(){return p},set:function(a){p=a}},rescaleY:{get:function(){return w},set:function(a){w=a}},showControls:{get:function(){return u},set:function(a){u=a}},showLegend:{get:function(){return q},set:function(a){q=a}},average:{get:function(){return B},set:function(a){B=a}},defaultState:{get:function(){return z},set:function(a){z=a}},noData:{get:function(){return A},set:function(a){A=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},noErrorCheck:{get:function(){return E},set:function(a){E=a}},tooltips:{get:function(){return l.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),l.enabled(!!b)}},tooltipContent:{get:function(){return l.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),l.contentGenerator(b)}},margin:{get:function(){return m},set:function(a){m.top=void 0!==a.top?a.top:m.top,m.right=void 0!==a.right?a.right:m.right,m.bottom=void 0!==a.bottom?a.bottom:m.bottom,m.left=void 0!==a.left?a.left:m.left}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),i.color(n)}},useInteractiveGuideline:{get:function(){return v},set:function(a){v=a,a===!0&&(b.interactive(!1),b.useVoronoi(!1))}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,h.orient(a?"right":"left")}},duration:{get:function(){return D},set:function(a){D=a,f.duration(D),g.duration(D),h.duration(D),H.reset(D)}}}),a.utils.inheritOptions(b,f),a.utils.initOptions(b),b},a.models.discreteBar=function(){"use strict";function b(m){return y.reset(),m.each(function(b){var m=k-j.left-j.right,x=l-j.top-j.bottom;c=d3.select(this),a.utils.initSVG(c),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var z=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),y0:a.y0}})});n.domain(d||d3.merge(z).map(function(a){return a.x})).rangeBands(f||[0,m],.1),o.domain(e||d3.extent(d3.merge(z).map(function(a){return a.y}).concat(r))),o.range(t?g||[x-(o.domain()[0]<0?12:0),o.domain()[1]>0?12:0]:g||[x,0]),h=h||n,i=i||o.copy().range([o(0),o(0)]);{var A=c.selectAll("g.nv-wrap.nv-discretebar").data([b]),B=A.enter().append("g").attr("class","nvd3 nv-wrap nv-discretebar"),C=B.append("g");A.select("g")}C.append("g").attr("class","nv-groups"),A.attr("transform","translate("+j.left+","+j.top+")");var D=A.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});D.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),D.exit().watchTransition(y,"discreteBar: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),D.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),D.watchTransition(y,"discreteBar: groups").style("stroke-opacity",1).style("fill-opacity",.75);var E=D.selectAll("g.nv-bar").data(function(a){return a.values});E.exit().remove();var F=E.enter().append("g").attr("transform",function(a,b){return"translate("+(n(p(a,b))+.05*n.rangeBand())+", "+o(0)+")"}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),v.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),v.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){v.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){v.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()});F.append("rect").attr("height",0).attr("width",.9*n.rangeBand()/b.length),t?(F.append("text").attr("text-anchor","middle"),E.select("text").text(function(a,b){return u(q(a,b))}).watchTransition(y,"discreteBar: bars text").attr("x",.9*n.rangeBand()/2).attr("y",function(a,b){return q(a,b)<0?o(q(a,b))-o(0)+12:-4})):E.selectAll("text").remove(),E.attr("class",function(a,b){return q(a,b)<0?"nv-bar negative":"nv-bar positive"}).style("fill",function(a,b){return a.color||s(a,b)}).style("stroke",function(a,b){return a.color||s(a,b)}).select("rect").attr("class",w).watchTransition(y,"discreteBar: bars rect").attr("width",.9*n.rangeBand()/b.length),E.watchTransition(y,"discreteBar: bars").attr("transform",function(a,b){var c=n(p(a,b))+.05*n.rangeBand(),d=q(a,b)<0?o(0):o(0)-o(q(a,b))<1?o(0)-1:o(q(a,b));return"translate("+c+", "+d+")"}).select("rect").attr("height",function(a,b){return Math.max(Math.abs(o(q(a,b))-o(e&&e[0]||0))||1)}),h=n.copy(),i=o.copy()}),y.renderEnd("discreteBar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=d3.scale.ordinal(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=[0],s=a.utils.defaultColor(),t=!1,u=d3.format(",.2f"),v=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),w="discreteBar",x=250,y=a.utils.renderWatch(v,x);return b.dispatch=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},forceY:{get:function(){return r},set:function(a){r=a}},showValues:{get:function(){return t},set:function(a){t=a}},x:{get:function(){return p},set:function(a){p=a}},y:{get:function(){return q},set:function(a){q=a}},xScale:{get:function(){return n},set:function(a){n=a}},yScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},valueFormat:{get:function(){return u},set:function(a){u=a}},id:{get:function(){return m},set:function(a){m=a}},rectClass:{get:function(){return w},set:function(a){w=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b)}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x)}}}),a.utils.initOptions(b),b},a.models.discreteBarChart=function(){"use strict";function b(h){return t.reset(),t.models(e),m&&t.models(f),n&&t.models(g),h.each(function(h){var l=d3.select(this);a.utils.initSVG(l);var q=a.utils.availableWidth(j,l,i),t=a.utils.availableHeight(k,l,i);if(b.update=function(){r.beforeUpdate(),l.transition().duration(s).call(b)},b.container=this,!(h&&h.length&&h.filter(function(a){return a.values.length}).length))return a.utils.noData(b,l),b;l.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var u=l.selectAll("g.nv-wrap.nv-discreteBarWithAxes").data([h]),v=u.enter().append("g").attr("class","nvd3 nv-wrap nv-discreteBarWithAxes").append("g"),w=v.append("defs"),x=u.select("g");v.append("g").attr("class","nv-x nv-axis"),v.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),v.append("g").attr("class","nv-barsWrap"),x.attr("transform","translate("+i.left+","+i.top+")"),o&&x.select(".nv-y.nv-axis").attr("transform","translate("+q+",0)"),e.width(q).height(t);var y=x.select(".nv-barsWrap").datum(h.filter(function(a){return!a.disabled}));if(y.transition().call(e),w.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),x.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(p?2:1)).attr("height",16).attr("x",-c.rangeBand()/(p?1:2)),m){f.scale(c)._ticks(a.utils.calcTicksX(q/100,h)).tickSize(-t,0),x.select(".nv-x.nv-axis").attr("transform","translate(0,"+(d.range()[0]+(e.showValues()&&d.domain()[0]<0?16:0))+")"),x.select(".nv-x.nv-axis").call(f); +var z=x.select(".nv-x.nv-axis").selectAll("g");p&&z.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}n&&(g.scale(d)._ticks(a.utils.calcTicksY(t/36,h)).tickSize(-q,0),x.select(".nv-y.nv-axis").call(g)),x.select(".nv-zeroLine line").attr("x1",0).attr("x2",q).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("discreteBar chart immediate"),b}var c,d,e=a.models.discreteBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.tooltip(),i={top:15,right:10,bottom:50,left:60},j=null,k=null,l=a.utils.getColor(),m=!0,n=!0,o=!1,p=!1,q=null,r=d3.dispatch("beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(o?"right":"left").tickFormat(d3.format(",.1f")),h.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).keyFormatter(function(a,b){return f.tickFormat()(a,b)});var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},h.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){h.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){h.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.discretebar=e,b.xAxis=f,b.yAxis=g,b.tooltip=h,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},staggerLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return m},set:function(a){m=a}},showYAxis:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return q},set:function(a){q=a}},tooltips:{get:function(){return h.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),h.enabled(!!b)}},tooltipContent:{get:function(){return h.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),h.contentGenerator(b)}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),e.color(l)}},rightAlignYAxis:{get:function(){return o},set:function(a){o=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.distribution=function(){"use strict";function b(k){return m.reset(),k.each(function(b){var k=(e-("x"===g?d.left+d.right:d.top+d.bottom),"x"==g?"y":"x"),l=d3.select(this);a.utils.initSVG(l),c=c||j;var n=l.selectAll("g.nv-distribution").data([b]),o=n.enter().append("g").attr("class","nvd3 nv-distribution"),p=(o.append("g"),n.select("g"));n.attr("transform","translate("+d.left+","+d.top+")");var q=p.selectAll("g.nv-dist").data(function(a){return a},function(a){return a.key});q.enter().append("g"),q.attr("class",function(a,b){return"nv-dist nv-series-"+b}).style("stroke",function(a,b){return i(a,b)});var r=q.selectAll("line.nv-dist"+g).data(function(a){return a.values});r.enter().append("line").attr(g+"1",function(a,b){return c(h(a,b))}).attr(g+"2",function(a,b){return c(h(a,b))}),m.transition(q.exit().selectAll("line.nv-dist"+g),"dist exit").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}).style("stroke-opacity",0).remove(),r.attr("class",function(a,b){return"nv-dist"+g+" nv-dist"+g+"-"+b}).attr(k+"1",0).attr(k+"2",f),m.transition(r,"dist").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}),c=j.copy()}),m.renderEnd("distribution immediate"),b}var c,d={top:0,right:0,bottom:0,left:0},e=400,f=8,g="x",h=function(a){return a[g]},i=a.utils.defaultColor(),j=d3.scale.linear(),k=250,l=d3.dispatch("renderEnd"),m=a.utils.renderWatch(l,k);return b.options=a.utils.optionsFunc.bind(b),b.dispatch=l,b.margin=function(a){return arguments.length?(d.top="undefined"!=typeof a.top?a.top:d.top,d.right="undefined"!=typeof a.right?a.right:d.right,d.bottom="undefined"!=typeof a.bottom?a.bottom:d.bottom,d.left="undefined"!=typeof a.left?a.left:d.left,b):d},b.width=function(a){return arguments.length?(e=a,b):e},b.axis=function(a){return arguments.length?(g=a,b):g},b.size=function(a){return arguments.length?(f=a,b):f},b.getData=function(a){return arguments.length?(h=d3.functor(a),b):h},b.scale=function(a){return arguments.length?(j=a,b):j},b.color=function(c){return arguments.length?(i=a.utils.getColor(c),b):i},b.duration=function(a){return arguments.length?(k=a,m.reset(k),b):k},b},a.models.furiousLegend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?g(a,b):"#fff":m?void 0:a.disabled?g(a,b):"#fff"}function r(a,b){return m&&"furious"==o?a.disengaged?"#fff":g(a,b):a.disabled?"#fff":g(a,b)}return p.each(function(b){var p=d-c.left-c.right,s=d3.select(this);a.utils.initSVG(s);var t=s.selectAll("g.nv-legend").data([b]),u=(t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),t.select("g"));t.attr("transform","translate("+c.left+","+c.top+")");var v,w=u.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),x=w.enter().append("g").attr("class","nv-series");if("classic"==o)x.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),v=w.select("circle");else if("furious"==o){x.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),v=w.select("rect"),x.append("g").attr("class","nv-check-box").property("innerHTML",'').attr("transform","translate(-10,-8)scale(0.5)");var y=w.select(".nv-check-box");y.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}x.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var z=w.select("text.nv-legend-text");w.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=w.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=w.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),w.classed("nv-disabled",function(a){return a.userDisabled}),w.exit().remove(),z.attr("fill",q).text(f);var A;switch(o){case"furious":A=23;break;case"classic":A=20}if(h){var B=[];w.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}B.push(b+i)});for(var C=0,D=0,E=[];p>D&&Cp&&C>1;){E=[],C--;for(var F=0;F(E[F%C]||0)&&(E[F%C]=B[F]);D=E.reduce(function(a,b){return a+b})}for(var G=[],H=0,I=0;C>H;H++)G[H]=I,I+=E[H];w.attr("transform",function(a,b){return"translate("+G[b%C]+","+(5+Math.floor(b/C)*A)+")"}),j?u.attr("transform","translate("+(d-c.right-D)+","+c.top+")"):u.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(B.length/C)*A}else{var J,K=5,L=5,M=0;w.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return J=L,dM&&(M=L),"translate("+J+","+K+")"}),u.attr("transform","translate("+(d-c.right-M)+","+c.top+")"),e=c.top+c.bottom+K+15}"furious"==o&&v.attr("width",function(a,b){return z[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),v.style("fill",r).style("stroke",function(a,b){return a.color||g(a,b)})}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=28,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBar=function(){"use strict";function b(x){return x.each(function(b){w.reset(),k=d3.select(this);var x=a.utils.availableWidth(h,k,g),y=a.utils.availableHeight(i,k,g);a.utils.initSVG(k),l.domain(c||d3.extent(b[0].values.map(n).concat(p))),l.range(r?e||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:e||[0,x]),m.domain(d||d3.extent(b[0].values.map(o).concat(q))).range(f||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var z=k.selectAll("g.nv-wrap.nv-historicalBar-"+j).data([b[0].values]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBar-"+j),B=A.append("defs"),C=A.append("g"),D=z.select("g");C.append("g").attr("class","nv-bars"),z.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){u.chartClick({data:a,index:b,pos:d3.event,id:j})}),B.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),z.select("#nv-chart-clip-path-"+j+" rect").attr("width",x).attr("height",y),D.attr("clip-path",s?"url(#nv-chart-clip-path-"+j+")":"");var E=z.select(".nv-bars").selectAll(".nv-bar").data(function(a){return a},function(a,b){return n(a,b)});E.exit().remove(),E.enter().append("rect").attr("x",0).attr("y",function(b,c){return a.utils.NaNtoZero(m(Math.max(0,o(b,c))))}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.abs(m(o(b,c))-m(0)))}).attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).on("mouseover",function(a,b){v&&(d3.select(this).classed("hover",!0),u.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mouseout",function(a,b){v&&(d3.select(this).classed("hover",!1),u.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mousemove",function(a,b){v&&u.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v&&(u.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}).on("dblclick",function(a,b){v&&(u.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}),E.attr("fill",function(a,b){return t(a,b)}).attr("class",function(a,b,c){return(o(a,b)<0?"nv-bar negative":"nv-bar positive")+" nv-bar-"+c+"-"+b}).watchTransition(w,"bars").attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).attr("width",x/b[0].values.length*.9),E.watchTransition(w,"bars").attr("y",function(b,c){var d=o(b,c)<0?m(0):m(0)-m(o(b,c))<1?m(0)-1:m(o(b,c));return a.utils.NaNtoZero(d)}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.max(Math.abs(m(o(b,c))-m(0)),1))})}),w.renderEnd("historicalBar immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=[],q=[0],r=!1,s=!0,t=a.utils.defaultColor(),u=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),v=!0,w=a.utils.renderWatch(u,0);return b.highlightPoint=function(a,b){k.select(".nv-bars .nv-bar-0-"+a).classed("hover",b)},b.clearHighlights=function(){k.select(".nv-bars .nv-bar.hover").classed("hover",!1)},b.dispatch=u,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},forceX:{get:function(){return p},set:function(a){p=a}},forceY:{get:function(){return q},set:function(a){q=a}},padData:{get:function(){return r},set:function(a){r=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},clipEdge:{get:function(){return s},set:function(a){s=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return v},set:function(a){v=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return t},set:function(b){t=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBarChart=function(b){"use strict";function c(b){return b.each(function(k){z.reset(),z.models(f),q&&z.models(g),r&&z.models(h);var w=d3.select(this),A=this;a.utils.initSVG(w);var B=a.utils.availableWidth(n,w,l),C=a.utils.availableHeight(o,w,l);if(c.update=function(){w.transition().duration(y).call(c)},c.container=this,u.disabled=k.map(function(a){return!!a.disabled}),!v){var D;v={};for(D in u)v[D]=u[D]instanceof Array?u[D].slice(0):u[D]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(c,w),c;w.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale();var E=w.selectAll("g.nv-wrap.nv-historicalBarChart").data([k]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBarChart").append("g"),G=E.select("g");F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-barsWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),p&&(i.width(B),G.select(".nv-legendWrap").datum(k).call(i),l.top!=i.height()&&(l.top=i.height(),C=a.utils.availableHeight(o,w,l)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-l.top+")")),E.attr("transform","translate("+l.left+","+l.top+")"),s&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),t&&(j.width(B).height(C).margin({left:l.left,top:l.top}).svgContainer(w).xScale(d),E.select(".nv-interactive").call(j)),f.width(B).height(C).color(k.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!k[b].disabled}));var H=G.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));H.transition().call(f),q&&(g.scale(d)._ticks(a.utils.calcTicksX(B/100,k)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),G.select(".nv-x.nv-axis").transition().call(g)),r&&(h.scale(e)._ticks(a.utils.calcTicksY(C/36,k)).tickSize(-B,0),G.select(".nv-y.nv-axis").transition().call(h)),j.dispatch.on("elementMousemove",function(b){f.clearHighlights();var d,e,i,n=[];k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g){e=a.interactiveBisect(g.values,b.pointXValue,c.x()),f.highlightPoint(e,!0);var h=g.values[e];void 0!==h&&(void 0===d&&(d=h),void 0===i&&(i=c.xScale()(c.x()(h,e))),n.push({key:g.key,value:c.y()(h,e),color:m(g,g.seriesIndex),data:g.values[e]}))});var o=g.tickFormat()(c.x()(d,e));j.tooltip.position({left:i+l.left,top:b.mouseY+l.top}).chartContainer(A.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:o,index:e,series:n})(),j.renderGuideLine(i)}),j.dispatch.on("elementMouseout",function(){x.tooltipHide(),f.clearHighlights()}),i.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,k.filter(function(a){return!a.disabled}).length||k.map(function(a){return a.disabled=!1,E.selectAll(".nv-series").classed("disabled",!1),a}),u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),b.transition().call(c)}),i.dispatch.on("legendDblclick",function(a){k.forEach(function(a){a.disabled=!0}),a.disabled=!1,u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),c.update()}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),c.update()})}),z.renderEnd("historicalBarChart immediate"),c}var d,e,f=b||a.models.historicalBar(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:90,bottom:50,left:90},m=a.utils.defaultColor(),n=null,o=null,p=!1,q=!0,r=!0,s=!1,t=!1,u={},v=null,w=null,x=d3.dispatch("tooltipHide","stateChange","changeState","renderEnd"),y=250;g.orient("bottom").tickPadding(7),h.orient(s?"right":"left"),k.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)});var z=a.utils.renderWatch(x,0);return f.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:c.x()(a.data),value:c.y()(a.data),color:a.color},k.data(a).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),f.dispatch.on("elementMousemove.tooltip",function(){k.position({top:d3.event.pageY,left:d3.event.pageX})()}),c.dispatch=x,c.bars=f,c.legend=i,c.xAxis=g,c.yAxis=h,c.interactiveLayer=j,c.tooltip=k,c.options=a.utils.optionsFunc.bind(c),c._options=Object.create({},{width:{get:function(){return n},set:function(a){n=a}},height:{get:function(){return o},set:function(a){o=a}},showLegend:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return q},set:function(a){q=a}},showYAxis:{get:function(){return r},set:function(a){r=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b),i.color(m),f.color(m)}},duration:{get:function(){return y},set:function(a){y=a,z.reset(y),h.duration(y),g.duration(y)}},rightAlignYAxis:{get:function(){return s},set:function(a){s=a,h.orient(a?"right":"left")}},useInteractiveGuideline:{get:function(){return t},set:function(a){t=a,a===!0&&c.interactive(!1)}}}),a.utils.inheritOptions(c,f),a.utils.initOptions(c),c},a.models.ohlcBarChart=function(){var b=a.models.historicalBarChart(a.models.ohlcBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open'+a.value+"
      open:"+b.yAxis.tickFormat()(c.open)+"
      close:"+b.yAxis.tickFormat()(c.close)+"
      high"+b.yAxis.tickFormat()(c.high)+"
      low:"+b.yAxis.tickFormat()(c.low)+"
      "}),b},a.models.candlestickBarChart=function(){var b=a.models.historicalBarChart(a.models.candlestickBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open'+a.value+"
      open:"+b.yAxis.tickFormat()(c.open)+"
      close:"+b.yAxis.tickFormat()(c.close)+"
      high"+b.yAxis.tickFormat()(c.high)+"
      low:"+b.yAxis.tickFormat()(c.low)+"
      "}),b},a.models.legend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?"#000":"#fff":m?void 0:(a.color||(a.color=g(a,b)),a.disabled?a.color:"#fff")}function r(a,b){return m&&"furious"==o&&a.disengaged?"#eee":a.color||g(a,b)}function s(a){return m&&"furious"==o?1:a.disabled?0:1}return p.each(function(b){var g=d-c.left-c.right,p=d3.select(this);a.utils.initSVG(p);var t=p.selectAll("g.nv-legend").data([b]),u=t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),v=t.select("g");t.attr("transform","translate("+c.left+","+c.top+")");var w,x,y=v.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),z=y.enter().append("g").attr("class","nv-series");switch(o){case"furious":x=23;break;case"classic":x=20}if("classic"==o)z.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),w=y.select("circle");else if("furious"==o){z.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),w=y.select(".nv-legend-symbol"),z.append("g").attr("class","nv-check-box").property("innerHTML",'').attr("transform","translate(-10,-8)scale(0.5)");var A=y.select(".nv-check-box");A.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}z.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var B=y.select("text.nv-legend-text");y.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=y.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=y.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),y.classed("nv-disabled",function(a){return a.userDisabled}),y.exit().remove(),B.attr("fill",q).text(f);var C=0;if(h){var D=[];y.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}D.push(b+i)});var E=0,F=[];for(C=0;g>C&&Eg&&E>1;){F=[],E--;for(var G=0;G(F[G%E]||0)&&(F[G%E]=D[G]);C=F.reduce(function(a,b){return a+b})}for(var H=[],I=0,J=0;E>I;I++)H[I]=J,J+=F[I];y.attr("transform",function(a,b){return"translate("+H[b%E]+","+(5+Math.floor(b/E)*x)+")"}),j?v.attr("transform","translate("+(d-c.right-C)+","+c.top+")"):v.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(D.length/E)*x}else{var K,L=5,M=5,N=0;y.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return K=M,dN&&(N=M),K+N>C&&(C=K+N),"translate("+K+","+L+")"}),v.attr("transform","translate("+(d-c.right-N)+","+c.top+")"),e=c.top+c.bottom+L+15}if("furious"==o){w.attr("width",function(a,b){return B[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),u.insert("rect",":first-child").attr("class","nv-legend-bg").attr("fill","#eee").attr("opacity",0);var O=v.select(".nv-legend-bg");O.transition().duration(300).attr("x",-x).attr("width",C+x-12).attr("height",e+10).attr("y",-c.top-10).attr("opacity",m?1:0)}w.style("fill",r).style("fill-opacity",s).style("stroke",r)}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=32,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.line=function(){"use strict";function b(r){return v.reset(),v.models(e),r.each(function(b){i=d3.select(this);var r=a.utils.availableWidth(g,i,f),s=a.utils.availableHeight(h,i,f);a.utils.initSVG(i),c=e.xScale(),d=e.yScale(),t=t||c,u=u||d;var w=i.selectAll("g.nv-wrap.nv-line").data([b]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-line"),y=x.append("defs"),z=x.append("g"),A=w.select("g");z.append("g").attr("class","nv-groups"),z.append("g").attr("class","nv-scatterWrap"),w.attr("transform","translate("+f.left+","+f.top+")"),e.width(r).height(s);var B=w.select(".nv-scatterWrap");B.call(e),y.append("clipPath").attr("id","nv-edge-clip-"+e.id()).append("rect"),w.select("#nv-edge-clip-"+e.id()+" rect").attr("width",r).attr("height",s>0?s:0),A.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":""),B.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":"");var C=w.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});C.enter().append("g").style("stroke-opacity",1e-6).style("stroke-width",function(a){return a.strokeWidth||j}).style("fill-opacity",1e-6),C.exit().remove(),C.attr("class",function(a,b){return(a.classed||"")+" nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return k(a,b)}).style("stroke",function(a,b){return k(a,b)}),C.watchTransition(v,"line: groups").style("stroke-opacity",1).style("fill-opacity",function(a){return a.fillOpacity||.5});var D=C.selectAll("path.nv-area").data(function(a){return o(a)?[a]:[]});D.enter().append("path").attr("class","nv-area").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y0(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))}).y1(function(){return u(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])}),C.exit().selectAll("path.nv-area").remove(),D.watchTransition(v,"line: areaPaths").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y0(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))}).y1(function(){return d(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])});var E=C.selectAll("path.nv-line").data(function(a){return[a.values]});E.enter().append("path").attr("class","nv-line").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))})),E.watchTransition(v,"line: linePaths").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))})),t=c.copy(),u=d.copy()}),v.renderEnd("line immediate"),b}var c,d,e=a.models.scatter(),f={top:0,right:0,bottom:0,left:0},g=960,h=500,i=null,j=1.5,k=a.utils.defaultColor(),l=function(a){return a.x},m=function(a){return a.y},n=function(a,b){return!isNaN(m(a,b))&&null!==m(a,b)},o=function(a){return a.area},p=!1,q="linear",r=250,s=d3.dispatch("elementClick","elementMouseover","elementMouseout","renderEnd");e.pointSize(16).pointDomain([16,256]);var t,u,v=a.utils.renderWatch(s,r);return b.dispatch=s,b.scatter=e,e.dispatch.on("elementClick",function(){s.elementClick.apply(this,arguments)}),e.dispatch.on("elementMouseover",function(){s.elementMouseover.apply(this,arguments)}),e.dispatch.on("elementMouseout",function(){s.elementMouseout.apply(this,arguments)}),b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},defined:{get:function(){return n},set:function(a){n=a}},interpolate:{get:function(){return q},set:function(a){q=a}},clipEdge:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}},duration:{get:function(){return r},set:function(a){r=a,v.reset(r),e.duration(r)}},isArea:{get:function(){return o},set:function(a){o=d3.functor(a)}},x:{get:function(){return l},set:function(a){l=a,e.x(a)}},y:{get:function(){return m},set:function(a){m=a,e.y(a)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.lineChart=function(){"use strict";function b(j){return y.reset(),y.models(e),p&&y.models(f),q&&y.models(g),j.each(function(j){var v=d3.select(this),y=this;a.utils.initSVG(v);var B=a.utils.availableWidth(m,v,k),C=a.utils.availableHeight(n,v,k);if(b.update=function(){0===x?v.call(b):v.transition().duration(x).call(b)},b.container=this,t.setter(A(j),b.update).getter(z(j)).update(),t.disabled=j.map(function(a){return!!a.disabled}),!u){var D;u={};for(D in t)u[D]=t[D]instanceof Array?t[D].slice(0):t[D] +}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,v),b;v.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var E=v.selectAll("g.nv-wrap.nv-lineChart").data([j]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-lineChart").append("g"),G=E.select("g");F.append("rect").style("opacity",0),F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-linesWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),G.select("rect").attr("width",B).attr("height",C>0?C:0),o&&(h.width(B),G.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),C=a.utils.availableHeight(n,v,k)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-k.top+")")),E.attr("transform","translate("+k.left+","+k.top+")"),r&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),s&&(i.width(B).height(C).margin({left:k.left,top:k.top}).svgContainer(v).xScale(c),E.select(".nv-interactive").call(i)),e.width(B).height(C).color(j.map(function(a,b){return a.color||l(a,b)}).filter(function(a,b){return!j[b].disabled}));var H=G.select(".nv-linesWrap").datum(j.filter(function(a){return!a.disabled}));H.call(e),p&&(f.scale(c)._ticks(a.utils.calcTicksX(B/100,j)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),G.select(".nv-x.nv-axis").call(f)),q&&(g.scale(d)._ticks(a.utils.calcTicksY(C/36,j)).tickSize(-B,0),G.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)t[c]=a[c];w.stateChange(t),b.update()}),i.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,h,m,n=[];if(j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,g){h=a.interactiveBisect(f.values,c.pointXValue,b.x());var i=f.values[h],j=b.y()(i,h);null!=j&&e.highlightPoint(g,h,!0),void 0!==i&&(void 0===d&&(d=i),void 0===m&&(m=b.xScale()(b.x()(i,h))),n.push({key:f.key,value:j,color:l(f,f.seriesIndex)}))}),n.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(n.map(function(a){return a.value}),o,q);null!==r&&(n[r].highlight=!0)}var s=f.tickFormat()(b.x()(d,h));i.tooltip.position({left:c.mouseX+k.left,top:c.mouseY+k.top}).chartContainer(y.parentNode).valueFormatter(function(a){return null==a?"N/A":g.tickFormat()(a)}).data({value:s,index:h,series:n})(),i.renderGuideLine(m)}),i.dispatch.on("elementClick",function(c){var d,f=[];j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(e){var g=a.interactiveBisect(e.values,c.pointXValue,b.x()),h=e.values[g];if("undefined"!=typeof h){"undefined"==typeof d&&(d=b.xScale()(b.x()(h,g)));var i=b.yScale()(b.y()(h,g));f.push({point:h,pointIndex:g,pos:[d,i],seriesIndex:e.seriesIndex,series:e})}}),e.dispatch.elementClick(f)}),i.dispatch.on("elementMouseout",function(){e.clearHighlights()}),w.on("changeState",function(a){"undefined"!=typeof a.disabled&&j.length===a.disabled.length&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),t.disabled=a.disabled),b.update()})}),y.renderEnd("lineChart immediate"),b}var c,d,e=a.models.line(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.interactiveGuideline(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=a.utils.defaultColor(),m=null,n=null,o=!0,p=!0,q=!0,r=!1,s=!1,t=a.utils.state(),u=null,v=null,w=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),x=250;f.orient("bottom").tickPadding(7),g.orient(r?"right":"left"),j.valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)});var y=a.utils.renderWatch(w,x),z=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},A=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){j.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),b.dispatch=w,b.lines=e,b.legend=h,b.xAxis=f,b.yAxis=g,b.interactiveLayer=i,b.tooltip=j,b.dispatch=w,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return p},set:function(a){p=a}},showYAxis:{get:function(){return q},set:function(a){q=a}},defaultState:{get:function(){return u},set:function(a){u=a}},noData:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x),e.duration(x),f.duration(x),g.duration(x)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),h.color(l),e.color(l)}},rightAlignYAxis:{get:function(){return r},set:function(a){r=a,g.orient(r?"right":"left")}},useInteractiveGuideline:{get:function(){return s},set:function(a){s=a,s&&(e.interactive(!1),e.useVoronoi(!1))}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.linePlusBarChart=function(){"use strict";function b(v){return v.each(function(v){function J(a){var b=+("e"==a),c=b?1:-1,d=X/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function S(){u.empty()||u.extent(I),kb.data([u.empty()?e.domain():I]).each(function(a){var b=e(a[0])-e.range()[0],c=e.range()[1]-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>c?0:c)})}function T(){I=u.empty()?null:u.extent(),c=u.empty()?e.domain():u.extent(),K.brush({extent:c,brush:u}),S(),l.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),j.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var b=db.select(".nv-focus .nv-barsWrap").datum(Z.length?Z.map(function(a){return{key:a.key,values:a.values.filter(function(a,b){return l.x()(a,b)>=c[0]&&l.x()(a,b)<=c[1]})}}):[{values:[]}]),h=db.select(".nv-focus .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$.map(function(a){return{area:a.area,fillOpacity:a.fillOpacity,key:a.key,values:a.values.filter(function(a,b){return j.x()(a,b)>=c[0]&&j.x()(a,b)<=c[1]})}}));d=Z.length?l.xScale():j.xScale(),n.scale(d)._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-W,0),n.domain([Math.ceil(c[0]),Math.floor(c[1])]),db.select(".nv-x.nv-axis").transition().duration(L).call(n),b.transition().duration(L).call(l),h.transition().duration(L).call(j),db.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),p.scale(f)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(-V,0),q.scale(g)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(Z.length?0:-V,0),db.select(".nv-focus .nv-y1.nv-axis").style("opacity",Z.length?1:0),db.select(".nv-focus .nv-y2.nv-axis").style("opacity",$.length&&!$[0].disabled?1:0).attr("transform","translate("+d.range()[1]+",0)"),db.select(".nv-focus .nv-y1.nv-axis").transition().duration(L).call(p),db.select(".nv-focus .nv-y2.nv-axis").transition().duration(L).call(q)}var U=d3.select(this);a.utils.initSVG(U);var V=a.utils.availableWidth(y,U,w),W=a.utils.availableHeight(z,U,w)-(E?H:0),X=H-x.top-x.bottom;if(b.update=function(){U.transition().duration(L).call(b)},b.container=this,M.setter(R(v),b.update).getter(Q(v)).update(),M.disabled=v.map(function(a){return!!a.disabled}),!N){var Y;N={};for(Y in M)N[Y]=M[Y]instanceof Array?M[Y].slice(0):M[Y]}if(!(v&&v.length&&v.filter(function(a){return a.values.length}).length))return a.utils.noData(b,U),b;U.selectAll(".nv-noData").remove();var Z=v.filter(function(a){return!a.disabled&&a.bar}),$=v.filter(function(a){return!a.bar});d=l.xScale(),e=o.scale(),f=l.yScale(),g=j.yScale(),h=m.yScale(),i=k.yScale();var _=v.filter(function(a){return!a.disabled&&a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})}),ab=v.filter(function(a){return!a.disabled&&!a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})});d.range([0,V]),e.domain(d3.extent(d3.merge(_.concat(ab)),function(a){return a.x})).range([0,V]);var bb=U.selectAll("g.nv-wrap.nv-linePlusBar").data([v]),cb=bb.enter().append("g").attr("class","nvd3 nv-wrap nv-linePlusBar").append("g"),db=bb.select("g");cb.append("g").attr("class","nv-legendWrap");var eb=cb.append("g").attr("class","nv-focus");eb.append("g").attr("class","nv-x nv-axis"),eb.append("g").attr("class","nv-y1 nv-axis"),eb.append("g").attr("class","nv-y2 nv-axis"),eb.append("g").attr("class","nv-barsWrap"),eb.append("g").attr("class","nv-linesWrap");var fb=cb.append("g").attr("class","nv-context");if(fb.append("g").attr("class","nv-x nv-axis"),fb.append("g").attr("class","nv-y1 nv-axis"),fb.append("g").attr("class","nv-y2 nv-axis"),fb.append("g").attr("class","nv-barsWrap"),fb.append("g").attr("class","nv-linesWrap"),fb.append("g").attr("class","nv-brushBackground"),fb.append("g").attr("class","nv-x nv-brush"),D){var gb=t.align()?V/2:V,hb=t.align()?gb:0;t.width(gb),db.select(".nv-legendWrap").datum(v.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(a.bar?O:P),a})).call(t),w.top!=t.height()&&(w.top=t.height(),W=a.utils.availableHeight(z,U,w)-H),db.select(".nv-legendWrap").attr("transform","translate("+hb+","+-w.top+")")}bb.attr("transform","translate("+w.left+","+w.top+")"),db.select(".nv-context").style("display",E?"initial":"none"),m.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),k.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var ib=db.select(".nv-context .nv-barsWrap").datum(Z.length?Z:[{values:[]}]),jb=db.select(".nv-context .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$);db.select(".nv-context").attr("transform","translate(0,"+(W+w.bottom+x.top)+")"),ib.transition().call(m),jb.transition().call(k),G&&(o._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-X,0),db.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+h.range()[0]+")"),db.select(".nv-context .nv-x.nv-axis").transition().call(o)),F&&(r.scale(h)._ticks(X/36).tickSize(-V,0),s.scale(i)._ticks(X/36).tickSize(Z.length?0:-V,0),db.select(".nv-context .nv-y3.nv-axis").style("opacity",Z.length?1:0).attr("transform","translate(0,"+e.range()[0]+")"),db.select(".nv-context .nv-y2.nv-axis").style("opacity",$.length?1:0).attr("transform","translate("+e.range()[1]+",0)"),db.select(".nv-context .nv-y1.nv-axis").transition().call(r),db.select(".nv-context .nv-y2.nv-axis").transition().call(s)),u.x(e).on("brush",T),I&&u.extent(I);var kb=db.select(".nv-brushBackground").selectAll("g").data([I||u.extent()]),lb=kb.enter().append("g");lb.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",X),lb.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",X);var mb=db.select(".nv-x.nv-brush").call(u);mb.selectAll("rect").attr("height",X),mb.selectAll(".resize").append("path").attr("d",J),t.dispatch.on("stateChange",function(a){for(var c in a)M[c]=a[c];K.stateChange(M),b.update()}),K.on("changeState",function(a){"undefined"!=typeof a.disabled&&(v.forEach(function(b,c){b.disabled=a.disabled[c]}),M.disabled=a.disabled),b.update()}),T()}),b}var c,d,e,f,g,h,i,j=a.models.line(),k=a.models.line(),l=a.models.historicalBar(),m=a.models.historicalBar(),n=a.models.axis(),o=a.models.axis(),p=a.models.axis(),q=a.models.axis(),r=a.models.axis(),s=a.models.axis(),t=a.models.legend(),u=d3.svg.brush(),v=a.models.tooltip(),w={top:30,right:30,bottom:30,left:60},x={top:0,right:30,bottom:20,left:60},y=null,z=null,A=function(a){return a.x},B=function(a){return a.y},C=a.utils.defaultColor(),D=!0,E=!0,F=!1,G=!0,H=50,I=null,J=null,K=d3.dispatch("brush","stateChange","changeState"),L=0,M=a.utils.state(),N=null,O=" (left axis)",P=" (right axis)";j.clipEdge(!0),k.interactive(!1),n.orient("bottom").tickPadding(5),p.orient("left"),q.orient("right"),o.orient("bottom").tickPadding(5),r.orient("left"),s.orient("right"),v.headerEnabled(!0).headerFormatter(function(a,b){return n.tickFormat()(a,b)});var Q=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},R=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return j.dispatch.on("elementMouseover.tooltip",function(a){v.duration(100).valueFormatter(function(a,b){return q.tickFormat()(a,b)}).data(a).position(a.pos).hidden(!1)}),j.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={value:b.y()(a.data),color:a.color},v.duration(0).valueFormatter(function(a,b){return p.tickFormat()(a,b)}).data(a).hidden(!1)}),l.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMousemove.tooltip",function(){v.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=K,b.legend=t,b.lines=j,b.lines2=k,b.bars=l,b.bars2=m,b.xAxis=n,b.x2Axis=o,b.y1Axis=p,b.y2Axis=q,b.y3Axis=r,b.y4Axis=s,b.tooltip=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return y},set:function(a){y=a}},height:{get:function(){return z},set:function(a){z=a}},showLegend:{get:function(){return D},set:function(a){D=a}},brushExtent:{get:function(){return I},set:function(a){I=a}},noData:{get:function(){return J},set:function(a){J=a}},focusEnable:{get:function(){return E},set:function(a){E=a}},focusHeight:{get:function(){return H},set:function(a){H=a}},focusShowAxisX:{get:function(){return G},set:function(a){G=a}},focusShowAxisY:{get:function(){return F},set:function(a){F=a}},legendLeftAxisHint:{get:function(){return O},set:function(a){O=a}},legendRightAxisHint:{get:function(){return P},set:function(a){P=a}},tooltips:{get:function(){return v.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),v.enabled(!!b)}},tooltipContent:{get:function(){return v.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),v.contentGenerator(b)}},margin:{get:function(){return w},set:function(a){w.top=void 0!==a.top?a.top:w.top,w.right=void 0!==a.right?a.right:w.right,w.bottom=void 0!==a.bottom?a.bottom:w.bottom,w.left=void 0!==a.left?a.left:w.left}},duration:{get:function(){return L},set:function(a){L=a}},color:{get:function(){return C},set:function(b){C=a.utils.getColor(b),t.color(C)}},x:{get:function(){return A},set:function(a){A=a,j.x(a),k.x(a),l.x(a),m.x(a)}},y:{get:function(){return B},set:function(a){B=a,j.y(a),k.y(a),l.y(a),m.y(a)}}}),a.utils.inheritOptions(b,j),a.utils.initOptions(b),b},a.models.lineWithFocusChart=function(){"use strict";function b(o){return o.each(function(o){function z(a){var b=+("e"==a),c=b?1:-1,d=M/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function G(){n.empty()||n.extent(y),U.data([n.empty()?e.domain():y]).each(function(a){var b=e(a[0])-c.range()[0],d=K-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>d?0:d)})}function H(){y=n.empty()?null:n.extent();var a=n.empty()?e.domain():n.extent();if(!(Math.abs(a[0]-a[1])<=1)){A.brush({extent:a,brush:n}),G();var b=Q.select(".nv-focus .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}).map(function(b){return{key:b.key,area:b.area,values:b.values.filter(function(b,c){return g.x()(b,c)>=a[0]&&g.x()(b,c)<=a[1]})}}));b.transition().duration(B).call(g),Q.select(".nv-focus .nv-x.nv-axis").transition().duration(B).call(i),Q.select(".nv-focus .nv-y.nv-axis").transition().duration(B).call(j)}}var I=d3.select(this),J=this;a.utils.initSVG(I);var K=a.utils.availableWidth(t,I,q),L=a.utils.availableHeight(u,I,q)-v,M=v-r.top-r.bottom;if(b.update=function(){I.transition().duration(B).call(b)},b.container=this,C.setter(F(o),b.update).getter(E(o)).update(),C.disabled=o.map(function(a){return!!a.disabled}),!D){var N;D={};for(N in C)D[N]=C[N]instanceof Array?C[N].slice(0):C[N]}if(!(o&&o.length&&o.filter(function(a){return a.values.length}).length))return a.utils.noData(b,I),b;I.selectAll(".nv-noData").remove(),c=g.xScale(),d=g.yScale(),e=h.xScale(),f=h.yScale();var O=I.selectAll("g.nv-wrap.nv-lineWithFocusChart").data([o]),P=O.enter().append("g").attr("class","nvd3 nv-wrap nv-lineWithFocusChart").append("g"),Q=O.select("g");P.append("g").attr("class","nv-legendWrap");var R=P.append("g").attr("class","nv-focus");R.append("g").attr("class","nv-x nv-axis"),R.append("g").attr("class","nv-y nv-axis"),R.append("g").attr("class","nv-linesWrap"),R.append("g").attr("class","nv-interactive");var S=P.append("g").attr("class","nv-context");S.append("g").attr("class","nv-x nv-axis"),S.append("g").attr("class","nv-y nv-axis"),S.append("g").attr("class","nv-linesWrap"),S.append("g").attr("class","nv-brushBackground"),S.append("g").attr("class","nv-x nv-brush"),x&&(m.width(K),Q.select(".nv-legendWrap").datum(o).call(m),q.top!=m.height()&&(q.top=m.height(),L=a.utils.availableHeight(u,I,q)-v),Q.select(".nv-legendWrap").attr("transform","translate(0,"+-q.top+")")),O.attr("transform","translate("+q.left+","+q.top+")"),w&&(p.width(K).height(L).margin({left:q.left,top:q.top}).svgContainer(I).xScale(c),O.select(".nv-interactive").call(p)),g.width(K).height(L).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),h.defined(g.defined()).width(K).height(M).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),Q.select(".nv-context").attr("transform","translate(0,"+(L+q.bottom+r.top)+")");var T=Q.select(".nv-context .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}));d3.transition(T).call(h),i.scale(c)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-L,0),j.scale(d)._ticks(a.utils.calcTicksY(L/36,o)).tickSize(-K,0),Q.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+L+")"),n.x(e).on("brush",function(){H()}),y&&n.extent(y);var U=Q.select(".nv-brushBackground").selectAll("g").data([y||n.extent()]),V=U.enter().append("g");V.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",M),V.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",M);var W=Q.select(".nv-x.nv-brush").call(n);W.selectAll("rect").attr("height",M),W.selectAll(".resize").append("path").attr("d",z),H(),k.scale(e)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-M,0),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),d3.transition(Q.select(".nv-context .nv-x.nv-axis")).call(k),l.scale(f)._ticks(a.utils.calcTicksY(M/36,o)).tickSize(-K,0),d3.transition(Q.select(".nv-context .nv-y.nv-axis")).call(l),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),m.dispatch.on("stateChange",function(a){for(var c in a)C[c]=a[c];A.stateChange(C),b.update()}),p.dispatch.on("elementMousemove",function(c){g.clearHighlights();var d,f,h,k=[];if(o.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(i,j){var l=n.empty()?e.domain():n.extent(),m=i.values.filter(function(a,b){return g.x()(a,b)>=l[0]&&g.x()(a,b)<=l[1]});f=a.interactiveBisect(m,c.pointXValue,g.x());var o=m[f],p=b.y()(o,f);null!=p&&g.highlightPoint(j,f,!0),void 0!==o&&(void 0===d&&(d=o),void 0===h&&(h=b.xScale()(b.x()(o,f))),k.push({key:i.key,value:b.y()(o,f),color:s(i,i.seriesIndex)}))}),k.length>2){var l=b.yScale().invert(c.mouseY),m=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),r=.03*m,t=a.nearestValueIndex(k.map(function(a){return a.value}),l,r);null!==t&&(k[t].highlight=!0)}var u=i.tickFormat()(b.x()(d,f));p.tooltip.position({left:c.mouseX+q.left,top:c.mouseY+q.top}).chartContainer(J.parentNode).valueFormatter(function(a){return null==a?"N/A":j.tickFormat()(a)}).data({value:u,index:f,series:k})(),p.renderGuideLine(h)}),p.dispatch.on("elementMouseout",function(){g.clearHighlights()}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&o.forEach(function(b,c){b.disabled=a.disabled[c]}),b.update()})}),b}var c,d,e,f,g=a.models.line(),h=a.models.line(),i=a.models.axis(),j=a.models.axis(),k=a.models.axis(),l=a.models.axis(),m=a.models.legend(),n=d3.svg.brush(),o=a.models.tooltip(),p=a.interactiveGuideline(),q={top:30,right:30,bottom:30,left:60},r={top:0,right:30,bottom:20,left:60},s=a.utils.defaultColor(),t=null,u=null,v=50,w=!1,x=!0,y=null,z=null,A=d3.dispatch("brush","stateChange","changeState"),B=250,C=a.utils.state(),D=null;g.clipEdge(!0).duration(0),h.interactive(!1),i.orient("bottom").tickPadding(5),j.orient("left"),k.orient("bottom").tickPadding(5),l.orient("left"),o.valueFormatter(function(a,b){return j.tickFormat()(a,b)}).headerFormatter(function(a,b){return i.tickFormat()(a,b)});var E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return g.dispatch.on("elementMouseover.tooltip",function(a){o.data(a).position(a.pos).hidden(!1)}),g.dispatch.on("elementMouseout.tooltip",function(){o.hidden(!0)}),b.dispatch=A,b.legend=m,b.lines=g,b.lines2=h,b.xAxis=i,b.yAxis=j,b.x2Axis=k,b.y2Axis=l,b.interactiveLayer=p,b.tooltip=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return t},set:function(a){t=a}},height:{get:function(){return u},set:function(a){u=a}},focusHeight:{get:function(){return v},set:function(a){v=a}},showLegend:{get:function(){return x},set:function(a){x=a}},brushExtent:{get:function(){return y},set:function(a){y=a}},defaultState:{get:function(){return D},set:function(a){D=a}},noData:{get:function(){return z},set:function(a){z=a}},tooltips:{get:function(){return o.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),o.enabled(!!b)}},tooltipContent:{get:function(){return o.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),o.contentGenerator(b)}},margin:{get:function(){return q},set:function(a){q.top=void 0!==a.top?a.top:q.top,q.right=void 0!==a.right?a.right:q.right,q.bottom=void 0!==a.bottom?a.bottom:q.bottom,q.left=void 0!==a.left?a.left:q.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b),m.color(s)}},interpolate:{get:function(){return g.interpolate()},set:function(a){g.interpolate(a),h.interpolate(a)}},xTickFormat:{get:function(){return i.tickFormat()},set:function(a){i.tickFormat(a),k.tickFormat(a)}},yTickFormat:{get:function(){return j.tickFormat()},set:function(a){j.tickFormat(a),l.tickFormat(a)}},duration:{get:function(){return B},set:function(a){B=a,j.duration(B),l.duration(B),i.duration(B),k.duration(B)}},x:{get:function(){return g.x()},set:function(a){g.x(a),h.x(a)}},y:{get:function(){return g.y()},set:function(a){g.y(a),h.y(a)}},useInteractiveGuideline:{get:function(){return w},set:function(a){w=a,w&&(g.interactive(!1),g.useVoronoi(!1))}}}),a.utils.inheritOptions(b,g),a.utils.initOptions(b),b},a.models.multiBar=function(){"use strict";function b(E){return C.reset(),E.each(function(b){var E=k-j.left-j.right,F=l-j.top-j.bottom;p=d3.select(this),a.utils.initSVG(p);var G=0;if(x&&b.length&&(x=[{values:b[0].values.map(function(a){return{x:a.x,y:0,series:a.series,size:.01}})}]),u){var H=d3.layout.stack().offset(v).values(function(a){return a.values}).y(r)(!b.length&&x?x:b);H.forEach(function(a,c){a.nonStackable?(b[c].nonStackableSeries=G++,H[c]=b[c]):c>0&&H[c-1].nonStackable&&H[c].values.map(function(a,b){a.y0-=H[c-1].values[b].y,a.y1=a.y0+a.y})}),b=H}b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),u&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a,f){if(!b[f].nonStackable){var g=a.values[c];g.size=Math.abs(g.y),g.y<0?(g.y1=e,e-=g.size):(g.y1=g.size+d,d+=g.size)}})});var I=d&&e?[]:b.map(function(a,b){return a.values.map(function(a,c){return{x:q(a,c),y:r(a,c),y0:a.y0,y1:a.y1,idx:b}})});m.domain(d||d3.merge(I).map(function(a){return a.x})).rangeBands(f||[0,E],A),n.domain(e||d3.extent(d3.merge(I).map(function(a){var c=a.y;return u&&!b[a.idx].nonStackable&&(c=a.y>0?a.y1:a.y1+a.y),c}).concat(s))).range(g||[F,0]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]+.01*n.domain()[0],n.domain()[1]-.01*n.domain()[1]]:[-1,1]),h=h||m,i=i||n;var J=p.selectAll("g.nv-wrap.nv-multibar").data([b]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multibar"),L=K.append("defs"),M=K.append("g"),N=J.select("g");M.append("g").attr("class","nv-groups"),J.attr("transform","translate("+j.left+","+j.top+")"),L.append("clipPath").attr("id","nv-edge-clip-"+o).append("rect"),J.select("#nv-edge-clip-"+o+" rect").attr("width",E).attr("height",F),N.attr("clip-path",t?"url(#nv-edge-clip-"+o+")":"");var O=J.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});O.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);var P=C.transition(O.exit().selectAll("rect.nv-bar"),"multibarExit",Math.min(100,z)).attr("y",function(a){var c=i(0)||0;return u&&b[a.series]&&!b[a.series].nonStackable&&(c=i(a.y0)),c}).attr("height",0).remove();P.delay&&P.delay(function(a,b){var c=b*(z/(D+1))-b;return c}),O.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return w(a,b)}).style("stroke",function(a,b){return w(a,b)}),O.style("stroke-opacity",1).style("fill-opacity",.75);var Q=O.selectAll("rect.nv-bar").data(function(a){return x&&!b.length?x.values:a.values});Q.exit().remove();Q.enter().append("rect").attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("x",function(a,c,d){return u&&!b[d].nonStackable?0:d*m.rangeBand()/b.length}).attr("y",function(a,c,d){return i(u&&!b[d].nonStackable?a.y0:0)||0}).attr("height",0).attr("width",function(a,c,d){return m.rangeBand()/(u&&!b[d].nonStackable?1:b.length)}).attr("transform",function(a,b){return"translate("+m(q(a,b))+",0)"});Q.style("fill",function(a,b,c){return w(a,c,b)}).style("stroke",function(a,b,c){return w(a,c,b)}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),B.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),B.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){B.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){B.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){B.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),Q.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("transform",function(a,b){return"translate("+m(q(a,b))+",0)"}),y&&(c||(c=b.map(function(){return!0})),Q.style("fill",function(a,b,d){return d3.rgb(y(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(y(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}));var R=Q.watchTransition(C,"multibar",Math.min(250,z)).delay(function(a,c){return c*z/b[0].values.length});u?R.attr("y",function(a,c,d){var e=0;return e=b[d].nonStackable?r(a,c)<0?n(0):n(0)-n(r(a,c))<-1?n(0)-1:n(r(a,c))||0:n(a.y1)}).attr("height",function(a,c,d){return b[d].nonStackable?Math.max(Math.abs(n(r(a,c))-n(0)),1)||0:Math.max(Math.abs(n(a.y+a.y0)-n(a.y0)),1)}).attr("x",function(a,c,d){var e=0;return b[d].nonStackable&&(e=a.series*m.rangeBand()/b.length,b.length!==G&&(e=b[d].nonStackableSeries*m.rangeBand()/(2*G))),e}).attr("width",function(a,c,d){if(b[d].nonStackable){var e=m.rangeBand()/G;return b.length!==G&&(e=m.rangeBand()/(2*G)),e}return m.rangeBand()}):R.attr("x",function(a){return a.series*m.rangeBand()/b.length}).attr("width",m.rangeBand()/b.length).attr("y",function(a,b){return r(a,b)<0?n(0):n(0)-n(r(a,b))<1?n(0)-1:n(r(a,b))||0}).attr("height",function(a,b){return Math.max(Math.abs(n(r(a,b))-n(0)),1)||0}),h=m.copy(),i=n.copy(),b[0]&&b[0].values&&(D=b[0].values.length)}),C.renderEnd("multibar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=d3.scale.ordinal(),n=d3.scale.linear(),o=Math.floor(1e4*Math.random()),p=null,q=function(a){return a.x},r=function(a){return a.y},s=[0],t=!0,u=!1,v="zero",w=a.utils.defaultColor(),x=!1,y=null,z=500,A=.1,B=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),C=a.utils.renderWatch(B,z),D=0;return b.dispatch=B,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return s},set:function(a){s=a}},stacked:{get:function(){return u},set:function(a){u=a}},stackOffset:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return t},set:function(a){t=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return o},set:function(a){o=a}},hideable:{get:function(){return x},set:function(a){x=a}},groupSpacing:{get:function(){return A},set:function(a){A=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z)}},color:{get:function(){return w},set:function(b){w=a.utils.getColor(b)}},barColor:{get:function(){return y},set:function(b){y=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarChart=function(){"use strict";function b(j){return D.reset(),D.models(e),r&&D.models(f),s&&D.models(g),j.each(function(j){var z=d3.select(this);a.utils.initSVG(z);var D=a.utils.availableWidth(l,z,k),H=a.utils.availableHeight(m,z,k);if(b.update=function(){0===C?z.call(b):z.transition().duration(C).call(b)},b.container=this,x.setter(G(j),b.update).getter(F(j)).update(),x.disabled=j.map(function(a){return!!a.disabled}),!y){var I;y={};for(I in x)y[I]=x[I]instanceof Array?x[I].slice(0):x[I]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,z),b;z.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale(); +var J=z.selectAll("g.nv-wrap.nv-multiBarWithLegend").data([j]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarWithLegend").append("g"),L=J.select("g");if(K.append("g").attr("class","nv-x nv-axis"),K.append("g").attr("class","nv-y nv-axis"),K.append("g").attr("class","nv-barsWrap"),K.append("g").attr("class","nv-legendWrap"),K.append("g").attr("class","nv-controlsWrap"),q&&(h.width(D-B()),L.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),H=a.utils.availableHeight(m,z,k)),L.select(".nv-legendWrap").attr("transform","translate("+B()+","+-k.top+")")),o){var M=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(B()).color(["#444","#444","#444"]),L.select(".nv-controlsWrap").datum(M).attr("transform","translate(0,"+-k.top+")").call(i)}J.attr("transform","translate("+k.left+","+k.top+")"),t&&L.select(".nv-y.nv-axis").attr("transform","translate("+D+",0)"),e.disabled(j.map(function(a){return a.disabled})).width(D).height(H).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var N=L.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(N.call(e),r){f.scale(c)._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-H,0),L.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),L.select(".nv-x.nv-axis").call(f);var O=L.select(".nv-x.nv-axis > g").selectAll("g");if(O.selectAll("line, text").style("opacity",1),v){var P=function(a,b){return"translate("+a+","+b+")"},Q=5,R=17;O.selectAll("text").attr("transform",function(a,b,c){return P(0,c%2==0?Q:R)});var S=d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;L.selectAll(".nv-x.nv-axis .nv-axisMaxMin text").attr("transform",function(a,b){return P(0,0===b||S%2!==0?R:Q)})}u&&O.filter(function(a,b){return b%Math.ceil(j[0].values.length/(D/100))!==0}).selectAll("text, line").style("opacity",0),w&&O.selectAll(".tick text").attr("transform","rotate("+w+" 0,0)").style("text-anchor",w>0?"start":"end"),L.select(".nv-x.nv-axis").selectAll("g.nv-axisMaxMin text").style("opacity",1)}s&&(g.scale(d)._ticks(a.utils.calcTicksY(H/36,j)).tickSize(-D,0),L.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)x[c]=a[c];A.stateChange(x),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(M=M.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":case p.grouped:e.stacked(!1);break;case"Stacked":case p.stacked:e.stacked(!0)}x.stacked=e.stacked(),A.stateChange(x),b.update()}}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),x.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),x.stacked=a.stacked,E=a.stacked),b.update()})}),D.renderEnd("multibarchart immediate"),b}var c,d,e=a.models.multiBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=0,x=a.utils.state(),y=null,z=null,A=d3.dispatch("stateChange","changeState","renderEnd"),B=function(){return o?180:0},C=250;x.stacked=!1,e.stacked(!1),f.orient("bottom").tickPadding(7).showMaxMin(!1).tickFormat(function(a){return a}),g.orient(t?"right":"left").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var D=a.utils.renderWatch(A),E=!1,F=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:E}}},G=function(a){return function(b){void 0!==b.stacked&&(E=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=A,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=x,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return y},set:function(a){y=a}},noData:{get:function(){return z},set:function(a){z=a}},reduceXTicks:{get:function(){return u},set:function(a){u=a}},rotateLabels:{get:function(){return w},set:function(a){w=a}},staggerLabels:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return C},set:function(a){C=a,e.duration(C),f.duration(C),g.duration(C),D.reset(C)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiBarHorizontal=function(){"use strict";function b(m){return E.reset(),m.each(function(b){var m=k-j.left-j.right,C=l-j.top-j.bottom;n=d3.select(this),a.utils.initSVG(n),w&&(b=d3.layout.stack().offset("zero").values(function(a){return a.values}).y(r)(b)),b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),w&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a){var b=a.values[c];b.size=Math.abs(b.y),b.y<0?(b.y1=e-b.size,e-=b.size):(b.y1=d,d+=b.size)})});var F=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:q(a,b),y:r(a,b),y0:a.y0,y1:a.y1}})});o.domain(d||d3.merge(F).map(function(a){return a.x})).rangeBands(f||[0,C],A),p.domain(e||d3.extent(d3.merge(F).map(function(a){return w?a.y>0?a.y1+a.y:a.y1:a.y}).concat(t))),p.range(x&&!w?g||[p.domain()[0]<0?z:0,m-(p.domain()[1]>0?z:0)]:g||[0,m]),h=h||o,i=i||d3.scale.linear().domain(p.domain()).range([p(0),p(0)]);{var G=d3.select(this).selectAll("g.nv-wrap.nv-multibarHorizontal").data([b]),H=G.enter().append("g").attr("class","nvd3 nv-wrap nv-multibarHorizontal"),I=(H.append("defs"),H.append("g"));G.select("g")}I.append("g").attr("class","nv-groups"),G.attr("transform","translate("+j.left+","+j.top+")");var J=G.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});J.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),J.exit().watchTransition(E,"multibarhorizontal: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),J.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return u(a,b)}).style("stroke",function(a,b){return u(a,b)}),J.watchTransition(E,"multibarhorizontal: groups").style("stroke-opacity",1).style("fill-opacity",.75);var K=J.selectAll("g.nv-bar").data(function(a){return a.values});K.exit().remove();var L=K.enter().append("g").attr("transform",function(a,c,d){return"translate("+i(w?a.y0:0)+","+(w?0:d*o.rangeBand()/b.length+o(q(a,c)))+")"});L.append("rect").attr("width",0).attr("height",o.rangeBand()/(w?1:b.length)),K.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),D.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){D.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){D.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){D.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),s(b[0],0)&&(L.append("polyline"),K.select("polyline").attr("fill","none").attr("points",function(a,c){var d=s(a,c),e=.8*o.rangeBand()/(2*(w?1:b.length));d=d.length?d:[-Math.abs(d),Math.abs(d)],d=d.map(function(a){return p(a)-p(0)});var f=[[d[0],-e],[d[0],e],[d[0],0],[d[1],0],[d[1],-e],[d[1],e]];return f.map(function(a){return a.join(",")}).join(" ")}).attr("transform",function(a,c){var d=o.rangeBand()/(2*(w?1:b.length));return"translate("+(r(a,c)<0?0:p(r(a,c))-p(0))+", "+d+")"})),L.append("text"),x&&!w?(K.select("text").attr("text-anchor",function(a,b){return r(a,b)<0?"end":"start"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){var c=B(r(a,b)),d=s(a,b);return void 0===d?c:d.length?c+"+"+B(Math.abs(d[1]))+"-"+B(Math.abs(d[0])):c+"±"+B(Math.abs(d))}),K.watchTransition(E,"multibarhorizontal: bars").select("text").attr("x",function(a,b){return r(a,b)<0?-4:p(r(a,b))-p(0)+4})):K.selectAll("text").text(""),y&&!w?(L.append("text").classed("nv-bar-label",!0),K.select("text.nv-bar-label").attr("text-anchor",function(a,b){return r(a,b)<0?"start":"end"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){return q(a,b)}),K.watchTransition(E,"multibarhorizontal: bars").select("text.nv-bar-label").attr("x",function(a,b){return r(a,b)<0?p(0)-p(r(a,b))+4:-4})):K.selectAll("text.nv-bar-label").text(""),K.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}),v&&(c||(c=b.map(function(){return!0})),K.style("fill",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()})),w?K.watchTransition(E,"multibarhorizontal: bars").attr("transform",function(a,b){return"translate("+p(a.y1)+","+o(q(a,b))+")"}).select("rect").attr("width",function(a,b){return Math.abs(p(r(a,b)+a.y0)-p(a.y0))}).attr("height",o.rangeBand()):K.watchTransition(E,"multibarhorizontal: bars").attr("transform",function(a,c){return"translate("+p(r(a,c)<0?r(a,c):0)+","+(a.series*o.rangeBand()/b.length+o(q(a,c)))+")"}).select("rect").attr("height",o.rangeBand()/b.length).attr("width",function(a,b){return Math.max(Math.abs(p(r(a,b))-p(0)),1)}),h=o.copy(),i=p.copy()}),E.renderEnd("multibarHorizontal immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=null,o=d3.scale.ordinal(),p=d3.scale.linear(),q=function(a){return a.x},r=function(a){return a.y},s=function(a){return a.yErr},t=[0],u=a.utils.defaultColor(),v=null,w=!1,x=!1,y=!1,z=60,A=.1,B=d3.format(",.2f"),C=250,D=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),E=a.utils.renderWatch(D,C);return b.dispatch=D,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},yErr:{get:function(){return s},set:function(a){s=a}},xScale:{get:function(){return o},set:function(a){o=a}},yScale:{get:function(){return p},set:function(a){p=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return t},set:function(a){t=a}},stacked:{get:function(){return w},set:function(a){w=a}},showValues:{get:function(){return x},set:function(a){x=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return m},set:function(a){m=a}},valueFormat:{get:function(){return B},set:function(a){B=a}},valuePadding:{get:function(){return z},set:function(a){z=a}},groupSpacing:{get:function(){return A},set:function(a){A=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return C},set:function(a){C=a,E.reset(C)}},color:{get:function(){return u},set:function(b){u=a.utils.getColor(b)}},barColor:{get:function(){return v},set:function(b){v=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarHorizontalChart=function(){"use strict";function b(j){return C.reset(),C.models(e),r&&C.models(f),s&&C.models(g),j.each(function(j){var w=d3.select(this);a.utils.initSVG(w);var C=a.utils.availableWidth(l,w,k),D=a.utils.availableHeight(m,w,k);if(b.update=function(){w.transition().duration(z).call(b)},b.container=this,t=e.stacked(),u.setter(B(j),b.update).getter(A(j)).update(),u.disabled=j.map(function(a){return!!a.disabled}),!v){var E;v={};for(E in u)v[E]=u[E]instanceof Array?u[E].slice(0):u[E]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,w),b;w.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var F=w.selectAll("g.nv-wrap.nv-multiBarHorizontalChart").data([j]),G=F.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarHorizontalChart").append("g"),H=F.select("g");if(G.append("g").attr("class","nv-x nv-axis"),G.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),G.append("g").attr("class","nv-barsWrap"),G.append("g").attr("class","nv-legendWrap"),G.append("g").attr("class","nv-controlsWrap"),q&&(h.width(C-y()),H.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),D=a.utils.availableHeight(m,w,k)),H.select(".nv-legendWrap").attr("transform","translate("+y()+","+-k.top+")")),o){var I=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(y()).color(["#444","#444","#444"]),H.select(".nv-controlsWrap").datum(I).attr("transform","translate(0,"+-k.top+")").call(i)}F.attr("transform","translate("+k.left+","+k.top+")"),e.disabled(j.map(function(a){return a.disabled})).width(C).height(D).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var J=H.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(J.transition().call(e),r){f.scale(c)._ticks(a.utils.calcTicksY(D/24,j)).tickSize(-C,0),H.select(".nv-x.nv-axis").call(f);var K=H.select(".nv-x.nv-axis").selectAll("g");K.selectAll("line, text")}s&&(g.scale(d)._ticks(a.utils.calcTicksX(C/100,j)).tickSize(-D,0),H.select(".nv-y.nv-axis").attr("transform","translate(0,"+D+")"),H.select(".nv-y.nv-axis").call(g)),H.select(".nv-zeroLine line").attr("x1",d(0)).attr("x2",d(0)).attr("y1",0).attr("y2",-D),h.dispatch.on("stateChange",function(a){for(var c in a)u[c]=a[c];x.stateChange(u),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(I=I.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":e.stacked(!1);break;case"Stacked":e.stacked(!0)}u.stacked=e.stacked(),x.stateChange(u),t=e.stacked(),b.update()}}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),u.stacked=a.stacked,t=a.stacked),b.update()})}),C.renderEnd("multibar horizontal chart immediate"),b}var c,d,e=a.models.multiBarHorizontal(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend().height(30),i=a.models.legend().height(30),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=a.utils.state(),v=null,w=null,x=d3.dispatch("stateChange","changeState","renderEnd"),y=function(){return o?180:0},z=250;u.stacked=!1,e.stacked(t),f.orient("left").tickPadding(5).showMaxMin(!1).tickFormat(function(a){return a}),g.orient("bottom").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var A=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:t}}},B=function(a){return function(b){void 0!==b.stacked&&(t=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},C=a.utils.renderWatch(x,z);return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=x,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=u,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z),e.duration(z),f.duration(z),g.duration(z)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiChart=function(){"use strict";function b(j){return j.each(function(j){function k(a){var b=2===j[a.seriesIndex].yAxis?z:y;a.value=a.point.x,a.series={value:a.point.y,color:a.point.color},B.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function l(a){var b=2===j[a.seriesIndex].yAxis?z:y;a.point.x=v.x()(a.point),a.point.y=v.y()(a.point),B.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function n(a){var b=2===j[a.data.series].yAxis?z:y;a.value=t.x()(a.data),a.series={value:t.y()(a.data),color:a.color},B.duration(0).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).hidden(!1)}var C=d3.select(this);a.utils.initSVG(C),b.update=function(){C.transition().call(b)},b.container=this;var D=a.utils.availableWidth(g,C,e),E=a.utils.availableHeight(h,C,e),F=j.filter(function(a){return"line"==a.type&&1==a.yAxis}),G=j.filter(function(a){return"line"==a.type&&2==a.yAxis}),H=j.filter(function(a){return"bar"==a.type&&1==a.yAxis}),I=j.filter(function(a){return"bar"==a.type&&2==a.yAxis}),J=j.filter(function(a){return"area"==a.type&&1==a.yAxis}),K=j.filter(function(a){return"area"==a.type&&2==a.yAxis});if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,C),b;C.selectAll(".nv-noData").remove();var L=j.filter(function(a){return!a.disabled&&1==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})}),M=j.filter(function(a){return!a.disabled&&2==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})});o.domain(d3.extent(d3.merge(L.concat(M)),function(a){return a.x})).range([0,D]);var N=C.selectAll("g.wrap.multiChart").data([j]),O=N.enter().append("g").attr("class","wrap nvd3 multiChart").append("g");O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y1 nv-axis"),O.append("g").attr("class","nv-y2 nv-axis"),O.append("g").attr("class","lines1Wrap"),O.append("g").attr("class","lines2Wrap"),O.append("g").attr("class","bars1Wrap"),O.append("g").attr("class","bars2Wrap"),O.append("g").attr("class","stack1Wrap"),O.append("g").attr("class","stack2Wrap"),O.append("g").attr("class","legendWrap");var P=N.select("g"),Q=j.map(function(a,b){return j[b].color||f(a,b)});if(i){var R=A.align()?D/2:D,S=A.align()?R:0;A.width(R),A.color(Q),P.select(".legendWrap").datum(j.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(1==a.yAxis?"":" (right axis)"),a})).call(A),e.top!=A.height()&&(e.top=A.height(),E=a.utils.availableHeight(h,C,e)),P.select(".legendWrap").attr("transform","translate("+S+","+-e.top+")")}r.width(D).height(E).interpolate(m).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"line"==j[b].type})),s.width(D).height(E).interpolate(m).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"line"==j[b].type})),t.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"bar"==j[b].type})),u.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"bar"==j[b].type})),v.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"area"==j[b].type})),w.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"area"==j[b].type})),P.attr("transform","translate("+e.left+","+e.top+")");var T=P.select(".lines1Wrap").datum(F.filter(function(a){return!a.disabled})),U=P.select(".bars1Wrap").datum(H.filter(function(a){return!a.disabled})),V=P.select(".stack1Wrap").datum(J.filter(function(a){return!a.disabled})),W=P.select(".lines2Wrap").datum(G.filter(function(a){return!a.disabled})),X=P.select(".bars2Wrap").datum(I.filter(function(a){return!a.disabled})),Y=P.select(".stack2Wrap").datum(K.filter(function(a){return!a.disabled})),Z=J.length?J.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[],$=K.length?K.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[];p.domain(c||d3.extent(d3.merge(L).concat(Z),function(a){return a.y})).range([0,E]),q.domain(d||d3.extent(d3.merge(M).concat($),function(a){return a.y})).range([0,E]),r.yDomain(p.domain()),t.yDomain(p.domain()),v.yDomain(p.domain()),s.yDomain(q.domain()),u.yDomain(q.domain()),w.yDomain(q.domain()),J.length&&d3.transition(V).call(v),K.length&&d3.transition(Y).call(w),H.length&&d3.transition(U).call(t),I.length&&d3.transition(X).call(u),F.length&&d3.transition(T).call(r),G.length&&d3.transition(W).call(s),x._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-E,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+E+")"),d3.transition(P.select(".nv-x.nv-axis")).call(x),y._ticks(a.utils.calcTicksY(E/36,j)).tickSize(-D,0),d3.transition(P.select(".nv-y1.nv-axis")).call(y),z._ticks(a.utils.calcTicksY(E/36,j)).tickSize(-D,0),d3.transition(P.select(".nv-y2.nv-axis")).call(z),P.select(".nv-y1.nv-axis").classed("nv-disabled",L.length?!1:!0).attr("transform","translate("+o.range()[0]+",0)"),P.select(".nv-y2.nv-axis").classed("nv-disabled",M.length?!1:!0).attr("transform","translate("+o.range()[1]+",0)"),A.dispatch.on("stateChange",function(){b.update()}),r.dispatch.on("elementMouseover.tooltip",k),s.dispatch.on("elementMouseover.tooltip",k),r.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),s.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),v.dispatch.on("elementMouseover.tooltip",l),w.dispatch.on("elementMouseover.tooltip",l),v.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),w.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),t.dispatch.on("elementMouseover.tooltip",n),u.dispatch.on("elementMouseover.tooltip",n),t.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),u.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),t.dispatch.on("elementMousemove.tooltip",function(){B.position({top:d3.event.pageY,left:d3.event.pageX})()}),u.dispatch.on("elementMousemove.tooltip",function(){B.position({top:d3.event.pageY,left:d3.event.pageX})()})}),b}var c,d,e={top:30,right:20,bottom:50,left:60},f=a.utils.defaultColor(),g=null,h=null,i=!0,j=null,k=function(a){return a.x},l=function(a){return a.y},m="monotone",n=!0,o=d3.scale.linear(),p=d3.scale.linear(),q=d3.scale.linear(),r=a.models.line().yScale(p),s=a.models.line().yScale(q),t=a.models.multiBar().stacked(!1).yScale(p),u=a.models.multiBar().stacked(!1).yScale(q),v=a.models.stackedArea().yScale(p),w=a.models.stackedArea().yScale(q),x=a.models.axis().scale(o).orient("bottom").tickPadding(5),y=a.models.axis().scale(p).orient("left"),z=a.models.axis().scale(q).orient("right"),A=a.models.legend().height(30),B=a.models.tooltip(),C=d3.dispatch();return b.dispatch=C,b.lines1=r,b.lines2=s,b.bars1=t,b.bars2=u,b.stack1=v,b.stack2=w,b.xAxis=x,b.yAxis1=y,b.yAxis2=z,b.tooltip=B,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},showLegend:{get:function(){return i},set:function(a){i=a}},yDomain1:{get:function(){return c},set:function(a){c=a}},yDomain2:{get:function(){return d},set:function(a){d=a}},noData:{get:function(){return j},set:function(a){j=a}},interpolate:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return B.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),B.enabled(!!b)}},tooltipContent:{get:function(){return B.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),B.contentGenerator(b)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return f},set:function(b){f=a.utils.getColor(b)}},x:{get:function(){return k},set:function(a){k=a,r.x(a),s.x(a),t.x(a),u.x(a),v.x(a),w.x(a)}},y:{get:function(){return l},set:function(a){l=a,r.y(a),s.y(a),v.y(a),w.y(a),t.y(a),u.y(a)}},useVoronoi:{get:function(){return n},set:function(a){n=a,r.useVoronoi(a),s.useVoronoi(a),v.useVoronoi(a),w.useVoronoi(a)}}}),a.utils.initOptions(b),b},a.models.ohlcBar=function(){"use strict";function b(y){return y.each(function(b){k=d3.select(this);var y=a.utils.availableWidth(h,k,g),A=a.utils.availableHeight(i,k,g);a.utils.initSVG(k);var B=y/b[0].values.length*.9;l.domain(c||d3.extent(b[0].values.map(n).concat(t))),l.range(v?e||[.5*y/b[0].values.length,y*(b[0].values.length-.5)/b[0].values.length]:e||[5+B/2,y-B/2-5]),m.domain(d||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(f||[A,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var C=d3.select(this).selectAll("g.nv-wrap.nv-ohlcBar").data([b[0].values]),D=C.enter().append("g").attr("class","nvd3 nv-wrap nv-ohlcBar"),E=D.append("defs"),F=D.append("g"),G=C.select("g");F.append("g").attr("class","nv-ticks"),C.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:j})}),E.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),C.select("#nv-chart-clip-path-"+j+" rect").attr("width",y).attr("height",A),G.attr("clip-path",w?"url(#nv-chart-clip-path-"+j+")":"");var H=C.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});H.exit().remove(),H.enter().append("path").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}).attr("d",function(a,b){return"m0,0l0,"+(m(p(a,b))-m(r(a,b)))+"l"+-B/2+",0l"+B/2+",0l0,"+(m(s(a,b))-m(p(a,b)))+"l0,"+(m(q(a,b))-m(s(a,b)))+"l"+B/2+",0l"+-B/2+",0z"}).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("fill",function(){return x[0]}).attr("stroke",function(){return x[0]}).attr("x",0).attr("y",function(a,b){return m(Math.max(0,o(a,b)))}).attr("height",function(a,b){return Math.abs(m(o(a,b))-m(0))}),H.attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}),d3.transition(H).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("d",function(a,c){var d=y/b[0].values.length*.9;return"m0,0l0,"+(m(p(a,c))-m(r(a,c)))+"l"+-d/2+",0l"+d/2+",0l0,"+(m(s(a,c))-m(p(a,c)))+"l0,"+(m(q(a,c))-m(s(a,c)))+"l"+d/2+",0l"+-d/2+",0z"})}),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,c){b.clearHighlights(),k.select(".nv-ohlcBar .nv-tick-0-"+a).classed("hover",c)},b.clearHighlights=function(){k.select(".nv-ohlcBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!=a.top?a.top:g.top,g.right=void 0!=a.right?a.right:g.right,g.bottom=void 0!=a.bottom?a.bottom:g.bottom,g.left=void 0!=a.left?a.left:g.left +}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.parallelCoordinates=function(){"use strict";function b(p){return p.each(function(b){function p(a){return F(h.map(function(b){if(isNaN(a[b])||isNaN(parseFloat(a[b]))){var c=g[b].domain(),d=g[b].range(),e=c[0]-(c[1]-c[0])/9;if(J.indexOf(b)<0){var h=d3.scale.linear().domain([e,c[1]]).range([x-12,d[1]]);g[b].brush.y(h),J.push(b)}return[f(b),g[b](e)]}return J.length>0?(D.style("display","inline"),E.style("display","inline")):(D.style("display","none"),E.style("display","none")),[f(b),g[b](a[b])]}))}function q(){var a=h.filter(function(a){return!g[a].brush.empty()}),b=a.map(function(a){return g[a].brush.extent()});k=[],a.forEach(function(a,c){k[c]={dimension:a,extent:b[c]}}),l=[],M.style("display",function(c){var d=a.every(function(a,d){return isNaN(c[a])&&b[d][0]==g[a].brush.y().domain()[0]?!0:b[d][0]<=c[a]&&c[a]<=b[d][1]});return d&&l.push(c),d?null:"none"}),o.brush({filters:k,active:l})}function r(a){m[a]=this.parentNode.__origin__=f(a),L.attr("visibility","hidden")}function s(a){m[a]=Math.min(w,Math.max(0,this.parentNode.__origin__+=d3.event.x)),M.attr("d",p),h.sort(function(a,b){return u(a)-u(b)}),f.domain(h),N.attr("transform",function(a){return"translate("+u(a)+")"})}function t(a){delete this.parentNode.__origin__,delete m[a],d3.select(this.parentNode).attr("transform","translate("+f(a)+")"),M.attr("d",p),L.attr("d",p).attr("visibility",null)}function u(a){var b=m[a];return null==b?f(a):b}var v=d3.select(this),w=a.utils.availableWidth(d,v,c),x=a.utils.availableHeight(e,v,c);a.utils.initSVG(v),l=b,f.rangePoints([0,w],1).domain(h);var y={};h.forEach(function(a){var c=d3.extent(b,function(b){return+b[a]});return y[a]=!1,void 0===c[0]&&(y[a]=!0,c[0]=0,c[1]=0),c[0]===c[1]&&(c[0]=c[0]-1,c[1]=c[1]+1),g[a]=d3.scale.linear().domain(c).range([.9*(x-12),0]),g[a].brush=d3.svg.brush().y(g[a]).on("brush",q),"name"!=a});var z=v.selectAll("g.nv-wrap.nv-parallelCoordinates").data([b]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-parallelCoordinates"),B=A.append("g"),C=z.select("g");B.append("g").attr("class","nv-parallelCoordinates background"),B.append("g").attr("class","nv-parallelCoordinates foreground"),B.append("g").attr("class","nv-parallelCoordinates missingValuesline"),z.attr("transform","translate("+c.left+","+c.top+")");var D,E,F=d3.svg.line().interpolate("cardinal").tension(n),G=d3.svg.axis().orient("left"),H=d3.behavior.drag().on("dragstart",r).on("drag",s).on("dragend",t),I=f.range()[1]-f.range()[0],J=[],K=[0+I/2,x-12,w-I/2,x-12];D=z.select(".missingValuesline").selectAll("line").data([K]),D.enter().append("line"),D.exit().remove(),D.attr("x1",function(a){return a[0]}).attr("y1",function(a){return a[1]}).attr("x2",function(a){return a[2]}).attr("y2",function(a){return a[3]}),E=z.select(".missingValuesline").selectAll("text").data(["undefined values"]),E.append("text").data(["undefined values"]),E.enter().append("text"),E.exit().remove(),E.attr("y",x).attr("x",w-92-I/2).text(function(a){return a});var L=z.select(".background").selectAll("path").data(b);L.enter().append("path"),L.exit().remove(),L.attr("d",p);var M=z.select(".foreground").selectAll("path").data(b);M.enter().append("path"),M.exit().remove(),M.attr("d",p).attr("stroke",j),M.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),o.elementMouseover({label:a.name,data:a.data,index:b,pos:[d3.mouse(this.parentNode)[0],d3.mouse(this.parentNode)[1]]})}),M.on("mouseout",function(a,b){d3.select(this).classed("hover",!1),o.elementMouseout({label:a.name,data:a.data,index:b})});var N=C.selectAll(".dimension").data(h),O=N.enter().append("g").attr("class","nv-parallelCoordinates dimension");O.append("g").attr("class","nv-parallelCoordinates nv-axis"),O.append("g").attr("class","nv-parallelCoordinates-brush"),O.append("text").attr("class","nv-parallelCoordinates nv-label"),N.attr("transform",function(a){return"translate("+f(a)+",0)"}),N.exit().remove(),N.select(".nv-label").style("cursor","move").attr("dy","-1em").attr("text-anchor","middle").text(String).on("mouseover",function(a){o.elementMouseover({dim:a,pos:[d3.mouse(this.parentNode.parentNode)[0],d3.mouse(this.parentNode.parentNode)[1]]})}).on("mouseout",function(a){o.elementMouseout({dim:a})}).call(H),N.select(".nv-axis").each(function(a,b){d3.select(this).call(G.scale(g[a]).tickFormat(d3.format(i[b])))}),N.select(".nv-parallelCoordinates-brush").each(function(a){d3.select(this).call(g[a].brush)}).selectAll("rect").attr("x",-8).attr("width",16)}),b}var c={top:30,right:0,bottom:10,left:0},d=null,e=null,f=d3.scale.ordinal(),g={},h=[],i=[],j=a.utils.defaultColor(),k=[],l=[],m=[],n=1,o=d3.dispatch("brush","elementMouseover","elementMouseout");return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},dimensionNames:{get:function(){return h},set:function(a){h=a}},dimensionFormats:{get:function(){return i},set:function(a){i=a}},lineTension:{get:function(){return n},set:function(a){n=a}},dimensions:{get:function(){return h},set:function(b){a.deprecated("dimensions","use dimensionNames instead"),h=b}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.pie=function(){"use strict";function b(E){return D.reset(),E.each(function(b){function E(a,b){a.endAngle=isNaN(a.endAngle)?0:a.endAngle,a.startAngle=isNaN(a.startAngle)?0:a.startAngle,p||(a.innerRadius=0);var c=d3.interpolate(this._current,a);return this._current=c(0),function(a){return B[b](c(a))}}var F=d-c.left-c.right,G=e-c.top-c.bottom,H=Math.min(F,G)/2,I=[],J=[];if(i=d3.select(this),0===z.length)for(var K=H-H/5,L=y*H,M=0;Mc)return"";if("function"==typeof n)d=n(a,b,{key:f(a.data),value:g(a.data),percent:k(c)});else switch(n){case"key":d=f(a.data);break;case"value":d=k(g(a.data));break;case"percent":d=d3.format("%")(c)}return d})}}),D.renderEnd("pie immediate"),b}var c={top:0,right:0,bottom:0,left:0},d=500,e=500,f=function(a){return a.x},g=function(a){return a.y},h=Math.floor(1e4*Math.random()),i=null,j=a.utils.defaultColor(),k=d3.format(",.2f"),l=!0,m=!1,n="key",o=.02,p=!1,q=!1,r=!0,s=0,t=!1,u=!1,v=!1,w=!1,x=0,y=.5,z=[],A=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),B=[],C=[],D=a.utils.renderWatch(A);return b.dispatch=A,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{arcsRadius:{get:function(){return z},set:function(a){z=a}},width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},showLabels:{get:function(){return l},set:function(a){l=a}},title:{get:function(){return q},set:function(a){q=a}},titleOffset:{get:function(){return s},set:function(a){s=a}},labelThreshold:{get:function(){return o},set:function(a){o=a}},valueFormat:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return h},set:function(a){h=a}},endAngle:{get:function(){return w},set:function(a){w=a}},startAngle:{get:function(){return u},set:function(a){u=a}},padAngle:{get:function(){return v},set:function(a){v=a}},cornerRadius:{get:function(){return x},set:function(a){x=a}},donutRatio:{get:function(){return y},set:function(a){y=a}},labelsOutside:{get:function(){return m},set:function(a){m=a}},labelSunbeamLayout:{get:function(){return t},set:function(a){t=a}},donut:{get:function(){return p},set:function(a){p=a}},growOnHover:{get:function(){return r},set:function(a){r=a}},pieLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("pieLabelsOutside","use labelsOutside instead")}},donutLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("donutLabelsOutside","use labelsOutside instead")}},labelFormat:{get:function(){return k},set:function(b){k=b,a.deprecated("labelFormat","use valueFormat instead")}},margin:{get:function(){return c},set:function(a){c.top="undefined"!=typeof a.top?a.top:c.top,c.right="undefined"!=typeof a.right?a.right:c.right,c.bottom="undefined"!=typeof a.bottom?a.bottom:c.bottom,c.left="undefined"!=typeof a.left?a.left:c.left}},y:{get:function(){return g},set:function(a){g=d3.functor(a)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},labelType:{get:function(){return n},set:function(a){n=a||"key"}}}),a.utils.initOptions(b),b},a.models.pieChart=function(){"use strict";function b(e){return q.reset(),q.models(c),e.each(function(e){var k=d3.select(this);a.utils.initSVG(k);var n=a.utils.availableWidth(g,k,f),o=a.utils.availableHeight(h,k,f);if(b.update=function(){k.transition().call(b)},b.container=this,l.setter(s(e),b.update).getter(r(e)).update(),l.disabled=e.map(function(a){return!!a.disabled}),!m){var q;m={};for(q in l)m[q]=l[q]instanceof Array?l[q].slice(0):l[q]}if(!e||!e.length)return a.utils.noData(b,k),b;k.selectAll(".nv-noData").remove();var t=k.selectAll("g.nv-wrap.nv-pieChart").data([e]),u=t.enter().append("g").attr("class","nvd3 nv-wrap nv-pieChart").append("g"),v=t.select("g");if(u.append("g").attr("class","nv-pieWrap"),u.append("g").attr("class","nv-legendWrap"),i)if("top"===j)d.width(n).key(c.x()),t.select(".nv-legendWrap").datum(e).call(d),f.top!=d.height()&&(f.top=d.height(),o=a.utils.availableHeight(h,k,f)),t.select(".nv-legendWrap").attr("transform","translate(0,"+-f.top+")");else if("right"===j){var w=a.models.legend().width();w>n/2&&(w=n/2),d.height(o).key(c.x()),d.width(w),n-=d.width(),t.select(".nv-legendWrap").datum(e).call(d).attr("transform","translate("+n+",0)")}t.attr("transform","translate("+f.left+","+f.top+")"),c.width(n).height(o);var x=v.select(".nv-pieWrap").datum([e]);d3.transition(x).call(c),d.dispatch.on("stateChange",function(a){for(var c in a)l[c]=a[c];p.stateChange(l),b.update()}),p.on("changeState",function(a){"undefined"!=typeof a.disabled&&(e.forEach(function(b,c){b.disabled=a.disabled[c]}),l.disabled=a.disabled),b.update()})}),q.renderEnd("pieChart immediate"),b}var c=a.models.pie(),d=a.models.legend(),e=a.models.tooltip(),f={top:30,right:20,bottom:20,left:20},g=null,h=null,i=!0,j="top",k=a.utils.defaultColor(),l=a.utils.state(),m=null,n=null,o=250,p=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd");e.headerEnabled(!1).duration(0).valueFormatter(function(a,b){return c.valueFormat()(a,b)});var q=a.utils.renderWatch(p),r=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},s=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},e.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){e.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){e.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.legend=d,b.dispatch=p,b.pie=c,b.tooltip=e,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return i},set:function(a){i=a}},legendPosition:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return e.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),e.enabled(!!b)}},tooltipContent:{get:function(){return e.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),e.contentGenerator(b)}},color:{get:function(){return k},set:function(a){k=a,d.color(k),c.color(k)}},duration:{get:function(){return o},set:function(a){o=a,q.reset(o)}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.scatter=function(){"use strict";function b(N){return P.reset(),N.each(function(b){function N(){if(O=!1,!w)return!1;if(M===!0){var a=d3.merge(b.map(function(a,b){return a.values.map(function(a,c){var d=p(a,c),e=q(a,c);return[m(d)+1e-4*Math.random(),n(e)+1e-4*Math.random(),b,c,a]}).filter(function(a,b){return x(a[4],b)})}));if(0==a.length)return!1;a.length<3&&(a.push([m.range()[0]-20,n.range()[0]-20,null,null]),a.push([m.range()[1]+20,n.range()[1]+20,null,null]),a.push([m.range()[0]-20,n.range()[0]+20,null,null]),a.push([m.range()[1]+20,n.range()[1]-20,null,null]));var c=d3.geom.polygon([[-10,-10],[-10,i+10],[h+10,i+10],[h+10,-10]]),d=d3.geom.voronoi(a).map(function(b,d){return{data:c.clip(b),series:a[d][2],point:a[d][3]}});U.select(".nv-point-paths").selectAll("path").remove();var e=U.select(".nv-point-paths").selectAll("path").data(d),f=e.enter().append("svg:path").attr("d",function(a){return a&&a.data&&0!==a.data.length?"M"+a.data.join(",")+"Z":"M 0 0"}).attr("id",function(a,b){return"nv-path-"+b}).attr("clip-path",function(a,b){return"url(#nv-clip-"+b+")"});C&&f.style("fill",d3.rgb(230,230,230)).style("fill-opacity",.4).style("stroke-opacity",1).style("stroke",d3.rgb(200,200,200)),B&&(U.select(".nv-point-clips").selectAll("clipPath").remove(),U.select(".nv-point-clips").selectAll("clipPath").data(a).enter().append("svg:clipPath").attr("id",function(a,b){return"nv-clip-"+b}).append("svg:circle").attr("cx",function(a){return a[0]}).attr("cy",function(a){return a[1]}).attr("r",D));var k=function(a,c){if(O)return 0;var d=b[a.series];if(void 0!==d){var e=d.values[a.point];e.color=j(d,a.series),e.x=p(e),e.y=q(e);var f=l.node().getBoundingClientRect(),h=window.pageYOffset||document.documentElement.scrollTop,i=window.pageXOffset||document.documentElement.scrollLeft,k={left:m(p(e,a.point))+f.left+i+g.left+10,top:n(q(e,a.point))+f.top+h+g.top+10};c({point:e,series:d,pos:k,seriesIndex:a.series,pointIndex:a.point})}};e.on("click",function(a){k(a,L.elementClick)}).on("dblclick",function(a){k(a,L.elementDblClick)}).on("mouseover",function(a){k(a,L.elementMouseover)}).on("mouseout",function(a){k(a,L.elementMouseout)})}else U.select(".nv-groups").selectAll(".nv-group").selectAll(".nv-point").on("click",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("dblclick",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementDblClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("mouseover",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseover({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c,color:j(a,c)})}).on("mouseout",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseout({point:e,series:d,seriesIndex:a.series,pointIndex:c,color:j(a,c)})})}l=d3.select(this);var R=a.utils.availableWidth(h,l,g),S=a.utils.availableHeight(i,l,g);a.utils.initSVG(l),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var T=E&&F&&I?[]:d3.merge(b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),size:r(a,b)}})}));m.domain(E||d3.extent(T.map(function(a){return a.x}).concat(t))),m.range(y&&b[0]?G||[(R*z+R)/(2*b[0].values.length),R-R*(1+z)/(2*b[0].values.length)]:G||[0,R]),n.domain(F||d3.extent(T.map(function(a){return a.y}).concat(u))).range(H||[S,0]),o.domain(I||d3.extent(T.map(function(a){return a.size}).concat(v))).range(J||Q),K=m.domain()[0]===m.domain()[1]||n.domain()[0]===n.domain()[1],m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]-.01*n.domain()[0],n.domain()[1]+.01*n.domain()[1]]:[-1,1]),isNaN(m.domain()[0])&&m.domain([-1,1]),isNaN(n.domain()[0])&&n.domain([-1,1]),c=c||m,d=d||n,e=e||o;var U=l.selectAll("g.nv-wrap.nv-scatter").data([b]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-scatter nv-chart-"+k),W=V.append("defs"),X=V.append("g"),Y=U.select("g");U.classed("nv-single-point",K),X.append("g").attr("class","nv-groups"),X.append("g").attr("class","nv-point-paths"),V.append("g").attr("class","nv-point-clips"),U.attr("transform","translate("+g.left+","+g.top+")"),W.append("clipPath").attr("id","nv-edge-clip-"+k).append("rect"),U.select("#nv-edge-clip-"+k+" rect").attr("width",R).attr("height",S>0?S:0),Y.attr("clip-path",A?"url(#nv-edge-clip-"+k+")":""),O=!0;var Z=U.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});Z.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),Z.exit().remove(),Z.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),Z.watchTransition(P,"scatter: groups").style("fill",function(a,b){return j(a,b)}).style("stroke",function(a,b){return j(a,b)}).style("stroke-opacity",1).style("fill-opacity",.5);var $=Z.selectAll("path.nv-point").data(function(a){return a.values.map(function(a,b){return[a,b]}).filter(function(a,b){return x(a[0],b)})});$.enter().append("path").style("fill",function(a){return a.color}).style("stroke",function(a){return a.color}).attr("transform",function(a){return"translate("+c(p(a[0],a[1]))+","+d(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),$.exit().remove(),Z.exit().selectAll("path.nv-point").watchTransition(P,"scatter exit").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).remove(),$.each(function(a){d3.select(this).classed("nv-point",!0).classed("nv-point-"+a[1],!0).classed("nv-noninteractive",!w).classed("hover",!1)}),$.watchTransition(P,"scatter points").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),clearTimeout(f),f=setTimeout(N,300),c=m.copy(),d=n.copy(),e=o.copy()}),P.renderEnd("scatter immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=a.utils.defaultColor(),k=Math.floor(1e5*Math.random()),l=null,m=d3.scale.linear(),n=d3.scale.linear(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=function(a){return a.size||1},s=function(a){return a.shape||"circle"},t=[],u=[],v=[],w=!0,x=function(a){return!a.notActive},y=!1,z=.1,A=!1,B=!0,C=!1,D=function(){return 25},E=null,F=null,G=null,H=null,I=null,J=null,K=!1,L=d3.dispatch("elementClick","elementDblClick","elementMouseover","elementMouseout","renderEnd"),M=!0,N=250,O=!1,P=a.utils.renderWatch(L,N),Q=[16,256];return b.dispatch=L,b.options=a.utils.optionsFunc.bind(b),b._calls=new function(){this.clearHighlights=function(){return a.dom.write(function(){l.selectAll(".nv-point.hover").classed("hover",!1)}),null},this.highlightPoint=function(b,c,d){a.dom.write(function(){l.select(" .nv-series-"+b+" .nv-point-"+c).classed("hover",d)})}},L.on("elementMouseover.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!0)}),L.on("elementMouseout.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!1)}),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},pointScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return E},set:function(a){E=a}},yDomain:{get:function(){return F},set:function(a){F=a}},pointDomain:{get:function(){return I},set:function(a){I=a}},xRange:{get:function(){return G},set:function(a){G=a}},yRange:{get:function(){return H},set:function(a){H=a}},pointRange:{get:function(){return J},set:function(a){J=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},forcePoint:{get:function(){return v},set:function(a){v=a}},interactive:{get:function(){return w},set:function(a){w=a}},pointActive:{get:function(){return x},set:function(a){x=a}},padDataOuter:{get:function(){return z},set:function(a){z=a}},padData:{get:function(){return y},set:function(a){y=a}},clipEdge:{get:function(){return A},set:function(a){A=a}},clipVoronoi:{get:function(){return B},set:function(a){B=a}},clipRadius:{get:function(){return D},set:function(a){D=a}},showVoronoi:{get:function(){return C},set:function(a){C=a}},id:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return p},set:function(a){p=d3.functor(a)}},y:{get:function(){return q},set:function(a){q=d3.functor(a)}},pointSize:{get:function(){return r},set:function(a){r=d3.functor(a)}},pointShape:{get:function(){return s},set:function(a){s=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},duration:{get:function(){return N},set:function(a){N=a,P.reset(N)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},useVoronoi:{get:function(){return M},set:function(a){M=a,M===!1&&(B=!1)}}}),a.utils.initOptions(b),b},a.models.scatterChart=function(){"use strict";function b(z){return D.reset(),D.models(c),t&&D.models(d),u&&D.models(e),q&&D.models(g),r&&D.models(h),z.each(function(z){m=d3.select(this),a.utils.initSVG(m);var G=a.utils.availableWidth(k,m,j),H=a.utils.availableHeight(l,m,j);if(b.update=function(){0===A?m.call(b):m.transition().duration(A).call(b)},b.container=this,w.setter(F(z),b.update).getter(E(z)).update(),w.disabled=z.map(function(a){return!!a.disabled}),!x){var I;x={};for(I in w)x[I]=w[I]instanceof Array?w[I].slice(0):w[I]}if(!(z&&z.length&&z.filter(function(a){return a.values.length}).length))return a.utils.noData(b,m),D.renderEnd("scatter immediate"),b;m.selectAll(".nv-noData").remove(),o=c.xScale(),p=c.yScale();var J=m.selectAll("g.nv-wrap.nv-scatterChart").data([z]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-scatterChart nv-chart-"+c.id()),L=K.append("g"),M=J.select("g");if(L.append("rect").attr("class","nvd3 nv-background").style("pointer-events","none"),L.append("g").attr("class","nv-x nv-axis"),L.append("g").attr("class","nv-y nv-axis"),L.append("g").attr("class","nv-scatterWrap"),L.append("g").attr("class","nv-regressionLinesWrap"),L.append("g").attr("class","nv-distWrap"),L.append("g").attr("class","nv-legendWrap"),v&&M.select(".nv-y.nv-axis").attr("transform","translate("+G+",0)"),s){var N=G;f.width(N),J.select(".nv-legendWrap").datum(z).call(f),j.top!=f.height()&&(j.top=f.height(),H=a.utils.availableHeight(l,m,j)),J.select(".nv-legendWrap").attr("transform","translate(0,"+-j.top+")")}J.attr("transform","translate("+j.left+","+j.top+")"),c.width(G).height(H).color(z.map(function(a,b){return a.color=a.color||n(a,b),a.color}).filter(function(a,b){return!z[b].disabled})),J.select(".nv-scatterWrap").datum(z.filter(function(a){return!a.disabled})).call(c),J.select(".nv-regressionLinesWrap").attr("clip-path","url(#nv-edge-clip-"+c.id()+")");var O=J.select(".nv-regressionLinesWrap").selectAll(".nv-regLines").data(function(a){return a});O.enter().append("g").attr("class","nv-regLines");var P=O.selectAll(".nv-regLine").data(function(a){return[a]});P.enter().append("line").attr("class","nv-regLine").style("stroke-opacity",0),P.filter(function(a){return a.intercept&&a.slope}).watchTransition(D,"scatterPlusLineChart: regline").attr("x1",o.range()[0]).attr("x2",o.range()[1]).attr("y1",function(a){return p(o.domain()[0]*a.slope+a.intercept)}).attr("y2",function(a){return p(o.domain()[1]*a.slope+a.intercept)}).style("stroke",function(a,b,c){return n(a,c)}).style("stroke-opacity",function(a){return a.disabled||"undefined"==typeof a.slope||"undefined"==typeof a.intercept?0:1}),t&&(d.scale(o)._ticks(a.utils.calcTicksX(G/100,z)).tickSize(-H,0),M.select(".nv-x.nv-axis").attr("transform","translate(0,"+p.range()[0]+")").call(d)),u&&(e.scale(p)._ticks(a.utils.calcTicksY(H/36,z)).tickSize(-G,0),M.select(".nv-y.nv-axis").call(e)),q&&(g.getData(c.x()).scale(o).width(G).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionX"),M.select(".nv-distributionX").attr("transform","translate(0,"+p.range()[0]+")").datum(z.filter(function(a){return!a.disabled})).call(g)),r&&(h.getData(c.y()).scale(p).width(H).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionY"),M.select(".nv-distributionY").attr("transform","translate("+(v?G:-h.size())+",0)").datum(z.filter(function(a){return!a.disabled})).call(h)),f.dispatch.on("stateChange",function(a){for(var c in a)w[c]=a[c];y.stateChange(w),b.update()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&(z.forEach(function(b,c){b.disabled=a.disabled[c]}),w.disabled=a.disabled),b.update()}),c.dispatch.on("elementMouseout.tooltip",function(a){i.hidden(!0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",h.size())}),c.dispatch.on("elementMouseover.tooltip",function(a){m.select(".nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",a.pos.top-H-j.top),m.select(".nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",a.pos.left+g.size()-j.left),i.position(a.pos).data(a).hidden(!1)}),B=o.copy(),C=p.copy()}),D.renderEnd("scatter with line immediate"),b}var c=a.models.scatter(),d=a.models.axis(),e=a.models.axis(),f=a.models.legend(),g=a.models.distribution(),h=a.models.distribution(),i=a.models.tooltip(),j={top:30,right:20,bottom:50,left:75},k=null,l=null,m=null,n=a.utils.defaultColor(),o=c.xScale(),p=c.yScale(),q=!1,r=!1,s=!0,t=!0,u=!0,v=!1,w=a.utils.state(),x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=null,A=250;c.xScale(o).yScale(p),d.orient("bottom").tickPadding(10),e.orient(v?"right":"left").tickPadding(10),g.axis("x"),h.axis("y"),i.headerFormatter(function(a,b){return d.tickFormat()(a,b)}).valueFormatter(function(a,b){return e.tickFormat()(a,b)});var B,C,D=a.utils.renderWatch(y,A),E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return b.dispatch=y,b.scatter=c,b.legend=f,b.xAxis=d,b.yAxis=e,b.distX=g,b.distY=h,b.tooltip=i,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},container:{get:function(){return m},set:function(a){m=a}},showDistX:{get:function(){return q},set:function(a){q=a}},showDistY:{get:function(){return r},set:function(a){r=a}},showLegend:{get:function(){return s},set:function(a){s=a}},showXAxis:{get:function(){return t},set:function(a){t=a}},showYAxis:{get:function(){return u},set:function(a){u=a}},defaultState:{get:function(){return x},set:function(a){x=a}},noData:{get:function(){return z},set:function(a){z=a}},duration:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return i.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),i.enabled(!!b) +}},tooltipContent:{get:function(){return i.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),i.contentGenerator(b)}},tooltipXContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},tooltipYContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},rightAlignYAxis:{get:function(){return v},set:function(a){v=a,e.orient(a?"right":"left")}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),f.color(n),g.color(n),h.color(n)}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.sparkline=function(){"use strict";function b(k){return k.each(function(b){var k=h-g.left-g.right,q=i-g.top-g.bottom;j=d3.select(this),a.utils.initSVG(j),l.domain(c||d3.extent(b,n)).range(e||[0,k]),m.domain(d||d3.extent(b,o)).range(f||[q,0]);{var r=j.selectAll("g.nv-wrap.nv-sparkline").data([b]),s=r.enter().append("g").attr("class","nvd3 nv-wrap nv-sparkline");s.append("g"),r.select("g")}r.attr("transform","translate("+g.left+","+g.top+")");var t=r.selectAll("path").data(function(a){return[a]});t.enter().append("path"),t.exit().remove(),t.style("stroke",function(a,b){return a.color||p(a,b)}).attr("d",d3.svg.line().x(function(a,b){return l(n(a,b))}).y(function(a,b){return m(o(a,b))}));var u=r.selectAll("circle.nv-point").data(function(a){function b(b){if(-1!=b){var c=a[b];return c.pointIndex=b,c}return null}var c=a.map(function(a,b){return o(a,b)}),d=b(c.lastIndexOf(m.domain()[1])),e=b(c.indexOf(m.domain()[0])),f=b(c.length-1);return[e,d,f].filter(function(a){return null!=a})});u.enter().append("circle"),u.exit().remove(),u.attr("cx",function(a){return l(n(a,a.pointIndex))}).attr("cy",function(a){return m(o(a,a.pointIndex))}).attr("r",2).attr("class",function(a){return n(a,a.pointIndex)==l.domain()[1]?"nv-point nv-currentValue":o(a,a.pointIndex)==m.domain()[0]?"nv-point nv-minValue":"nv-point nv-maxValue"})}),b}var c,d,e,f,g={top:2,right:0,bottom:2,left:0},h=400,i=32,j=null,k=!0,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=a.utils.getColor(["#000"]);return b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},animate:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return n},set:function(a){n=d3.functor(a)}},y:{get:function(){return o},set:function(a){o=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return p},set:function(b){p=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sparklinePlus=function(){"use strict";function b(p){return p.each(function(p){function q(){if(!j){var a=z.selectAll(".nv-hoverValue").data(i),b=a.enter().append("g").attr("class","nv-hoverValue").style("stroke-opacity",0).style("fill-opacity",0);a.exit().transition().duration(250).style("stroke-opacity",0).style("fill-opacity",0).remove(),a.attr("transform",function(a){return"translate("+c(e.x()(p[a],a))+",0)"}).transition().duration(250).style("stroke-opacity",1).style("fill-opacity",1),i.length&&(b.append("line").attr("x1",0).attr("y1",-f.top).attr("x2",0).attr("y2",u),b.append("text").attr("class","nv-xValue").attr("x",-6).attr("y",-f.top).attr("text-anchor","end").attr("dy",".9em"),z.select(".nv-hoverValue .nv-xValue").text(k(e.x()(p[i[0]],i[0]))),b.append("text").attr("class","nv-yValue").attr("x",6).attr("y",-f.top).attr("text-anchor","start").attr("dy",".9em"),z.select(".nv-hoverValue .nv-yValue").text(l(e.y()(p[i[0]],i[0]))))}}function r(){function a(a,b){for(var c=Math.abs(e.x()(a[0],0)-b),d=0,f=0;fc;++c){for(b=0,d=0;bb;b++)a[b][c][1]/=d;else for(b=0;e>b;b++)a[b][c][1]=0}for(c=0;f>c;++c)g[c]=0;return g}}),u.renderEnd("stackedArea immediate"),b}var c,d,e={top:0,right:0,bottom:0,left:0},f=960,g=500,h=a.utils.defaultColor(),i=Math.floor(1e5*Math.random()),j=null,k=function(a){return a.x},l=function(a){return a.y},m="stack",n="zero",o="default",p="linear",q=!1,r=a.models.scatter(),s=250,t=d3.dispatch("areaClick","areaMouseover","areaMouseout","renderEnd","elementClick","elementMouseover","elementMouseout");r.pointSize(2.2).pointDomain([2.2,2.2]);var u=a.utils.renderWatch(t,s);return b.dispatch=t,b.scatter=r,r.dispatch.on("elementClick",function(){t.elementClick.apply(this,arguments)}),r.dispatch.on("elementMouseover",function(){t.elementMouseover.apply(this,arguments)}),r.dispatch.on("elementMouseout",function(){t.elementMouseout.apply(this,arguments)}),b.interpolate=function(a){return arguments.length?(p=a,b):p},b.duration=function(a){return arguments.length?(s=a,u.reset(s),r.duration(s),b):s},b.dispatch=t,b.scatter=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return f},set:function(a){f=a}},height:{get:function(){return g},set:function(a){g=a}},clipEdge:{get:function(){return q},set:function(a){q=a}},offset:{get:function(){return n},set:function(a){n=a}},order:{get:function(){return o},set:function(a){o=a}},interpolate:{get:function(){return p},set:function(a){p=a}},x:{get:function(){return k},set:function(a){k=d3.functor(a)}},y:{get:function(){return l},set:function(a){l=d3.functor(a)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return h},set:function(b){h=a.utils.getColor(b)}},style:{get:function(){return m},set:function(a){switch(m=a){case"stack":b.offset("zero"),b.order("default");break;case"stream":b.offset("wiggle"),b.order("inside-out");break;case"stream-center":b.offset("silhouette"),b.order("inside-out");break;case"expand":b.offset("expand"),b.order("default");break;case"stack_percent":b.offset(b.d3_stackedOffset_stackPercent),b.order("default")}}},duration:{get:function(){return s},set:function(a){s=a,u.reset(s),r.duration(s)}}}),a.utils.inheritOptions(b,r),a.utils.initOptions(b),b},a.models.stackedAreaChart=function(){"use strict";function b(k){return F.reset(),F.models(e),r&&F.models(f),s&&F.models(g),k.each(function(k){var x=d3.select(this),F=this;a.utils.initSVG(x);var K=a.utils.availableWidth(m,x,l),L=a.utils.availableHeight(n,x,l);if(b.update=function(){x.transition().duration(C).call(b)},b.container=this,v.setter(I(k),b.update).getter(H(k)).update(),v.disabled=k.map(function(a){return!!a.disabled}),!w){var M;w={};for(M in v)w[M]=v[M]instanceof Array?v[M].slice(0):v[M]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(b,x),b;x.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var N=x.selectAll("g.nv-wrap.nv-stackedAreaChart").data([k]),O=N.enter().append("g").attr("class","nvd3 nv-wrap nv-stackedAreaChart").append("g"),P=N.select("g");if(O.append("rect").style("opacity",0),O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y nv-axis"),O.append("g").attr("class","nv-stackedWrap"),O.append("g").attr("class","nv-legendWrap"),O.append("g").attr("class","nv-controlsWrap"),O.append("g").attr("class","nv-interactive"),P.select("rect").attr("width",K).attr("height",L),q){var Q=p?K-z:K;h.width(Q),P.select(".nv-legendWrap").datum(k).call(h),l.top!=h.height()&&(l.top=h.height(),L=a.utils.availableHeight(n,x,l)),P.select(".nv-legendWrap").attr("transform","translate("+(K-Q)+","+-l.top+")")}if(p){var R=[{key:B.stacked||"Stacked",metaKey:"Stacked",disabled:"stack"!=e.style(),style:"stack"},{key:B.stream||"Stream",metaKey:"Stream",disabled:"stream"!=e.style(),style:"stream"},{key:B.expanded||"Expanded",metaKey:"Expanded",disabled:"expand"!=e.style(),style:"expand"},{key:B.stack_percent||"Stack %",metaKey:"Stack_Percent",disabled:"stack_percent"!=e.style(),style:"stack_percent"}];z=A.length/3*260,R=R.filter(function(a){return-1!==A.indexOf(a.metaKey)}),i.width(z).color(["#444","#444","#444"]),P.select(".nv-controlsWrap").datum(R).call(i),l.top!=Math.max(i.height(),h.height())&&(l.top=Math.max(i.height(),h.height()),L=a.utils.availableHeight(n,x,l)),P.select(".nv-controlsWrap").attr("transform","translate(0,"+-l.top+")")}N.attr("transform","translate("+l.left+","+l.top+")"),t&&P.select(".nv-y.nv-axis").attr("transform","translate("+K+",0)"),u&&(j.width(K).height(L).margin({left:l.left,top:l.top}).svgContainer(x).xScale(c),N.select(".nv-interactive").call(j)),e.width(K).height(L);var S=P.select(".nv-stackedWrap").datum(k);if(S.transition().call(e),r&&(f.scale(c)._ticks(a.utils.calcTicksX(K/100,k)).tickSize(-L,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+L+")"),P.select(".nv-x.nv-axis").transition().duration(0).call(f)),s){var T;if(T="wiggle"===e.offset()?0:a.utils.calcTicksY(L/36,k),g.scale(d)._ticks(T).tickSize(-K,0),"expand"===e.style()||"stack_percent"===e.style()){var U=g.tickFormat();D&&U===J||(D=U),g.tickFormat(J)}else D&&(g.tickFormat(D),D=null);P.select(".nv-y.nv-axis").transition().duration(0).call(g)}e.dispatch.on("areaClick.toggle",function(a){k.forEach(1===k.filter(function(a){return!a.disabled}).length?function(a){a.disabled=!1}:function(b,c){b.disabled=c!=a.seriesIndex}),v.disabled=k.map(function(a){return!!a.disabled}),y.stateChange(v),b.update()}),h.dispatch.on("stateChange",function(a){for(var c in a)v[c]=a[c];y.stateChange(v),b.update()}),i.dispatch.on("legendClick",function(a){a.disabled&&(R=R.map(function(a){return a.disabled=!0,a}),a.disabled=!1,e.style(a.style),v.style=e.style(),y.stateChange(v),b.update())}),j.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,g,h,i=[];if(k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,j){g=a.interactiveBisect(f.values,c.pointXValue,b.x());var k=f.values[g],l=b.y()(k,g);if(null!=l&&e.highlightPoint(j,g,!0),"undefined"!=typeof k){"undefined"==typeof d&&(d=k),"undefined"==typeof h&&(h=b.xScale()(b.x()(k,g)));var m="expand"==e.style()?k.display.y:b.y()(k,g);i.push({key:f.key,value:m,color:o(f,f.seriesIndex),stackedValue:k.display})}}),i.reverse(),i.length>2){var m=b.yScale().invert(c.mouseY),n=null;i.forEach(function(a,b){m=Math.abs(m);var c=Math.abs(a.stackedValue.y0),d=Math.abs(a.stackedValue.y);return m>=c&&d+c>=m?void(n=b):void 0}),null!=n&&(i[n].highlight=!0)}var p=f.tickFormat()(b.x()(d,g)),q=j.tooltip.valueFormatter();"expand"===e.style()||"stack_percent"===e.style()?(E||(E=q),q=d3.format(".1%")):E&&(q=E,E=null),j.tooltip.position({left:h+l.left,top:c.mouseY+l.top}).chartContainer(F.parentNode).valueFormatter(q).data({value:p,series:i})(),j.renderGuideLine(h)}),j.dispatch.on("elementMouseout",function(){e.clearHighlights()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&k.length===a.disabled.length&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),v.disabled=a.disabled),"undefined"!=typeof a.style&&(e.style(a.style),G=a.style),b.update()})}),F.renderEnd("stacked Area chart immediate"),b}var c,d,e=a.models.stackedArea(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:25,bottom:50,left:60},m=null,n=null,o=a.utils.defaultColor(),p=!0,q=!0,r=!0,s=!0,t=!1,u=!1,v=a.utils.state(),w=null,x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=250,A=["Stacked","Stream","Expanded"],B={},C=250;v.style=e.style(),f.orient("bottom").tickPadding(7),g.orient(t?"right":"left"),k.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)}),j.tooltip.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)});var D=null,E=null;i.updateState(!1);var F=a.utils.renderWatch(y),G=e.style(),H=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),style:e.style()}}},I=function(a){return function(b){void 0!==b.style&&(G=b.style),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},J=d3.format("%");return e.dispatch.on("elementMouseover.tooltip",function(a){a.point.x=e.x()(a.point),a.point.y=e.y()(a.point),k.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),b.dispatch=y,b.stacked=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.interactiveLayer=j,b.tooltip=k,b.dispatch=y,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return w},set:function(a){w=a}},noData:{get:function(){return x},set:function(a){x=a}},showControls:{get:function(){return p},set:function(a){p=a}},controlLabels:{get:function(){return B},set:function(a){B=a}},controlOptions:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},duration:{get:function(){return C},set:function(a){C=a,F.reset(C),e.duration(C),f.duration(C),g.duration(C)}},color:{get:function(){return o},set:function(b){o=a.utils.getColor(b),h.color(o),e.color(o)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},useInteractiveGuideline:{get:function(){return u},set:function(a){u=!!a,b.interactive(!a),b.useVoronoi(!a),e.scatter.interactive(!a)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.sunburst=function(){"use strict";function b(u){return t.reset(),u.each(function(b){function t(a){a.x0=a.x,a.dx0=a.dx}function u(a){var b=d3.interpolate(p.domain(),[a.x,a.x+a.dx]),c=d3.interpolate(q.domain(),[a.y,1]),d=d3.interpolate(q.range(),[a.y?20:0,y]);return function(a,e){return e?function(){return s(a)}:function(e){return p.domain(b(e)),q.domain(c(e)).range(d(e)),s(a)}}}l=d3.select(this);var v,w=a.utils.availableWidth(g,l,f),x=a.utils.availableHeight(h,l,f),y=Math.min(w,x)/2;a.utils.initSVG(l);var z=l.selectAll(".nv-wrap.nv-sunburst").data(b),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburst nv-chart-"+k),B=A.selectAll("nv-sunburst");z.attr("transform","translate("+w/2+","+x/2+")"),l.on("click",function(a,b){o.chartClick({data:a,index:b,pos:d3.event,id:k})}),q.range([0,y]),c=c||b,e=b[0],r.value(j[i]||j.count),v=B.data(r.nodes).enter().append("path").attr("d",s).style("fill",function(a){return m((a.children?a:a.parent).name)}).style("stroke","#FFF").on("click",function(a){d!==c&&c!==a&&(d=c),c=a,v.transition().duration(n).attrTween("d",u(a))}).each(t).on("dblclick",function(a){d.parent==a&&v.transition().duration(n).attrTween("d",u(e))}).each(t).on("mouseover",function(a){d3.select(this).classed("hover",!0).style("opacity",.8),o.elementMouseover({data:a,color:d3.select(this).style("fill")})}).on("mouseout",function(a){d3.select(this).classed("hover",!1).style("opacity",1),o.elementMouseout({data:a})}).on("mousemove",function(a){o.elementMousemove({data:a})})}),t.renderEnd("sunburst immediate"),b}var c,d,e,f={top:0,right:0,bottom:0,left:0},g=null,h=null,i="count",j={count:function(){return 1},size:function(a){return a.size}},k=Math.floor(1e4*Math.random()),l=null,m=a.utils.defaultColor(),n=500,o=d3.dispatch("chartClick","elementClick","elementDblClick","elementMousemove","elementMouseover","elementMouseout","renderEnd"),p=d3.scale.linear().range([0,2*Math.PI]),q=d3.scale.sqrt(),r=d3.layout.partition().sort(null).value(function(){return 1}),s=d3.svg.arc().startAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x)))}).endAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x+a.dx)))}).innerRadius(function(a){return Math.max(0,q(a.y))}).outerRadius(function(a){return Math.max(0,q(a.y+a.dy))}),t=a.utils.renderWatch(o);return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},mode:{get:function(){return i},set:function(a){i=a}},id:{get:function(){return k},set:function(a){k=a}},duration:{get:function(){return n},set:function(a){n=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!=a.top?a.top:f.top,f.right=void 0!=a.right?a.right:f.right,f.bottom=void 0!=a.bottom?a.bottom:f.bottom,f.left=void 0!=a.left?a.left:f.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sunburstChart=function(){"use strict";function b(d){return m.reset(),m.models(c),d.each(function(d){var h=d3.select(this);a.utils.initSVG(h);var i=a.utils.availableWidth(f,h,e),j=a.utils.availableHeight(g,h,e);if(b.update=function(){0===k?h.call(b):h.transition().duration(k).call(b)},b.container=this,!d||!d.length)return a.utils.noData(b,h),b;h.selectAll(".nv-noData").remove();var l=h.selectAll("g.nv-wrap.nv-sunburstChart").data(d),m=l.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburstChart").append("g"),n=l.select("g");m.append("g").attr("class","nv-sunburstWrap"),l.attr("transform","translate("+e.left+","+e.top+")"),c.width(i).height(j);var o=n.select(".nv-sunburstWrap").datum(d);d3.transition(o).call(c)}),m.renderEnd("sunburstChart immediate"),b}var c=a.models.sunburst(),d=a.models.tooltip(),e={top:30,right:20,bottom:20,left:20},f=null,g=null,h=a.utils.defaultColor(),i=(Math.round(1e5*Math.random()),null),j=null,k=250,l=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),m=a.utils.renderWatch(l);return d.headerEnabled(!1).duration(0).valueFormatter(function(a){return a}),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.data.name,value:a.data.size,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=l,b.sunburst=c,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return i},set:function(a){i=a}},color:{get:function(){return h},set:function(a){h=a,c.color(h)}},duration:{get:function(){return k},set:function(a){k=a,m.reset(k),c.duration(k)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.version="1.8.1"}(); $(function() { + var $window = $(window) + , $top_link = $('#toplink') + , $body = $('body, html') + , offset = $('#code').offset().top + , hidePopover = function ($target) { + $target.data('popover-hover', false); + + setTimeout(function () { + if (!$target.data('popover-hover')) { + $target.popover('hide'); + } + }, 300); + }; + + $top_link.hide().click(function(event) { + event.preventDefault(); + $body.animate({scrollTop:0}, 800); + }); + + $window.scroll(function() { + if($window.scrollTop() > offset) { + $top_link.fadeIn(); + } else { + $top_link.fadeOut(); + } + }).scroll(); + + $('.popin') + .popover({trigger: 'manual'}) + .on({ + 'mouseenter.popover': function () { + var $target = $(this); + var $container = $target.children().first(); + + $target.data('popover-hover', true); + + // popover already displayed + if ($target.next('.popover').length) { + return; + } + + // show the popover + $container.popover('show'); + + // register mouse events on the popover + $target.next('.popover:not(.popover-initialized)') + .on({ + 'mouseenter': function () { + $target.data('popover-hover', true); + }, + 'mouseleave': function () { + hidePopover($container); } - return null; - } + }) + .addClass('popover-initialized'); + }, + 'mouseleave.popover': function () { + hidePopover($(this).children().first()); + } + }); + }); +!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:NaN}function r(n){return null===n?NaN:+n}function i(n){return!isNaN(n)}function u(n){return{left:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)<0?r=u+1:i=u}return r},right:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)>0?i=u:r=u+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function l(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function c(){this._=Object.create(null)}function f(n){return(n+="")===bo||n[0]===_o?_o+n:n}function s(n){return(n+="")[0]===_o?n.slice(1):n}function h(n){return f(n)in this._}function p(n){return(n=f(n))in this._&&delete this._[n]}function g(){var n=[];for(var t in this._)n.push(s(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function y(){this._=Object.create(null)}function m(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=wo.length;r>e;++e){var i=wo[e]+t;if(i in n)return i}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,i=-1,u=r.length;++ie;e++)for(var i,u=n[e],o=0,a=u.length;a>o;o++)(i=u[o])&&t(i,o,e);return n}function Z(n){return ko(n,qo),n}function V(n){var t,e;return function(r,i,u){var o,a=n[u].update,l=a.length;for(u!=e&&(e=u,t=0),i>=t&&(t=i+1);!(o=a[t])&&++t0&&(n=n.slice(0,a));var c=To.get(n);return c&&(n=c,l=B),a?t?i:r:t?b:u}function $(n,t){return function(e){var r=ao.event;ao.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ao.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Do,i="click"+r,u=ao.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ro&&(Ro="onselectstart"in e?!1:x(e.style,"userSelect")),Ro){var o=n(e).style,a=o[Ro];o[Ro]="none"}return function(n){if(u.on(r,null),Ro&&(o[Ro]=a),n){var t=function(){u.on(i,null)};u.on(i,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var i=r.createSVGPoint();if(0>Po){var u=t(n);if(u.scrollX||u.scrollY){r=ao.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Po=!(o.f||o.e),r.remove()}}return Po?(i.x=e.pageX,i.y=e.pageY):(i.x=e.clientX,i.y=e.clientY),i=i.matrixTransform(n.getScreenCTM().inverse()),[i.x,i.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ao.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nn(n){return n>1?0:-1>n?Fo:Math.acos(n)}function tn(n){return n>1?Io:-1>n?-Io:Math.asin(n)}function en(n){return((n=Math.exp(n))-1/n)/2}function rn(n){return((n=Math.exp(n))+1/n)/2}function un(n){return((n=Math.exp(2*n))-1)/(n+1)}function on(n){return(n=Math.sin(n/2))*n}function an(){}function ln(n,t,e){return this instanceof ln?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof ln?new ln(n.h,n.s,n.l):_n(""+n,wn,ln):new ln(n,t,e)}function cn(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?u+(o-u)*n/60:180>n?o:240>n?u+(o-u)*(240-n)/60:u}function i(n){return Math.round(255*r(n))}var u,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,u=2*e-o,new mn(i(n+120),i(n),i(n-120))}function fn(n,t,e){return this instanceof fn?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof fn?new fn(n.h,n.c,n.l):n instanceof hn?gn(n.l,n.a,n.b):gn((n=Sn((n=ao.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new fn(n,t,e)}function sn(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new hn(e,Math.cos(n*=Yo)*t,Math.sin(n)*t)}function hn(n,t,e){return this instanceof hn?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof hn?new hn(n.l,n.a,n.b):n instanceof fn?sn(n.h,n.c,n.l):Sn((n=mn(n)).r,n.g,n.b):new hn(n,t,e)}function pn(n,t,e){var r=(n+16)/116,i=r+t/500,u=r-e/200;return i=vn(i)*na,r=vn(r)*ta,u=vn(u)*ea,new mn(yn(3.2404542*i-1.5371385*r-.4985314*u),yn(-.969266*i+1.8760108*r+.041556*u),yn(.0556434*i-.2040259*r+1.0572252*u))}function gn(n,t,e){return n>0?new fn(Math.atan2(e,t)*Zo,Math.sqrt(t*t+e*e),n):new fn(NaN,NaN,n)}function vn(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function dn(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function yn(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mn(n,t,e){return this instanceof mn?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mn?new mn(n.r,n.g,n.b):_n(""+n,mn,cn):new mn(n,t,e)}function Mn(n){return new mn(n>>16,n>>8&255,255&n)}function xn(n){return Mn(n)+""}function bn(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function _n(n,t,e){var r,i,u,o=0,a=0,l=0;if(r=/([a-z]+)\((.*)\)/.exec(n=n.toLowerCase()))switch(i=r[2].split(","),r[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Nn(i[0]),Nn(i[1]),Nn(i[2]))}return(u=ua.get(n))?t(u.r,u.g,u.b):(null==n||"#"!==n.charAt(0)||isNaN(u=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&u)>>4,o=o>>4|o,a=240&u,a=a>>4|a,l=15&u,l=l<<4|l):7===n.length&&(o=(16711680&u)>>16,a=(65280&u)>>8,l=255&u)),t(o,a,l))}function wn(n,t,e){var r,i,u=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-u,l=(o+u)/2;return a?(i=.5>l?a/(o+u):a/(2-o-u),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=NaN,i=l>0&&1>l?0:r),new ln(r,i,l)}function Sn(n,t,e){n=kn(n),t=kn(t),e=kn(e);var r=dn((.4124564*n+.3575761*t+.1804375*e)/na),i=dn((.2126729*n+.7151522*t+.072175*e)/ta),u=dn((.0193339*n+.119192*t+.9503041*e)/ea);return hn(116*i-16,500*(r-i),200*(i-u))}function kn(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Nn(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function En(n){return"function"==typeof n?n:function(){return n}}function An(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Cn(t,e,n,r)}}function Cn(n,t,e,r){function i(){var n,t=l.status;if(!t&&Ln(l)||t>=200&&300>t||304===t){try{n=e.call(u,l)}catch(r){return void o.error.call(u,r)}o.load.call(u,n)}else o.error.call(u,l)}var u={},o=ao.dispatch("beforesend","progress","load","error"),a={},l=new XMLHttpRequest,c=null;return!this.XDomainRequest||"withCredentials"in l||!/^(http(s)?:)?\/\//.test(n)||(l=new XDomainRequest),"onload"in l?l.onload=l.onerror=i:l.onreadystatechange=function(){l.readyState>3&&i()},l.onprogress=function(n){var t=ao.event;ao.event=n;try{o.progress.call(u,l)}finally{ao.event=t}},u.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",u)},u.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",u):t},u.responseType=function(n){return arguments.length?(c=n,u):c},u.response=function(n){return e=n,u},["get","post"].forEach(function(n){u[n]=function(){return u.send.apply(u,[n].concat(co(arguments)))}}),u.send=function(e,r,i){if(2===arguments.length&&"function"==typeof r&&(i=r,r=null),l.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),l.setRequestHeader)for(var f in a)l.setRequestHeader(f,a[f]);return null!=t&&l.overrideMimeType&&l.overrideMimeType(t),null!=c&&(l.responseType=c),null!=i&&u.on("error",i).on("load",function(n){i(null,n)}),o.beforesend.call(u,l),l.send(null==r?null:r),u},u.abort=function(){return l.abort(),u},ao.rebind(u,o,"on"),null==r?u:u.get(zn(r))}function zn(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Ln(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qn(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var i=e+t,u={c:n,t:i,n:null};return aa?aa.n=u:oa=u,aa=u,la||(ca=clearTimeout(ca),la=1,fa(Tn)),u}function Tn(){var n=Rn(),t=Dn()-n;t>24?(isFinite(t)&&(clearTimeout(ca),ca=setTimeout(Tn,t)),la=0):(la=1,fa(Tn))}function Rn(){for(var n=Date.now(),t=oa;t;)n>=t.t&&t.c(n-t.t)&&(t.c=null),t=t.n;return n}function Dn(){for(var n,t=oa,e=1/0;t;)t.c?(t.t8?function(n){return n/e}:function(n){return n*e},symbol:n}}function jn(n){var t=n.decimal,e=n.thousands,r=n.grouping,i=n.currency,u=r&&e?function(n,t){for(var i=n.length,u=[],o=0,a=r[0],l=0;i>0&&a>0&&(l+a+1>t&&(a=Math.max(1,t-l)),u.push(n.substring(i-=a,i+a)),!((l+=a+1)>t));)a=r[o=(o+1)%r.length];return u.reverse().join(e)}:m;return function(n){var e=ha.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",l=e[4]||"",c=e[5],f=+e[6],s=e[7],h=e[8],p=e[9],g=1,v="",d="",y=!1,m=!0;switch(h&&(h=+h.substring(1)),(c||"0"===r&&"="===o)&&(c=r="0",o="="),p){case"n":s=!0,p="g";break;case"%":g=100,d="%",p="f";break;case"p":g=100,d="%",p="r";break;case"b":case"o":case"x":case"X":"#"===l&&(v="0"+p.toLowerCase());case"c":m=!1;case"d":y=!0,h=0;break;case"s":g=-1,p="r"}"$"===l&&(v=i[0],d=i[1]),"r"!=p||h||(p="g"),null!=h&&("g"==p?h=Math.max(1,Math.min(21,h)):"e"!=p&&"f"!=p||(h=Math.max(0,Math.min(20,h)))),p=pa.get(p)||Fn;var M=c&&s;return function(n){var e=d;if(y&&n%1)return"";var i=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>g){var l=ao.formatPrefix(n,h);n=l.scale(n),e=l.symbol+d}else n*=g;n=p(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=m?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!c&&s&&(x=u(x,1/0));var S=v.length+x.length+b.length+(M?0:i.length),k=f>S?new Array(S=f-S+1).join(r):"";return M&&(x=u(k+x,k.length?f-b.length:1/0)),i+=v,n=x+b,("<"===o?i+n+k:">"===o?k+i+n:"^"===o?k.substring(0,S>>=1)+i+n+k.substring(S):i+(M?n:k+n))+e}}}function Fn(n){return n+""}function Hn(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function On(n,t,e){function r(t){var e=n(t),r=u(e,1);return r-t>t-e?e:r}function i(e){return t(e=n(new va(e-1)),1),e}function u(n,e){return t(n=new va(+n),e),n}function o(n,r,u){var o=i(n),a=[];if(u>1)for(;r>o;)e(o)%u||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{va=Hn;var r=new Hn;return r._=n,o(r,t,e)}finally{va=Date}}n.floor=n,n.round=r,n.ceil=i,n.offset=u,n.range=o;var l=n.utc=In(n);return l.floor=l,l.round=In(r),l.ceil=In(i),l.offset=In(u),l.range=a,n}function In(n){return function(t,e){try{va=Hn;var r=new Hn;return r._=t,n(r,e)._}finally{va=Date}}}function Yn(n){function t(n){function t(t){for(var e,i,u,o=[],a=-1,l=0;++aa;){if(r>=c)return-1;if(i=t.charCodeAt(a++),37===i){if(o=t.charAt(a++),u=C[o in ya?t.charAt(a++):o],!u||(r=u(n,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){N.lastIndex=0;var r=N.exec(t.slice(e));return r?(n.m=E.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,A.c.toString(),t,r)}function l(n,t,r){return e(n,A.x.toString(),t,r)}function c(n,t,r){return e(n,A.X.toString(),t,r)}function f(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var s=n.dateTime,h=n.date,p=n.time,g=n.periods,v=n.days,d=n.shortDays,y=n.months,m=n.shortMonths;t.utc=function(n){function e(n){try{va=Hn;var t=new va;return t._=n,r(t)}finally{va=Date}}var r=t(n);return e.parse=function(n){try{va=Hn;var t=r.parse(n);return t&&t._}finally{va=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ct;var M=ao.map(),x=Vn(v),b=Xn(v),_=Vn(d),w=Xn(d),S=Vn(y),k=Xn(y),N=Vn(m),E=Xn(m);g.forEach(function(n,t){M.set(n.toLowerCase(),t)});var A={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return m[n.getMonth()]},B:function(n){return y[n.getMonth()]},c:t(s),d:function(n,t){return Zn(n.getDate(),t,2)},e:function(n,t){return Zn(n.getDate(),t,2)},H:function(n,t){return Zn(n.getHours(),t,2)},I:function(n,t){return Zn(n.getHours()%12||12,t,2)},j:function(n,t){return Zn(1+ga.dayOfYear(n),t,3)},L:function(n,t){return Zn(n.getMilliseconds(),t,3)},m:function(n,t){return Zn(n.getMonth()+1,t,2)},M:function(n,t){return Zn(n.getMinutes(),t,2)},p:function(n){return g[+(n.getHours()>=12)]},S:function(n,t){return Zn(n.getSeconds(),t,2)},U:function(n,t){return Zn(ga.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Zn(ga.mondayOfYear(n),t,2)},x:t(h),X:t(p),y:function(n,t){return Zn(n.getFullYear()%100,t,2)},Y:function(n,t){return Zn(n.getFullYear()%1e4,t,4)},Z:at,"%":function(){return"%"}},C={a:r,A:i,b:u,B:o,c:a,d:tt,e:tt,H:rt,I:rt,j:et,L:ot,m:nt,M:it,p:f,S:ut,U:Bn,w:$n,W:Wn,x:l,X:c,y:Gn,Y:Jn,Z:Kn,"%":lt};return t}function Zn(n,t,e){var r=0>n?"-":"",i=(r?-n:n)+"",u=i.length;return r+(e>u?new Array(e-u+1).join(t)+i:i)}function Vn(n){return new RegExp("^(?:"+n.map(ao.requote).join("|")+")","i")}function Xn(n){for(var t=new c,e=-1,r=n.length;++e68?1900:2e3)}function nt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function tt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function et(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function rt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function it(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function ut(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ot(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function at(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=xo(t)/60|0,i=xo(t)%60;return e+Zn(r,"0",2)+Zn(i,"0",2)}function lt(n,t,e){Ma.lastIndex=0;var r=Ma.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ct(n){for(var t=n.length,e=-1;++e=0?1:-1,a=o*e,l=Math.cos(t),c=Math.sin(t),f=u*c,s=i*l+f*Math.cos(a),h=f*o*Math.sin(a);ka.add(Math.atan2(h,s)),r=n,i=l,u=c}var t,e,r,i,u;Na.point=function(o,a){Na.point=n,r=(t=o)*Yo,i=Math.cos(a=(e=a)*Yo/2+Fo/4),u=Math.sin(a)},Na.lineEnd=function(){n(t,e)}}function dt(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function yt(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function mt(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function Mt(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function xt(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function bt(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function _t(n){return[Math.atan2(n[1],n[0]),tn(n[2])]}function wt(n,t){return xo(n[0]-t[0])a;++a)i.point((e=n[a])[0],e[1]);return void i.lineEnd()}var l=new Tt(e,n,null,!0),c=new Tt(e,null,l,!1);l.o=c,u.push(l),o.push(c),l=new Tt(r,n,null,!1),c=new Tt(r,null,l,!0),l.o=c,u.push(l),o.push(c)}}),o.sort(t),qt(u),qt(o),u.length){for(var a=0,l=e,c=o.length;c>a;++a)o[a].e=l=!l;for(var f,s,h=u[0];;){for(var p=h,g=!0;p.v;)if((p=p.n)===h)return;f=p.z,i.lineStart();do{if(p.v=p.o.v=!0,p.e){if(g)for(var a=0,c=f.length;c>a;++a)i.point((s=f[a])[0],s[1]);else r(p.x,p.n.x,1,i);p=p.n}else{if(g){f=p.p.z;for(var a=f.length-1;a>=0;--a)i.point((s=f[a])[0],s[1])}else r(p.x,p.p.x,-1,i);p=p.p}p=p.o,f=p.z,g=!g}while(!p.v);i.lineEnd()}}}function qt(n){if(t=n.length){for(var t,e,r=0,i=n[0];++r0){for(b||(u.polygonStart(),b=!0),u.lineStart();++o1&&2&t&&e.push(e.pop().concat(e.shift())),p.push(e.filter(Dt))}var p,g,v,d=t(u),y=i.invert(r[0],r[1]),m={point:o,lineStart:l,lineEnd:c,polygonStart:function(){m.point=f,m.lineStart=s,m.lineEnd=h,p=[],g=[]},polygonEnd:function(){m.point=o,m.lineStart=l,m.lineEnd=c,p=ao.merge(p);var n=Ot(y,g);p.length?(b||(u.polygonStart(),b=!0),Lt(p,Ut,n,e,u)):n&&(b||(u.polygonStart(),b=!0),u.lineStart(),e(null,null,1,u),u.lineEnd()),b&&(u.polygonEnd(),b=!1),p=g=null},sphere:function(){u.polygonStart(),u.lineStart(),e(null,null,1,u),u.lineEnd(),u.polygonEnd()}},M=Pt(),x=t(M),b=!1;return m}}function Dt(n){return n.length>1}function Pt(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ut(n,t){return((n=n.x)[0]<0?n[1]-Io-Uo:Io-n[1])-((t=t.x)[0]<0?t[1]-Io-Uo:Io-t[1])}function jt(n){var t,e=NaN,r=NaN,i=NaN;return{lineStart:function(){n.lineStart(),t=1},point:function(u,o){var a=u>0?Fo:-Fo,l=xo(u-e);xo(l-Fo)0?Io:-Io),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(u,r),t=0):i!==a&&l>=Fo&&(xo(e-i)Uo?Math.atan((Math.sin(t)*(u=Math.cos(r))*Math.sin(e)-Math.sin(r)*(i=Math.cos(t))*Math.sin(n))/(i*u*o)):(t+r)/2}function Ht(n,t,e,r){var i;if(null==n)i=e*Io,r.point(-Fo,i),r.point(0,i),r.point(Fo,i),r.point(Fo,0),r.point(Fo,-i),r.point(0,-i),r.point(-Fo,-i),r.point(-Fo,0),r.point(-Fo,i);else if(xo(n[0]-t[0])>Uo){var u=n[0]a;++a){var c=t[a],f=c.length;if(f)for(var s=c[0],h=s[0],p=s[1]/2+Fo/4,g=Math.sin(p),v=Math.cos(p),d=1;;){d===f&&(d=0),n=c[d];var y=n[0],m=n[1]/2+Fo/4,M=Math.sin(m),x=Math.cos(m),b=y-h,_=b>=0?1:-1,w=_*b,S=w>Fo,k=g*M;if(ka.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),u+=S?b+_*Ho:b,S^h>=e^y>=e){var N=mt(dt(s),dt(n));bt(N);var E=mt(i,N);bt(E);var A=(S^b>=0?-1:1)*tn(E[2]);(r>A||r===A&&(N[0]||N[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=y,g=M,v=x,s=n}}return(-Uo>u||Uo>u&&-Uo>ka)^1&o}function It(n){function t(n,t){return Math.cos(n)*Math.cos(t)>u}function e(n){var e,u,l,c,f;return{lineStart:function(){c=l=!1,f=1},point:function(s,h){var p,g=[s,h],v=t(s,h),d=o?v?0:i(s,h):v?i(s+(0>s?Fo:-Fo),h):0;if(!e&&(c=l=v)&&n.lineStart(),v!==l&&(p=r(e,g),(wt(e,p)||wt(g,p))&&(g[0]+=Uo,g[1]+=Uo,v=t(g[0],g[1]))),v!==l)f=0,v?(n.lineStart(),p=r(g,e),n.point(p[0],p[1])):(p=r(e,g),n.point(p[0],p[1]),n.lineEnd()),e=p;else if(a&&e&&o^v){var y;d&u||!(y=r(g,e,!0))||(f=0,o?(n.lineStart(),n.point(y[0][0],y[0][1]),n.point(y[1][0],y[1][1]),n.lineEnd()):(n.point(y[1][0],y[1][1]),n.lineEnd(),n.lineStart(),n.point(y[0][0],y[0][1])))}!v||e&&wt(e,g)||n.point(g[0],g[1]),e=g,l=v,u=d},lineEnd:function(){l&&n.lineEnd(),e=null},clean:function(){return f|(c&&l)<<1}}}function r(n,t,e){var r=dt(n),i=dt(t),o=[1,0,0],a=mt(r,i),l=yt(a,a),c=a[0],f=l-c*c;if(!f)return!e&&n;var s=u*l/f,h=-u*c/f,p=mt(o,a),g=xt(o,s),v=xt(a,h);Mt(g,v);var d=p,y=yt(g,d),m=yt(d,d),M=y*y-m*(yt(g,g)-1);if(!(0>M)){var x=Math.sqrt(M),b=xt(d,(-y-x)/m);if(Mt(b,g),b=_t(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],N=t[1];w>S&&(_=w,w=S,S=_);var E=S-w,A=xo(E-Fo)E;if(!A&&k>N&&(_=k,k=N,N=_),C?A?k+N>0^b[1]<(xo(b[0]-w)Fo^(w<=b[0]&&b[0]<=S)){var z=xt(d,(-y+x)/m);return Mt(z,g),[b,_t(z)]}}}function i(t,e){var r=o?n:Fo-n,i=0;return-r>t?i|=1:t>r&&(i|=2),-r>e?i|=4:e>r&&(i|=8),i}var u=Math.cos(n),o=u>0,a=xo(u)>Uo,l=ve(n,6*Yo);return Rt(t,e,l,o?[0,-n]:[-Fo,n-Fo])}function Yt(n,t,e,r){return function(i){var u,o=i.a,a=i.b,l=o.x,c=o.y,f=a.x,s=a.y,h=0,p=1,g=f-l,v=s-c;if(u=n-l,g||!(u>0)){if(u/=g,0>g){if(h>u)return;p>u&&(p=u)}else if(g>0){if(u>p)return;u>h&&(h=u)}if(u=e-l,g||!(0>u)){if(u/=g,0>g){if(u>p)return;u>h&&(h=u)}else if(g>0){if(h>u)return;p>u&&(p=u)}if(u=t-c,v||!(u>0)){if(u/=v,0>v){if(h>u)return;p>u&&(p=u)}else if(v>0){if(u>p)return;u>h&&(h=u)}if(u=r-c,v||!(0>u)){if(u/=v,0>v){if(u>p)return;u>h&&(h=u)}else if(v>0){if(h>u)return;p>u&&(p=u)}return h>0&&(i.a={x:l+h*g,y:c+h*v}),1>p&&(i.b={x:l+p*g,y:c+p*v}),i}}}}}}function Zt(n,t,e,r){function i(r,i){return xo(r[0]-n)0?0:3:xo(r[0]-e)0?2:1:xo(r[1]-t)0?1:0:i>0?3:2}function u(n,t){return o(n.x,t.x)}function o(n,t){var e=i(n,1),r=i(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function l(n){for(var t=0,e=d.length,r=n[1],i=0;e>i;++i)for(var u,o=1,a=d[i],l=a.length,c=a[0];l>o;++o)u=a[o],c[1]<=r?u[1]>r&&Q(c,u,n)>0&&++t:u[1]<=r&&Q(c,u,n)<0&&--t,c=u;return 0!==t}function c(u,a,l,c){var f=0,s=0;if(null==u||(f=i(u,l))!==(s=i(a,l))||o(u,a)<0^l>0){do c.point(0===f||3===f?n:e,f>1?r:t);while((f=(f+l+4)%4)!==s)}else c.point(a[0],a[1])}function f(i,u){return i>=n&&e>=i&&u>=t&&r>=u}function s(n,t){f(n,t)&&a.point(n,t)}function h(){C.point=g,d&&d.push(y=[]),S=!0,w=!1,b=_=NaN}function p(){v&&(g(m,M),x&&w&&E.rejoin(),v.push(E.buffer())),C.point=s,w&&a.lineEnd()}function g(n,t){n=Math.max(-Ha,Math.min(Ha,n)),t=Math.max(-Ha,Math.min(Ha,t));var e=f(n,t);if(d&&y.push([n,t]),S)m=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};A(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,y,m,M,x,b,_,w,S,k,N=a,E=Pt(),A=Yt(n,t,e,r),C={point:s,lineStart:h,lineEnd:p,polygonStart:function(){a=E,v=[],d=[],k=!0},polygonEnd:function(){a=N,v=ao.merge(v);var t=l([n,r]),e=k&&t,i=v.length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),c(null,null,1,a),a.lineEnd()),i&&Lt(v,u,t,c,a),a.polygonEnd()),v=d=y=null}};return C}}function Vt(n){var t=0,e=Fo/3,r=ae(n),i=r(t,e);return i.parallels=function(n){return arguments.length?r(t=n[0]*Fo/180,e=n[1]*Fo/180):[t/Fo*180,e/Fo*180]},i}function Xt(n,t){function e(n,t){var e=Math.sqrt(u-2*i*Math.sin(t))/i;return[e*Math.sin(n*=i),o-e*Math.cos(n)]}var r=Math.sin(n),i=(r+Math.sin(t))/2,u=1+r*(2*i-r),o=Math.sqrt(u)/i;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/i,tn((u-(n*n+e*e)*i*i)/(2*i))]},e}function $t(){function n(n,t){Ia+=i*n-r*t,r=n,i=t}var t,e,r,i;$a.point=function(u,o){$a.point=n,t=r=u,e=i=o},$a.lineEnd=function(){n(t,e)}}function Bt(n,t){Ya>n&&(Ya=n),n>Va&&(Va=n),Za>t&&(Za=t),t>Xa&&(Xa=t)}function Wt(){function n(n,t){o.push("M",n,",",t,u)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function i(){o.push("Z")}var u=Jt(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return u=Jt(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Jt(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Gt(n,t){Ca+=n,za+=t,++La}function Kt(){function n(n,r){var i=n-t,u=r-e,o=Math.sqrt(i*i+u*u);qa+=o*(t+n)/2,Ta+=o*(e+r)/2,Ra+=o,Gt(t=n,e=r)}var t,e;Wa.point=function(r,i){Wa.point=n,Gt(t=r,e=i)}}function Qt(){Wa.point=Gt}function ne(){function n(n,t){var e=n-r,u=t-i,o=Math.sqrt(e*e+u*u);qa+=o*(r+n)/2,Ta+=o*(i+t)/2,Ra+=o,o=i*n-r*t,Da+=o*(r+n),Pa+=o*(i+t),Ua+=3*o,Gt(r=n,i=t)}var t,e,r,i;Wa.point=function(u,o){Wa.point=n,Gt(t=r=u,e=i=o)},Wa.lineEnd=function(){n(t,e)}}function te(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,Ho)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function i(){a.point=t}function u(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:i,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=i,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function ee(n){function t(n){return(a?r:e)(n)}function e(t){return ue(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=NaN,S.point=u,t.lineStart()}function u(e,r){var u=dt([e,r]),o=n(e,r);i(M,x,m,b,_,w,M=o[0],x=o[1],m=e,b=u[0],_=u[1],w=u[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function l(){ +r(),S.point=c,S.lineEnd=f}function c(n,t){u(s=n,h=t),p=M,g=x,v=b,d=_,y=w,S.point=u}function f(){i(M,x,m,b,_,w,p,g,s,v,d,y,a,t),S.lineEnd=o,o()}var s,h,p,g,v,d,y,m,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=l},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function i(t,e,r,a,l,c,f,s,h,p,g,v,d,y){var m=f-t,M=s-e,x=m*m+M*M;if(x>4*u&&d--){var b=a+p,_=l+g,w=c+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),N=xo(xo(w)-1)u||xo((m*z+M*L)/x-.5)>.3||o>a*p+l*g+c*v)&&(i(t,e,r,a,l,c,A,C,N,b/=S,_/=S,w,d,y),y.point(A,C),i(A,C,N,b,_,w,f,s,h,p,g,v,d,y))}}var u=.5,o=Math.cos(30*Yo),a=16;return t.precision=function(n){return arguments.length?(a=(u=n*n)>0&&16,t):Math.sqrt(u)},t}function re(n){var t=ee(function(t,e){return n([t*Zo,e*Zo])});return function(n){return le(t(n))}}function ie(n){this.stream=n}function ue(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function oe(n){return ae(function(){return n})()}function ae(n){function t(n){return n=a(n[0]*Yo,n[1]*Yo),[n[0]*h+l,c-n[1]*h]}function e(n){return n=a.invert((n[0]-l)/h,(c-n[1])/h),n&&[n[0]*Zo,n[1]*Zo]}function r(){a=Ct(o=se(y,M,x),u);var n=u(v,d);return l=p-n[0]*h,c=g+n[1]*h,i()}function i(){return f&&(f.valid=!1,f=null),t}var u,o,a,l,c,f,s=ee(function(n,t){return n=u(n,t),[n[0]*h+l,c-n[1]*h]}),h=150,p=480,g=250,v=0,d=0,y=0,M=0,x=0,b=Fa,_=m,w=null,S=null;return t.stream=function(n){return f&&(f.valid=!1),f=le(b(o,s(_(n)))),f.valid=!0,f},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Fa):It((w=+n)*Yo),i()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Zt(n[0][0],n[0][1],n[1][0],n[1][1]):m,i()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(p=+n[0],g=+n[1],r()):[p,g]},t.center=function(n){return arguments.length?(v=n[0]%360*Yo,d=n[1]%360*Yo,r()):[v*Zo,d*Zo]},t.rotate=function(n){return arguments.length?(y=n[0]%360*Yo,M=n[1]%360*Yo,x=n.length>2?n[2]%360*Yo:0,r()):[y*Zo,M*Zo,x*Zo]},ao.rebind(t,s,"precision"),function(){return u=n.apply(this,arguments),t.invert=u.invert&&e,r()}}function le(n){return ue(n,function(t,e){n.point(t*Yo,e*Yo)})}function ce(n,t){return[n,t]}function fe(n,t){return[n>Fo?n-Ho:-Fo>n?n+Ho:n,t]}function se(n,t,e){return n?t||e?Ct(pe(n),ge(t,e)):pe(n):t||e?ge(t,e):fe}function he(n){return function(t,e){return t+=n,[t>Fo?t-Ho:-Fo>t?t+Ho:t,e]}}function pe(n){var t=he(n);return t.invert=he(-n),t}function ge(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*r+a*i;return[Math.atan2(l*u-f*o,a*r-c*i),tn(f*u+l*o)]}var r=Math.cos(n),i=Math.sin(n),u=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*u-l*o;return[Math.atan2(l*u+c*o,a*r+f*i),tn(f*r-a*i)]},e}function ve(n,t){var e=Math.cos(n),r=Math.sin(n);return function(i,u,o,a){var l=o*t;null!=i?(i=de(e,i),u=de(e,u),(o>0?u>i:i>u)&&(i+=o*Ho)):(i=n+o*Ho,u=n-.5*l);for(var c,f=i;o>0?f>u:u>f;f-=l)a.point((c=_t([e,-r*Math.cos(f),-r*Math.sin(f)]))[0],c[1])}}function de(n,t){var e=dt(t);e[0]-=n,bt(e);var r=nn(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Uo)%(2*Math.PI)}function ye(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function me(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function Me(n){return n.source}function xe(n){return n.target}function be(n,t,e,r){var i=Math.cos(t),u=Math.sin(t),o=Math.cos(r),a=Math.sin(r),l=i*Math.cos(n),c=i*Math.sin(n),f=o*Math.cos(e),s=o*Math.sin(e),h=2*Math.asin(Math.sqrt(on(r-t)+i*o*on(e-n))),p=1/Math.sin(h),g=h?function(n){var t=Math.sin(n*=h)*p,e=Math.sin(h-n)*p,r=e*l+t*f,i=e*c+t*s,o=e*u+t*a;return[Math.atan2(i,r)*Zo,Math.atan2(o,Math.sqrt(r*r+i*i))*Zo]}:function(){return[n*Zo,t*Zo]};return g.distance=h,g}function _e(){function n(n,i){var u=Math.sin(i*=Yo),o=Math.cos(i),a=xo((n*=Yo)-t),l=Math.cos(a);Ja+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*u-e*o*l)*a),e*u+r*o*l),t=n,e=u,r=o}var t,e,r;Ga.point=function(i,u){t=i*Yo,e=Math.sin(u*=Yo),r=Math.cos(u),Ga.point=n},Ga.lineEnd=function(){Ga.point=Ga.lineEnd=b}}function we(n,t){function e(t,e){var r=Math.cos(t),i=Math.cos(e),u=n(r*i);return[u*i*Math.sin(t),u*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),i=t(r),u=Math.sin(i),o=Math.cos(i);return[Math.atan2(n*u,r*o),Math.asin(r&&e*u/r)]},e}function Se(n,t){function e(n,t){o>0?-Io+Uo>t&&(t=-Io+Uo):t>Io-Uo&&(t=Io-Uo);var e=o/Math.pow(i(t),u);return[e*Math.sin(u*n),o-e*Math.cos(u*n)]}var r=Math.cos(n),i=function(n){return Math.tan(Fo/4+n/2)},u=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(i(t)/i(n)),o=r*Math.pow(i(n),u)/u;return u?(e.invert=function(n,t){var e=o-t,r=K(u)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/u,2*Math.atan(Math.pow(o/r,1/u))-Io]},e):Ne}function ke(n,t){function e(n,t){var e=u-t;return[e*Math.sin(i*n),u-e*Math.cos(i*n)]}var r=Math.cos(n),i=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),u=r/i+n;return xo(i)i;i++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function qe(n,t){return n[0]-t[0]||n[1]-t[1]}function Te(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Re(n,t,e,r){var i=n[0],u=e[0],o=t[0]-i,a=r[0]-u,l=n[1],c=e[1],f=t[1]-l,s=r[1]-c,h=(a*(l-c)-s*(i-u))/(s*o-a*f);return[i+h*o,l+h*f]}function De(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Pe(){rr(this),this.edge=this.site=this.circle=null}function Ue(n){var t=cl.pop()||new Pe;return t.site=n,t}function je(n){Be(n),ol.remove(n),cl.push(n),rr(n)}function Fe(n){var t=n.circle,e=t.x,r=t.cy,i={x:e,y:r},u=n.P,o=n.N,a=[n];je(n);for(var l=u;l.circle&&xo(e-l.circle.x)f;++f)c=a[f],l=a[f-1],nr(c.edge,l.site,c.site,i);l=a[0],c=a[s-1],c.edge=Ke(l.site,c.site,null,i),$e(l),$e(c)}function He(n){for(var t,e,r,i,u=n.x,o=n.y,a=ol._;a;)if(r=Oe(a,o)-u,r>Uo)a=a.L;else{if(i=u-Ie(a,o),!(i>Uo)){r>-Uo?(t=a.P,e=a):i>-Uo?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var l=Ue(n);if(ol.insert(t,l),t||e){if(t===e)return Be(t),e=Ue(t.site),ol.insert(l,e),l.edge=e.edge=Ke(t.site,l.site),$e(t),void $e(e);if(!e)return void(l.edge=Ke(t.site,l.site));Be(t),Be(e);var c=t.site,f=c.x,s=c.y,h=n.x-f,p=n.y-s,g=e.site,v=g.x-f,d=g.y-s,y=2*(h*d-p*v),m=h*h+p*p,M=v*v+d*d,x={x:(d*m-p*M)/y+f,y:(h*M-v*m)/y+s};nr(e.edge,c,g,x),l.edge=Ke(c,n,null,x),e.edge=Ke(n,g,null,x),$e(t),$e(e)}}function Oe(n,t){var e=n.site,r=e.x,i=e.y,u=i-t;if(!u)return r;var o=n.P;if(!o)return-(1/0);e=o.site;var a=e.x,l=e.y,c=l-t;if(!c)return a;var f=a-r,s=1/u-1/c,h=f/c;return s?(-h+Math.sqrt(h*h-2*s*(f*f/(-2*c)-l+c/2+i-u/2)))/s+r:(r+a)/2}function Ie(n,t){var e=n.N;if(e)return Oe(e,t);var r=n.site;return r.y===t?r.x:1/0}function Ye(n){this.site=n,this.edges=[]}function Ze(n){for(var t,e,r,i,u,o,a,l,c,f,s=n[0][0],h=n[1][0],p=n[0][1],g=n[1][1],v=ul,d=v.length;d--;)if(u=v[d],u&&u.prepare())for(a=u.edges,l=a.length,o=0;l>o;)f=a[o].end(),r=f.x,i=f.y,c=a[++o%l].start(),t=c.x,e=c.y,(xo(r-t)>Uo||xo(i-e)>Uo)&&(a.splice(o,0,new tr(Qe(u.site,f,xo(r-s)Uo?{x:s,y:xo(t-s)Uo?{x:xo(e-g)Uo?{x:h,y:xo(t-h)Uo?{x:xo(e-p)=-jo)){var p=l*l+c*c,g=f*f+s*s,v=(s*p-c*g)/h,d=(l*g-f*p)/h,s=d+a,y=fl.pop()||new Xe;y.arc=n,y.site=i,y.x=v+o,y.y=s+Math.sqrt(v*v+d*d),y.cy=s,n.circle=y;for(var m=null,M=ll._;M;)if(y.yd||d>=a)return;if(h>g){if(u){if(u.y>=c)return}else u={x:d,y:l};e={x:d,y:c}}else{if(u){if(u.yr||r>1)if(h>g){if(u){if(u.y>=c)return}else u={x:(l-i)/r,y:l};e={x:(c-i)/r,y:c}}else{if(u){if(u.yp){if(u){if(u.x>=a)return}else u={x:o,y:r*o+i};e={x:a,y:r*a+i}}else{if(u){if(u.xu||s>o||r>h||i>p)){if(g=n.point){var g,v=t-n.x,d=e-n.y,y=v*v+d*d;if(l>y){var m=Math.sqrt(l=y);r=t-m,i=e-m,u=t+m,o=e+m,a=g}}for(var M=n.nodes,x=.5*(f+h),b=.5*(s+p),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:c(n,f,s,x,b);break;case 1:c(n,x,s,h,b);break;case 2:c(n,f,b,x,p);break;case 3:c(n,x,b,h,p)}}}(n,r,i,u,o),a}function vr(n,t){n=ao.rgb(n),t=ao.rgb(t);var e=n.r,r=n.g,i=n.b,u=t.r-e,o=t.g-r,a=t.b-i;return function(n){return"#"+bn(Math.round(e+u*n))+bn(Math.round(r+o*n))+bn(Math.round(i+a*n))}}function dr(n,t){var e,r={},i={};for(e in n)e in t?r[e]=Mr(n[e],t[e]):i[e]=n[e];for(e in t)e in n||(i[e]=t[e]);return function(n){for(e in r)i[e]=r[e](n);return i}}function yr(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function mr(n,t){var e,r,i,u=hl.lastIndex=pl.lastIndex=0,o=-1,a=[],l=[];for(n+="",t+="";(e=hl.exec(n))&&(r=pl.exec(t));)(i=r.index)>u&&(i=t.slice(u,i),a[o]?a[o]+=i:a[++o]=i),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,l.push({i:o,x:yr(e,r)})),u=pl.lastIndex;return ur;++r)a[(e=l[r]).i]=e.x(n);return a.join("")})}function Mr(n,t){for(var e,r=ao.interpolators.length;--r>=0&&!(e=ao.interpolators[r](n,t)););return e}function xr(n,t){var e,r=[],i=[],u=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(Mr(n[e],t[e]));for(;u>e;++e)i[e]=n[e];for(;o>e;++e)i[e]=t[e];return function(n){for(e=0;a>e;++e)i[e]=r[e](n);return i}}function br(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function _r(n){return function(t){return 1-n(1-t)}}function wr(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function Sr(n){return n*n}function kr(n){return n*n*n}function Nr(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function Er(n){return function(t){return Math.pow(t,n)}}function Ar(n){return 1-Math.cos(n*Io)}function Cr(n){return Math.pow(2,10*(n-1))}function zr(n){return 1-Math.sqrt(1-n*n)}function Lr(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/Ho*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*Ho/t)}}function qr(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Tr(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Rr(n,t){n=ao.hcl(n),t=ao.hcl(t);var e=n.h,r=n.c,i=n.l,u=t.h-e,o=t.c-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return sn(e+u*n,r+o*n,i+a*n)+""}}function Dr(n,t){n=ao.hsl(n),t=ao.hsl(t);var e=n.h,r=n.s,i=n.l,u=t.h-e,o=t.s-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return cn(e+u*n,r+o*n,i+a*n)+""}}function Pr(n,t){n=ao.lab(n),t=ao.lab(t);var e=n.l,r=n.a,i=n.b,u=t.l-e,o=t.a-r,a=t.b-i;return function(n){return pn(e+u*n,r+o*n,i+a*n)+""}}function Ur(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function jr(n){var t=[n.a,n.b],e=[n.c,n.d],r=Hr(t),i=Fr(t,e),u=Hr(Or(e,t,-i))||0;t[0]*e[1]180?t+=360:t-n>180&&(n+=360),r.push({i:e.push(Ir(e)+"rotate(",null,")")-2,x:yr(n,t)})):t&&e.push(Ir(e)+"rotate("+t+")")}function Vr(n,t,e,r){n!==t?r.push({i:e.push(Ir(e)+"skewX(",null,")")-2,x:yr(n,t)}):t&&e.push(Ir(e)+"skewX("+t+")")}function Xr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push(Ir(e)+"scale(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else 1===t[0]&&1===t[1]||e.push(Ir(e)+"scale("+t+")")}function $r(n,t){var e=[],r=[];return n=ao.transform(n),t=ao.transform(t),Yr(n.translate,t.translate,e,r),Zr(n.rotate,t.rotate,e,r),Vr(n.skew,t.skew,e,r),Xr(n.scale,t.scale,e,r),n=t=null,function(n){for(var t,i=-1,u=r.length;++i=0;)e.push(i[r])}function oi(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(u=n.children)&&(i=u.length))for(var i,u,o=-1;++oe;++e)(t=n[e][1])>i&&(r=e,i=t);return r}function yi(n){return n.reduce(mi,0)}function mi(n,t){return n+t[1]}function Mi(n,t){return xi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function xi(n,t){for(var e=-1,r=+n[0],i=(n[1]-r)/t,u=[];++e<=t;)u[e]=i*e+r;return u}function bi(n){return[ao.min(n),ao.max(n)]}function _i(n,t){return n.value-t.value}function wi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Si(n,t){n._pack_next=t,t._pack_prev=n}function ki(n,t){var e=t.x-n.x,r=t.y-n.y,i=n.r+t.r;return.999*i*i>e*e+r*r}function Ni(n){function t(n){f=Math.min(n.x-n.r,f),s=Math.max(n.x+n.r,s),h=Math.min(n.y-n.r,h),p=Math.max(n.y+n.r,p)}if((e=n.children)&&(c=e.length)){var e,r,i,u,o,a,l,c,f=1/0,s=-(1/0),h=1/0,p=-(1/0);if(e.forEach(Ei),r=e[0],r.x=-r.r,r.y=0,t(r),c>1&&(i=e[1],i.x=i.r,i.y=0,t(i),c>2))for(u=e[2],zi(r,i,u),t(u),wi(r,u),r._pack_prev=u,wi(u,i),i=r._pack_next,o=3;c>o;o++){zi(r,i,u=e[o]);var g=0,v=1,d=1;for(a=i._pack_next;a!==i;a=a._pack_next,v++)if(ki(a,u)){g=1;break}if(1==g)for(l=r._pack_prev;l!==a._pack_prev&&!ki(l,u);l=l._pack_prev,d++);g?(d>v||v==d&&i.ro;o++)u=e[o],u.x-=y,u.y-=m,M=Math.max(M,u.r+Math.sqrt(u.x*u.x+u.y*u.y));n.r=M,e.forEach(Ai)}}function Ei(n){n._pack_next=n._pack_prev=n}function Ai(n){delete n._pack_next,delete n._pack_prev}function Ci(n,t,e,r){var i=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,i)for(var u=-1,o=i.length;++u=0;)t=i[u],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Pi(n,t,e){return n.a.parent===t.parent?n.a:e}function Ui(n){return 1+ao.max(n,function(n){return n.y})}function ji(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Fi(n){var t=n.children;return t&&t.length?Fi(t[0]):n}function Hi(n){var t,e=n.children;return e&&(t=e.length)?Hi(e[t-1]):n}function Oi(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Ii(n,t){var e=n.x+t[3],r=n.y+t[0],i=n.dx-t[1]-t[3],u=n.dy-t[0]-t[2];return 0>i&&(e+=i/2,i=0),0>u&&(r+=u/2,u=0),{x:e,y:r,dx:i,dy:u}}function Yi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Zi(n){return n.rangeExtent?n.rangeExtent():Yi(n.range())}function Vi(n,t,e,r){var i=e(n[0],n[1]),u=r(t[0],t[1]);return function(n){return u(i(n))}}function Xi(n,t){var e,r=0,i=n.length-1,u=n[r],o=n[i];return u>o&&(e=r,r=i,i=e,e=u,u=o,o=e),n[r]=t.floor(u),n[i]=t.ceil(o),n}function $i(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:Sl}function Bi(n,t,e,r){var i=[],u=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]2?Bi:Vi,l=r?Wr:Br;return o=i(n,t,l,e),a=i(t,n,l,Mr),u}function u(n){return o(n)}var o,a;return u.invert=function(n){return a(n)},u.domain=function(t){return arguments.length?(n=t.map(Number),i()):n},u.range=function(n){return arguments.length?(t=n,i()):t},u.rangeRound=function(n){return u.range(n).interpolate(Ur)},u.clamp=function(n){return arguments.length?(r=n,i()):r},u.interpolate=function(n){return arguments.length?(e=n,i()):e},u.ticks=function(t){return Qi(n,t)},u.tickFormat=function(t,e){return nu(n,t,e)},u.nice=function(t){return Gi(n,t),i()},u.copy=function(){return Wi(n,t,e,r)},i()}function Ji(n,t){return ao.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Gi(n,t){return Xi(n,$i(Ki(n,t)[2])),Xi(n,$i(Ki(n,t)[2])),n}function Ki(n,t){null==t&&(t=10);var e=Yi(n),r=e[1]-e[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),u=t/r*i;return.15>=u?i*=10:.35>=u?i*=5:.75>=u&&(i*=2),e[0]=Math.ceil(e[0]/i)*i,e[1]=Math.floor(e[1]/i)*i+.5*i,e[2]=i,e}function Qi(n,t){return ao.range.apply(ao,Ki(n,t))}function nu(n,t,e){var r=Ki(n,t);if(e){var i=ha.exec(e);if(i.shift(),"s"===i[8]){var u=ao.formatPrefix(Math.max(xo(r[0]),xo(r[1])));return i[7]||(i[7]="."+tu(u.scale(r[2]))),i[8]="f",e=ao.format(i.join("")),function(n){return e(u.scale(n))+u.symbol}}i[7]||(i[7]="."+eu(i[8],r)),e=i.join("")}else e=",."+tu(r[2])+"f";return ao.format(e)}function tu(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function eu(n,t){var e=tu(t[2]);return n in kl?Math.abs(e-tu(Math.max(xo(t[0]),xo(t[1]))))+ +("e"!==n):e-2*("%"===n)}function ru(n,t,e,r){function i(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function u(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(i(t))}return o.invert=function(t){return u(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(i)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(i)),o):t},o.nice=function(){var t=Xi(r.map(i),e?Math:El);return n.domain(t),r=t.map(u),o},o.ticks=function(){var n=Yi(r),o=[],a=n[0],l=n[1],c=Math.floor(i(a)),f=Math.ceil(i(l)),s=t%1?2:t;if(isFinite(f-c)){if(e){for(;f>c;c++)for(var h=1;s>h;h++)o.push(u(c)*h);o.push(u(c))}else for(o.push(u(c));c++0;h--)o.push(u(c)*h);for(c=0;o[c]l;f--);o=o.slice(c,f)}return o},o.tickFormat=function(n,e){if(!arguments.length)return Nl;arguments.length<2?e=Nl:"function"!=typeof e&&(e=ao.format(e));var r=Math.max(1,t*n/o.ticks().length);return function(n){var o=n/u(Math.round(i(n)));return t-.5>o*t&&(o*=t),r>=o?e(n):""}},o.copy=function(){return ru(n.copy(),t,e,r)},Ji(o,n)}function iu(n,t,e){function r(t){return n(i(t))}var i=uu(t),u=uu(1/t);return r.invert=function(t){return u(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(i)),r):e},r.ticks=function(n){return Qi(e,n)},r.tickFormat=function(n,t){return nu(e,n,t)},r.nice=function(n){return r.domain(Gi(e,n))},r.exponent=function(o){return arguments.length?(i=uu(t=o),u=uu(1/t),n.domain(e.map(i)),r):t},r.copy=function(){return iu(n.copy(),t,e)},Ji(r,n)}function uu(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function ou(n,t){function e(e){return u[((i.get(e)||("range"===t.t?i.set(e,n.push(e)):NaN))-1)%u.length]}function r(t,e){return ao.range(n.length).map(function(n){return t+e*n})}var i,u,o;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new c;for(var u,o=-1,a=r.length;++oe?[NaN,NaN]:[e>0?a[e-1]:n[0],et?NaN:t/u+n,[t,t+1/u]},r.copy=function(){return lu(n,t,e)},i()}function cu(n,t){function e(e){return e>=e?t[ao.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return cu(n,t)},e}function fu(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Qi(n,t)},t.tickFormat=function(t,e){return nu(n,t,e)},t.copy=function(){return fu(n)},t}function su(){return 0}function hu(n){return n.innerRadius}function pu(n){return n.outerRadius}function gu(n){return n.startAngle}function vu(n){return n.endAngle}function du(n){return n&&n.padAngle}function yu(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function mu(n,t,e,r,i){var u=n[0]-t[0],o=n[1]-t[1],a=(i?r:-r)/Math.sqrt(u*u+o*o),l=a*o,c=-a*u,f=n[0]+l,s=n[1]+c,h=t[0]+l,p=t[1]+c,g=(f+h)/2,v=(s+p)/2,d=h-f,y=p-s,m=d*d+y*y,M=e-r,x=f*p-h*s,b=(0>y?-1:1)*Math.sqrt(Math.max(0,M*M*m-x*x)),_=(x*y-d*b)/m,w=(-x*d-y*b)/m,S=(x*y+d*b)/m,k=(-x*d+y*b)/m,N=_-g,E=w-v,A=S-g,C=k-v;return N*N+E*E>A*A+C*C&&(_=S,w=k),[[_-l,w-c],[_*e/M,w*e/M]]}function Mu(n){function t(t){function o(){c.push("M",u(n(f),a))}for(var l,c=[],f=[],s=-1,h=t.length,p=En(e),g=En(r);++s1?n.join("L"):n+"Z"}function bu(n){return n.join("L")+"Z"}function _u(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t1&&i.push("H",r[0]),i.join("")}function wu(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t1){a=t[1],u=n[l],l++,r+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1];for(var c=2;c9&&(i=3*t/Math.sqrt(i),o[a]=i*e,o[a+1]=i*r));for(a=-1;++a<=l;)i=(n[Math.min(l,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),u.push([i||0,o[a]*i||0]);return u}function Fu(n){return n.length<3?xu(n):n[0]+Au(n,ju(n))}function Hu(n){for(var t,e,r,i=-1,u=n.length;++i=t?o(n-t):void(f.c=o)}function o(e){var i=g.active,u=g[i];u&&(u.timer.c=null,u.timer.t=NaN,--g.count,delete g[i],u.event&&u.event.interrupt.call(n,n.__data__,u.index));for(var o in g)if(r>+o){var c=g[o];c.timer.c=null,c.timer.t=NaN,--g.count,delete g[o]}f.c=a,qn(function(){return f.c&&a(e||1)&&(f.c=null,f.t=NaN),1},0,l),g.active=r,v.event&&v.event.start.call(n,n.__data__,t),p=[],v.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&p.push(r)}),h=v.ease,s=v.duration}function a(i){for(var u=i/s,o=h(u),a=p.length;a>0;)p[--a].call(n,o);return u>=1?(v.event&&v.event.end.call(n,n.__data__,t),--g.count?delete g[r]:delete n[e],1):void 0}var l,f,s,h,p,g=n[e]||(n[e]={active:0,count:0}),v=g[r];v||(l=i.time,f=qn(u,0,l),v=g[r]={tween:new c,time:l,timer:f,delay:i.delay,duration:i.duration,ease:i.ease,index:t},i=null,++g.count)}function no(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function to(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function eo(n){return n.toISOString()}function ro(n,t,e){function r(t){return n(t)}function i(n,e){var r=n[1]-n[0],i=r/e,u=ao.bisect(Kl,i);return u==Kl.length?[t.year,Ki(n.map(function(n){return n/31536e6}),e)[2]]:u?t[i/Kl[u-1]1?{floor:function(t){for(;e(t=n.floor(t));)t=io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Yi(r.domain()),u=null==n?i(e,10):"number"==typeof n?i(e,n):!n.range&&[{range:n},t];return u&&(n=u[0],t=u[1]),n.range(e[0],io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return ro(n.copy(),t,e)},Ji(r,n)}function io(n){return new Date(n)}function uo(n){return JSON.parse(n.responseText)}function oo(n){var t=fo.createRange();return t.selectNode(fo.body),t.createContextualFragment(n.responseText)}var ao={version:"3.5.17"},lo=[].slice,co=function(n){return lo.call(n)},fo=this.document;if(fo)try{co(fo.documentElement.childNodes)[0].nodeType}catch(so){co=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),fo)try{fo.createElement("DIV").style.setProperty("opacity",0,"")}catch(ho){var po=this.Element.prototype,go=po.setAttribute,vo=po.setAttributeNS,yo=this.CSSStyleDeclaration.prototype,mo=yo.setProperty;po.setAttribute=function(n,t){go.call(this,n,t+"")},po.setAttributeNS=function(n,t,e){vo.call(this,n,t,e+"")},yo.setProperty=function(n,t,e){mo.call(this,n,t+"",e)}}ao.ascending=e,ao.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:NaN},ao.min=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i=r){e=r;break}for(;++ir&&(e=r)}else{for(;++i=r){e=r;break}for(;++ir&&(e=r)}return e},ao.max=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i=r){e=r;break}for(;++ie&&(e=r)}else{for(;++i=r){e=r;break}for(;++ie&&(e=r)}return e},ao.extent=function(n,t){var e,r,i,u=-1,o=n.length;if(1===arguments.length){for(;++u=r){e=i=r;break}for(;++ur&&(e=r),r>i&&(i=r))}else{for(;++u=r){e=i=r;break}for(;++ur&&(e=r),r>i&&(i=r))}return[e,i]},ao.sum=function(n,t){var e,r=0,u=n.length,o=-1;if(1===arguments.length)for(;++o1?l/(f-1):void 0},ao.deviation=function(){var n=ao.variance.apply(this,arguments);return n?Math.sqrt(n):n};var Mo=u(e);ao.bisectLeft=Mo.left,ao.bisect=ao.bisectRight=Mo.right,ao.bisector=function(n){return u(1===n.length?function(t,r){return e(n(t),r)}:n)},ao.shuffle=function(n,t,e){(u=arguments.length)<3&&(e=n.length,2>u&&(t=0));for(var r,i,u=e-t;u;)i=Math.random()*u--|0,r=n[u+t],n[u+t]=n[i+t],n[i+t]=r;return n},ao.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ao.pairs=function(n){for(var t,e=0,r=n.length-1,i=n[0],u=new Array(0>r?0:r);r>e;)u[e]=[t=i,i=n[++e]];return u},ao.transpose=function(n){if(!(i=n.length))return[];for(var t=-1,e=ao.min(n,o),r=new Array(e);++t=0;)for(r=n[i],t=r.length;--t>=0;)e[--o]=r[t];return e};var xo=Math.abs;ao.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,i=[],u=a(xo(e)),o=-1;if(n*=u,t*=u,e*=u,0>e)for(;(r=n+e*++o)>t;)i.push(r/u);else for(;(r=n+e*++o)=u.length)return r?r.call(i,o):e?o.sort(e):o;for(var l,f,s,h,p=-1,g=o.length,v=u[a++],d=new c;++p=u.length)return n;var r=[],i=o[e++];return n.forEach(function(n,i){r.push({key:n,values:t(i,e)})}),i?r.sort(function(n,t){return i(n.key,t.key)}):r}var e,r,i={},u=[],o=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(ao.map,e,0),0)},i.key=function(n){return u.push(n),i},i.sortKeys=function(n){return o[u.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},ao.set=function(n){var t=new y;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},l(y,{has:h,add:function(n){return this._[f(n+="")]=!0,n},remove:p,values:g,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t))}}),ao.behavior={},ao.rebind=function(n,t){for(var e,r=1,i=arguments.length;++r=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ao.event=null,ao.requote=function(n){return n.replace(So,"\\$&")};var So=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ko={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},No=function(n,t){return t.querySelector(n)},Eo=function(n,t){return t.querySelectorAll(n)},Ao=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(Ao=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(No=function(n,t){return Sizzle(n,t)[0]||null},Eo=Sizzle,Ao=Sizzle.matchesSelector),ao.selection=function(){return ao.select(fo.documentElement)};var Co=ao.selection.prototype=[];Co.select=function(n){var t,e,r,i,u=[];n=A(n);for(var o=-1,a=this.length;++o=0&&"xmlns"!==(e=n.slice(0,t))&&(n=n.slice(t+1)),Lo.hasOwnProperty(e)?{space:Lo[e],local:n}:n}},Co.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ao.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},Co.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,i=-1;if(t=e.classList){for(;++ii){if("string"!=typeof n){2>i&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>i){var u=this.node();return t(u).getComputedStyle(u,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},Co.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},Co.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},Co.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},Co.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},Co.insert=function(n,t){return n=j(n),t=A(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},Co.remove=function(){return this.each(F)},Co.data=function(n,t){function e(n,e){var r,i,u,o=n.length,s=e.length,h=Math.min(o,s),p=new Array(s),g=new Array(s),v=new Array(o);if(t){var d,y=new c,m=new Array(o);for(r=-1;++rr;++r)g[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}g.update=p,g.parentNode=p.parentNode=v.parentNode=n.parentNode,a.push(g),l.push(p),f.push(v)}var r,i,u=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++uu;u++){i.push(t=[]),t.parentNode=(e=this[u]).parentNode;for(var a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return E(i)},Co.order=function(){for(var n=-1,t=this.length;++n=0;)(e=r[i])&&(u&&u!==e.nextSibling&&u.parentNode.insertBefore(e,u),u=e);return this},Co.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++tn;n++)for(var e=this[n],r=0,i=e.length;i>r;r++){var u=e[r];if(u)return u}return null},Co.size=function(){var n=0;return Y(this,function(){++n}),n};var qo=[];ao.selection.enter=Z,ao.selection.enter.prototype=qo,qo.append=Co.append,qo.empty=Co.empty,qo.node=Co.node,qo.call=Co.call,qo.size=Co.size,qo.select=function(n){for(var t,e,r,i,u,o=[],a=-1,l=this.length;++ar){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var To=ao.map({mouseenter:"mouseover",mouseleave:"mouseout"});fo&&To.forEach(function(n){"on"+n in fo&&To.remove(n)});var Ro,Do=0;ao.mouse=function(n){return J(n,k())};var Po=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ao.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,i=0,u=t.length;u>i;++i)if((r=t[i]).identifier===e)return J(n,r)},ao.behavior.drag=function(){function n(){this.on("mousedown.drag",u).on("touchstart.drag",o)}function e(n,t,e,u,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],g|=n|e,M=r,p({type:"drag",x:r[0]+c[0],y:r[1]+c[1],dx:n,dy:e}))}function l(){t(h,v)&&(y.on(u+d,null).on(o+d,null),m(g),p({type:"dragend"}))}var c,f=this,s=ao.event.target.correspondingElement||ao.event.target,h=f.parentNode,p=r.of(f,arguments),g=0,v=n(),d=".drag"+(null==v?"":"-"+v),y=ao.select(e(s)).on(u+d,a).on(o+d,l),m=W(s),M=t(h,v);i?(c=i.apply(f,arguments),c=[c.x-M[0],c.y-M[1]]):c=[0,0],p({type:"dragstart"})}}var r=N(n,"drag","dragstart","dragend"),i=null,u=e(b,ao.mouse,t,"mousemove","mouseup"),o=e(G,ao.touch,m,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},ao.rebind(n,r,"on")},ao.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?co(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Uo=1e-6,jo=Uo*Uo,Fo=Math.PI,Ho=2*Fo,Oo=Ho-Uo,Io=Fo/2,Yo=Fo/180,Zo=180/Fo,Vo=Math.SQRT2,Xo=2,$o=4;ao.interpolateZoom=function(n,t){var e,r,i=n[0],u=n[1],o=n[2],a=t[0],l=t[1],c=t[2],f=a-i,s=l-u,h=f*f+s*s;if(jo>h)r=Math.log(c/o)/Vo,e=function(n){return[i+n*f,u+n*s,o*Math.exp(Vo*n*r)]};else{var p=Math.sqrt(h),g=(c*c-o*o+$o*h)/(2*o*Xo*p),v=(c*c-o*o-$o*h)/(2*c*Xo*p),d=Math.log(Math.sqrt(g*g+1)-g),y=Math.log(Math.sqrt(v*v+1)-v);r=(y-d)/Vo,e=function(n){var t=n*r,e=rn(d),a=o/(Xo*p)*(e*un(Vo*t+d)-en(d));return[i+a*f,u+a*s,o*e/rn(Vo*t+d)]}}return e.duration=1e3*r,e},ao.behavior.zoom=function(){function n(n){n.on(L,s).on(Wo+".zoom",p).on("dblclick.zoom",g).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function i(n){k.k=Math.max(A[0],Math.min(A[1],n))}function u(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},i(Math.pow(2,o)),u(d=e,r),t=ao.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function l(n){z++||n({type:"zoomstart"})}function c(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function f(n){--z||(n({type:"zoomend"}),d=null)}function s(){function n(){a=1,u(ao.mouse(i),h),c(o)}function r(){s.on(q,null).on(T,null),p(a),f(o)}var i=this,o=D.of(i,arguments),a=0,s=ao.select(t(i)).on(q,n).on(T,r),h=e(ao.mouse(i)),p=W(i);Il.call(i),l(o)}function h(){function n(){var n=ao.touches(g);return p=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ao.event.target;ao.select(t).on(x,r).on(b,a),_.push(t);for(var e=ao.event.changedTouches,i=0,u=e.length;u>i;++i)d[e[i].identifier]=null;var l=n(),c=Date.now();if(1===l.length){if(500>c-M){var f=l[0];o(g,f,d[f.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=c}else if(l.length>1){var f=l[0],s=l[1],h=f[0]-s[0],p=f[1]-s[1];y=h*h+p*p}}function r(){var n,t,e,r,o=ao.touches(g);Il.call(g);for(var a=0,l=o.length;l>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var f=(f=e[0]-n[0])*f+(f=e[1]-n[1])*f,s=y&&Math.sqrt(f/y);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],i(s*p)}M=null,u(n,t),c(v)}function a(){if(ao.event.touches.length){for(var t=ao.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var i in d)return void n()}ao.selectAll(_).on(m,null),w.on(L,s).on(R,h),N(),f(v)}var p,g=this,v=D.of(g,arguments),d={},y=0,m=".zoom-"+ao.event.changedTouches[0].identifier,x="touchmove"+m,b="touchend"+m,_=[],w=ao.select(g),N=W(g);t(),l(v),w.on(L,null).on(R,t)}function p(){var n=D.of(this,arguments);m?clearTimeout(m):(Il.call(this),v=e(d=y||ao.mouse(this)),l(n)),m=setTimeout(function(){m=null,f(n)},50),S(),i(Math.pow(2,.002*Bo())*k.k),u(d,v),c(n)}function g(){var n=ao.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ao.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,y,m,M,x,b,_,w,k={x:0,y:0,k:1},E=[960,500],A=Jo,C=250,z=0,L="mousedown.zoom",q="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=N(n,"zoomstart","zoom","zoomend");return Wo||(Wo="onwheel"in fo?(Bo=function(){return-ao.event.deltaY*(ao.event.deltaMode?120:1)},"wheel"):"onmousewheel"in fo?(Bo=function(){return ao.event.wheelDelta},"mousewheel"):(Bo=function(){return-ao.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Hl?ao.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},l(n)}).tween("zoom:zoom",function(){var e=E[0],r=E[1],i=d?d[0]:e/2,u=d?d[1]:r/2,o=ao.interpolateZoom([(i-k.x)/k.k,(u-k.y)/k.k,e/k.k],[(i-t.x)/t.k,(u-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:i-r[0]*a,y:u-r[1]*a,k:a},c(n)}}).each("interrupt.zoom",function(){f(n)}).each("end.zoom",function(){f(n)}):(this.__chart__=k,l(n),c(n),f(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:null},i(+t),a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(A=null==t?Jo:[+t[0],+t[1]],n):A},n.center=function(t){return arguments.length?(y=t&&[+t[0],+t[1]],n):y},n.size=function(t){return arguments.length?(E=t&&[+t[0],+t[1]],n):E},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ao.rebind(n,D,"on")};var Bo,Wo,Jo=[0,1/0];ao.color=an,an.prototype.toString=function(){return this.rgb()+""},ao.hsl=ln;var Go=ln.prototype=new an;Go.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,this.l/n)},Go.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,n*this.l)},Go.rgb=function(){return cn(this.h,this.s,this.l)},ao.hcl=fn;var Ko=fn.prototype=new an;Ko.brighter=function(n){return new fn(this.h,this.c,Math.min(100,this.l+Qo*(arguments.length?n:1)))},Ko.darker=function(n){return new fn(this.h,this.c,Math.max(0,this.l-Qo*(arguments.length?n:1)))},Ko.rgb=function(){return sn(this.h,this.c,this.l).rgb()},ao.lab=hn;var Qo=18,na=.95047,ta=1,ea=1.08883,ra=hn.prototype=new an;ra.brighter=function(n){return new hn(Math.min(100,this.l+Qo*(arguments.length?n:1)),this.a,this.b)},ra.darker=function(n){return new hn(Math.max(0,this.l-Qo*(arguments.length?n:1)),this.a,this.b)},ra.rgb=function(){return pn(this.l,this.a,this.b)},ao.rgb=mn;var ia=mn.prototype=new an;ia.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,i=30;return t||e||r?(t&&i>t&&(t=i),e&&i>e&&(e=i),r&&i>r&&(r=i),new mn(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mn(i,i,i)},ia.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mn(n*this.r,n*this.g,n*this.b)},ia.hsl=function(){return wn(this.r,this.g,this.b)},ia.toString=function(){return"#"+bn(this.r)+bn(this.g)+bn(this.b)};var ua=ao.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});ua.forEach(function(n,t){ua.set(n,Mn(t))}),ao.functor=En,ao.xhr=An(m),ao.dsv=function(n,t){function e(n,e,u){arguments.length<3&&(u=e,e=null);var o=Cn(n,t,null==e?r:i(e),u);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:i(n)):e},o}function r(n){return e.parse(n.responseText)}function i(n){return function(t){return e.parse(t.responseText,n)}}function u(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),l=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var i=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(i(n),e)}:i})},e.parseRows=function(n,t){function e(){if(f>=c)return o;if(i)return i=!1,u;var t=f;if(34===n.charCodeAt(t)){for(var e=t;e++f;){var r=n.charCodeAt(f++),a=1;if(10===r)i=!0;else if(13===r)i=!0,10===n.charCodeAt(f)&&(++f,++a);else if(r!==l)continue;return n.slice(t,f-a)}return n.slice(t)}for(var r,i,u={},o={},a=[],c=n.length,f=0,s=0;(r=e())!==o;){for(var h=[];r!==u&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,s++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new y,i=[];return t.forEach(function(n){for(var t in n)r.has(t)||i.push(r.add(t))}),[i.map(o).join(n)].concat(t.map(function(t){return i.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(u).join("\n")},e},ao.csv=ao.dsv(",","text/csv"),ao.tsv=ao.dsv(" ","text/tab-separated-values");var oa,aa,la,ca,fa=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ao.timer=function(){qn.apply(this,arguments)},ao.timer.flush=function(){Rn(),Dn()},ao.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var sa=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Un);ao.formatPrefix=function(n,t){var e=0;return(n=+n)&&(0>n&&(n*=-1),t&&(n=ao.round(n,Pn(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),sa[8+e/3]};var ha=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,pa=ao.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ao.round(n,Pn(n,t))).toFixed(Math.max(0,Math.min(20,Pn(n*(1+1e-15),t))))}}),ga=ao.time={},va=Date;Hn.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){da.setUTCDate.apply(this._,arguments)},setDay:function(){da.setUTCDay.apply(this._,arguments)},setFullYear:function(){da.setUTCFullYear.apply(this._,arguments)},setHours:function(){da.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){da.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){da.setUTCMinutes.apply(this._,arguments)},setMonth:function(){da.setUTCMonth.apply(this._,arguments)},setSeconds:function(){da.setUTCSeconds.apply(this._,arguments)},setTime:function(){da.setTime.apply(this._,arguments)}};var da=Date.prototype;ga.year=On(function(n){return n=ga.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ga.years=ga.year.range,ga.years.utc=ga.year.utc.range,ga.day=On(function(n){var t=new va(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ga.days=ga.day.range,ga.days.utc=ga.day.utc.range,ga.dayOfYear=function(n){var t=ga.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ga[n]=On(function(n){return(n=ga.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ga[n+"s"]=e.range,ga[n+"s"].utc=e.utc.range,ga[n+"OfYear"]=function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)}}),ga.week=ga.sunday,ga.weeks=ga.sunday.range,ga.weeks.utc=ga.sunday.utc.range,ga.weekOfYear=ga.sundayOfYear;var ya={"-":"",_:" ",0:"0"},ma=/^\s*\d+/,Ma=/^%/;ao.locale=function(n){return{numberFormat:jn(n),timeFormat:Yn(n)}};var xa=ao.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"], +shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ao.format=xa.numberFormat,ao.geo={},ft.prototype={s:0,t:0,add:function(n){st(n,this.t,ba),st(ba.s,this.s,this),this.s?this.t+=ba.t:this.s=ba.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var ba=new ft;ao.geo.stream=function(n,t){n&&_a.hasOwnProperty(n.type)?_a[n.type](n,t):ht(n,t)};var _a={Feature:function(n,t){ht(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,i=e.length;++rn?4*Fo+n:n,Na.lineStart=Na.lineEnd=Na.point=b}};ao.geo.bounds=function(){function n(n,t){M.push(x=[f=n,h=n]),s>t&&(s=t),t>p&&(p=t)}function t(t,e){var r=dt([t*Yo,e*Yo]);if(y){var i=mt(y,r),u=[i[1],-i[0],0],o=mt(u,i);bt(o),o=_t(o);var l=t-g,c=l>0?1:-1,v=o[0]*Zo*c,d=xo(l)>180;if(d^(v>c*g&&c*t>v)){var m=o[1]*Zo;m>p&&(p=m)}else if(v=(v+360)%360-180,d^(v>c*g&&c*t>v)){var m=-o[1]*Zo;s>m&&(s=m)}else s>e&&(s=e),e>p&&(p=e);d?g>t?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t):h>=f?(f>t&&(f=t),t>h&&(h=t)):t>g?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t)}else n(t,e);y=r,g=t}function e(){b.point=t}function r(){x[0]=f,x[1]=h,b.point=n,y=null}function i(n,e){if(y){var r=n-g;m+=xo(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Na.point(n,e),t(n,e)}function u(){Na.lineStart()}function o(){i(v,d),Na.lineEnd(),xo(m)>Uo&&(f=-(h=180)),x[0]=f,x[1]=h,y=null}function a(n,t){return(t-=n)<0?t+360:t}function l(n,t){return n[0]-t[0]}function c(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nka?(f=-(h=180),s=-(p=90)):m>Uo?p=90:-Uo>m&&(s=-90),x[0]=f,x[1]=h}};return function(n){p=h=-(f=s=1/0),M=[],ao.geo.stream(n,b);var t=M.length;if(t){M.sort(l);for(var e,r=1,i=M[0],u=[i];t>r;++r)e=M[r],c(e[0],i)||c(e[1],i)?(a(i[0],e[1])>a(i[0],i[1])&&(i[1]=e[1]),a(e[0],i[1])>a(i[0],i[1])&&(i[0]=e[0])):u.push(i=e);for(var o,e,g=-(1/0),t=u.length-1,r=0,i=u[t];t>=r;i=e,++r)e=u[r],(o=a(i[1],e[0]))>g&&(g=o,f=e[0],h=i[1])}return M=x=null,f===1/0||s===1/0?[[NaN,NaN],[NaN,NaN]]:[[f,s],[h,p]]}}(),ao.geo.centroid=function(n){Ea=Aa=Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,ja);var t=Da,e=Pa,r=Ua,i=t*t+e*e+r*r;return jo>i&&(t=qa,e=Ta,r=Ra,Uo>Aa&&(t=Ca,e=za,r=La),i=t*t+e*e+r*r,jo>i)?[NaN,NaN]:[Math.atan2(e,t)*Zo,tn(r/Math.sqrt(i))*Zo]};var Ea,Aa,Ca,za,La,qa,Ta,Ra,Da,Pa,Ua,ja={sphere:b,point:St,lineStart:Nt,lineEnd:Et,polygonStart:function(){ja.lineStart=At},polygonEnd:function(){ja.lineStart=Nt}},Fa=Rt(zt,jt,Ht,[-Fo,-Fo/2]),Ha=1e9;ao.geo.clipExtent=function(){var n,t,e,r,i,u,o={stream:function(n){return i&&(i.valid=!1),i=u(n),i.valid=!0,i},extent:function(a){return arguments.length?(u=Zt(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),i&&(i.valid=!1,i=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ao.geo.conicEqualArea=function(){return Vt(Xt)}).raw=Xt,ao.geo.albers=function(){return ao.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ao.geo.albersUsa=function(){function n(n){var u=n[0],o=n[1];return t=null,e(u,o),t||(r(u,o),t)||i(u,o),t}var t,e,r,i,u=ao.geo.albers(),o=ao.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ao.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),l={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=u.scale(),e=u.translate(),r=(n[0]-e[0])/t,i=(n[1]-e[1])/t;return(i>=.12&&.234>i&&r>=-.425&&-.214>r?o:i>=.166&&.234>i&&r>=-.214&&-.115>r?a:u).invert(n)},n.stream=function(n){var t=u.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,i){t.point(n,i),e.point(n,i),r.point(n,i)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(u.precision(t),o.precision(t),a.precision(t),n):u.precision()},n.scale=function(t){return arguments.length?(u.scale(t),o.scale(.35*t),a.scale(t),n.translate(u.translate())):u.scale()},n.translate=function(t){if(!arguments.length)return u.translate();var c=u.scale(),f=+t[0],s=+t[1];return e=u.translate(t).clipExtent([[f-.455*c,s-.238*c],[f+.455*c,s+.238*c]]).stream(l).point,r=o.translate([f-.307*c,s+.201*c]).clipExtent([[f-.425*c+Uo,s+.12*c+Uo],[f-.214*c-Uo,s+.234*c-Uo]]).stream(l).point,i=a.translate([f-.205*c,s+.212*c]).clipExtent([[f-.214*c+Uo,s+.166*c+Uo],[f-.115*c-Uo,s+.234*c-Uo]]).stream(l).point,n},n.scale(1070)};var Oa,Ia,Ya,Za,Va,Xa,$a={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Ia=0,$a.lineStart=$t},polygonEnd:function(){$a.lineStart=$a.lineEnd=$a.point=b,Oa+=xo(Ia/2)}},Ba={point:Bt,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Wa={point:Gt,lineStart:Kt,lineEnd:Qt,polygonStart:function(){Wa.lineStart=ne},polygonEnd:function(){Wa.point=Gt,Wa.lineStart=Kt,Wa.lineEnd=Qt}};ao.geo.path=function(){function n(n){return n&&("function"==typeof a&&u.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=i(u)),ao.geo.stream(n,o)),u.result()}function t(){return o=null,n}var e,r,i,u,o,a=4.5;return n.area=function(n){return Oa=0,ao.geo.stream(n,i($a)),Oa},n.centroid=function(n){return Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,i(Wa)),Ua?[Da/Ua,Pa/Ua]:Ra?[qa/Ra,Ta/Ra]:La?[Ca/La,za/La]:[NaN,NaN]},n.bounds=function(n){return Va=Xa=-(Ya=Za=1/0),ao.geo.stream(n,i(Ba)),[[Ya,Za],[Va,Xa]]},n.projection=function(n){return arguments.length?(i=(e=n)?n.stream||re(n):m,t()):e},n.context=function(n){return arguments.length?(u=null==(r=n)?new Wt:new te(n),"function"!=typeof a&&u.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(u.pointRadius(+t),+t),n):a},n.projection(ao.geo.albersUsa()).context(null)},ao.geo.transform=function(n){return{stream:function(t){var e=new ie(t);for(var r in n)e[r]=n[r];return e}}},ie.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ao.geo.projection=oe,ao.geo.projectionMutator=ae,(ao.geo.equirectangular=function(){return oe(ce)}).raw=ce.invert=ce,ao.geo.rotation=function(n){function t(t){return t=n(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t}return n=se(n[0]%360*Yo,n[1]*Yo,n.length>2?n[2]*Yo:0),t.invert=function(t){return t=n.invert(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t},t},fe.invert=ce,ao.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=se(-n[0]*Yo,-n[1]*Yo,0).invert,i=[];return e(null,null,1,{point:function(n,e){i.push(n=t(n,e)),n[0]*=Zo,n[1]*=Zo}}),{type:"Polygon",coordinates:[i]}}var t,e,r=[0,0],i=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=ve((t=+r)*Yo,i*Yo),n):t},n.precision=function(r){return arguments.length?(e=ve(t*Yo,(i=+r)*Yo),n):i},n.angle(90)},ao.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Yo,i=n[1]*Yo,u=t[1]*Yo,o=Math.sin(r),a=Math.cos(r),l=Math.sin(i),c=Math.cos(i),f=Math.sin(u),s=Math.cos(u);return Math.atan2(Math.sqrt((e=s*o)*e+(e=c*f-l*s*a)*e),l*f+c*s*a)},ao.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ao.range(Math.ceil(u/d)*d,i,d).map(h).concat(ao.range(Math.ceil(c/y)*y,l,y).map(p)).concat(ao.range(Math.ceil(r/g)*g,e,g).filter(function(n){return xo(n%d)>Uo}).map(f)).concat(ao.range(Math.ceil(a/v)*v,o,v).filter(function(n){return xo(n%y)>Uo}).map(s))}var e,r,i,u,o,a,l,c,f,s,h,p,g=10,v=g,d=90,y=360,m=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(u).concat(p(l).slice(1),h(i).reverse().slice(1),p(c).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(u=+t[0][0],i=+t[1][0],c=+t[0][1],l=+t[1][1],u>i&&(t=u,u=i,i=t),c>l&&(t=c,c=l,l=t),n.precision(m)):[[u,c],[i,l]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(m)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],y=+t[1],n):[d,y]},n.minorStep=function(t){return arguments.length?(g=+t[0],v=+t[1],n):[g,v]},n.precision=function(t){return arguments.length?(m=+t,f=ye(a,o,90),s=me(r,e,m),h=ye(c,l,90),p=me(u,i,m),n):m},n.majorExtent([[-180,-90+Uo],[180,90-Uo]]).minorExtent([[-180,-80-Uo],[180,80+Uo]])},ao.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||i.apply(this,arguments)]}}var t,e,r=Me,i=xe;return n.distance=function(){return ao.geo.distance(t||r.apply(this,arguments),e||i.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(i=t,e="function"==typeof t?null:t,n):i},n.precision=function(){return arguments.length?n:0},n},ao.geo.interpolate=function(n,t){return be(n[0]*Yo,n[1]*Yo,t[0]*Yo,t[1]*Yo)},ao.geo.length=function(n){return Ja=0,ao.geo.stream(n,Ga),Ja};var Ja,Ga={sphere:b,point:b,lineStart:_e,lineEnd:b,polygonStart:b,polygonEnd:b},Ka=we(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ao.geo.azimuthalEqualArea=function(){return oe(Ka)}).raw=Ka;var Qa=we(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},m);(ao.geo.azimuthalEquidistant=function(){return oe(Qa)}).raw=Qa,(ao.geo.conicConformal=function(){return Vt(Se)}).raw=Se,(ao.geo.conicEquidistant=function(){return Vt(ke)}).raw=ke;var nl=we(function(n){return 1/n},Math.atan);(ao.geo.gnomonic=function(){return oe(nl)}).raw=nl,Ne.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Io]},(ao.geo.mercator=function(){return Ee(Ne)}).raw=Ne;var tl=we(function(){return 1},Math.asin);(ao.geo.orthographic=function(){return oe(tl)}).raw=tl;var el=we(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ao.geo.stereographic=function(){return oe(el)}).raw=el,Ae.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Io]},(ao.geo.transverseMercator=function(){var n=Ee(Ae),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Ae,ao.geom={},ao.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,i=En(e),u=En(r),o=n.length,a=[],l=[];for(t=0;o>t;t++)a.push([+i.call(this,n[t],t),+u.call(this,n[t],t),t]);for(a.sort(qe),t=0;o>t;t++)l.push([a[t][0],-a[t][1]]);var c=Le(a),f=Le(l),s=f[0]===c[0],h=f[f.length-1]===c[c.length-1],p=[];for(t=c.length-1;t>=0;--t)p.push(n[a[c[t]][2]]);for(t=+s;t=r&&c.x<=u&&c.y>=i&&c.y<=o?[[r,o],[u,o],[u,i],[r,i]]:[];f.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(u(n,t)/Uo)*Uo,y:Math.round(o(n,t)/Uo)*Uo,i:t}})}var r=Ce,i=ze,u=r,o=i,a=sl;return n?t(n):(t.links=function(n){return ar(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return ar(e(n)).cells.forEach(function(e,r){for(var i,u,o=e.site,a=e.edges.sort(Ve),l=-1,c=a.length,f=a[c-1].edge,s=f.l===o?f.r:f.l;++l=c,h=r>=f,p=h<<1|s;n.leaf=!1,n=n.nodes[p]||(n.nodes[p]=hr()),s?i=c:a=c,h?o=f:l=f,u(n,t,e,r,i,o,a,l)}var f,s,h,p,g,v,d,y,m,M=En(a),x=En(l);if(null!=t)v=t,d=e,y=r,m=i;else if(y=m=-(v=d=1/0),s=[],h=[],g=n.length,o)for(p=0;g>p;++p)f=n[p],f.xy&&(y=f.x),f.y>m&&(m=f.y),s.push(f.x),h.push(f.y);else for(p=0;g>p;++p){var b=+M(f=n[p],p),_=+x(f,p);v>b&&(v=b),d>_&&(d=_),b>y&&(y=b),_>m&&(m=_),s.push(b),h.push(_)}var w=y-v,S=m-d;w>S?m=d+w:y=v+S;var k=hr();if(k.add=function(n){u(k,n,+M(n,++p),+x(n,p),v,d,y,m)},k.visit=function(n){pr(n,k,v,d,y,m)},k.find=function(n){return gr(k,n[0],n[1],v,d,y,m)},p=-1,null==t){for(;++p=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=vl.get(e)||gl,r=dl.get(r)||m,br(r(e.apply(null,lo.call(arguments,1))))},ao.interpolateHcl=Rr,ao.interpolateHsl=Dr,ao.interpolateLab=Pr,ao.interpolateRound=Ur,ao.transform=function(n){var t=fo.createElementNS(ao.ns.prefix.svg,"g");return(ao.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new jr(e?e.matrix:yl)})(n)},jr.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var yl={a:1,b:0,c:0,d:1,e:0,f:0};ao.interpolateTransform=$r,ao.layout={},ao.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++ea*a/y){if(v>l){var c=t.charge/l;n.px-=u*c,n.py-=o*c}return!0}if(t.point&&l&&v>l){var c=t.pointCharge/l;n.px-=u*c,n.py-=o*c}}return!t.charge}}function t(n){n.px=ao.event.x,n.py=ao.event.y,l.resume()}var e,r,i,u,o,a,l={},c=ao.dispatch("start","tick","end"),f=[1,1],s=.9,h=ml,p=Ml,g=-30,v=xl,d=.1,y=.64,M=[],x=[];return l.tick=function(){if((i*=.99)<.005)return e=null,c.end({type:"end",alpha:i=0}),!0;var t,r,l,h,p,v,y,m,b,_=M.length,w=x.length;for(r=0;w>r;++r)l=x[r],h=l.source,p=l.target,m=p.x-h.x,b=p.y-h.y,(v=m*m+b*b)&&(v=i*o[r]*((v=Math.sqrt(v))-u[r])/v,m*=v,b*=v,p.x-=m*(y=h.weight+p.weight?h.weight/(h.weight+p.weight):.5),p.y-=b*y,h.x+=m*(y=1-y),h.y+=b*y);if((y=i*d)&&(m=f[0]/2,b=f[1]/2,r=-1,y))for(;++r<_;)l=M[r],l.x+=(m-l.x)*y,l.y+=(b-l.y)*y;if(g)for(ri(t=ao.geom.quadtree(M),i,a),r=-1;++r<_;)(l=M[r]).fixed||t.visit(n(l));for(r=-1;++r<_;)l=M[r],l.fixed?(l.x=l.px,l.y=l.py):(l.x-=(l.px-(l.px=l.x))*s,l.y-=(l.py-(l.py=l.y))*s);c.tick({type:"tick",alpha:i})},l.nodes=function(n){return arguments.length?(M=n,l):M},l.links=function(n){return arguments.length?(x=n,l):x},l.size=function(n){return arguments.length?(f=n,l):f},l.linkDistance=function(n){return arguments.length?(h="function"==typeof n?n:+n,l):h},l.distance=l.linkDistance,l.linkStrength=function(n){return arguments.length?(p="function"==typeof n?n:+n,l):p},l.friction=function(n){return arguments.length?(s=+n,l):s},l.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,l):g},l.chargeDistance=function(n){return arguments.length?(v=n*n,l):Math.sqrt(v)},l.gravity=function(n){return arguments.length?(d=+n,l):d},l.theta=function(n){return arguments.length?(y=n*n,l):Math.sqrt(y)},l.alpha=function(n){return arguments.length?(n=+n,i?n>0?i=n:(e.c=null,e.t=NaN,e=null,c.end({type:"end",alpha:i=0})):n>0&&(c.start({type:"start",alpha:i=n}),e=qn(l.tick)),l):i},l.start=function(){function n(n,r){if(!e){for(e=new Array(i),l=0;i>l;++l)e[l]=[];for(l=0;c>l;++l){var u=x[l];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var o,a=e[t],l=-1,f=a.length;++lt;++t)(r=M[t]).index=t,r.weight=0;for(t=0;c>t;++t)r=x[t],"number"==typeof r.source&&(r.source=M[r.source]),"number"==typeof r.target&&(r.target=M[r.target]),++r.source.weight,++r.target.weight;for(t=0;i>t;++t)r=M[t],isNaN(r.x)&&(r.x=n("x",s)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof h)for(t=0;c>t;++t)u[t]=+h.call(this,x[t],t);else for(t=0;c>t;++t)u[t]=h;if(o=[],"function"==typeof p)for(t=0;c>t;++t)o[t]=+p.call(this,x[t],t);else for(t=0;c>t;++t)o[t]=p;if(a=[],"function"==typeof g)for(t=0;i>t;++t)a[t]=+g.call(this,M[t],t);else for(t=0;i>t;++t)a[t]=g;return l.resume()},l.resume=function(){return l.alpha(.1)},l.stop=function(){return l.alpha(0)},l.drag=function(){return r||(r=ao.behavior.drag().origin(m).on("dragstart.force",Qr).on("drag.force",t).on("dragend.force",ni)),arguments.length?void this.on("mouseover.force",ti).on("mouseout.force",ei).call(r):r},ao.rebind(l,c,"on")};var ml=20,Ml=1,xl=1/0;ao.layout.hierarchy=function(){function n(i){var u,o=[i],a=[];for(i.depth=0;null!=(u=o.pop());)if(a.push(u),(c=e.call(n,u,u.depth))&&(l=c.length)){for(var l,c,f;--l>=0;)o.push(f=c[l]),f.parent=u,f.depth=u.depth+1;r&&(u.value=0),u.children=c}else r&&(u.value=+r.call(n,u,u.depth)||0),delete u.children;return oi(i,function(n){var e,i;t&&(e=n.children)&&e.sort(t),r&&(i=n.parent)&&(i.value+=n.value)}),a}var t=ci,e=ai,r=li;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(ui(t,function(n){n.children&&(n.value=0)}),oi(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ao.layout.partition=function(){function n(t,e,r,i){var u=t.children;if(t.x=e,t.y=t.depth*i,t.dx=r,t.dy=i,u&&(o=u.length)){var o,a,l,c=-1;for(r=t.value?r/t.value:0;++cs?-1:1),g=ao.sum(c),v=g?(s-l*p)/g:0,d=ao.range(l),y=[];return null!=e&&d.sort(e===bl?function(n,t){return c[t]-c[n]}:function(n,t){return e(o[n],o[t])}),d.forEach(function(n){y[n]={data:o[n],value:a=c[n],startAngle:f,endAngle:f+=a*v+p,padAngle:h}}),y}var t=Number,e=bl,r=0,i=Ho,u=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(i=t,n):i},n.padAngle=function(t){return arguments.length?(u=t,n):u},n};var bl={};ao.layout.stack=function(){function n(a,l){if(!(h=a.length))return a;var c=a.map(function(e,r){return t.call(n,e,r)}),f=c.map(function(t){return t.map(function(t,e){return[u.call(n,t,e),o.call(n,t,e)]})}),s=e.call(n,f,l);c=ao.permute(c,s),f=ao.permute(f,s);var h,p,g,v,d=r.call(n,f,l),y=c[0].length;for(g=0;y>g;++g)for(i.call(n,c[0][g],v=d[g],f[0][g][1]),p=1;h>p;++p)i.call(n,c[p][g],v+=f[p-1][g][1],f[p][g][1]);return a}var t=m,e=gi,r=vi,i=pi,u=si,o=hi;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:_l.get(t)||gi,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:wl.get(t)||vi,n):r},n.x=function(t){return arguments.length?(u=t,n):u},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(i=t,n):i},n};var _l=ao.map({"inside-out":function(n){var t,e,r=n.length,i=n.map(di),u=n.map(yi),o=ao.range(r).sort(function(n,t){return i[n]-i[t]}),a=0,l=0,c=[],f=[];for(t=0;r>t;++t)e=o[t],l>a?(a+=u[e],c.push(e)):(l+=u[e],f.push(e));return f.reverse().concat(c)},reverse:function(n){return ao.range(n.length).reverse()},"default":gi}),wl=ao.map({silhouette:function(n){var t,e,r,i=n.length,u=n[0].length,o=[],a=0,l=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;u>e;++e)l[e]=(a-o[e])/2;return l},wiggle:function(n){var t,e,r,i,u,o,a,l,c,f=n.length,s=n[0],h=s.length,p=[];for(p[0]=l=c=0,e=1;h>e;++e){for(t=0,i=0;f>t;++t)i+=n[t][e][1];for(t=0,u=0,a=s[e][0]-s[e-1][0];f>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;u+=o*n[t][e][1]}p[e]=l-=i?u/i*a:0,c>l&&(c=l)}for(e=0;h>e;++e)p[e]-=c;return p},expand:function(n){var t,e,r,i=n.length,u=n[0].length,o=1/i,a=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];if(r)for(t=0;i>t;t++)n[t][e][1]/=r;else for(t=0;i>t;t++)n[t][e][1]=o}for(e=0;u>e;++e)a[e]=0;return a},zero:vi});ao.layout.histogram=function(){function n(n,u){for(var o,a,l=[],c=n.map(e,this),f=r.call(this,c,u),s=i.call(this,f,c,u),u=-1,h=c.length,p=s.length-1,g=t?1:1/h;++u0)for(u=-1;++u=f[0]&&a<=f[1]&&(o=l[ao.bisect(s,a,1,p)-1],o.y+=g,o.push(n[u]));return l}var t=!0,e=Number,r=bi,i=Mi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=En(t),n):r},n.bins=function(t){return arguments.length?(i="number"==typeof t?function(n){return xi(n,t)}:En(t),n):i},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ao.layout.pack=function(){function n(n,u){var o=e.call(this,n,u),a=o[0],l=i[0],c=i[1],f=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,oi(a,function(n){n.r=+f(n.value)}),oi(a,Ni),r){var s=r*(t?1:Math.max(2*a.r/l,2*a.r/c))/2;oi(a,function(n){n.r+=s}),oi(a,Ni),oi(a,function(n){n.r-=s})}return Ci(a,l/2,c/2,t?1:1/Math.max(2*a.r/l,2*a.r/c)),o}var t,e=ao.layout.hierarchy().sort(_i),r=0,i=[1,1];return n.size=function(t){return arguments.length?(i=t,n):i},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},ii(n,e)},ao.layout.tree=function(){function n(n,i){var f=o.call(this,n,i),s=f[0],h=t(s);if(oi(h,e),h.parent.m=-h.z,ui(h,r),c)ui(s,u);else{var p=s,g=s,v=s;ui(s,function(n){n.xg.x&&(g=n),n.depth>v.depth&&(v=n)});var d=a(p,g)/2-p.x,y=l[0]/(g.x+a(g,p)/2+d),m=l[1]/(v.depth||1);ui(s,function(n){n.x=(n.x+d)*y,n.y=n.depth*m})}return f}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var i,u=t.children,o=0,a=u.length;a>o;++o)r.push((u[o]=i={_:u[o],parent:t,children:(i=u[o].children)&&i.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=i);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Di(n);var u=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-u):n.z=u}else r&&(n.z=r.z+a(n._,r._));n.parent.A=i(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function i(n,t,e){if(t){for(var r,i=n,u=n,o=t,l=i.parent.children[0],c=i.m,f=u.m,s=o.m,h=l.m;o=Ti(o),i=qi(i),o&&i;)l=qi(l),u=Ti(u),u.a=n,r=o.z+s-i.z-c+a(o._,i._),r>0&&(Ri(Pi(o,n,e),n,r),c+=r,f+=r),s+=o.m,c+=i.m,h+=l.m,f+=u.m;o&&!Ti(u)&&(u.t=o,u.m+=s-f),i&&!qi(l)&&(l.t=i,l.m+=c-h,e=n)}return e}function u(n){n.x*=l[0],n.y=n.depth*l[1]}var o=ao.layout.hierarchy().sort(null).value(null),a=Li,l=[1,1],c=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(c=null==(l=t)?u:null,n):c?null:l},n.nodeSize=function(t){return arguments.length?(c=null==(l=t)?null:u,n):c?l:null},ii(n,o)},ao.layout.cluster=function(){function n(n,u){var o,a=t.call(this,n,u),l=a[0],c=0;oi(l,function(n){var t=n.children;t&&t.length?(n.x=ji(t),n.y=Ui(t)):(n.x=o?c+=e(n,o):0,n.y=0,o=n)});var f=Fi(l),s=Hi(l),h=f.x-e(f,s)/2,p=s.x+e(s,f)/2;return oi(l,i?function(n){n.x=(n.x-l.x)*r[0],n.y=(l.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(p-h)*r[0],n.y=(1-(l.y?n.y/l.y:1))*r[1]}),a}var t=ao.layout.hierarchy().sort(null).value(null),e=Li,r=[1,1],i=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},ii(n,t)},ao.layout.treemap=function(){function n(n,t){for(var e,r,i=-1,u=n.length;++it?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var u=e.children;if(u&&u.length){var o,a,l,c=s(e),f=[],h=u.slice(),g=1/0,v="slice"===p?c.dx:"dice"===p?c.dy:"slice-dice"===p?1&e.depth?c.dy:c.dx:Math.min(c.dx,c.dy);for(n(h,c.dx*c.dy/e.value),f.area=0;(l=h.length)>0;)f.push(o=h[l-1]),f.area+=o.area,"squarify"!==p||(a=r(f,v))<=g?(h.pop(),g=a):(f.area-=f.pop().area,i(f,v,c,!1),v=Math.min(c.dx,c.dy),f.length=f.area=0,g=1/0);f.length&&(i(f,v,c,!0),f.length=f.area=0),u.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var u,o=s(t),a=r.slice(),l=[];for(n(a,o.dx*o.dy/t.value),l.area=0;u=a.pop();)l.push(u),l.area+=u.area,null!=u.z&&(i(l,u.z?o.dx:o.dy,o,!a.length),l.length=l.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,i=0,u=1/0,o=-1,a=n.length;++oe&&(u=e),e>i&&(i=e));return r*=r,t*=t,r?Math.max(t*i*g/r,r/(t*u*g)):1/0}function i(n,t,e,r){var i,u=-1,o=n.length,a=e.x,c=e.y,f=t?l(n.area/t):0; +if(t==e.dx){for((r||f>e.dy)&&(f=e.dy);++ue.dx)&&(f=e.dx);++ue&&(t=1),1>e&&(n=0),function(){var e,r,i;do e=2*Math.random()-1,r=2*Math.random()-1,i=e*e+r*r;while(!i||i>1);return n+t*e*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var n=ao.random.normal.apply(ao,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ao.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ao.scale={};var Sl={floor:m,ceil:m};ao.scale.linear=function(){return Wi([0,1],[0,1],Mr,!1)};var kl={s:1,g:1,p:1,r:1,e:1};ao.scale.log=function(){return ru(ao.scale.linear().domain([0,1]),10,!0,[1,10])};var Nl=ao.format(".0e"),El={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ao.scale.pow=function(){return iu(ao.scale.linear(),1,[0,1])},ao.scale.sqrt=function(){return ao.scale.pow().exponent(.5)},ao.scale.ordinal=function(){return ou([],{t:"range",a:[[]]})},ao.scale.category10=function(){return ao.scale.ordinal().range(Al)},ao.scale.category20=function(){return ao.scale.ordinal().range(Cl)},ao.scale.category20b=function(){return ao.scale.ordinal().range(zl)},ao.scale.category20c=function(){return ao.scale.ordinal().range(Ll)};var Al=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(xn),Cl=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(xn),zl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(xn),Ll=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(xn);ao.scale.quantile=function(){return au([],[])},ao.scale.quantize=function(){return lu(0,1,[0,1])},ao.scale.threshold=function(){return cu([.5],[0,1])},ao.scale.identity=function(){return fu([0,1])},ao.svg={},ao.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),c=Math.max(0,+r.apply(this,arguments)),f=o.apply(this,arguments)-Io,s=a.apply(this,arguments)-Io,h=Math.abs(s-f),p=f>s?0:1;if(n>c&&(g=c,c=n,n=g),h>=Oo)return t(c,p)+(n?t(n,1-p):"")+"Z";var g,v,d,y,m,M,x,b,_,w,S,k,N=0,E=0,A=[];if((y=(+l.apply(this,arguments)||0)/2)&&(d=u===ql?Math.sqrt(n*n+c*c):+u.apply(this,arguments),p||(E*=-1),c&&(E=tn(d/c*Math.sin(y))),n&&(N=tn(d/n*Math.sin(y)))),c){m=c*Math.cos(f+E),M=c*Math.sin(f+E),x=c*Math.cos(s-E),b=c*Math.sin(s-E);var C=Math.abs(s-f-2*E)<=Fo?0:1;if(E&&yu(m,M,x,b)===p^C){var z=(f+s)/2;m=c*Math.cos(z),M=c*Math.sin(z),x=b=null}}else m=M=0;if(n){_=n*Math.cos(s-N),w=n*Math.sin(s-N),S=n*Math.cos(f+N),k=n*Math.sin(f+N);var L=Math.abs(f-s+2*N)<=Fo?0:1;if(N&&yu(_,w,S,k)===1-p^L){var q=(f+s)/2;_=n*Math.cos(q),w=n*Math.sin(q),S=k=null}}else _=w=0;if(h>Uo&&(g=Math.min(Math.abs(c-n)/2,+i.apply(this,arguments)))>.001){v=c>n^p?0:1;var T=g,R=g;if(Fo>h){var D=null==S?[_,w]:null==x?[m,M]:Re([m,M],[S,k],[x,b],[_,w]),P=m-D[0],U=M-D[1],j=x-D[0],F=b-D[1],H=1/Math.sin(Math.acos((P*j+U*F)/(Math.sqrt(P*P+U*U)*Math.sqrt(j*j+F*F)))/2),O=Math.sqrt(D[0]*D[0]+D[1]*D[1]);R=Math.min(g,(n-O)/(H-1)),T=Math.min(g,(c-O)/(H+1))}if(null!=x){var I=mu(null==S?[_,w]:[S,k],[m,M],c,T,p),Y=mu([x,b],[_,w],c,T,p);g===T?A.push("M",I[0],"A",T,",",T," 0 0,",v," ",I[1],"A",c,",",c," 0 ",1-p^yu(I[1][0],I[1][1],Y[1][0],Y[1][1]),",",p," ",Y[1],"A",T,",",T," 0 0,",v," ",Y[0]):A.push("M",I[0],"A",T,",",T," 0 1,",v," ",Y[0])}else A.push("M",m,",",M);if(null!=S){var Z=mu([m,M],[S,k],n,-R,p),V=mu([_,w],null==x?[m,M]:[x,b],n,-R,p);g===R?A.push("L",V[0],"A",R,",",R," 0 0,",v," ",V[1],"A",n,",",n," 0 ",p^yu(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-p," ",Z[1],"A",R,",",R," 0 0,",v," ",Z[0]):A.push("L",V[0],"A",R,",",R," 0 0,",v," ",Z[0])}else A.push("L",_,",",w)}else A.push("M",m,",",M),null!=x&&A.push("A",c,",",c," 0 ",C,",",p," ",x,",",b),A.push("L",_,",",w),null!=S&&A.push("A",n,",",n," 0 ",L,",",1-p," ",S,",",k);return A.push("Z"),A.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=hu,r=pu,i=su,u=ql,o=gu,a=vu,l=du;return n.innerRadius=function(t){return arguments.length?(e=En(t),n):e},n.outerRadius=function(t){return arguments.length?(r=En(t),n):r},n.cornerRadius=function(t){return arguments.length?(i=En(t),n):i},n.padRadius=function(t){return arguments.length?(u=t==ql?ql:En(t),n):u},n.startAngle=function(t){return arguments.length?(o=En(t),n):o},n.endAngle=function(t){return arguments.length?(a=En(t),n):a},n.padAngle=function(t){return arguments.length?(l=En(t),n):l},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Io;return[Math.cos(t)*n,Math.sin(t)*n]},n};var ql="auto";ao.svg.line=function(){return Mu(m)};var Tl=ao.map({linear:xu,"linear-closed":bu,step:_u,"step-before":wu,"step-after":Su,basis:zu,"basis-open":Lu,"basis-closed":qu,bundle:Tu,cardinal:Eu,"cardinal-open":ku,"cardinal-closed":Nu,monotone:Fu});Tl.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Rl=[0,2/3,1/3,0],Dl=[0,1/3,2/3,0],Pl=[0,1/6,2/3,1/6];ao.svg.line.radial=function(){var n=Mu(Hu);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},wu.reverse=Su,Su.reverse=wu,ao.svg.area=function(){return Ou(m)},ao.svg.area.radial=function(){var n=Ou(Hu);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ao.svg.chord=function(){function n(n,a){var l=t(this,u,n,a),c=t(this,o,n,a);return"M"+l.p0+r(l.r,l.p1,l.a1-l.a0)+(e(l,c)?i(l.r,l.p1,l.r,l.p0):i(l.r,l.p1,c.r,c.p0)+r(c.r,c.p1,c.a1-c.a0)+i(c.r,c.p1,l.r,l.p0))+"Z"}function t(n,t,e,r){var i=t.call(n,e,r),u=a.call(n,i,r),o=l.call(n,i,r)-Io,f=c.call(n,i,r)-Io;return{r:u,a0:o,a1:f,p0:[u*Math.cos(o),u*Math.sin(o)],p1:[u*Math.cos(f),u*Math.sin(f)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Fo)+",1 "+t}function i(n,t,e,r){return"Q 0,0 "+r}var u=Me,o=xe,a=Iu,l=gu,c=vu;return n.radius=function(t){return arguments.length?(a=En(t),n):a},n.source=function(t){return arguments.length?(u=En(t),n):u},n.target=function(t){return arguments.length?(o=En(t),n):o},n.startAngle=function(t){return arguments.length?(l=En(t),n):l},n.endAngle=function(t){return arguments.length?(c=En(t),n):c},n},ao.svg.diagonal=function(){function n(n,i){var u=t.call(this,n,i),o=e.call(this,n,i),a=(u.y+o.y)/2,l=[u,{x:u.x,y:a},{x:o.x,y:a},o];return l=l.map(r),"M"+l[0]+"C"+l[1]+" "+l[2]+" "+l[3]}var t=Me,e=xe,r=Yu;return n.source=function(e){return arguments.length?(t=En(e),n):t},n.target=function(t){return arguments.length?(e=En(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ao.svg.diagonal.radial=function(){var n=ao.svg.diagonal(),t=Yu,e=n.projection;return n.projection=function(n){return arguments.length?e(Zu(t=n)):t},n},ao.svg.symbol=function(){function n(n,r){return(Ul.get(t.call(this,n,r))||$u)(e.call(this,n,r))}var t=Xu,e=Vu;return n.type=function(e){return arguments.length?(t=En(e),n):t},n.size=function(t){return arguments.length?(e=En(t),n):e},n};var Ul=ao.map({circle:$u,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Fl)),e=t*Fl;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ao.svg.symbolTypes=Ul.keys();var jl=Math.sqrt(3),Fl=Math.tan(30*Yo);Co.transition=function(n){for(var t,e,r=Hl||++Zl,i=Ku(n),u=[],o=Ol||{time:Date.now(),ease:Nr,delay:0,duration:250},a=-1,l=this.length;++au;u++){i.push(t=[]);for(var e=this[u],a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return Wu(i,this.namespace,this.id)},Yl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(i){i[r][e].tween.set(n,t)})},Yl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function i(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function u(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?$r:Mr,a=ao.ns.qualify(n);return Ju(this,"attr."+n,t,a.local?u:i)},Yl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(i));return r&&function(n){this.setAttribute(i,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(i.space,i.local));return r&&function(n){this.setAttributeNS(i.space,i.local,r(n))}}var i=ao.ns.qualify(n);return this.tween("attr."+n,i.local?r:e)},Yl.style=function(n,e,r){function i(){this.style.removeProperty(n)}function u(e){return null==e?i:(e+="",function(){var i,u=t(this).getComputedStyle(this,null).getPropertyValue(n);return u!==e&&(i=Mr(u,e),function(t){this.style.setProperty(n,i(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Ju(this,"style."+n,e,u)},Yl.styleTween=function(n,e,r){function i(i,u){var o=e.call(this,i,u,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,i)},Yl.text=function(n){return Ju(this,"text",n,Gu)},Yl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Yl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ao.ease.apply(ao,arguments)),Y(this,function(r){r[e][t].ease=n}))},Yl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,i,u){r[e][t].delay=+n.call(r,r.__data__,i,u)}:(n=+n,function(r){r[e][t].delay=n}))},Yl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,i,u){r[e][t].duration=Math.max(1,n.call(r,r.__data__,i,u))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Yl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var i=Ol,u=Hl;try{Hl=e,Y(this,function(t,i,u){Ol=t[r][e],n.call(t,t.__data__,i,u)})}finally{Ol=i,Hl=u}}else Y(this,function(i){var u=i[r][e];(u.event||(u.event=ao.dispatch("start","end","interrupt"))).on(n,t)});return this},Yl.transition=function(){for(var n,t,e,r,i=this.id,u=++Zl,o=this.namespace,a=[],l=0,c=this.length;c>l;l++){a.push(n=[]);for(var t=this[l],f=0,s=t.length;s>f;f++)(e=t[f])&&(r=e[o][i],Qu(e,f,o,u,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Wu(a,o,u)},ao.svg.axis=function(){function n(n){n.each(function(){var n,c=ao.select(this),f=this.__chart__||e,s=this.__chart__=e.copy(),h=null==l?s.ticks?s.ticks.apply(s,a):s.domain():l,p=null==t?s.tickFormat?s.tickFormat.apply(s,a):m:t,g=c.selectAll(".tick").data(h,s),v=g.enter().insert("g",".domain").attr("class","tick").style("opacity",Uo),d=ao.transition(g.exit()).style("opacity",Uo).remove(),y=ao.transition(g.order()).style("opacity",1),M=Math.max(i,0)+o,x=Zi(s),b=c.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ao.transition(b));v.append("line"),v.append("text");var w,S,k,N,E=v.select("line"),A=y.select("line"),C=g.select("text").text(p),z=v.select("text"),L=y.select("text"),q="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=no,w="x",k="y",S="x2",N="y2",C.attr("dy",0>q?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+q*u+"V0H"+x[1]+"V"+q*u)):(n=to,w="y",k="x",S="y2",N="x2",C.attr("dy",".32em").style("text-anchor",0>q?"end":"start"),_.attr("d","M"+q*u+","+x[0]+"H0V"+x[1]+"H"+q*u)),E.attr(N,q*i),z.attr(k,q*M),A.attr(S,0).attr(N,q*i),L.attr(w,0).attr(k,q*M),s.rangeBand){var T=s,R=T.rangeBand()/2;f=s=function(n){return T(n)+R}}else f.rangeBand?f=s:d.call(n,s,f);v.call(n,f,s),y.call(n,s,s)})}var t,e=ao.scale.linear(),r=Vl,i=6,u=6,o=3,a=[10],l=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Xl?t+"":Vl,n):r},n.ticks=function(){return arguments.length?(a=co(arguments),n):a},n.tickValues=function(t){return arguments.length?(l=t,n):l},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(i=+t,u=+arguments[e-1],n):i},n.innerTickSize=function(t){return arguments.length?(i=+t,n):i},n.outerTickSize=function(t){return arguments.length?(u=+t,n):u},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Vl="bottom",Xl={top:1,right:1,bottom:1,left:1};ao.svg.brush=function(){function n(t){t.each(function(){var t=ao.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,m);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return $l[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,s=ao.transition(t),h=ao.transition(o);c&&(l=Zi(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),r(s)),f&&(l=Zi(f),h.attr("y",l[0]).attr("height",l[1]-l[0]),i(s)),e(s)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+s[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",s[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",s[1]-s[0])}function i(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function u(){function u(){32==ao.event.keyCode&&(C||(M=null,L[0]-=s[1],L[1]-=h[1],C=2),S())}function v(){32==ao.event.keyCode&&2==C&&(L[0]+=s[1],L[1]+=h[1],C=0,S())}function d(){var n=ao.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ao.event.altKey?(M||(M=[(s[0]+s[1])/2,(h[0]+h[1])/2]),L[0]=s[+(n[0]f?(i=r,r=f):i=f),v[0]!=r||v[1]!=i?(e?a=null:o=null,v[0]=r,v[1]=i,!0):void 0}function m(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ao.select("body").style("cursor",null),q.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ao.select(ao.event.target),w=l.of(b,arguments),k=ao.select(b),N=_.datum(),E=!/^(n|s)$/.test(N)&&c,A=!/^(e|w)$/.test(N)&&f,C=_.classed("extent"),z=W(b),L=ao.mouse(b),q=ao.select(t(b)).on("keydown.brush",u).on("keyup.brush",v);if(ao.event.changedTouches?q.on("touchmove.brush",d).on("touchend.brush",m):q.on("mousemove.brush",d).on("mouseup.brush",m),k.interrupt().selectAll("*").interrupt(),C)L[0]=s[0]-L[0],L[1]=h[0]-L[1];else if(N){var T=+/w$/.test(N),R=+/^n/.test(N);x=[s[1-T]-L[0],h[1-R]-L[1]],L[0]=s[T],L[1]=h[R]}else ao.event.altKey&&(M=L.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ao.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,l=N(n,"brushstart","brush","brushend"),c=null,f=null,s=[0,0],h=[0,0],p=!0,g=!0,v=Bl[0];return n.event=function(n){n.each(function(){var n=l.of(this,arguments),t={x:s,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Hl?ao.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,s=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=xr(s,t.x),r=xr(h,t.y);return o=a=null,function(i){s=t.x=e(i),h=t.y=r(i),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,v=Bl[!c<<1|!f],n):c},n.y=function(t){return arguments.length?(f=t,v=Bl[!c<<1|!f],n):f},n.clamp=function(t){return arguments.length?(c&&f?(p=!!t[0],g=!!t[1]):c?p=!!t:f&&(g=!!t),n):c&&f?[p,g]:c?p:f?g:null},n.extent=function(t){var e,r,i,u,l;return arguments.length?(c&&(e=t[0],r=t[1],f&&(e=e[0],r=r[0]),o=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(l=e,e=r,r=l),e==s[0]&&r==s[1]||(s=[e,r])),f&&(i=t[0],u=t[1],c&&(i=i[1],u=u[1]),a=[i,u],f.invert&&(i=f(i),u=f(u)),i>u&&(l=i,i=u,u=l),i==h[0]&&u==h[1]||(h=[i,u])),n):(c&&(o?(e=o[0],r=o[1]):(e=s[0],r=s[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(l=e,e=r,r=l))),f&&(a?(i=a[0],u=a[1]):(i=h[0],u=h[1],f.invert&&(i=f.invert(i),u=f.invert(u)),i>u&&(l=i,i=u,u=l))),c&&f?[[e,i],[r,u]]:c?[e,r]:f&&[i,u])},n.clear=function(){return n.empty()||(s=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!c&&s[0]==s[1]||!!f&&h[0]==h[1]},ao.rebind(n,l,"on")};var $l={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Bl=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Wl=ga.format=xa.timeFormat,Jl=Wl.utc,Gl=Jl("%Y-%m-%dT%H:%M:%S.%LZ");Wl.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?eo:Gl,eo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},eo.toString=Gl.toString,ga.second=On(function(n){return new va(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ga.seconds=ga.second.range,ga.seconds.utc=ga.second.utc.range,ga.minute=On(function(n){return new va(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ga.minutes=ga.minute.range,ga.minutes.utc=ga.minute.utc.range,ga.hour=On(function(n){var t=n.getTimezoneOffset()/60;return new va(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ga.hours=ga.hour.range,ga.hours.utc=ga.hour.utc.range,ga.month=On(function(n){return n=ga.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ga.months=ga.month.range,ga.months.utc=ga.month.utc.range;var Kl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Ql=[[ga.second,1],[ga.second,5],[ga.second,15],[ga.second,30],[ga.minute,1],[ga.minute,5],[ga.minute,15],[ga.minute,30],[ga.hour,1],[ga.hour,3],[ga.hour,6],[ga.hour,12],[ga.day,1],[ga.day,2],[ga.week,1],[ga.month,1],[ga.month,3],[ga.year,1]],nc=Wl.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",zt]]),tc={range:function(n,t,e){return ao.range(Math.ceil(n/e)*e,+t,e).map(io)},floor:m,ceil:m};Ql.year=ga.year,ga.scale=function(){return ro(ao.scale.linear(),Ql,nc)};var ec=Ql.map(function(n){return[n[0].utc,n[1]]}),rc=Jl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",zt]]);ec.year=ga.year.utc,ga.scale.utc=function(){return ro(ao.scale.linear(),ec,rc)},ao.text=An(function(n){return n.responseText}),ao.json=function(n,t){return Cn(n,"application/json",uo,t)},ao.html=function(n,t){return Cn(n,"text/html",oo,t)},ao.xml=An(function(n){return n.responseXML}),"function"==typeof define&&define.amd?(this.d3=ao,define(ao)):"object"==typeof module&&module.exports?module.exports=ao:this.d3=ao}();/* + Copyright (C) Federico Zivolo 2019 + Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT). + */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=e.ownerDocument.defaultView,n=o.getComputedStyle(e,null);return t?n[t]:n}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function r(e){return 11===e?pe:10===e?se:pe||se}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent||null;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TH','TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=Q(f[o],a[e]-('right'===e?f.width:f.height))),le({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=fe({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!K(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-us[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,w=t(e.instance.popper),y=parseFloat(w['margin'+f],10),E=parseFloat(w['border'+f+'Width'],10),v=b-e.offsets.popper[m]-y-E;return v=ee(Q(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},le(n,m,$(v)),le(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case ge.FLIP:p=[n,i];break;case ge.CLOCKWISE:p=G(n);break;case ge.COUNTERCLOCKWISE:p=G(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)f(l.top)||'bottom'===n&&f(a.top)f(o.right),g=f(a.top)f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,w=-1!==['top','bottom'].indexOf(n),y=!!t.flipVariations&&(w&&'start'===r&&h||w&&'end'===r&&c||!w&&'start'===r&&g||!w&&'end'===r&&u),E=!!t.flipVariationsByContent&&(w&&'start'===r&&c||w&&'end'===r&&h||!w&&'start'===r&&u||!w&&'end'===r&&g),v=y||E;(m||b||v)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),v&&(r=z(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=fe({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport',flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!K(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.rightwindow.devicePixelRatio||!me),c='bottom'===o?'top':'bottom',g='right'===n?'left':'right',b=B('transform');if(d='bottom'==c?'HTML'===l.nodeName?-l.clientHeight+h.bottom:-f.height+h.bottom:h.top,s='right'==g?'HTML'===l.nodeName?-l.clientWidth+h.right:-f.width+h.right:h.left,a&&b)m[b]='translate3d('+s+'px, '+d+'px, 0)',m[c]=0,m[g]=0,m.willChange='transform';else{var w='bottom'==c?-1:1,y='right'==g?-1:1;m[c]=d*w,m[g]=s*y,m.willChange=c+', '+g}var E={"x-placement":e.placement};return e.attributes=fe({},E,e.attributes),e.styles=fe({},m,e.styles),e.arrowStyles=fe({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:'bottom',y:'right'},applyStyle:{order:900,enabled:!0,fn:function(e){return V(e.instance.popper,e.styles),j(e.instance.popper,e.attributes),e.arrowElement&&Object.keys(e.arrowStyles).length&&V(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,o,n,i){var r=L(i,t,e,o.positionFixed),p=O(o.placement,r,t,e,o.modifiers.flip.boundariesElement,o.modifiers.flip.padding);return t.setAttribute('x-placement',p),V(t,{position:o.positionFixed?'fixed':'absolute'}),o},gpuAcceleration:void 0}}},ue}); +//# sourceMappingURL=popper.min.js.map +/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
      "],col:[2,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
      ",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}};c.jQueryDetection(),e.fn.emulateTransitionEnd=l,e.event.special[c.TRANSITION_END]={bindType:"transitionend",delegateType:"transitionend",handle:function(t){if(e(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}};var h="alert",u=e.fn[h],d=function(){function t(t){this._element=t}var n=t.prototype;return n.close=function(t){var e=this._element;t&&(e=this._getRootElement(t)),this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},n.dispose=function(){e.removeData(this._element,"bs.alert"),this._element=null},n._getRootElement=function(t){var n=c.getSelectorFromElement(t),i=!1;return n&&(i=document.querySelector(n)),i||(i=e(t).closest(".alert")[0]),i},n._triggerCloseEvent=function(t){var n=e.Event("close.bs.alert");return e(t).trigger(n),n},n._removeElement=function(t){var n=this;if(e(t).removeClass("show"),e(t).hasClass("fade")){var i=c.getTransitionDurationFromElement(t);e(t).one(c.TRANSITION_END,(function(e){return n._destroyElement(t,e)})).emulateTransitionEnd(i)}else this._destroyElement(t)},n._destroyElement=function(t){e(t).detach().trigger("closed.bs.alert").remove()},t._jQueryInterface=function(n){return this.each((function(){var i=e(this),o=i.data("bs.alert");o||(o=new t(this),i.data("bs.alert",o)),"close"===n&&o[n](this)}))},t._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}}]),t}();e(document).on("click.bs.alert.data-api",'[data-dismiss="alert"]',d._handleDismiss(new d)),e.fn[h]=d._jQueryInterface,e.fn[h].Constructor=d,e.fn[h].noConflict=function(){return e.fn[h]=u,d._jQueryInterface};var f=e.fn.button,g=function(){function t(t){this._element=t}var n=t.prototype;return n.toggle=function(){var t=!0,n=!0,i=e(this._element).closest('[data-toggle="buttons"]')[0];if(i){var o=this._element.querySelector('input:not([type="hidden"])');if(o){if("radio"===o.type)if(o.checked&&this._element.classList.contains("active"))t=!1;else{var s=i.querySelector(".active");s&&e(s).removeClass("active")}t&&("checkbox"!==o.type&&"radio"!==o.type||(o.checked=!this._element.classList.contains("active")),e(o).trigger("change")),o.focus(),n=!1}}this._element.hasAttribute("disabled")||this._element.classList.contains("disabled")||(n&&this._element.setAttribute("aria-pressed",!this._element.classList.contains("active")),t&&e(this._element).toggleClass("active"))},n.dispose=function(){e.removeData(this._element,"bs.button"),this._element=null},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.button");i||(i=new t(this),e(this).data("bs.button",i)),"toggle"===n&&i[n]()}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}}]),t}();e(document).on("click.bs.button.data-api",'[data-toggle^="button"]',(function(t){var n=t.target,i=n;if(e(n).hasClass("btn")||(n=e(n).closest(".btn")[0]),!n||n.hasAttribute("disabled")||n.classList.contains("disabled"))t.preventDefault();else{var o=n.querySelector('input:not([type="hidden"])');if(o&&(o.hasAttribute("disabled")||o.classList.contains("disabled")))return void t.preventDefault();"LABEL"===i.tagName&&o&&"checkbox"===o.type&&t.preventDefault(),g._jQueryInterface.call(e(n),"toggle")}})).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',(function(t){var n=e(t.target).closest(".btn")[0];e(n).toggleClass("focus",/^focus(in)?$/.test(t.type))})),e(window).on("load.bs.button.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-toggle="buttons"] .btn')),e=0,n=t.length;e0,this._pointerEvent=Boolean(window.PointerEvent||window.MSPointerEvent),this._addEventListeners()}var n=t.prototype;return n.next=function(){this._isSliding||this._slide("next")},n.nextWhenVisible=function(){!document.hidden&&e(this._element).is(":visible")&&"hidden"!==e(this._element).css("visibility")&&this.next()},n.prev=function(){this._isSliding||this._slide("prev")},n.pause=function(t){t||(this._isPaused=!0),this._element.querySelector(".carousel-item-next, .carousel-item-prev")&&(c.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},n.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},n.to=function(t){var n=this;this._activeElement=this._element.querySelector(".active.carousel-item");var i=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)e(this._element).one("slid.bs.carousel",(function(){return n.to(t)}));else{if(i===t)return this.pause(),void this.cycle();var o=t>i?"next":"prev";this._slide(o,this._items[t])}},n.dispose=function(){e(this._element).off(p),e.removeData(this._element,"bs.carousel"),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},n._getConfig=function(t){return t=a(a({},v),t),c.typeCheckConfig(m,t,b),t},n._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&this.prev(),e<0&&this.next()}},n._addEventListeners=function(){var t=this;this._config.keyboard&&e(this._element).on("keydown.bs.carousel",(function(e){return t._keydown(e)})),"hover"===this._config.pause&&e(this._element).on("mouseenter.bs.carousel",(function(e){return t.pause(e)})).on("mouseleave.bs.carousel",(function(e){return t.cycle(e)})),this._config.touch&&this._addTouchEventListeners()},n._addTouchEventListeners=function(){var t=this;if(this._touchSupported){var n=function(e){t._pointerEvent&&y[e.originalEvent.pointerType.toUpperCase()]?t.touchStartX=e.originalEvent.clientX:t._pointerEvent||(t.touchStartX=e.originalEvent.touches[0].clientX)},i=function(e){t._pointerEvent&&y[e.originalEvent.pointerType.toUpperCase()]&&(t.touchDeltaX=e.originalEvent.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),500+t._config.interval))};e(this._element.querySelectorAll(".carousel-item img")).on("dragstart.bs.carousel",(function(t){return t.preventDefault()})),this._pointerEvent?(e(this._element).on("pointerdown.bs.carousel",(function(t){return n(t)})),e(this._element).on("pointerup.bs.carousel",(function(t){return i(t)})),this._element.classList.add("pointer-event")):(e(this._element).on("touchstart.bs.carousel",(function(t){return n(t)})),e(this._element).on("touchmove.bs.carousel",(function(e){return function(e){e.originalEvent.touches&&e.originalEvent.touches.length>1?t.touchDeltaX=0:t.touchDeltaX=e.originalEvent.touches[0].clientX-t.touchStartX}(e)})),e(this._element).on("touchend.bs.carousel",(function(t){return i(t)})))}},n._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case 37:t.preventDefault(),this.prev();break;case 39:t.preventDefault(),this.next()}},n._getItemIndex=function(t){return this._items=t&&t.parentNode?[].slice.call(t.parentNode.querySelectorAll(".carousel-item")):[],this._items.indexOf(t)},n._getItemByDirection=function(t,e){var n="next"===t,i="prev"===t,o=this._getItemIndex(e),s=this._items.length-1;if((i&&0===o||n&&o===s)&&!this._config.wrap)return e;var r=(o+("prev"===t?-1:1))%this._items.length;return-1===r?this._items[this._items.length-1]:this._items[r]},n._triggerSlideEvent=function(t,n){var i=this._getItemIndex(t),o=this._getItemIndex(this._element.querySelector(".active.carousel-item")),s=e.Event("slide.bs.carousel",{relatedTarget:t,direction:n,from:o,to:i});return e(this._element).trigger(s),s},n._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var n=[].slice.call(this._indicatorsElement.querySelectorAll(".active"));e(n).removeClass("active");var i=this._indicatorsElement.children[this._getItemIndex(t)];i&&e(i).addClass("active")}},n._slide=function(t,n){var i,o,s,r=this,a=this._element.querySelector(".active.carousel-item"),l=this._getItemIndex(a),h=n||a&&this._getItemByDirection(t,a),u=this._getItemIndex(h),d=Boolean(this._interval);if("next"===t?(i="carousel-item-left",o="carousel-item-next",s="left"):(i="carousel-item-right",o="carousel-item-prev",s="right"),h&&e(h).hasClass("active"))this._isSliding=!1;else if(!this._triggerSlideEvent(h,s).isDefaultPrevented()&&a&&h){this._isSliding=!0,d&&this.pause(),this._setActiveIndicatorElement(h);var f=e.Event("slid.bs.carousel",{relatedTarget:h,direction:s,from:l,to:u});if(e(this._element).hasClass("slide")){e(h).addClass(o),c.reflow(h),e(a).addClass(i),e(h).addClass(i);var g=parseInt(h.getAttribute("data-interval"),10);g?(this._config.defaultInterval=this._config.defaultInterval||this._config.interval,this._config.interval=g):this._config.interval=this._config.defaultInterval||this._config.interval;var m=c.getTransitionDurationFromElement(a);e(a).one(c.TRANSITION_END,(function(){e(h).removeClass(i+" "+o).addClass("active"),e(a).removeClass("active "+o+" "+i),r._isSliding=!1,setTimeout((function(){return e(r._element).trigger(f)}),0)})).emulateTransitionEnd(m)}else e(a).removeClass("active"),e(h).addClass("active"),this._isSliding=!1,e(this._element).trigger(f);d&&this.cycle()}},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.carousel"),o=a(a({},v),e(this).data());"object"==typeof n&&(o=a(a({},o),n));var s="string"==typeof n?n:o.slide;if(i||(i=new t(this,o),e(this).data("bs.carousel",i)),"number"==typeof n)i.to(n);else if("string"==typeof s){if("undefined"==typeof i[s])throw new TypeError('No method named "'+s+'"');i[s]()}else o.interval&&o.ride&&(i.pause(),i.cycle())}))},t._dataApiClickHandler=function(n){var i=c.getSelectorFromElement(this);if(i){var o=e(i)[0];if(o&&e(o).hasClass("carousel")){var s=a(a({},e(o).data()),e(this).data()),r=this.getAttribute("data-slide-to");r&&(s.interval=!1),t._jQueryInterface.call(e(o),s),r&&e(o).data("bs.carousel").to(r),n.preventDefault()}}},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}},{key:"Default",get:function(){return v}}]),t}();e(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",E._dataApiClickHandler),e(window).on("load.bs.carousel.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-ride="carousel"]')),n=0,i=t.length;n0&&(this._selector=r,this._triggerArray.push(s))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var n=t.prototype;return n.toggle=function(){e(this._element).hasClass("show")?this.hide():this.show()},n.show=function(){var n,i,o=this;if(!this._isTransitioning&&!e(this._element).hasClass("show")&&(this._parent&&0===(n=[].slice.call(this._parent.querySelectorAll(".show, .collapsing")).filter((function(t){return"string"==typeof o._config.parent?t.getAttribute("data-parent")===o._config.parent:t.classList.contains("collapse")}))).length&&(n=null),!(n&&(i=e(n).not(this._selector).data("bs.collapse"))&&i._isTransitioning))){var s=e.Event("show.bs.collapse");if(e(this._element).trigger(s),!s.isDefaultPrevented()){n&&(t._jQueryInterface.call(e(n).not(this._selector),"hide"),i||e(n).data("bs.collapse",null));var r=this._getDimension();e(this._element).removeClass("collapse").addClass("collapsing"),this._element.style[r]=0,this._triggerArray.length&&e(this._triggerArray).removeClass("collapsed").attr("aria-expanded",!0),this.setTransitioning(!0);var a="scroll"+(r[0].toUpperCase()+r.slice(1)),l=c.getTransitionDurationFromElement(this._element);e(this._element).one(c.TRANSITION_END,(function(){e(o._element).removeClass("collapsing").addClass("collapse show"),o._element.style[r]="",o.setTransitioning(!1),e(o._element).trigger("shown.bs.collapse")})).emulateTransitionEnd(l),this._element.style[r]=this._element[a]+"px"}}},n.hide=function(){var t=this;if(!this._isTransitioning&&e(this._element).hasClass("show")){var n=e.Event("hide.bs.collapse");if(e(this._element).trigger(n),!n.isDefaultPrevented()){var i=this._getDimension();this._element.style[i]=this._element.getBoundingClientRect()[i]+"px",c.reflow(this._element),e(this._element).addClass("collapsing").removeClass("collapse show");var o=this._triggerArray.length;if(o>0)for(var s=0;s0},i._getOffset=function(){var t=this,e={};return"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=a(a({},e.offsets),t._config.offset(e.offsets,t._element)||{}),e}:e.offset=this._config.offset,e},i._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:this._getOffset(),flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(t.modifiers.applyStyle={enabled:!1}),a(a({},t),this._config.popperConfig)},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.dropdown");if(i||(i=new t(this,"object"==typeof n?n:null),e(this).data("bs.dropdown",i)),"string"==typeof n){if("undefined"==typeof i[n])throw new TypeError('No method named "'+n+'"');i[n]()}}))},t._clearMenus=function(n){if(!n||3!==n.which&&("keyup"!==n.type||9===n.which))for(var i=[].slice.call(document.querySelectorAll('[data-toggle="dropdown"]')),o=0,s=i.length;o0&&r--,40===n.which&&rdocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},n._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},n._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)
      ',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:F,popperConfig:null},Y={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},$=function(){function t(t,e){if("undefined"==typeof n)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var i=t.prototype;return i.enable=function(){this._isEnabled=!0},i.disable=function(){this._isEnabled=!1},i.toggleEnabled=function(){this._isEnabled=!this._isEnabled},i.toggle=function(t){if(this._isEnabled)if(t){var n=this.constructor.DATA_KEY,i=e(t.currentTarget).data(n);i||(i=new this.constructor(t.currentTarget,this._getDelegateConfig()),e(t.currentTarget).data(n,i)),i._activeTrigger.click=!i._activeTrigger.click,i._isWithActiveTrigger()?i._enter(null,i):i._leave(null,i)}else{if(e(this.getTipElement()).hasClass("show"))return void this._leave(null,this);this._enter(null,this)}},i.dispose=function(){clearTimeout(this._timeout),e.removeData(this.element,this.constructor.DATA_KEY),e(this.element).off(this.constructor.EVENT_KEY),e(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&e(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},i.show=function(){var t=this;if("none"===e(this.element).css("display"))throw new Error("Please use show on visible elements");var i=e.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){e(this.element).trigger(i);var o=c.findShadowRoot(this.element),s=e.contains(null!==o?o:this.element.ownerDocument.documentElement,this.element);if(i.isDefaultPrevented()||!s)return;var r=this.getTipElement(),a=c.getUID(this.constructor.NAME);r.setAttribute("id",a),this.element.setAttribute("aria-describedby",a),this.setContent(),this.config.animation&&e(r).addClass("fade");var l="function"==typeof this.config.placement?this.config.placement.call(this,r,this.element):this.config.placement,h=this._getAttachment(l);this.addAttachmentClass(h);var u=this._getContainer();e(r).data(this.constructor.DATA_KEY,this),e.contains(this.element.ownerDocument.documentElement,this.tip)||e(r).appendTo(u),e(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new n(this.element,r,this._getPopperConfig(h)),e(r).addClass("show"),"ontouchstart"in document.documentElement&&e(document.body).children().on("mouseover",null,e.noop);var d=function(){t.config.animation&&t._fixTransition();var n=t._hoverState;t._hoverState=null,e(t.element).trigger(t.constructor.Event.SHOWN),"out"===n&&t._leave(null,t)};if(e(this.tip).hasClass("fade")){var f=c.getTransitionDurationFromElement(this.tip);e(this.tip).one(c.TRANSITION_END,d).emulateTransitionEnd(f)}else d()}},i.hide=function(t){var n=this,i=this.getTipElement(),o=e.Event(this.constructor.Event.HIDE),s=function(){"show"!==n._hoverState&&i.parentNode&&i.parentNode.removeChild(i),n._cleanTipClass(),n.element.removeAttribute("aria-describedby"),e(n.element).trigger(n.constructor.Event.HIDDEN),null!==n._popper&&n._popper.destroy(),t&&t()};if(e(this.element).trigger(o),!o.isDefaultPrevented()){if(e(i).removeClass("show"),"ontouchstart"in document.documentElement&&e(document.body).children().off("mouseover",null,e.noop),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,e(this.tip).hasClass("fade")){var r=c.getTransitionDurationFromElement(i);e(i).one(c.TRANSITION_END,s).emulateTransitionEnd(r)}else s();this._hoverState=""}},i.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},i.isWithContent=function(){return Boolean(this.getTitle())},i.addAttachmentClass=function(t){e(this.getTipElement()).addClass("bs-tooltip-"+t)},i.getTipElement=function(){return this.tip=this.tip||e(this.config.template)[0],this.tip},i.setContent=function(){var t=this.getTipElement();this.setElementContent(e(t.querySelectorAll(".tooltip-inner")),this.getTitle()),e(t).removeClass("fade show")},i.setElementContent=function(t,n){"object"!=typeof n||!n.nodeType&&!n.jquery?this.config.html?(this.config.sanitize&&(n=H(n,this.config.whiteList,this.config.sanitizeFn)),t.html(n)):t.text(n):this.config.html?e(n).parent().is(t)||t.empty().append(n):t.text(e(n).text())},i.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},i._getPopperConfig=function(t){var e=this;return a(a({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:".arrow"},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}}),this.config.popperConfig)},i._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=a(a({},e.offsets),t.config.offset(e.offsets,t.element)||{}),e}:e.offset=this.config.offset,e},i._getContainer=function(){return!1===this.config.container?document.body:c.isElement(this.config.container)?e(this.config.container):e(document).find(this.config.container)},i._getAttachment=function(t){return K[t.toUpperCase()]},i._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(n){if("click"===n)e(t.element).on(t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if("manual"!==n){var i="hover"===n?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,o="hover"===n?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;e(t.element).on(i,t.config.selector,(function(e){return t._enter(e)})).on(o,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t.element&&t.hide()},e(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=a(a({},this.config),{},{trigger:"manual",selector:""}):this._fixTitle()},i._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},i._enter=function(t,n){var i=this.constructor.DATA_KEY;(n=n||e(t.currentTarget).data(i))||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),e(t.currentTarget).data(i,n)),t&&(n._activeTrigger["focusin"===t.type?"focus":"hover"]=!0),e(n.getTipElement()).hasClass("show")||"show"===n._hoverState?n._hoverState="show":(clearTimeout(n._timeout),n._hoverState="show",n.config.delay&&n.config.delay.show?n._timeout=setTimeout((function(){"show"===n._hoverState&&n.show()}),n.config.delay.show):n.show())},i._leave=function(t,n){var i=this.constructor.DATA_KEY;(n=n||e(t.currentTarget).data(i))||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),e(t.currentTarget).data(i,n)),t&&(n._activeTrigger["focusout"===t.type?"focus":"hover"]=!1),n._isWithActiveTrigger()||(clearTimeout(n._timeout),n._hoverState="out",n.config.delay&&n.config.delay.hide?n._timeout=setTimeout((function(){"out"===n._hoverState&&n.hide()}),n.config.delay.hide):n.hide())},i._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},i._getConfig=function(t){var n=e(this.element).data();return Object.keys(n).forEach((function(t){-1!==V.indexOf(t)&&delete n[t]})),"number"==typeof(t=a(a(a({},this.constructor.Default),n),"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),c.typeCheckConfig(U,t,this.constructor.DefaultType),t.sanitize&&(t.template=H(t.template,t.whiteList,t.sanitizeFn)),t},i._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},i._cleanTipClass=function(){var t=e(this.getTipElement()),n=t.attr("class").match(W);null!==n&&n.length&&t.removeClass(n.join(""))},i._handlePopperPlacementChange=function(t){this.tip=t.instance.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},i._fixTransition=function(){var t=this.getTipElement(),n=this.config.animation;null===t.getAttribute("x-placement")&&(e(t).removeClass("fade"),this.config.animation=!1,this.hide(),this.show(),this.config.animation=n)},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.tooltip"),o="object"==typeof n&&n;if((i||!/dispose|hide/.test(n))&&(i||(i=new t(this,o),e(this).data("bs.tooltip",i)),"string"==typeof n)){if("undefined"==typeof i[n])throw new TypeError('No method named "'+n+'"');i[n]()}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}},{key:"Default",get:function(){return X}},{key:"NAME",get:function(){return U}},{key:"DATA_KEY",get:function(){return"bs.tooltip"}},{key:"Event",get:function(){return Y}},{key:"EVENT_KEY",get:function(){return".bs.tooltip"}},{key:"DefaultType",get:function(){return z}}]),t}();e.fn[U]=$._jQueryInterface,e.fn[U].Constructor=$,e.fn[U].noConflict=function(){return e.fn[U]=M,$._jQueryInterface};var J="popover",G=e.fn[J],Z=new RegExp("(^|\\s)bs-popover\\S+","g"),tt=a(a({},$.Default),{},{placement:"right",trigger:"click",content:"",template:''}),et=a(a({},$.DefaultType),{},{content:"(string|element|function)"}),nt={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"},it=function(t){var n,i;function s(){return t.apply(this,arguments)||this}i=t,(n=s).prototype=Object.create(i.prototype),n.prototype.constructor=n,n.__proto__=i;var r=s.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(t){e(this.getTipElement()).addClass("bs-popover-"+t)},r.getTipElement=function(){return this.tip=this.tip||e(this.config.template)[0],this.tip},r.setContent=function(){var t=e(this.getTipElement());this.setElementContent(t.find(".popover-header"),this.getTitle());var n=this._getContent();"function"==typeof n&&(n=n.call(this.element)),this.setElementContent(t.find(".popover-body"),n),t.removeClass("fade show")},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var t=e(this.getTipElement()),n=t.attr("class").match(Z);null!==n&&n.length>0&&t.removeClass(n.join(""))},s._jQueryInterface=function(t){return this.each((function(){var n=e(this).data("bs.popover"),i="object"==typeof t?t:null;if((n||!/dispose|hide/.test(t))&&(n||(n=new s(this,i),e(this).data("bs.popover",n)),"string"==typeof t)){if("undefined"==typeof n[t])throw new TypeError('No method named "'+t+'"');n[t]()}}))},o(s,null,[{key:"VERSION",get:function(){return"4.5.0"}},{key:"Default",get:function(){return tt}},{key:"NAME",get:function(){return J}},{key:"DATA_KEY",get:function(){return"bs.popover"}},{key:"Event",get:function(){return nt}},{key:"EVENT_KEY",get:function(){return".bs.popover"}},{key:"DefaultType",get:function(){return et}}]),s}($);e.fn[J]=it._jQueryInterface,e.fn[J].Constructor=it,e.fn[J].noConflict=function(){return e.fn[J]=G,it._jQueryInterface};var ot="scrollspy",st=e.fn[ot],rt={offset:10,method:"auto",target:""},at={offset:"number",method:"string",target:"(string|element)"},lt=function(){function t(t,n){var i=this;this._element=t,this._scrollElement="BODY"===t.tagName?window:t,this._config=this._getConfig(n),this._selector=this._config.target+" .nav-link,"+this._config.target+" .list-group-item,"+this._config.target+" .dropdown-item",this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,e(this._scrollElement).on("scroll.bs.scrollspy",(function(t){return i._process(t)})),this.refresh(),this._process()}var n=t.prototype;return n.refresh=function(){var t=this,n=this._scrollElement===this._scrollElement.window?"offset":"position",i="auto"===this._config.method?n:this._config.method,o="position"===i?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map((function(t){var n,s=c.getSelectorFromElement(t);if(s&&(n=document.querySelector(s)),n){var r=n.getBoundingClientRect();if(r.width||r.height)return[e(n)[i]().top+o,s]}return null})).filter((function(t){return t})).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},n.dispose=function(){e.removeData(this._element,"bs.scrollspy"),e(this._scrollElement).off(".bs.scrollspy"),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},n._getConfig=function(t){if("string"!=typeof(t=a(a({},rt),"object"==typeof t&&t?t:{})).target&&c.isElement(t.target)){var n=e(t.target).attr("id");n||(n=c.getUID(ot),e(t.target).attr("id",n)),t.target="#"+n}return c.typeCheckConfig(ot,t,at),t},n._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},n._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},n._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},n._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;){this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t li > .active":".active";i=(i=e.makeArray(e(o).find(r)))[i.length-1]}var a=e.Event("hide.bs.tab",{relatedTarget:this._element}),l=e.Event("show.bs.tab",{relatedTarget:i});if(i&&e(i).trigger(a),e(this._element).trigger(l),!l.isDefaultPrevented()&&!a.isDefaultPrevented()){s&&(n=document.querySelector(s)),this._activate(this._element,o);var h=function(){var n=e.Event("hidden.bs.tab",{relatedTarget:t._element}),o=e.Event("shown.bs.tab",{relatedTarget:i});e(i).trigger(n),e(t._element).trigger(o)};n?this._activate(n,n.parentNode,h):h()}}},n.dispose=function(){e.removeData(this._element,"bs.tab"),this._element=null},n._activate=function(t,n,i){var o=this,s=(!n||"UL"!==n.nodeName&&"OL"!==n.nodeName?e(n).children(".active"):e(n).find("> li > .active"))[0],r=i&&s&&e(s).hasClass("fade"),a=function(){return o._transitionComplete(t,s,i)};if(s&&r){var l=c.getTransitionDurationFromElement(s);e(s).removeClass("show").one(c.TRANSITION_END,a).emulateTransitionEnd(l)}else a()},n._transitionComplete=function(t,n,i){if(n){e(n).removeClass("active");var o=e(n.parentNode).find("> .dropdown-menu .active")[0];o&&e(o).removeClass("active"),"tab"===n.getAttribute("role")&&n.setAttribute("aria-selected",!1)}if(e(t).addClass("active"),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),c.reflow(t),t.classList.contains("fade")&&t.classList.add("show"),t.parentNode&&e(t.parentNode).hasClass("dropdown-menu")){var s=e(t).closest(".dropdown")[0];if(s){var r=[].slice.call(s.querySelectorAll(".dropdown-toggle"));e(r).addClass("active")}t.setAttribute("aria-expanded",!0)}i&&i()},t._jQueryInterface=function(n){return this.each((function(){var i=e(this),o=i.data("bs.tab");if(o||(o=new t(this),i.data("bs.tab",o)),"string"==typeof n){if("undefined"==typeof o[n])throw new TypeError('No method named "'+n+'"');o[n]()}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}}]),t}();e(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',(function(t){t.preventDefault(),ht._jQueryInterface.call(e(this),"show")})),e.fn.tab=ht._jQueryInterface,e.fn.tab.Constructor=ht,e.fn.tab.noConflict=function(){return e.fn.tab=ct,ht._jQueryInterface};var ut=e.fn.toast,dt={animation:"boolean",autohide:"boolean",delay:"number"},ft={animation:!0,autohide:!0,delay:500},gt=function(){function t(t,e){this._element=t,this._config=this._getConfig(e),this._timeout=null,this._setListeners()}var n=t.prototype;return n.show=function(){var t=this,n=e.Event("show.bs.toast");if(e(this._element).trigger(n),!n.isDefaultPrevented()){this._config.animation&&this._element.classList.add("fade");var i=function(){t._element.classList.remove("showing"),t._element.classList.add("show"),e(t._element).trigger("shown.bs.toast"),t._config.autohide&&(t._timeout=setTimeout((function(){t.hide()}),t._config.delay))};if(this._element.classList.remove("hide"),c.reflow(this._element),this._element.classList.add("showing"),this._config.animation){var o=c.getTransitionDurationFromElement(this._element);e(this._element).one(c.TRANSITION_END,i).emulateTransitionEnd(o)}else i()}},n.hide=function(){if(this._element.classList.contains("show")){var t=e.Event("hide.bs.toast");e(this._element).trigger(t),t.isDefaultPrevented()||this._close()}},n.dispose=function(){clearTimeout(this._timeout),this._timeout=null,this._element.classList.contains("show")&&this._element.classList.remove("show"),e(this._element).off("click.dismiss.bs.toast"),e.removeData(this._element,"bs.toast"),this._element=null,this._config=null},n._getConfig=function(t){return t=a(a(a({},ft),e(this._element).data()),"object"==typeof t&&t?t:{}),c.typeCheckConfig("toast",t,this.constructor.DefaultType),t},n._setListeners=function(){var t=this;e(this._element).on("click.dismiss.bs.toast",'[data-dismiss="toast"]',(function(){return t.hide()}))},n._close=function(){var t=this,n=function(){t._element.classList.add("hide"),e(t._element).trigger("hidden.bs.toast")};if(this._element.classList.remove("show"),this._config.animation){var i=c.getTransitionDurationFromElement(this._element);e(this._element).one(c.TRANSITION_END,n).emulateTransitionEnd(i)}else n()},t._jQueryInterface=function(n){return this.each((function(){var i=e(this),o=i.data("bs.toast");if(o||(o=new t(this,"object"==typeof n&&n),i.data("bs.toast",o)),"string"==typeof n){if("undefined"==typeof o[n])throw new TypeError('No method named "'+n+'"');o[n](this)}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}},{key:"DefaultType",get:function(){return dt}},{key:"Default",get:function(){return ft}}]),t}();e.fn.toast=gt._jQueryInterface,e.fn.toast.Constructor=gt,e.fn.toast.noConflict=function(){return e.fn.toast=ut,gt._jQueryInterface},t.Alert=d,t.Button=g,t.Carousel=E,t.Collapse=D,t.Dropdown=j,t.Modal=R,t.Popover=it,t.Scrollspy=lt,t.Tab=ht,t.Toast=gt,t.Tooltip=$,t.Util=c,Object.defineProperty(t,"__esModule",{value:!0})})); +//# sourceMappingURL=bootstrap.min.js.map + {{name}} + {{methods_bar}} +
      {{methods_tested_percent}}
      +
      {{methods_number}}
      + {{crap}} + {{paths_bar}} +
      {{paths_executed_percent}}
      +
      {{paths_number}}
      + {{branches_bar}} +
      {{branches_executed_percent}}
      +
      {{branches_number}}
      + {{lines_bar}} +
      {{lines_executed_percent}}
      +
      {{lines_number}}
      + + + + + + + Code Coverage for {{full_path}} + + + + + + + +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      + + + + + + + + + + + + + + + + +{{items}} + +
       
      Code Coverage
       
      Classes and Traits
      Functions and Methods
      Paths
      Branches
      Lines
      +
      +{{lines}} +{{structure}} + +
      + + + + + + + + {{name}} + {{classes_bar}} +
      {{classes_tested_percent}}
      +
      {{classes_number}}
      + {{methods_bar}} +
      {{methods_tested_percent}}
      +
      {{methods_number}}
      + {{crap}} + {{paths_bar}} +
      {{paths_executed_percent}}
      +
      {{paths_number}}
      + {{branches_bar}} +
      {{branches_executed_percent}}
      +
      {{branches_number}}
      + {{lines_bar}} +
      {{lines_executed_percent}}
      +
      {{lines_number}}
      + + + + +{{lines}} + +
      + {{lineNumber}}{{lineContent}} + + {{icon}}{{name}} + {{lines_bar}} +
      {{lines_executed_percent}}
      +
      {{lines_number}}
      + {{methods_bar}} +
      {{methods_tested_percent}}
      +
      {{methods_number}}
      + {{classes_bar}} +
      {{classes_tested_percent}}
      +
      {{classes_number}}
      + + +
      +
      + {{percent}}% covered ({{level}}) +
      +
      + + {{name}} + {{methods_bar}} +
      {{methods_tested_percent}}
      +
      {{methods_number}}
      + {{crap}} + {{lines_bar}} +
      {{lines_executed_percent}}
      +
      {{lines_number}}
      + + +/*! + * Bootstrap v4.5.0 (https://getbootstrap.com/) + * Copyright 2011-2020 The Bootstrap Authors + * Copyright 2011-2020 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{-webkit-appearance:none;-moz-appearance:none;appearance:none}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item{display:-ms-flexbox;display:flex}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;line-height:0;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);height:-webkit-min-content;height:-moz-min-content;height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem);height:-webkit-min-content;height:-moz-min-content;height:min-content}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;-ms-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} +/*# sourceMappingURL=bootstrap.min.css.map */body { + padding-top: 10px; } -thead>tr>td { + border-bottom-width: 1px; +} + +.table tbody>tr>td, .table thead>tr>td { + padding-top: 3px; + padding-bottom: 3px; +} + +.table-condensed tbody>tr>td { + padding-top: 0; + padding-bottom: 0; +} + +.table .progress { + margin-bottom: inherit; +} + +.table-borderless th, .table-borderless td { + border: 0 !important; +} + +.table tbody tr.covered-by-large-tests, li.covered-by-large-tests, tr.success, td.success, li.success, span.success { + background-color: #dff0d8; +} + +.table tbody tr.covered-by-medium-tests, li.covered-by-medium-tests { + background-color: #c3e3b5; +} + +.table tbody tr.covered-by-small-tests, li.covered-by-small-tests { + background-color: #99cb84; +} + +.table tbody tr.danger, .table tbody td.danger, li.danger, span.danger { + background-color: #f2dede; +} + +.table tbody tr.warning, .table tbody td.warning, li.warning, span.warning { + background-color: #fcf8e3; +} + +.table tbody td.info { + background-color: #d9edf7; +} + +td.big { + width: 117px; +} + +td.small { +} + +td.codeLine { + font-family: "Source Code Pro", "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + white-space: pre-wrap; +} + +td span.comment { + color: #888a85; +} + +td span.default { + color: #2e3436; +} + +td span.html { + color: #888a85; +} + +td span.keyword { + color: #2e3436; + font-weight: bold; +} + +pre span.string { + color: #2e3436; +} + +span.success, span.warning, span.danger { + margin-right: 2px; + padding-left: 10px; + padding-right: 10px; + text-align: center; +} + +#classCoverageDistribution, #classComplexity { + height: 200px; + width: 475px; +} + +#toplink { + position: fixed; + left: 5px; + bottom: 5px; + outline: 0; +} + +svg text { + font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif; + font-size: 11px; + color: #666; + fill: #666; +} + +.scrollbox { + height:245px; + overflow-x:hidden; + overflow-y:scroll; +} + +table + .structure-heading { + border-top: 1px solid lightgrey; + padding-top: 0.5em; +} +.octicon { + display: inline-block; + vertical-align: text-top; + fill: currentColor; +} +.nvd3 .nv-axis{pointer-events:none;opacity:1}.nvd3 .nv-axis path{fill:none;stroke:#000;stroke-opacity:.75;shape-rendering:crispEdges}.nvd3 .nv-axis path.domain{stroke-opacity:.75}.nvd3 .nv-axis.nv-x path.domain{stroke-opacity:0}.nvd3 .nv-axis line{fill:none;stroke:#e5e5e5;shape-rendering:crispEdges}.nvd3 .nv-axis .zero line,.nvd3 .nv-axis line.zero{stroke-opacity:.75}.nvd3 .nv-axis .nv-axisMaxMin text{font-weight:700}.nvd3 .x .nv-axis .nv-axisMaxMin text,.nvd3 .x2 .nv-axis .nv-axisMaxMin text,.nvd3 .x3 .nv-axis .nv-axisMaxMin text{text-anchor:middle}.nvd3 .nv-axis.nv-disabled{opacity:0}.nvd3 .nv-bars rect{fill-opacity:.75;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-bars rect.hover{fill-opacity:1}.nvd3 .nv-bars .hover rect{fill:#add8e6}.nvd3 .nv-bars text{fill:rgba(0,0,0,0)}.nvd3 .nv-bars .hover text{fill:rgba(0,0,0,1)}.nvd3 .nv-multibar .nv-groups rect,.nvd3 .nv-multibarHorizontal .nv-groups rect,.nvd3 .nv-discretebar .nv-groups rect{stroke-opacity:0;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-multibar .nv-groups rect:hover,.nvd3 .nv-multibarHorizontal .nv-groups rect:hover,.nvd3 .nv-candlestickBar .nv-ticks rect:hover,.nvd3 .nv-discretebar .nv-groups rect:hover{fill-opacity:1}.nvd3 .nv-discretebar .nv-groups text,.nvd3 .nv-multibarHorizontal .nv-groups text{font-weight:700;fill:rgba(0,0,0,1);stroke:rgba(0,0,0,0)}.nvd3 .nv-boxplot circle{fill-opacity:.5}.nvd3 .nv-boxplot circle:hover{fill-opacity:1}.nvd3 .nv-boxplot rect:hover{fill-opacity:1}.nvd3 line.nv-boxplot-median{stroke:#000}.nv-boxplot-tick:hover{stroke-width:2.5px}.nvd3.nv-bullet{font:10px sans-serif}.nvd3.nv-bullet .nv-measure{fill-opacity:.8}.nvd3.nv-bullet .nv-measure:hover{fill-opacity:1}.nvd3.nv-bullet .nv-marker{stroke:#000;stroke-width:2px}.nvd3.nv-bullet .nv-markerTriangle{stroke:#000;fill:#fff;stroke-width:1.5px}.nvd3.nv-bullet .nv-tick line{stroke:#666;stroke-width:.5px}.nvd3.nv-bullet .nv-range.nv-s0{fill:#eee}.nvd3.nv-bullet .nv-range.nv-s1{fill:#ddd}.nvd3.nv-bullet .nv-range.nv-s2{fill:#ccc}.nvd3.nv-bullet .nv-title{font-size:14px;font-weight:700}.nvd3.nv-bullet .nv-subtitle{fill:#999}.nvd3.nv-bullet .nv-range{fill:#bababa;fill-opacity:.4}.nvd3.nv-bullet .nv-range:hover{fill-opacity:.7}.nvd3.nv-candlestickBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.positive rect{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.negative rect{stroke:#d62728;fill:#d62728}.with-transitions .nv-candlestickBar .nv-ticks .nv-tick{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-candlestickBar .nv-ticks line{stroke:#333}.nvd3 .nv-legend .nv-disabled rect{}.nvd3 .nv-check-box .nv-box{fill-opacity:0;stroke-width:2}.nvd3 .nv-check-box .nv-check{fill-opacity:0;stroke-width:4}.nvd3 .nv-series.nv-disabled .nv-check-box .nv-check{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-controlsWrap .nv-legend .nv-check-box .nv-check{opacity:0}.nvd3.nv-linePlusBar .nv-bar rect{fill-opacity:.75}.nvd3.nv-linePlusBar .nv-bar rect:hover{fill-opacity:1}.nvd3 .nv-groups path.nv-line{fill:none}.nvd3 .nv-groups path.nv-area{stroke:none}.nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point{fill-opacity:0;stroke-opacity:0}.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point{fill-opacity:.5!important;stroke-opacity:.5!important}.with-transitions .nvd3 .nv-groups .nv-point{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-scatter .nv-groups .nv-point.hover,.nvd3 .nv-groups .nv-point.hover{stroke-width:7px;fill-opacity:.95!important;stroke-opacity:.95!important}.nvd3 .nv-point-paths path{stroke:#aaa;stroke-opacity:0;fill:#eee;fill-opacity:0}.nvd3 .nv-indexLine{cursor:ew-resize}svg.nvd3-svg{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-ms-user-select:none;-moz-user-select:none;user-select:none;display:block;width:100%;height:100%}.nvtooltip.with-3d-shadow,.with-3d-shadow .nvtooltip{-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nvd3 text{font:400 12px Arial}.nvd3 .title{font:700 14px Arial}.nvd3 .nv-background{fill:#fff;fill-opacity:0}.nvd3.nv-noData{font-size:18px;font-weight:700}.nv-brush .extent{fill-opacity:.125;shape-rendering:crispEdges}.nv-brush .resize path{fill:#eee;stroke:#666}.nvd3 .nv-legend .nv-series{cursor:pointer}.nvd3 .nv-legend .nv-disabled circle{fill-opacity:0}.nvd3 .nv-brush .extent{fill-opacity:0!important}.nvd3 .nv-brushBackground rect{stroke:#000;stroke-width:.4;fill:#fff;fill-opacity:.7}.nvd3.nv-ohlcBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive{stroke:#2ca02c}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative{stroke:#d62728}.nvd3 .background path{fill:none;stroke:#EEE;stroke-opacity:.4;shape-rendering:crispEdges}.nvd3 .foreground path{fill:none;stroke-opacity:.7}.nvd3 .nv-parallelCoordinates-brush .extent{fill:#fff;fill-opacity:.6;stroke:gray;shape-rendering:crispEdges}.nvd3 .nv-parallelCoordinates .hover{fill-opacity:1;stroke-width:3px}.nvd3 .missingValuesline line{fill:none;stroke:#000;stroke-width:1;stroke-opacity:1;stroke-dasharray:5,5}.nvd3.nv-pie path{stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-pie .nv-pie-title{font-size:24px;fill:rgba(19,196,249,.59)}.nvd3.nv-pie .nv-slice text{stroke:#000;stroke-width:0}.nvd3.nv-pie path{stroke:#fff;stroke-width:1px;stroke-opacity:1}.nvd3.nv-pie .hover path{fill-opacity:.7}.nvd3.nv-pie .nv-label{pointer-events:none}.nvd3.nv-pie .nv-label rect{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-groups .nv-point.hover{stroke-width:20px;stroke-opacity:.5}.nvd3 .nv-scatter .nv-point.hover{fill-opacity:1}.nv-noninteractive{pointer-events:none}.nv-distx,.nv-disty{pointer-events:none}.nvd3.nv-sparkline path{fill:none}.nvd3.nv-sparklineplus g.nv-hoverValue{pointer-events:none}.nvd3.nv-sparklineplus .nv-hoverValue line{stroke:#333;stroke-width:1.5px}.nvd3.nv-sparklineplus,.nvd3.nv-sparklineplus g{pointer-events:all}.nvd3 .nv-hoverArea{fill-opacity:0;stroke-opacity:0}.nvd3.nv-sparklineplus .nv-xValue,.nvd3.nv-sparklineplus .nv-yValue{stroke-width:0;font-size:.9em;font-weight:400}.nvd3.nv-sparklineplus .nv-yValue{stroke:#f66}.nvd3.nv-sparklineplus .nv-maxValue{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-sparklineplus .nv-minValue{stroke:#d62728;fill:#d62728}.nvd3.nv-sparklineplus .nv-currentValue{font-weight:700;font-size:1.1em}.nvd3.nv-stackedarea path.nv-area{fill-opacity:.7;stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-stackedarea path.nv-area.hover{fill-opacity:.9}.nvd3.nv-stackedarea .nv-groups .nv-point{stroke-opacity:0;fill-opacity:0}.nvtooltip{position:absolute;background-color:rgba(255,255,255,1);color:rgba(0,0,0,1);padding:1px;border:1px solid rgba(0,0,0,.2);z-index:10000;display:block;font-family:Arial;font-size:13px;text-align:left;pointer-events:none;white-space:nowrap;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.nvtooltip{background:rgba(255,255,255,.8);border:1px solid rgba(0,0,0,.5);border-radius:4px}.nvtooltip.with-transitions,.with-transitions .nvtooltip{transition:opacity 50ms linear;-moz-transition:opacity 50ms linear;-webkit-transition:opacity 50ms linear;transition-delay:200ms;-moz-transition-delay:200ms;-webkit-transition-delay:200ms}.nvtooltip.x-nvtooltip,.nvtooltip.y-nvtooltip{padding:8px}.nvtooltip h3{margin:0;padding:4px 14px;line-height:18px;font-weight:400;background-color:rgba(247,247,247,.75);color:rgba(0,0,0,1);text-align:center;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.nvtooltip p{margin:0;padding:5px 14px;text-align:center}.nvtooltip span{display:inline-block;margin:2px 0}.nvtooltip table{margin:6px;border-spacing:0}.nvtooltip table td{padding:2px 9px 2px 0;vertical-align:middle}.nvtooltip table td.key{font-weight:400}.nvtooltip table td.value{text-align:right;font-weight:700}.nvtooltip table tr.highlight td{padding:1px 9px 1px 0;border-bottom-style:solid;border-bottom-width:1px;border-top-style:solid;border-top-width:1px}.nvtooltip table td.legend-color-guide div{width:8px;height:8px;vertical-align:middle}.nvtooltip table td.legend-color-guide div{width:12px;height:12px;border:1px solid #999}.nvtooltip .footer{padding:3px;text-align:center}.nvtooltip-pending-removal{pointer-events:none;display:none}.nvd3 .nv-interactiveGuideLine{pointer-events:none}.nvd3 line.nv-guideline{stroke:#ccc} + + + + Code Coverage for {{full_path}} + + + + + + + +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      + + + + + + + + + + + + + + + + +{{items}} + +
       
      Code Coverage
       
      Lines
      Branches
      Paths
      Functions and Methods
      Classes and Traits
      +
      +
      +
      +

      Legend

      +

      + Low: 0% to {{low_upper_bound}}% + Medium: {{low_upper_bound}}% to {{high_lower_bound}}% + High: {{high_lower_bound}}% to 100% +

      +

      + Generated by php-code-coverage {{version}} using {{runtime}}{{generator}} at {{date}}. +

      +
      +
      + + +
      +

      Branches

      +

      + Below are the source code lines that represent each code branch as identified by Xdebug. Please note a branch is not + necessarily coterminous with a line, a line may contain multiple branches and therefore show up more than once. + Please also be aware that some branches may be implicit rather than explicit, e.g. an if statement + always has an else as part of its logical flow even if you didn't write one. +

      +{{branches}} + + {{name}} + {{classes_bar}} +
      {{classes_tested_percent}}
      +
      {{classes_number}}
      + {{methods_bar}} +
      {{methods_tested_percent}}
      +
      {{methods_number}}
      + {{crap}} + {{lines_bar}} +
      {{lines_executed_percent}}
      +
      {{lines_number}}
      + + + + {{icon}}{{name}} + {{lines_bar}} +
      {{lines_executed_percent}}
      +
      {{lines_number}}
      + {{branches_bar}} +
      {{branches_executed_percent}}
      +
      {{branches_number}}
      + {{paths_bar}} +
      {{paths_executed_percent}}
      +
      {{paths_number}}
      + {{methods_bar}} +
      {{methods_tested_percent}}
      +
      {{methods_number}}
      + {{classes_bar}} +
      {{classes_tested_percent}}
      +
      {{classes_number}}
      + + + + + + + Dashboard for {{full_path}} + + + + + + + +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +
      +
      +

      Coverage Distribution

      +
      + +
      +
      +
      +

      Complexity

      +
      + +
      +
      +
      +
      +
      +

      Insufficient Coverage

      +
      + + + + + + + + +{{insufficient_coverage_classes}} + +
      ClassCoverage
      +
      +
      +
      +

      Project Risks

      +
      + + + + + + + + +{{project_risks_classes}} + +
      ClassCRAP
      +
      +
      +
      +
      +
      +

      Methods

      +
      +
      +
      +
      +

      Coverage Distribution

      +
      + +
      +
      +
      +

      Complexity

      +
      + +
      +
      +
      +
      +
      +

      Insufficient Coverage

      +
      + + + + + + + + +{{insufficient_coverage_methods}} + +
      MethodCoverage
      +
      +
      +
      +

      Project Risks

      +
      + + + + + + + + +{{project_risks_methods}} + +
      MethodCRAP
      +
      +
      +
      + +
      + + + + + + + * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Html; -use const PHP_EOL; -use const PHP_SAPI; -use const PHP_VERSION; -use function array_diff; -use function array_map; -use function assert; -use function class_exists; +use const ENT_COMPAT; +use const ENT_HTML401; +use const ENT_SUBSTITUTE; +use const T_ABSTRACT; +use const T_ARRAY; +use const T_AS; +use const T_BREAK; +use const T_CALLABLE; +use const T_CASE; +use const T_CATCH; +use const T_CLASS; +use const T_CLONE; +use const T_COMMENT; +use const T_CONST; +use const T_CONTINUE; +use const T_DECLARE; +use const T_DEFAULT; +use const T_DO; +use const T_DOC_COMMENT; +use const T_ECHO; +use const T_ELSE; +use const T_ELSEIF; +use const T_EMPTY; +use const T_ENDDECLARE; +use const T_ENDFOR; +use const T_ENDFOREACH; +use const T_ENDIF; +use const T_ENDSWITCH; +use const T_ENDWHILE; +use const T_EVAL; +use const T_EXIT; +use const T_EXTENDS; +use const T_FINAL; +use const T_FINALLY; +use const T_FOR; +use const T_FOREACH; +use const T_FUNCTION; +use const T_GLOBAL; +use const T_GOTO; +use const T_HALT_COMPILER; +use const T_IF; +use const T_IMPLEMENTS; +use const T_INCLUDE; +use const T_INCLUDE_ONCE; +use const T_INLINE_HTML; +use const T_INSTANCEOF; +use const T_INSTEADOF; +use const T_INTERFACE; +use const T_ISSET; +use const T_LIST; +use const T_NAMESPACE; +use const T_NEW; +use const T_PRINT; +use const T_PRIVATE; +use const T_PROTECTED; +use const T_PUBLIC; +use const T_REQUIRE; +use const T_REQUIRE_ONCE; +use const T_RETURN; +use const T_STATIC; +use const T_SWITCH; +use const T_THROW; +use const T_TRAIT; +use const T_TRY; +use const T_UNSET; +use const T_USE; +use const T_VAR; +use const T_WHILE; +use const T_YIELD; +use const T_YIELD_FROM; +use function array_key_exists; +use function array_pop; +use function array_unique; +use function constant; use function count; -use function dirname; -use function file_put_contents; +use function defined; +use function explode; +use function file_get_contents; use function htmlspecialchars; -use function is_array; -use function is_int; use function is_string; -use function mt_srand; -use function range; -use function realpath; use function sprintf; -use function time; -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\TestResult; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Runner\AfterLastTestHook; +use function str_replace; +use function substr; +use function token_get_all; +use function trim; use PHPUnit\Runner\BaseTestRunner; -use PHPUnit\Runner\BeforeFirstTestHook; -use PHPUnit\Runner\DefaultTestResultCache; -use PHPUnit\Runner\Filter\ExcludeGroupFilterIterator; -use PHPUnit\Runner\Filter\Factory; -use PHPUnit\Runner\Filter\IncludeGroupFilterIterator; -use PHPUnit\Runner\Filter\NameFilterIterator; -use PHPUnit\Runner\Hook; -use PHPUnit\Runner\NullTestResultCache; -use PHPUnit\Runner\ResultCacheExtension; -use PHPUnit\Runner\StandardTestSuiteLoader; -use PHPUnit\Runner\TestHook; -use PHPUnit\Runner\TestListenerAdapter; -use PHPUnit\Runner\TestSuiteLoader; -use PHPUnit\Runner\TestSuiteSorter; -use PHPUnit\Runner\Version; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\FilterMapper; -use PHPUnit\TextUI\XmlConfiguration\Configuration; -use PHPUnit\TextUI\XmlConfiguration\ExtensionHandler; -use PHPUnit\TextUI\XmlConfiguration\Loader; -use PHPUnit\TextUI\XmlConfiguration\PhpHandler; -use PHPUnit\Util\Filesystem; -use PHPUnit\Util\Log\JUnit; -use PHPUnit\Util\Log\TeamCity; -use PHPUnit\Util\Printer; -use PHPUnit\Util\TestDox\CliTestDoxPrinter; -use PHPUnit\Util\TestDox\HtmlResultPrinter; -use PHPUnit\Util\TestDox\TextResultPrinter; -use PHPUnit\Util\TestDox\XmlResultPrinter; -use PHPUnit\Util\XdebugFilterScriptGenerator; -use PHPUnit\Util\Xml\SchemaDetector; -use ReflectionClass; -use ReflectionException; -use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; -use PHPUnit\SebastianBergmann\CodeCoverage\Driver\Selector; -use PHPUnit\SebastianBergmann\CodeCoverage\Exception as CodeCoverageException; -use PHPUnit\SebastianBergmann\CodeCoverage\Filter as CodeCoverageFilter; -use PHPUnit\SebastianBergmann\CodeCoverage\Report\Clover as CloverReport; -use PHPUnit\SebastianBergmann\CodeCoverage\Report\Cobertura as CoberturaReport; -use PHPUnit\SebastianBergmann\CodeCoverage\Report\Crap4j as Crap4jReport; -use PHPUnit\SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlReport; -use PHPUnit\SebastianBergmann\CodeCoverage\Report\PHP as PhpReport; -use PHPUnit\SebastianBergmann\CodeCoverage\Report\Text as TextReport; -use PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Facade as XmlReport; -use PHPUnit\SebastianBergmann\Comparator\Comparator; -use PHPUnit\SebastianBergmann\Environment\Runtime; -use PHPUnit\SebastianBergmann\Invoker\Invoker; -use PHPUnit\SebastianBergmann\Timer\Timer; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\File as FileNode; +use PHPUnit\SebastianBergmann\CodeCoverage\Percentage; +use PHPUnit\SebastianBergmann\Template\Template; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -final class TestRunner extends \PHPUnit\Runner\BaseTestRunner +final class File extends Renderer { - public const SUCCESS_EXIT = 0; - public const FAILURE_EXIT = 1; - public const EXCEPTION_EXIT = 2; - /** - * @var bool - */ - private static $versionStringPrinted = \false; - /** - * @var CodeCoverageFilter - */ - private $codeCoverageFilter; - /** - * @var TestSuiteLoader - */ - private $loader; - /** - * @var ResultPrinter - */ - private $printer; - /** - * @var bool - */ - private $messagePrinted = \false; /** - * @var Hook[] + * @psalm-var array */ - private $extensions = []; + private static $keywordTokens = []; /** - * @var Timer + * @var array */ - private $timer; - public function __construct(\PHPUnit\Runner\TestSuiteLoader $loader = null, \PHPUnit\SebastianBergmann\CodeCoverage\Filter $filter = null) - { - if ($filter === null) { - $filter = new \PHPUnit\SebastianBergmann\CodeCoverage\Filter(); - } - $this->codeCoverageFilter = $filter; - $this->loader = $loader; - $this->timer = new \PHPUnit\SebastianBergmann\Timer\Timer(); - } + private static $formattedSourceCache = []; /** - * @throws \PHPUnit\Runner\Exception - * @throws \PHPUnit\TextUI\XmlConfiguration\Exception - * @throws Exception + * @var int */ - public function run(\PHPUnit\Framework\TestSuite $suite, array $arguments = [], array $warnings = [], bool $exit = \true) : \PHPUnit\Framework\TestResult + private $htmlSpecialCharsFlags = \ENT_COMPAT | \ENT_HTML401 | \ENT_SUBSTITUTE; + public function render(FileNode $node, string $file) : void { - if (isset($arguments['configuration'])) { - $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] = $arguments['configuration']; - } - $this->handleConfiguration($arguments); - if (\is_int($arguments['columns']) && $arguments['columns'] < 16) { - $arguments['columns'] = 16; - $tooFewColumnsRequested = \true; - } - if (isset($arguments['bootstrap'])) { - $GLOBALS['__PHPUNIT_BOOTSTRAP'] = $arguments['bootstrap']; - } - if ($arguments['backupGlobals'] === \true) { - $suite->setBackupGlobals(\true); - } - if ($arguments['backupStaticAttributes'] === \true) { - $suite->setBackupStaticAttributes(\true); - } - if ($arguments['beStrictAboutChangesToGlobalState'] === \true) { - $suite->setBeStrictAboutChangesToGlobalState(\true); - } - if ($arguments['executionOrder'] === \PHPUnit\Runner\TestSuiteSorter::ORDER_RANDOMIZED) { - \mt_srand($arguments['randomOrderSeed']); - } - if ($arguments['cacheResult']) { - if (!isset($arguments['cacheResultFile'])) { - if (isset($arguments['configurationObject'])) { - \assert($arguments['configurationObject'] instanceof \PHPUnit\TextUI\XmlConfiguration\Configuration); - $cacheLocation = $arguments['configurationObject']->filename(); - } else { - $cacheLocation = $_SERVER['PHP_SELF']; - } - $arguments['cacheResultFile'] = null; - $cacheResultFile = \realpath($cacheLocation); - if ($cacheResultFile !== \false) { - $arguments['cacheResultFile'] = \dirname($cacheResultFile); - } - } - $cache = new \PHPUnit\Runner\DefaultTestResultCache($arguments['cacheResultFile']); - $this->addExtension(new \PHPUnit\Runner\ResultCacheExtension($cache)); - } - if ($arguments['executionOrder'] !== \PHPUnit\Runner\TestSuiteSorter::ORDER_DEFAULT || $arguments['executionOrderDefects'] !== \PHPUnit\Runner\TestSuiteSorter::ORDER_DEFAULT || $arguments['resolveDependencies']) { - $cache = $cache ?? new \PHPUnit\Runner\NullTestResultCache(); - $cache->load(); - $sorter = new \PHPUnit\Runner\TestSuiteSorter($cache); - $sorter->reorderTestsInSuite($suite, $arguments['executionOrder'], $arguments['resolveDependencies'], $arguments['executionOrderDefects']); - $originalExecutionOrder = $sorter->getOriginalExecutionOrder(); - unset($sorter); - } - if (\is_int($arguments['repeat']) && $arguments['repeat'] > 0) { - $_suite = new \PHPUnit\Framework\TestSuite(); - /* @noinspection PhpUnusedLocalVariableInspection */ - foreach (\range(1, $arguments['repeat']) as $step) { - $_suite->addTest($suite); - } - $suite = $_suite; - unset($_suite); - } - $result = $this->createTestResult(); - $listener = new \PHPUnit\Runner\TestListenerAdapter(); - $listenerNeeded = \false; - foreach ($this->extensions as $extension) { - if ($extension instanceof \PHPUnit\Runner\TestHook) { - $listener->add($extension); - $listenerNeeded = \true; - } - } - if ($listenerNeeded) { - $result->addListener($listener); - } - unset($listener, $listenerNeeded); - if (!$arguments['convertDeprecationsToExceptions']) { - $result->convertDeprecationsToExceptions(\false); - } - if (!$arguments['convertErrorsToExceptions']) { - $result->convertErrorsToExceptions(\false); - } - if (!$arguments['convertNoticesToExceptions']) { - $result->convertNoticesToExceptions(\false); - } - if (!$arguments['convertWarningsToExceptions']) { - $result->convertWarningsToExceptions(\false); - } - if ($arguments['stopOnError']) { - $result->stopOnError(\true); - } - if ($arguments['stopOnFailure']) { - $result->stopOnFailure(\true); - } - if ($arguments['stopOnWarning']) { - $result->stopOnWarning(\true); - } - if ($arguments['stopOnIncomplete']) { - $result->stopOnIncomplete(\true); - } - if ($arguments['stopOnRisky']) { - $result->stopOnRisky(\true); - } - if ($arguments['stopOnSkipped']) { - $result->stopOnSkipped(\true); - } - if ($arguments['stopOnDefect']) { - $result->stopOnDefect(\true); - } - if ($arguments['registerMockObjectsFromTestArgumentsRecursively']) { - $result->setRegisterMockObjectsFromTestArgumentsRecursively(\true); - } - if ($this->printer === null) { - if (isset($arguments['printer'])) { - if ($arguments['printer'] instanceof \PHPUnit\TextUI\ResultPrinter) { - $this->printer = $arguments['printer']; - } elseif (\is_string($arguments['printer']) && \class_exists($arguments['printer'], \false)) { - try { - $reflector = new \ReflectionClass($arguments['printer']); - if ($reflector->implementsInterface(\PHPUnit\TextUI\ResultPrinter::class)) { - $this->printer = $this->createPrinter($arguments['printer'], $arguments); - } - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } - } else { - $this->printer = $this->createPrinter(\PHPUnit\TextUI\DefaultResultPrinter::class, $arguments); - } - } - if (isset($originalExecutionOrder) && $this->printer instanceof \PHPUnit\Util\TestDox\CliTestDoxPrinter) { - \assert($this->printer instanceof \PHPUnit\Util\TestDox\CliTestDoxPrinter); - $this->printer->setOriginalExecutionOrder($originalExecutionOrder); - $this->printer->setShowProgressAnimation(!$arguments['noInteraction']); - } - $this->printer->write(\PHPUnit\Runner\Version::getVersionString() . "\n"); - self::$versionStringPrinted = \true; - foreach ($arguments['listeners'] as $listener) { - $result->addListener($listener); - } - $result->addListener($this->printer); - $coverageFilterFromConfigurationFile = \false; - $coverageFilterFromOption = \false; - $codeCoverageReports = 0; - if (isset($arguments['testdoxHTMLFile'])) { - $result->addListener(new \PHPUnit\Util\TestDox\HtmlResultPrinter($arguments['testdoxHTMLFile'], $arguments['testdoxGroups'], $arguments['testdoxExcludeGroups'])); - } - if (isset($arguments['testdoxTextFile'])) { - $result->addListener(new \PHPUnit\Util\TestDox\TextResultPrinter($arguments['testdoxTextFile'], $arguments['testdoxGroups'], $arguments['testdoxExcludeGroups'])); - } - if (isset($arguments['testdoxXMLFile'])) { - $result->addListener(new \PHPUnit\Util\TestDox\XmlResultPrinter($arguments['testdoxXMLFile'])); - } - if (isset($arguments['teamcityLogfile'])) { - $result->addListener(new \PHPUnit\Util\Log\TeamCity($arguments['teamcityLogfile'])); - } - if (isset($arguments['junitLogfile'])) { - $result->addListener(new \PHPUnit\Util\Log\JUnit($arguments['junitLogfile'], $arguments['reportUselessTests'])); - } - if (isset($arguments['coverageClover'])) { - $codeCoverageReports++; - } - if (isset($arguments['coverageCobertura'])) { - $codeCoverageReports++; + $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'file_branch.html' : 'file.html'); + $template = new Template($templateName, '{{', '}}'); + $this->setCommonTemplateVariables($template, $node); + $template->setVar(['items' => $this->renderItems($node), 'lines' => $this->renderSourceWithLineCoverage($node), 'legend' => '

      ExecutedNot ExecutedDead Code

      ', 'structure' => '']); + $template->renderTo($file . '.html'); + if ($this->hasBranchCoverage) { + $template->setVar(['items' => $this->renderItems($node), 'lines' => $this->renderSourceWithBranchCoverage($node), 'legend' => '

      Fully coveredPartially coveredNot covered

      ', 'structure' => $this->renderBranchStructure($node)]); + $template->renderTo($file . '_branch.html'); + $template->setVar(['items' => $this->renderItems($node), 'lines' => $this->renderSourceWithPathCoverage($node), 'legend' => '

      Fully coveredPartially coveredNot covered

      ', 'structure' => $this->renderPathStructure($node)]); + $template->renderTo($file . '_path.html'); } - if (isset($arguments['coverageCrap4J'])) { - $codeCoverageReports++; + } + private function renderItems(FileNode $node) : string + { + $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'file_item_branch.html' : 'file_item.html'); + $template = new Template($templateName, '{{', '}}'); + $methodTemplateName = $this->templatePath . ($this->hasBranchCoverage ? 'method_item_branch.html' : 'method_item.html'); + $methodItemTemplate = new Template($methodTemplateName, '{{', '}}'); + $items = $this->renderItemTemplate($template, ['name' => 'Total', 'numClasses' => $node->numberOfClassesAndTraits(), 'numTestedClasses' => $node->numberOfTestedClassesAndTraits(), 'numMethods' => $node->numberOfFunctionsAndMethods(), 'numTestedMethods' => $node->numberOfTestedFunctionsAndMethods(), 'linesExecutedPercent' => $node->percentageOfExecutedLines()->asFloat(), 'linesExecutedPercentAsString' => $node->percentageOfExecutedLines()->asString(), 'numExecutedLines' => $node->numberOfExecutedLines(), 'numExecutableLines' => $node->numberOfExecutableLines(), 'branchesExecutedPercent' => $node->percentageOfExecutedBranches()->asFloat(), 'branchesExecutedPercentAsString' => $node->percentageOfExecutedBranches()->asString(), 'numExecutedBranches' => $node->numberOfExecutedBranches(), 'numExecutableBranches' => $node->numberOfExecutableBranches(), 'pathsExecutedPercent' => $node->percentageOfExecutedPaths()->asFloat(), 'pathsExecutedPercentAsString' => $node->percentageOfExecutedPaths()->asString(), 'numExecutedPaths' => $node->numberOfExecutedPaths(), 'numExecutablePaths' => $node->numberOfExecutablePaths(), 'testedMethodsPercent' => $node->percentageOfTestedFunctionsAndMethods()->asFloat(), 'testedMethodsPercentAsString' => $node->percentageOfTestedFunctionsAndMethods()->asString(), 'testedClassesPercent' => $node->percentageOfTestedClassesAndTraits()->asFloat(), 'testedClassesPercentAsString' => $node->percentageOfTestedClassesAndTraits()->asString(), 'crap' => 'CRAP']); + $items .= $this->renderFunctionItems($node->functions(), $methodItemTemplate); + $items .= $this->renderTraitOrClassItems($node->traits(), $template, $methodItemTemplate); + $items .= $this->renderTraitOrClassItems($node->classes(), $template, $methodItemTemplate); + return $items; + } + private function renderTraitOrClassItems(array $items, Template $template, Template $methodItemTemplate) : string + { + $buffer = ''; + if (empty($items)) { + return $buffer; } - if (isset($arguments['coverageHtml'])) { - $codeCoverageReports++; + foreach ($items as $name => $item) { + $numMethods = 0; + $numTestedMethods = 0; + foreach ($item['methods'] as $method) { + if ($method['executableLines'] > 0) { + $numMethods++; + if ($method['executedLines'] === $method['executableLines']) { + $numTestedMethods++; + } + } + } + if ($item['executableLines'] > 0) { + $numClasses = 1; + $numTestedClasses = $numTestedMethods === $numMethods ? 1 : 0; + $linesExecutedPercentAsString = Percentage::fromFractionAndTotal($item['executedLines'], $item['executableLines'])->asString(); + $branchesExecutedPercentAsString = Percentage::fromFractionAndTotal($item['executedBranches'], $item['executableBranches'])->asString(); + $pathsExecutedPercentAsString = Percentage::fromFractionAndTotal($item['executedPaths'], $item['executablePaths'])->asString(); + } else { + $numClasses = 0; + $numTestedClasses = 0; + $linesExecutedPercentAsString = 'n/a'; + $branchesExecutedPercentAsString = 'n/a'; + $pathsExecutedPercentAsString = 'n/a'; + } + $testedMethodsPercentage = Percentage::fromFractionAndTotal($numTestedMethods, $numMethods); + $testedClassesPercentage = Percentage::fromFractionAndTotal($numTestedMethods === $numMethods ? 1 : 0, 1); + $buffer .= $this->renderItemTemplate($template, ['name' => $this->abbreviateClassName($name), 'numClasses' => $numClasses, 'numTestedClasses' => $numTestedClasses, 'numMethods' => $numMethods, 'numTestedMethods' => $numTestedMethods, 'linesExecutedPercent' => Percentage::fromFractionAndTotal($item['executedLines'], $item['executableLines'])->asFloat(), 'linesExecutedPercentAsString' => $linesExecutedPercentAsString, 'numExecutedLines' => $item['executedLines'], 'numExecutableLines' => $item['executableLines'], 'branchesExecutedPercent' => Percentage::fromFractionAndTotal($item['executedBranches'], $item['executableBranches'])->asFloat(), 'branchesExecutedPercentAsString' => $branchesExecutedPercentAsString, 'numExecutedBranches' => $item['executedBranches'], 'numExecutableBranches' => $item['executableBranches'], 'pathsExecutedPercent' => Percentage::fromFractionAndTotal($item['executedPaths'], $item['executablePaths'])->asFloat(), 'pathsExecutedPercentAsString' => $pathsExecutedPercentAsString, 'numExecutedPaths' => $item['executedPaths'], 'numExecutablePaths' => $item['executablePaths'], 'testedMethodsPercent' => $testedMethodsPercentage->asFloat(), 'testedMethodsPercentAsString' => $testedMethodsPercentage->asString(), 'testedClassesPercent' => $testedClassesPercentage->asFloat(), 'testedClassesPercentAsString' => $testedClassesPercentage->asString(), 'crap' => $item['crap']]); + foreach ($item['methods'] as $method) { + $buffer .= $this->renderFunctionOrMethodItem($methodItemTemplate, $method, ' '); + } } - if (isset($arguments['coveragePHP'])) { - $codeCoverageReports++; + return $buffer; + } + private function renderFunctionItems(array $functions, Template $template) : string + { + if (empty($functions)) { + return ''; } - if (isset($arguments['coverageText'])) { - $codeCoverageReports++; + $buffer = ''; + foreach ($functions as $function) { + $buffer .= $this->renderFunctionOrMethodItem($template, $function); } - if (isset($arguments['coverageXml'])) { - $codeCoverageReports++; + return $buffer; + } + private function renderFunctionOrMethodItem(Template $template, array $item, string $indent = '') : string + { + $numMethods = 0; + $numTestedMethods = 0; + if ($item['executableLines'] > 0) { + $numMethods = 1; + if ($item['executedLines'] === $item['executableLines']) { + $numTestedMethods = 1; + } } - if ($codeCoverageReports > 0 || isset($arguments['xdebugFilterFile'])) { - if (isset($arguments['coverageFilter'])) { - if (!\is_array($arguments['coverageFilter'])) { - $coverageFilterDirectories = [$arguments['coverageFilter']]; + $executedLinesPercentage = Percentage::fromFractionAndTotal($item['executedLines'], $item['executableLines']); + $executedBranchesPercentage = Percentage::fromFractionAndTotal($item['executedBranches'], $item['executableBranches']); + $executedPathsPercentage = Percentage::fromFractionAndTotal($item['executedPaths'], $item['executablePaths']); + $testedMethodsPercentage = Percentage::fromFractionAndTotal($numTestedMethods, 1); + return $this->renderItemTemplate($template, ['name' => sprintf('%s%s', $indent, $item['startLine'], htmlspecialchars($item['signature'], $this->htmlSpecialCharsFlags), $item['functionName'] ?? $item['methodName']), 'numMethods' => $numMethods, 'numTestedMethods' => $numTestedMethods, 'linesExecutedPercent' => $executedLinesPercentage->asFloat(), 'linesExecutedPercentAsString' => $executedLinesPercentage->asString(), 'numExecutedLines' => $item['executedLines'], 'numExecutableLines' => $item['executableLines'], 'branchesExecutedPercent' => $executedBranchesPercentage->asFloat(), 'branchesExecutedPercentAsString' => $executedBranchesPercentage->asString(), 'numExecutedBranches' => $item['executedBranches'], 'numExecutableBranches' => $item['executableBranches'], 'pathsExecutedPercent' => $executedPathsPercentage->asFloat(), 'pathsExecutedPercentAsString' => $executedPathsPercentage->asString(), 'numExecutedPaths' => $item['executedPaths'], 'numExecutablePaths' => $item['executablePaths'], 'testedMethodsPercent' => $testedMethodsPercentage->asFloat(), 'testedMethodsPercentAsString' => $testedMethodsPercentage->asString(), 'crap' => $item['crap']]); + } + private function renderSourceWithLineCoverage(FileNode $node) : string + { + $linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}'); + $singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}'); + $coverageData = $node->lineCoverageData(); + $testData = $node->testData(); + $codeLines = $this->loadFile($node->pathAsString()); + $lines = ''; + $i = 1; + foreach ($codeLines as $line) { + $trClass = ''; + $popoverContent = ''; + $popoverTitle = ''; + if (array_key_exists($i, $coverageData)) { + $numTests = $coverageData[$i] ? count($coverageData[$i]) : 0; + if ($coverageData[$i] === null) { + $trClass = 'warning'; + } elseif ($numTests === 0) { + $trClass = 'danger'; } else { - $coverageFilterDirectories = $arguments['coverageFilter']; - } - foreach ($coverageFilterDirectories as $coverageFilterDirectory) { - $this->codeCoverageFilter->includeDirectory($coverageFilterDirectory); + if ($numTests > 1) { + $popoverTitle = $numTests . ' tests cover line ' . $i; + } else { + $popoverTitle = '1 test covers line ' . $i; + } + $lineCss = 'covered-by-large-tests'; + $popoverContent = '
        '; + foreach ($coverageData[$i] as $test) { + if ($lineCss === 'covered-by-large-tests' && $testData[$test]['size'] === 'medium') { + $lineCss = 'covered-by-medium-tests'; + } elseif ($testData[$test]['size'] === 'small') { + $lineCss = 'covered-by-small-tests'; + } + $popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]); + } + $popoverContent .= '
      '; + $trClass = $lineCss . ' popin'; } - $coverageFilterFromOption = \true; } - if (isset($arguments['configurationObject'])) { - \assert($arguments['configurationObject'] instanceof \PHPUnit\TextUI\XmlConfiguration\Configuration); - $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage(); - if ($codeCoverageConfiguration->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) { - $coverageFilterFromConfigurationFile = \true; - (new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\FilterMapper())->map($this->codeCoverageFilter, $codeCoverageConfiguration); - } + $popover = ''; + if (!empty($popoverTitle)) { + $popover = sprintf(' data-title="%s" data-content="%s" data-placement="top" data-html="true"', $popoverTitle, htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags)); } + $lines .= $this->renderLine($singleLineTemplate, $i, $line, $trClass, $popover); + $i++; } - if ($codeCoverageReports > 0) { - try { - if (isset($codeCoverageConfiguration) && ($codeCoverageConfiguration->pathCoverage() || isset($arguments['pathCoverage']) && $arguments['pathCoverage'] === \true)) { - $codeCoverageDriver = (new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Selector())->forLineAndPathCoverage($this->codeCoverageFilter); - } else { - $codeCoverageDriver = (new \PHPUnit\SebastianBergmann\CodeCoverage\Driver\Selector())->forLineCoverage($this->codeCoverageFilter); - } - $codeCoverage = new \PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage($codeCoverageDriver, $this->codeCoverageFilter); - if (isset($codeCoverageConfiguration) && $codeCoverageConfiguration->hasCacheDirectory()) { - $codeCoverage->cacheStaticAnalysis($codeCoverageConfiguration->cacheDirectory()->path()); - } - if (isset($arguments['coverageCacheDirectory'])) { - $codeCoverage->cacheStaticAnalysis($arguments['coverageCacheDirectory']); + $linesTemplate->setVar(['lines' => $lines]); + return $linesTemplate->render(); + } + private function renderSourceWithBranchCoverage(FileNode $node) : string + { + $linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}'); + $singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}'); + $functionCoverageData = $node->functionCoverageData(); + $testData = $node->testData(); + $codeLines = $this->loadFile($node->pathAsString()); + $lineData = []; + /** @var int $line */ + foreach (\array_keys($codeLines) as $line) { + $lineData[$line + 1] = ['includedInBranches' => 0, 'includedInHitBranches' => 0, 'tests' => []]; + } + foreach ($functionCoverageData as $method) { + foreach ($method['branches'] as $branch) { + foreach (\range($branch['line_start'], $branch['line_end']) as $line) { + if (!isset($lineData[$line])) { + // blank line at end of file is sometimes included here + continue; + } + $lineData[$line]['includedInBranches']++; + if ($branch['hit']) { + $lineData[$line]['includedInHitBranches']++; + $lineData[$line]['tests'] = array_unique(\array_merge($lineData[$line]['tests'], $branch['hit'])); + } } - $codeCoverage->excludeSubclassesOfThisClassFromUnintentionallyCoveredCodeCheck(\PHPUnit\SebastianBergmann\Comparator\Comparator::class); - if ($arguments['strictCoverage']) { - $codeCoverage->enableCheckForUnintentionallyCoveredCode(); + } + } + $lines = ''; + $i = 1; + /** @var string $line */ + foreach ($codeLines as $line) { + $trClass = ''; + $popover = ''; + if ($lineData[$i]['includedInBranches'] > 0) { + $lineCss = 'success'; + if ($lineData[$i]['includedInHitBranches'] === 0) { + $lineCss = 'danger'; + } elseif ($lineData[$i]['includedInHitBranches'] !== $lineData[$i]['includedInBranches']) { + $lineCss = 'warning'; } - if (isset($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'])) { - if ($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage']) { - $codeCoverage->ignoreDeprecatedCode(); - } else { - $codeCoverage->doNotIgnoreDeprecatedCode(); - } + $popoverContent = '
        '; + if (count($lineData[$i]['tests']) === 1) { + $popoverTitle = '1 test covers line ' . $i; + } else { + $popoverTitle = count($lineData[$i]['tests']) . ' tests cover line ' . $i; } - if (isset($arguments['disableCodeCoverageIgnore'])) { - if ($arguments['disableCodeCoverageIgnore']) { - $codeCoverage->disableAnnotationsForIgnoringCode(); - } else { - $codeCoverage->enableAnnotationsForIgnoringCode(); - } + $popoverTitle .= '. These are covering ' . $lineData[$i]['includedInHitBranches'] . ' out of the ' . $lineData[$i]['includedInBranches'] . ' code branches.'; + foreach ($lineData[$i]['tests'] as $test) { + $popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]); } - if (isset($arguments['configurationObject'])) { - $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage(); - if ($codeCoverageConfiguration->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) { - if ($codeCoverageConfiguration->includeUncoveredFiles()) { - $codeCoverage->includeUncoveredFiles(); - } else { - $codeCoverage->excludeUncoveredFiles(); + $popoverContent .= '
      '; + $trClass = $lineCss . ' popin'; + $popover = sprintf(' data-title="%s" data-content="%s" data-placement="top" data-html="true"', $popoverTitle, htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags)); + } + $lines .= $this->renderLine($singleLineTemplate, $i, $line, $trClass, $popover); + $i++; + } + $linesTemplate->setVar(['lines' => $lines]); + return $linesTemplate->render(); + } + private function renderSourceWithPathCoverage(FileNode $node) : string + { + $linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}'); + $singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}'); + $functionCoverageData = $node->functionCoverageData(); + $testData = $node->testData(); + $codeLines = $this->loadFile($node->pathAsString()); + $lineData = []; + /** @var int $line */ + foreach (\array_keys($codeLines) as $line) { + $lineData[$line + 1] = ['includedInPaths' => [], 'includedInHitPaths' => [], 'tests' => []]; + } + foreach ($functionCoverageData as $method) { + foreach ($method['paths'] as $pathId => $path) { + foreach ($path['path'] as $branchTaken) { + foreach (\range($method['branches'][$branchTaken]['line_start'], $method['branches'][$branchTaken]['line_end']) as $line) { + if (!isset($lineData[$line])) { + continue; } - if ($codeCoverageConfiguration->processUncoveredFiles()) { - $codeCoverage->processUncoveredFiles(); - } else { - $codeCoverage->doNotProcessUncoveredFiles(); + $lineData[$line]['includedInPaths'][] = $pathId; + if ($path['hit']) { + $lineData[$line]['includedInHitPaths'][] = $pathId; + $lineData[$line]['tests'] = array_unique(\array_merge($lineData[$line]['tests'], $path['hit'])); } } } - if ($this->codeCoverageFilter->isEmpty()) { - if (!$coverageFilterFromConfigurationFile && !$coverageFilterFromOption) { - $warnings[] = 'No filter is configured, code coverage will not be processed'; - } else { - $warnings[] = 'Incorrect filter configuration, code coverage will not be processed'; - } - unset($codeCoverage); - } - } catch (\PHPUnit\SebastianBergmann\CodeCoverage\Exception $e) { - $warnings[] = $e->getMessage(); } } - if ($arguments['verbose']) { - if (\PHP_SAPI === 'phpdbg') { - $this->writeMessage('Runtime', 'PHPDBG ' . \PHP_VERSION); - } else { - $runtime = 'PHP ' . \PHP_VERSION; - if (isset($codeCoverageDriver)) { - $runtime .= ' with ' . $codeCoverageDriver->nameAndVersion(); + $lines = ''; + $i = 1; + /** @var string $line */ + foreach ($codeLines as $line) { + $trClass = ''; + $popover = ''; + $includedInPathsCount = count(array_unique($lineData[$i]['includedInPaths'])); + $includedInHitPathsCount = count(array_unique($lineData[$i]['includedInHitPaths'])); + if ($includedInPathsCount > 0) { + $lineCss = 'success'; + if ($includedInHitPathsCount === 0) { + $lineCss = 'danger'; + } elseif ($includedInHitPathsCount !== $includedInPathsCount) { + $lineCss = 'warning'; } - $this->writeMessage('Runtime', $runtime); + $popoverContent = '
        '; + if (count($lineData[$i]['tests']) === 1) { + $popoverTitle = '1 test covers line ' . $i; + } else { + $popoverTitle = count($lineData[$i]['tests']) . ' tests cover line ' . $i; + } + $popoverTitle .= '. These are covering ' . $includedInHitPathsCount . ' out of the ' . $includedInPathsCount . ' code paths.'; + foreach ($lineData[$i]['tests'] as $test) { + $popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]); + } + $popoverContent .= '
      '; + $trClass = $lineCss . ' popin'; + $popover = sprintf(' data-title="%s" data-content="%s" data-placement="top" data-html="true"', $popoverTitle, htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags)); } - if (isset($arguments['configurationObject'])) { - \assert($arguments['configurationObject'] instanceof \PHPUnit\TextUI\XmlConfiguration\Configuration); - $this->writeMessage('Configuration', $arguments['configurationObject']->filename()); + $lines .= $this->renderLine($singleLineTemplate, $i, $line, $trClass, $popover); + $i++; + } + $linesTemplate->setVar(['lines' => $lines]); + return $linesTemplate->render(); + } + private function renderBranchStructure(FileNode $node) : string + { + $branchesTemplate = new Template($this->templatePath . 'branches.html.dist', '{{', '}}'); + $coverageData = $node->functionCoverageData(); + $testData = $node->testData(); + $codeLines = $this->loadFile($node->pathAsString()); + $branches = ''; + \ksort($coverageData); + foreach ($coverageData as $methodName => $methodData) { + if (!$methodData['branches']) { + continue; } - foreach ($arguments['loadedExtensions'] as $extension) { - $this->writeMessage('Extension', $extension); + $branchStructure = ''; + foreach ($methodData['branches'] as $branch) { + $branchStructure .= $this->renderBranchLines($branch, $codeLines, $testData); } - foreach ($arguments['notLoadedExtensions'] as $extension) { - $this->writeMessage('Extension', $extension); + if ($branchStructure !== '') { + // don't show empty branches + $branches .= '
      ' . $this->abbreviateMethodName($methodName) . '
      ' . "\n"; + $branches .= $branchStructure; } } - if ($arguments['executionOrder'] === \PHPUnit\Runner\TestSuiteSorter::ORDER_RANDOMIZED) { - $this->writeMessage('Random Seed', (string) $arguments['randomOrderSeed']); - } - if (isset($tooFewColumnsRequested)) { - $warnings[] = 'Less than 16 columns requested, number of columns set to 16'; - } - if ((new \PHPUnit\SebastianBergmann\Environment\Runtime())->discardsComments()) { - $warnings[] = 'opcache.save_comments=0 set; annotations will not work'; - } - if (isset($arguments['conflictBetweenPrinterClassAndTestdox'])) { - $warnings[] = 'Directives printerClass and testdox are mutually exclusive'; - } - foreach ($warnings as $warning) { - $this->writeMessage('Warning', $warning); - } - if (isset($arguments['configurationObject'])) { - \assert($arguments['configurationObject'] instanceof \PHPUnit\TextUI\XmlConfiguration\Configuration); - if ($arguments['configurationObject']->hasValidationErrors()) { - if ((new \PHPUnit\Util\Xml\SchemaDetector())->detect($arguments['configurationObject']->filename())->detected()) { - $this->writeMessage('Warning', 'Your XML configuration validates against a deprecated schema.'); - $this->writeMessage('Suggestion', 'Migrate your XML configuration using "--migrate-configuration"!'); + $branchesTemplate->setVar(['branches' => $branches]); + return $branchesTemplate->render(); + } + private function renderBranchLines(array $branch, array $codeLines, array $testData) : string + { + $linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}'); + $singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}'); + $lines = ''; + $branchLines = \range($branch['line_start'], $branch['line_end']); + \sort($branchLines); + // sometimes end_line < start_line + /** @var int $line */ + foreach ($branchLines as $line) { + if (!isset($codeLines[$line])) { + // blank line at end of file is sometimes included here + continue; + } + $popoverContent = ''; + $popoverTitle = ''; + $numTests = count($branch['hit']); + if ($numTests === 0) { + $trClass = 'danger'; + } else { + $lineCss = 'covered-by-large-tests'; + $popoverContent = '
        '; + if ($numTests > 1) { + $popoverTitle = $numTests . ' tests cover this branch'; } else { - $this->write("\n Warning - The configuration file did not pass validation!\n The following problems have been detected:\n"); - $this->write($arguments['configurationObject']->validationErrors()); - $this->write("\n Test results may not be as expected.\n\n"); + $popoverTitle = '1 test covers this branch'; + } + foreach ($branch['hit'] as $test) { + if ($lineCss === 'covered-by-large-tests' && $testData[$test]['size'] === 'medium') { + $lineCss = 'covered-by-medium-tests'; + } elseif ($testData[$test]['size'] === 'small') { + $lineCss = 'covered-by-small-tests'; + } + $popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]); } + $trClass = $lineCss . ' popin'; } - } - if (isset($arguments['xdebugFilterFile'], $codeCoverageConfiguration)) { - $this->write(\PHP_EOL . 'Please note that --dump-xdebug-filter and --prepend are deprecated and will be removed in PHPUnit 10.' . \PHP_EOL); - $script = (new \PHPUnit\Util\XdebugFilterScriptGenerator())->generate($codeCoverageConfiguration); - if ($arguments['xdebugFilterFile'] !== 'php://stdout' && $arguments['xdebugFilterFile'] !== 'php://stderr' && !\PHPUnit\Util\Filesystem::createDirectory(\dirname($arguments['xdebugFilterFile']))) { - $this->write(\sprintf('Cannot write Xdebug filter script to %s ' . \PHP_EOL, $arguments['xdebugFilterFile'])); - exit(self::EXCEPTION_EXIT); + $popover = ''; + if (!empty($popoverTitle)) { + $popover = sprintf(' data-title="%s" data-content="%s" data-placement="top" data-html="true"', $popoverTitle, htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags)); } - \file_put_contents($arguments['xdebugFilterFile'], $script); - $this->write(\sprintf('Wrote Xdebug filter script to %s ' . \PHP_EOL . \PHP_EOL, $arguments['xdebugFilterFile'])); - exit(self::SUCCESS_EXIT); - } - $this->printer->write("\n"); - if (isset($codeCoverage)) { - $result->setCodeCoverage($codeCoverage); - } - $result->beStrictAboutTestsThatDoNotTestAnything($arguments['reportUselessTests']); - $result->beStrictAboutOutputDuringTests($arguments['disallowTestOutput']); - $result->beStrictAboutTodoAnnotatedTests($arguments['disallowTodoAnnotatedTests']); - $result->beStrictAboutResourceUsageDuringSmallTests($arguments['beStrictAboutResourceUsageDuringSmallTests']); - if ($arguments['enforceTimeLimit'] === \true && !(new \PHPUnit\SebastianBergmann\Invoker\Invoker())->canInvokeWithTimeout()) { - $this->writeMessage('Error', 'PHP extension pcntl is required for enforcing time limits'); + $lines .= $this->renderLine($singleLineTemplate, $line, $codeLines[$line - 1], $trClass, $popover); } - $result->enforceTimeLimit($arguments['enforceTimeLimit']); - $result->setDefaultTimeLimit($arguments['defaultTimeLimit']); - $result->setTimeoutForSmallTests($arguments['timeoutForSmallTests']); - $result->setTimeoutForMediumTests($arguments['timeoutForMediumTests']); - $result->setTimeoutForLargeTests($arguments['timeoutForLargeTests']); - if (isset($arguments['forceCoversAnnotation']) && $arguments['forceCoversAnnotation'] === \true) { - $result->forceCoversAnnotation(); + if ($lines === '') { + return ''; } - $this->processSuiteFilters($suite, $arguments); - $suite->setRunTestInSeparateProcess($arguments['processIsolation']); - foreach ($this->extensions as $extension) { - if ($extension instanceof \PHPUnit\Runner\BeforeFirstTestHook) { - $extension->executeBeforeFirstTest(); + $linesTemplate->setVar(['lines' => $lines]); + return $linesTemplate->render(); + } + private function renderPathStructure(FileNode $node) : string + { + $pathsTemplate = new Template($this->templatePath . 'paths.html.dist', '{{', '}}'); + $coverageData = $node->functionCoverageData(); + $testData = $node->testData(); + $codeLines = $this->loadFile($node->pathAsString()); + $paths = ''; + \ksort($coverageData); + foreach ($coverageData as $methodName => $methodData) { + if (!$methodData['paths']) { + continue; } - } - $testSuiteWarningsPrinted = \false; - foreach ($suite->warnings() as $warning) { - $this->writeMessage('Warning', $warning); - $testSuiteWarningsPrinted = \true; - } - if ($testSuiteWarningsPrinted) { - $this->write(\PHP_EOL); - } - $suite->run($result); - foreach ($this->extensions as $extension) { - if ($extension instanceof \PHPUnit\Runner\AfterLastTestHook) { - $extension->executeAfterLastTest(); + $pathStructure = ''; + if (count($methodData['paths']) > 100) { + $pathStructure .= '

        ' . count($methodData['paths']) . ' is too many paths to sensibly render, consider refactoring your code to bring this number down.

        '; + continue; } - } - $result->flushListeners(); - $this->printer->printResult($result); - if (isset($codeCoverage)) { - if (isset($arguments['coverageClover'])) { - $this->codeCoverageGenerationStart('Clover XML'); - try { - $writer = new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Clover(); - $writer->process($codeCoverage, $arguments['coverageClover']); - $this->codeCoverageGenerationSucceeded(); - unset($writer); - } catch (\PHPUnit\SebastianBergmann\CodeCoverage\Exception $e) { - $this->codeCoverageGenerationFailed($e); - } + foreach ($methodData['paths'] as $path) { + $pathStructure .= $this->renderPathLines($path, $methodData['branches'], $codeLines, $testData); } - if (isset($arguments['coverageCobertura'])) { - $this->codeCoverageGenerationStart('Cobertura XML'); - try { - $writer = new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Cobertura(); - $writer->process($codeCoverage, $arguments['coverageCobertura']); - $this->codeCoverageGenerationSucceeded(); - unset($writer); - } catch (\PHPUnit\SebastianBergmann\CodeCoverage\Exception $e) { - $this->codeCoverageGenerationFailed($e); - } + if ($pathStructure !== '') { + $paths .= '
        ' . $this->abbreviateMethodName($methodName) . '
        ' . "\n"; + $paths .= $pathStructure; } - if (isset($arguments['coverageCrap4J'])) { - $this->codeCoverageGenerationStart('Crap4J XML'); - try { - $writer = new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Crap4j($arguments['crap4jThreshold']); - $writer->process($codeCoverage, $arguments['coverageCrap4J']); - $this->codeCoverageGenerationSucceeded(); - unset($writer); - } catch (\PHPUnit\SebastianBergmann\CodeCoverage\Exception $e) { - $this->codeCoverageGenerationFailed($e); + } + $pathsTemplate->setVar(['paths' => $paths]); + return $pathsTemplate->render(); + } + private function renderPathLines(array $path, array $branches, array $codeLines, array $testData) : string + { + $linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}'); + $singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}'); + $lines = ''; + foreach ($path['path'] as $branchId) { + $branchLines = \range($branches[$branchId]['line_start'], $branches[$branchId]['line_end']); + \sort($branchLines); + // sometimes end_line < start_line + /** @var int $line */ + foreach ($branchLines as $line) { + if (!isset($codeLines[$line])) { + // blank line at end of file is sometimes included here + continue; } - } - if (isset($arguments['coverageHtml'])) { - $this->codeCoverageGenerationStart('HTML'); - try { - $writer = new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Html\Facade($arguments['reportLowUpperBound'], $arguments['reportHighLowerBound'], \sprintf(' and PHPUnit %s', \PHPUnit\Runner\Version::id())); - $writer->process($codeCoverage, $arguments['coverageHtml']); - $this->codeCoverageGenerationSucceeded(); - unset($writer); - } catch (\PHPUnit\SebastianBergmann\CodeCoverage\Exception $e) { - $this->codeCoverageGenerationFailed($e); + $popoverContent = ''; + $popoverTitle = ''; + $numTests = count($path['hit']); + if ($numTests === 0) { + $trClass = 'danger'; + } else { + $lineCss = 'covered-by-large-tests'; + $popoverContent = '
          '; + if ($numTests > 1) { + $popoverTitle = $numTests . ' tests cover this path'; + } else { + $popoverTitle = '1 test covers this path'; + } + foreach ($path['hit'] as $test) { + if ($lineCss === 'covered-by-large-tests' && $testData[$test]['size'] === 'medium') { + $lineCss = 'covered-by-medium-tests'; + } elseif ($testData[$test]['size'] === 'small') { + $lineCss = 'covered-by-small-tests'; + } + $popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]); + } + $trClass = $lineCss . ' popin'; } - } - if (isset($arguments['coveragePHP'])) { - $this->codeCoverageGenerationStart('PHP'); - try { - $writer = new \PHPUnit\SebastianBergmann\CodeCoverage\Report\PHP(); - $writer->process($codeCoverage, $arguments['coveragePHP']); - $this->codeCoverageGenerationSucceeded(); - unset($writer); - } catch (\PHPUnit\SebastianBergmann\CodeCoverage\Exception $e) { - $this->codeCoverageGenerationFailed($e); + $popover = ''; + if (!empty($popoverTitle)) { + $popover = sprintf(' data-title="%s" data-content="%s" data-placement="top" data-html="true"', $popoverTitle, htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags)); } + $lines .= $this->renderLine($singleLineTemplate, $line, $codeLines[$line - 1], $trClass, $popover); } - if (isset($arguments['coverageText'])) { - if ($arguments['coverageText'] === 'php://stdout') { - $outputStream = $this->printer; - $colors = $arguments['colors'] && $arguments['colors'] !== \PHPUnit\TextUI\DefaultResultPrinter::COLOR_NEVER; + } + if ($lines === '') { + return ''; + } + $linesTemplate->setVar(['lines' => $lines]); + return $linesTemplate->render(); + } + private function renderLine(Template $template, int $lineNumber, string $lineContent, string $class, string $popover) : string + { + $template->setVar(['lineNumber' => $lineNumber, 'lineContent' => $lineContent, 'class' => $class, 'popover' => $popover]); + return $template->render(); + } + private function loadFile(string $file) : array + { + if (isset(self::$formattedSourceCache[$file])) { + return self::$formattedSourceCache[$file]; + } + $buffer = file_get_contents($file); + $tokens = token_get_all($buffer); + $result = ['']; + $i = 0; + $stringFlag = \false; + $fileEndsWithNewLine = substr($buffer, -1) === "\n"; + unset($buffer); + foreach ($tokens as $j => $token) { + if (is_string($token)) { + if ($token === '"' && $tokens[$j - 1] !== '\\') { + $result[$i] .= sprintf('%s', htmlspecialchars($token, $this->htmlSpecialCharsFlags)); + $stringFlag = !$stringFlag; } else { - $outputStream = new \PHPUnit\Util\Printer($arguments['coverageText']); - $colors = \false; + $result[$i] .= sprintf('%s', htmlspecialchars($token, $this->htmlSpecialCharsFlags)); } - $processor = new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Text($arguments['reportLowUpperBound'], $arguments['reportHighLowerBound'], $arguments['coverageTextShowUncoveredFiles'], $arguments['coverageTextShowOnlySummary']); - $outputStream->write($processor->process($codeCoverage, $colors)); + continue; } - if (isset($arguments['coverageXml'])) { - $this->codeCoverageGenerationStart('PHPUnit XML'); - try { - $writer = new \PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Facade(\PHPUnit\Runner\Version::id()); - $writer->process($codeCoverage, $arguments['coverageXml']); - $this->codeCoverageGenerationSucceeded(); - unset($writer); - } catch (\PHPUnit\SebastianBergmann\CodeCoverage\Exception $e) { - $this->codeCoverageGenerationFailed($e); + [$token, $value] = $token; + $value = str_replace(["\t", ' '], ['    ', ' '], htmlspecialchars($value, $this->htmlSpecialCharsFlags)); + if ($value === "\n") { + $result[++$i] = ''; + } else { + $lines = explode("\n", $value); + foreach ($lines as $jj => $line) { + $line = trim($line); + if ($line !== '') { + if ($stringFlag) { + $colour = 'string'; + } else { + $colour = 'default'; + if ($this->isInlineHtml($token)) { + $colour = 'html'; + } elseif ($this->isComment($token)) { + $colour = 'comment'; + } elseif ($this->isKeyword($token)) { + $colour = 'keyword'; + } + } + $result[$i] .= sprintf('%s', $colour, $line); + } + if (isset($lines[$jj + 1])) { + $result[++$i] = ''; + } } } } - if ($exit) { - if (isset($arguments['failOnEmptyTestSuite']) && $arguments['failOnEmptyTestSuite'] === \true && \count($result) === 0) { - exit(self::FAILURE_EXIT); - } - if ($result->wasSuccessfulIgnoringWarnings()) { - if ($arguments['failOnRisky'] && !$result->allHarmless()) { - exit(self::FAILURE_EXIT); - } - if ($arguments['failOnWarning'] && $result->warningCount() > 0) { - exit(self::FAILURE_EXIT); - } - if ($arguments['failOnIncomplete'] && $result->notImplementedCount() > 0) { - exit(self::FAILURE_EXIT); - } - if ($arguments['failOnSkipped'] && $result->skippedCount() > 0) { - exit(self::FAILURE_EXIT); - } - exit(self::SUCCESS_EXIT); - } - if ($result->errorCount() > 0) { - exit(self::EXCEPTION_EXIT); - } - if ($result->failureCount() > 0) { - exit(self::FAILURE_EXIT); - } + if ($fileEndsWithNewLine) { + unset($result[count($result) - 1]); } + self::$formattedSourceCache[$file] = $result; return $result; } - /** - * Returns the loader to be used. - */ - public function getLoader() : \PHPUnit\Runner\TestSuiteLoader + private function abbreviateClassName(string $className) : string { - if ($this->loader === null) { - $this->loader = new \PHPUnit\Runner\StandardTestSuiteLoader(); + $tmp = explode('\\', $className); + if (count($tmp) > 1) { + $className = sprintf('%s', $className, array_pop($tmp)); } - return $this->loader; + return $className; } - public function addExtension(\PHPUnit\Runner\Hook $extension) : void + private function abbreviateMethodName(string $methodName) : string { - $this->extensions[] = $extension; + $parts = explode('->', $methodName); + if (count($parts) === 2) { + return $this->abbreviateClassName($parts[0]) . '->' . $parts[1]; + } + return $methodName; } - /** - * Override to define how to handle a failed loading of - * a test suite. - */ - protected function runFailed(string $message) : void + private function createPopoverContentForTest(string $test, array $testData) : string { - $this->write($message . \PHP_EOL); - exit(self::FAILURE_EXIT); + $testCSS = ''; + if ($testData['fromTestcase']) { + switch ($testData['status']) { + case BaseTestRunner::STATUS_PASSED: + switch ($testData['size']) { + case 'small': + $testCSS = ' class="covered-by-small-tests"'; + break; + case 'medium': + $testCSS = ' class="covered-by-medium-tests"'; + break; + default: + $testCSS = ' class="covered-by-large-tests"'; + break; + } + break; + case BaseTestRunner::STATUS_SKIPPED: + case BaseTestRunner::STATUS_INCOMPLETE: + case BaseTestRunner::STATUS_RISKY: + case BaseTestRunner::STATUS_WARNING: + $testCSS = ' class="warning"'; + break; + case BaseTestRunner::STATUS_FAILURE: + case BaseTestRunner::STATUS_ERROR: + $testCSS = ' class="danger"'; + break; + } + } + return sprintf('%s', $testCSS, htmlspecialchars($test, $this->htmlSpecialCharsFlags)); } - private function createTestResult() : \PHPUnit\Framework\TestResult + private function isComment(int $token) : bool { - return new \PHPUnit\Framework\TestResult(); + return $token === \T_COMMENT || $token === \T_DOC_COMMENT; } - private function write(string $buffer) : void + private function isInlineHtml(int $token) : bool { - if (\PHP_SAPI !== 'cli' && \PHP_SAPI !== 'phpdbg') { - $buffer = \htmlspecialchars($buffer); + return $token === \T_INLINE_HTML; + } + private function isKeyword(int $token) : bool + { + return isset(self::keywordTokens()[$token]); + } + /** + * @psalm-return array + */ + private static function keywordTokens() : array + { + if (self::$keywordTokens !== []) { + return self::$keywordTokens; } - if ($this->printer !== null) { - $this->printer->write($buffer); - } else { - print $buffer; + self::$keywordTokens = [\T_ABSTRACT => \true, \T_ARRAY => \true, \T_AS => \true, \T_BREAK => \true, \T_CALLABLE => \true, \T_CASE => \true, \T_CATCH => \true, \T_CLASS => \true, \T_CLONE => \true, \T_CONST => \true, \T_CONTINUE => \true, \T_DECLARE => \true, \T_DEFAULT => \true, \T_DO => \true, \T_ECHO => \true, \T_ELSE => \true, \T_ELSEIF => \true, \T_EMPTY => \true, \T_ENDDECLARE => \true, \T_ENDFOR => \true, \T_ENDFOREACH => \true, \T_ENDIF => \true, \T_ENDSWITCH => \true, \T_ENDWHILE => \true, \T_EVAL => \true, \T_EXIT => \true, \T_EXTENDS => \true, \T_FINAL => \true, \T_FINALLY => \true, \T_FOR => \true, \T_FOREACH => \true, \T_FUNCTION => \true, \T_GLOBAL => \true, \T_GOTO => \true, \T_HALT_COMPILER => \true, \T_IF => \true, \T_IMPLEMENTS => \true, \T_INCLUDE => \true, \T_INCLUDE_ONCE => \true, \T_INSTANCEOF => \true, \T_INSTEADOF => \true, \T_INTERFACE => \true, \T_ISSET => \true, \T_LIST => \true, \T_NAMESPACE => \true, \T_NEW => \true, \T_PRINT => \true, \T_PRIVATE => \true, \T_PROTECTED => \true, \T_PUBLIC => \true, \T_REQUIRE => \true, \T_REQUIRE_ONCE => \true, \T_RETURN => \true, \T_STATIC => \true, \T_SWITCH => \true, \T_THROW => \true, \T_TRAIT => \true, \T_TRY => \true, \T_UNSET => \true, \T_USE => \true, \T_VAR => \true, \T_WHILE => \true, \T_YIELD => \true, \T_YIELD_FROM => \true]; + if (defined('T_FN')) { + self::$keywordTokens[constant('T_FN')] = \true; + } + if (defined('T_MATCH')) { + self::$keywordTokens[constant('T_MATCH')] = \true; } + if (defined('T_ENUM')) { + self::$keywordTokens[constant('T_ENUM')] = \true; + } + if (defined('T_READONLY')) { + self::$keywordTokens[constant('T_READONLY')] = \true; + } + return self::$keywordTokens; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Html; + +use function array_values; +use function arsort; +use function asort; +use function count; +use function explode; +use function floor; +use function json_encode; +use function sprintf; +use function str_replace; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; +use PHPUnit\SebastianBergmann\Template\Template; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Dashboard extends Renderer +{ + public function render(DirectoryNode $node, string $file) : void + { + $classes = $node->classesAndTraits(); + $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'dashboard_branch.html' : 'dashboard.html'); + $template = new Template($templateName, '{{', '}}'); + $this->setCommonTemplateVariables($template, $node); + $baseLink = $node->id() . '/'; + $complexity = $this->complexity($classes, $baseLink); + $coverageDistribution = $this->coverageDistribution($classes); + $insufficientCoverage = $this->insufficientCoverage($classes, $baseLink); + $projectRisks = $this->projectRisks($classes, $baseLink); + $template->setVar(['insufficient_coverage_classes' => $insufficientCoverage['class'], 'insufficient_coverage_methods' => $insufficientCoverage['method'], 'project_risks_classes' => $projectRisks['class'], 'project_risks_methods' => $projectRisks['method'], 'complexity_class' => $complexity['class'], 'complexity_method' => $complexity['method'], 'class_coverage_distribution' => $coverageDistribution['class'], 'method_coverage_distribution' => $coverageDistribution['method']]); + $template->renderTo($file); + } + protected function activeBreadcrumb(AbstractNode $node) : string + { + return sprintf(' ' . "\n" . ' ' . "\n", $node->name()); } /** - * @throws Exception - * @throws \PHPUnit\TextUI\XmlConfiguration\Exception + * Returns the data for the Class/Method Complexity charts. */ - private function handleConfiguration(array &$arguments) : void + private function complexity(array $classes, string $baseLink) : array { - if (!isset($arguments['configurationObject']) && isset($arguments['configuration'])) { - $arguments['configurationObject'] = (new \PHPUnit\TextUI\XmlConfiguration\Loader())->load($arguments['configuration']); - } - $arguments['debug'] = $arguments['debug'] ?? \false; - $arguments['filter'] = $arguments['filter'] ?? \false; - $arguments['listeners'] = $arguments['listeners'] ?? []; - if (isset($arguments['configurationObject'])) { - (new \PHPUnit\TextUI\XmlConfiguration\PhpHandler())->handle($arguments['configurationObject']->php()); - $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage(); - if (!isset($arguments['noCoverage'])) { - if (!isset($arguments['coverageClover']) && $codeCoverageConfiguration->hasClover()) { - $arguments['coverageClover'] = $codeCoverageConfiguration->clover()->target()->path(); - } - if (!isset($arguments['coverageCobertura']) && $codeCoverageConfiguration->hasCobertura()) { - $arguments['coverageCobertura'] = $codeCoverageConfiguration->cobertura()->target()->path(); - } - if (!isset($arguments['coverageCrap4J']) && $codeCoverageConfiguration->hasCrap4j()) { - $arguments['coverageCrap4J'] = $codeCoverageConfiguration->crap4j()->target()->path(); - if (!isset($arguments['crap4jThreshold'])) { - $arguments['crap4jThreshold'] = $codeCoverageConfiguration->crap4j()->threshold(); - } - } - if (!isset($arguments['coverageHtml']) && $codeCoverageConfiguration->hasHtml()) { - $arguments['coverageHtml'] = $codeCoverageConfiguration->html()->target()->path(); - if (!isset($arguments['reportLowUpperBound'])) { - $arguments['reportLowUpperBound'] = $codeCoverageConfiguration->html()->lowUpperBound(); - } - if (!isset($arguments['reportHighLowerBound'])) { - $arguments['reportHighLowerBound'] = $codeCoverageConfiguration->html()->highLowerBound(); - } - } - if (!isset($arguments['coveragePHP']) && $codeCoverageConfiguration->hasPhp()) { - $arguments['coveragePHP'] = $codeCoverageConfiguration->php()->target()->path(); - } - if (!isset($arguments['coverageText']) && $codeCoverageConfiguration->hasText()) { - $arguments['coverageText'] = $codeCoverageConfiguration->text()->target()->path(); - $arguments['coverageTextShowUncoveredFiles'] = $codeCoverageConfiguration->text()->showUncoveredFiles(); - $arguments['coverageTextShowOnlySummary'] = $codeCoverageConfiguration->text()->showOnlySummary(); - } - if (!isset($arguments['coverageXml']) && $codeCoverageConfiguration->hasXml()) { - $arguments['coverageXml'] = $codeCoverageConfiguration->xml()->target()->path(); + $result = ['class' => [], 'method' => []]; + foreach ($classes as $className => $class) { + foreach ($class['methods'] as $methodName => $method) { + if ($className !== '*') { + $methodName = $className . '::' . $methodName; } + $result['method'][] = [$method['coverage'], $method['ccn'], sprintf('%s', str_replace($baseLink, '', $method['link']), $methodName)]; } - $phpunitConfiguration = $arguments['configurationObject']->phpunit(); - $arguments['backupGlobals'] = $arguments['backupGlobals'] ?? $phpunitConfiguration->backupGlobals(); - $arguments['backupStaticAttributes'] = $arguments['backupStaticAttributes'] ?? $phpunitConfiguration->backupStaticAttributes(); - $arguments['beStrictAboutChangesToGlobalState'] = $arguments['beStrictAboutChangesToGlobalState'] ?? $phpunitConfiguration->beStrictAboutChangesToGlobalState(); - $arguments['cacheResult'] = $arguments['cacheResult'] ?? $phpunitConfiguration->cacheResult(); - $arguments['colors'] = $arguments['colors'] ?? $phpunitConfiguration->colors(); - $arguments['convertDeprecationsToExceptions'] = $arguments['convertDeprecationsToExceptions'] ?? $phpunitConfiguration->convertDeprecationsToExceptions(); - $arguments['convertErrorsToExceptions'] = $arguments['convertErrorsToExceptions'] ?? $phpunitConfiguration->convertErrorsToExceptions(); - $arguments['convertNoticesToExceptions'] = $arguments['convertNoticesToExceptions'] ?? $phpunitConfiguration->convertNoticesToExceptions(); - $arguments['convertWarningsToExceptions'] = $arguments['convertWarningsToExceptions'] ?? $phpunitConfiguration->convertWarningsToExceptions(); - $arguments['processIsolation'] = $arguments['processIsolation'] ?? $phpunitConfiguration->processIsolation(); - $arguments['stopOnDefect'] = $arguments['stopOnDefect'] ?? $phpunitConfiguration->stopOnDefect(); - $arguments['stopOnError'] = $arguments['stopOnError'] ?? $phpunitConfiguration->stopOnError(); - $arguments['stopOnFailure'] = $arguments['stopOnFailure'] ?? $phpunitConfiguration->stopOnFailure(); - $arguments['stopOnWarning'] = $arguments['stopOnWarning'] ?? $phpunitConfiguration->stopOnWarning(); - $arguments['stopOnIncomplete'] = $arguments['stopOnIncomplete'] ?? $phpunitConfiguration->stopOnIncomplete(); - $arguments['stopOnRisky'] = $arguments['stopOnRisky'] ?? $phpunitConfiguration->stopOnRisky(); - $arguments['stopOnSkipped'] = $arguments['stopOnSkipped'] ?? $phpunitConfiguration->stopOnSkipped(); - $arguments['failOnEmptyTestSuite'] = $arguments['failOnEmptyTestSuite'] ?? $phpunitConfiguration->failOnEmptyTestSuite(); - $arguments['failOnIncomplete'] = $arguments['failOnIncomplete'] ?? $phpunitConfiguration->failOnIncomplete(); - $arguments['failOnRisky'] = $arguments['failOnRisky'] ?? $phpunitConfiguration->failOnRisky(); - $arguments['failOnSkipped'] = $arguments['failOnSkipped'] ?? $phpunitConfiguration->failOnSkipped(); - $arguments['failOnWarning'] = $arguments['failOnWarning'] ?? $phpunitConfiguration->failOnWarning(); - $arguments['enforceTimeLimit'] = $arguments['enforceTimeLimit'] ?? $phpunitConfiguration->enforceTimeLimit(); - $arguments['defaultTimeLimit'] = $arguments['defaultTimeLimit'] ?? $phpunitConfiguration->defaultTimeLimit(); - $arguments['timeoutForSmallTests'] = $arguments['timeoutForSmallTests'] ?? $phpunitConfiguration->timeoutForSmallTests(); - $arguments['timeoutForMediumTests'] = $arguments['timeoutForMediumTests'] ?? $phpunitConfiguration->timeoutForMediumTests(); - $arguments['timeoutForLargeTests'] = $arguments['timeoutForLargeTests'] ?? $phpunitConfiguration->timeoutForLargeTests(); - $arguments['reportUselessTests'] = $arguments['reportUselessTests'] ?? $phpunitConfiguration->beStrictAboutTestsThatDoNotTestAnything(); - $arguments['strictCoverage'] = $arguments['strictCoverage'] ?? $phpunitConfiguration->beStrictAboutCoversAnnotation(); - $arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] = $arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] ?? $codeCoverageConfiguration->ignoreDeprecatedCodeUnits(); - $arguments['disallowTestOutput'] = $arguments['disallowTestOutput'] ?? $phpunitConfiguration->beStrictAboutOutputDuringTests(); - $arguments['disallowTodoAnnotatedTests'] = $arguments['disallowTodoAnnotatedTests'] ?? $phpunitConfiguration->beStrictAboutTodoAnnotatedTests(); - $arguments['beStrictAboutResourceUsageDuringSmallTests'] = $arguments['beStrictAboutResourceUsageDuringSmallTests'] ?? $phpunitConfiguration->beStrictAboutResourceUsageDuringSmallTests(); - $arguments['verbose'] = $arguments['verbose'] ?? $phpunitConfiguration->verbose(); - $arguments['reverseDefectList'] = $arguments['reverseDefectList'] ?? $phpunitConfiguration->reverseDefectList(); - $arguments['forceCoversAnnotation'] = $arguments['forceCoversAnnotation'] ?? $phpunitConfiguration->forceCoversAnnotation(); - $arguments['disableCodeCoverageIgnore'] = $arguments['disableCodeCoverageIgnore'] ?? $codeCoverageConfiguration->disableCodeCoverageIgnore(); - $arguments['registerMockObjectsFromTestArgumentsRecursively'] = $arguments['registerMockObjectsFromTestArgumentsRecursively'] ?? $phpunitConfiguration->registerMockObjectsFromTestArgumentsRecursively(); - $arguments['noInteraction'] = $arguments['noInteraction'] ?? $phpunitConfiguration->noInteraction(); - $arguments['executionOrder'] = $arguments['executionOrder'] ?? $phpunitConfiguration->executionOrder(); - $arguments['resolveDependencies'] = $arguments['resolveDependencies'] ?? $phpunitConfiguration->resolveDependencies(); - if (!isset($arguments['bootstrap']) && $phpunitConfiguration->hasBootstrap()) { - $arguments['bootstrap'] = $phpunitConfiguration->bootstrap(); - } - if (!isset($arguments['cacheResultFile']) && $phpunitConfiguration->hasCacheResultFile()) { - $arguments['cacheResultFile'] = $phpunitConfiguration->cacheResultFile(); - } - if (!isset($arguments['executionOrderDefects'])) { - $arguments['executionOrderDefects'] = $phpunitConfiguration->defectsFirst() ? \PHPUnit\Runner\TestSuiteSorter::ORDER_DEFECTS_FIRST : \PHPUnit\Runner\TestSuiteSorter::ORDER_DEFAULT; - } - if ($phpunitConfiguration->conflictBetweenPrinterClassAndTestdox()) { - $arguments['conflictBetweenPrinterClassAndTestdox'] = \true; - } - $groupCliArgs = []; - if (!empty($arguments['groups'])) { - $groupCliArgs = $arguments['groups']; - } - $groupConfiguration = $arguments['configurationObject']->groups(); - if (!isset($arguments['groups']) && $groupConfiguration->hasInclude()) { - $arguments['groups'] = $groupConfiguration->include()->asArrayOfStrings(); - } - if (!isset($arguments['excludeGroups']) && $groupConfiguration->hasExclude()) { - $arguments['excludeGroups'] = \array_diff($groupConfiguration->exclude()->asArrayOfStrings(), $groupCliArgs); + $result['class'][] = [$class['coverage'], $class['ccn'], sprintf('%s', str_replace($baseLink, '', $class['link']), $className)]; + } + return ['class' => json_encode($result['class']), 'method' => json_encode($result['method'])]; + } + /** + * Returns the data for the Class / Method Coverage Distribution chart. + */ + private function coverageDistribution(array $classes) : array + { + $result = ['class' => ['0%' => 0, '0-10%' => 0, '10-20%' => 0, '20-30%' => 0, '30-40%' => 0, '40-50%' => 0, '50-60%' => 0, '60-70%' => 0, '70-80%' => 0, '80-90%' => 0, '90-100%' => 0, '100%' => 0], 'method' => ['0%' => 0, '0-10%' => 0, '10-20%' => 0, '20-30%' => 0, '30-40%' => 0, '40-50%' => 0, '50-60%' => 0, '60-70%' => 0, '70-80%' => 0, '80-90%' => 0, '90-100%' => 0, '100%' => 0]]; + foreach ($classes as $class) { + foreach ($class['methods'] as $methodName => $method) { + if ($method['coverage'] === 0) { + $result['method']['0%']++; + } elseif ($method['coverage'] === 100) { + $result['method']['100%']++; + } else { + $key = floor($method['coverage'] / 10) * 10; + $key = $key . '-' . ($key + 10) . '%'; + $result['method'][$key]++; + } } - $extensionHandler = new \PHPUnit\TextUI\XmlConfiguration\ExtensionHandler(); - foreach ($arguments['configurationObject']->extensions() as $extension) { - $this->addExtension($extensionHandler->createHookInstance($extension)); + if ($class['coverage'] === 0) { + $result['class']['0%']++; + } elseif ($class['coverage'] === 100) { + $result['class']['100%']++; + } else { + $key = floor($class['coverage'] / 10) * 10; + $key = $key . '-' . ($key + 10) . '%'; + $result['class'][$key]++; } - foreach ($arguments['configurationObject']->listeners() as $listener) { - $arguments['listeners'][] = $extensionHandler->createTestListenerInstance($listener); + } + return ['class' => json_encode(array_values($result['class'])), 'method' => json_encode(array_values($result['method']))]; + } + /** + * Returns the classes / methods with insufficient coverage. + */ + private function insufficientCoverage(array $classes, string $baseLink) : array + { + $leastTestedClasses = []; + $leastTestedMethods = []; + $result = ['class' => '', 'method' => '']; + foreach ($classes as $className => $class) { + foreach ($class['methods'] as $methodName => $method) { + if ($method['coverage'] < $this->highLowerBound) { + $key = $methodName; + if ($className !== '*') { + $key = $className . '::' . $methodName; + } + $leastTestedMethods[$key] = $method['coverage']; + } } - unset($extensionHandler); - foreach ($arguments['unavailableExtensions'] as $extension) { - $arguments['warnings'][] = \sprintf('Extension "%s" is not available', $extension); + if ($class['coverage'] < $this->highLowerBound) { + $leastTestedClasses[$className] = $class['coverage']; } - $loggingConfiguration = $arguments['configurationObject']->logging(); - if (!isset($arguments['noLogging'])) { - if ($loggingConfiguration->hasText()) { - $arguments['listeners'][] = new \PHPUnit\TextUI\DefaultResultPrinter($loggingConfiguration->text()->target()->path(), \true); - } - if (!isset($arguments['teamcityLogfile']) && $loggingConfiguration->hasTeamCity()) { - $arguments['teamcityLogfile'] = $loggingConfiguration->teamCity()->target()->path(); - } - if (!isset($arguments['junitLogfile']) && $loggingConfiguration->hasJunit()) { - $arguments['junitLogfile'] = $loggingConfiguration->junit()->target()->path(); - } - if (!isset($arguments['testdoxHTMLFile']) && $loggingConfiguration->hasTestDoxHtml()) { - $arguments['testdoxHTMLFile'] = $loggingConfiguration->testDoxHtml()->target()->path(); - } - if (!isset($arguments['testdoxTextFile']) && $loggingConfiguration->hasTestDoxText()) { - $arguments['testdoxTextFile'] = $loggingConfiguration->testDoxText()->target()->path(); - } - if (!isset($arguments['testdoxXMLFile']) && $loggingConfiguration->hasTestDoxXml()) { - $arguments['testdoxXMLFile'] = $loggingConfiguration->testDoxXml()->target()->path(); + } + asort($leastTestedClasses); + asort($leastTestedMethods); + foreach ($leastTestedClasses as $className => $coverage) { + $result['class'] .= sprintf(' %s%d%%' . "\n", str_replace($baseLink, '', $classes[$className]['link']), $className, $coverage); + } + foreach ($leastTestedMethods as $methodName => $coverage) { + [$class, $method] = explode('::', $methodName); + $result['method'] .= sprintf(' %s%d%%' . "\n", str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']), $methodName, $method, $coverage); + } + return $result; + } + /** + * Returns the project risks according to the CRAP index. + */ + private function projectRisks(array $classes, string $baseLink) : array + { + $classRisks = []; + $methodRisks = []; + $result = ['class' => '', 'method' => '']; + foreach ($classes as $className => $class) { + foreach ($class['methods'] as $methodName => $method) { + if ($method['coverage'] < $this->highLowerBound && $method['ccn'] > 1) { + $key = $methodName; + if ($className !== '*') { + $key = $className . '::' . $methodName; + } + $methodRisks[$key] = $method['crap']; } } - $testdoxGroupConfiguration = $arguments['configurationObject']->testdoxGroups(); - if (!isset($arguments['testdoxGroups']) && $testdoxGroupConfiguration->hasInclude()) { - $arguments['testdoxGroups'] = $testdoxGroupConfiguration->include()->asArrayOfStrings(); + if ($class['coverage'] < $this->highLowerBound && $class['ccn'] > count($class['methods'])) { + $classRisks[$className] = $class['crap']; } - if (!isset($arguments['testdoxExcludeGroups']) && $testdoxGroupConfiguration->hasExclude()) { - $arguments['testdoxExcludeGroups'] = $testdoxGroupConfiguration->exclude()->asArrayOfStrings(); + } + arsort($classRisks); + arsort($methodRisks); + foreach ($classRisks as $className => $crap) { + $result['class'] .= sprintf(' %s%d' . "\n", str_replace($baseLink, '', $classes[$className]['link']), $className, $crap); + } + foreach ($methodRisks as $methodName => $crap) { + [$class, $method] = explode('::', $methodName); + $result['method'] .= sprintf(' %s%d' . "\n", str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']), $methodName, $method, $crap); + } + return $result; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Html; + +use function count; +use function sprintf; +use function str_repeat; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode as Node; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; +use PHPUnit\SebastianBergmann\Template\Template; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Directory extends Renderer +{ + public function render(DirectoryNode $node, string $file) : void + { + $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'directory_branch.html' : 'directory.html'); + $template = new Template($templateName, '{{', '}}'); + $this->setCommonTemplateVariables($template, $node); + $items = $this->renderItem($node, \true); + foreach ($node->directories() as $item) { + $items .= $this->renderItem($item); + } + foreach ($node->files() as $item) { + $items .= $this->renderItem($item); + } + $template->setVar(['id' => $node->id(), 'items' => $items]); + $template->renderTo($file); + } + private function renderItem(Node $node, bool $total = \false) : string + { + $data = ['numClasses' => $node->numberOfClassesAndTraits(), 'numTestedClasses' => $node->numberOfTestedClassesAndTraits(), 'numMethods' => $node->numberOfFunctionsAndMethods(), 'numTestedMethods' => $node->numberOfTestedFunctionsAndMethods(), 'linesExecutedPercent' => $node->percentageOfExecutedLines()->asFloat(), 'linesExecutedPercentAsString' => $node->percentageOfExecutedLines()->asString(), 'numExecutedLines' => $node->numberOfExecutedLines(), 'numExecutableLines' => $node->numberOfExecutableLines(), 'branchesExecutedPercent' => $node->percentageOfExecutedBranches()->asFloat(), 'branchesExecutedPercentAsString' => $node->percentageOfExecutedBranches()->asString(), 'numExecutedBranches' => $node->numberOfExecutedBranches(), 'numExecutableBranches' => $node->numberOfExecutableBranches(), 'pathsExecutedPercent' => $node->percentageOfExecutedPaths()->asFloat(), 'pathsExecutedPercentAsString' => $node->percentageOfExecutedPaths()->asString(), 'numExecutedPaths' => $node->numberOfExecutedPaths(), 'numExecutablePaths' => $node->numberOfExecutablePaths(), 'testedMethodsPercent' => $node->percentageOfTestedFunctionsAndMethods()->asFloat(), 'testedMethodsPercentAsString' => $node->percentageOfTestedFunctionsAndMethods()->asString(), 'testedClassesPercent' => $node->percentageOfTestedClassesAndTraits()->asFloat(), 'testedClassesPercentAsString' => $node->percentageOfTestedClassesAndTraits()->asString()]; + if ($total) { + $data['name'] = 'Total'; + } else { + $up = str_repeat('../', count($node->pathAsArray()) - 2); + $data['icon'] = sprintf('', $up); + if ($node instanceof DirectoryNode) { + $data['name'] = sprintf('%s', $node->name(), $node->name()); + $data['icon'] = sprintf('', $up); + } elseif ($this->hasBranchCoverage) { + $data['name'] = sprintf('%s [line] [branch] [path]', $node->name(), $node->name(), $node->name(), $node->name()); + } else { + $data['name'] = sprintf('%s', $node->name(), $node->name()); } } - $extensionHandler = new \PHPUnit\TextUI\XmlConfiguration\ExtensionHandler(); - foreach ($arguments['extensions'] as $extension) { - $this->addExtension($extensionHandler->createHookInstance($extension)); + $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'directory_item_branch.html' : 'directory_item.html'); + return $this->renderItemTemplate(new Template($templateName, '{{', '}}'), $data); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report; + +use function dirname; +use function file_put_contents; +use function serialize; +use function sprintf; +use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; +use PHPUnit\SebastianBergmann\CodeCoverage\Directory; +use PHPUnit\SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException; +final class PHP +{ + public function process(CodeCoverage $coverage, ?string $target = null) : string + { + $buffer = sprintf(" + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; + +use function constant; +use function phpversion; +use DateTimeImmutable; +use DOMElement; +use PHPUnit\SebastianBergmann\Environment\Runtime; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class BuildInformation +{ + /** + * @var DOMElement + */ + private $contextNode; + public function __construct(DOMElement $contextNode) + { + $this->contextNode = $contextNode; } - private function processSuiteFilters(\PHPUnit\Framework\TestSuite $suite, array $arguments) : void + public function setRuntimeInformation(Runtime $runtime) : void { - if (!$arguments['filter'] && empty($arguments['groups']) && empty($arguments['excludeGroups']) && empty($arguments['testsCovering']) && empty($arguments['testsUsing'])) { - return; - } - $filterFactory = new \PHPUnit\Runner\Filter\Factory(); - if (!empty($arguments['excludeGroups'])) { - $filterFactory->addFilter(new \ReflectionClass(\PHPUnit\Runner\Filter\ExcludeGroupFilterIterator::class), $arguments['excludeGroups']); - } - if (!empty($arguments['groups'])) { - $filterFactory->addFilter(new \ReflectionClass(\PHPUnit\Runner\Filter\IncludeGroupFilterIterator::class), $arguments['groups']); - } - if (!empty($arguments['testsCovering'])) { - $filterFactory->addFilter(new \ReflectionClass(\PHPUnit\Runner\Filter\IncludeGroupFilterIterator::class), \array_map(static function (string $name) : string { - return '__phpunit_covers_' . $name; - }, $arguments['testsCovering'])); + $runtimeNode = $this->nodeByName('runtime'); + $runtimeNode->setAttribute('name', $runtime->getName()); + $runtimeNode->setAttribute('version', $runtime->getVersion()); + $runtimeNode->setAttribute('url', $runtime->getVendorUrl()); + $driverNode = $this->nodeByName('driver'); + if ($runtime->hasPHPDBGCodeCoverage()) { + $driverNode->setAttribute('name', 'phpdbg'); + $driverNode->setAttribute('version', constant('PHPDBG_VERSION')); } - if (!empty($arguments['testsUsing'])) { - $filterFactory->addFilter(new \ReflectionClass(\PHPUnit\Runner\Filter\IncludeGroupFilterIterator::class), \array_map(static function (string $name) : string { - return '__phpunit_uses_' . $name; - }, $arguments['testsUsing'])); + if ($runtime->hasXdebug()) { + $driverNode->setAttribute('name', 'xdebug'); + $driverNode->setAttribute('version', phpversion('xdebug')); } - if ($arguments['filter']) { - $filterFactory->addFilter(new \ReflectionClass(\PHPUnit\Runner\Filter\NameFilterIterator::class), $arguments['filter']); + if ($runtime->hasPCOV()) { + $driverNode->setAttribute('name', 'pcov'); + $driverNode->setAttribute('version', phpversion('pcov')); } - $suite->injectFilter($filterFactory); } - private function writeMessage(string $type, string $message) : void + public function setBuildTime(DateTimeImmutable $date) : void { - if (!$this->messagePrinted) { - $this->write("\n"); - } - $this->write(\sprintf("%-15s%s\n", $type . ':', $message)); - $this->messagePrinted = \true; + $this->contextNode->setAttribute('time', $date->format('D M j G:i:s T Y')); } - private function createPrinter(string $class, array $arguments) : \PHPUnit\TextUI\ResultPrinter + public function setGeneratorVersions(string $phpUnitVersion, string $coverageVersion) : void { - $object = new $class(isset($arguments['stderr']) && $arguments['stderr'] === \true ? 'php://stderr' : null, $arguments['verbose'], $arguments['colors'], $arguments['debug'], $arguments['columns'], $arguments['reverseList']); - \assert($object instanceof \PHPUnit\TextUI\ResultPrinter); - return $object; + $this->contextNode->setAttribute('phpunit', $phpUnitVersion); + $this->contextNode->setAttribute('coverage', $coverageVersion); } - private function codeCoverageGenerationStart(string $format) : void + private function nodeByName(string $name) : DOMElement { - $this->printer->write(\sprintf("\nGenerating code coverage report in %s format ... ", $format)); - $this->timer->start(); + $node = $this->contextNode->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', $name)->item(0); + if (!$node) { + $node = $this->contextNode->appendChild($this->contextNode->ownerDocument->createElementNS('https://schema.phpunit.de/coverage/1.0', $name)); + } + return $node; } - private function codeCoverageGenerationSucceeded() : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; + +use DOMElement; +use PHPUnit\TheSeer\Tokenizer\NamespaceUri; +use PHPUnit\TheSeer\Tokenizer\Tokenizer; +use PHPUnit\TheSeer\Tokenizer\XMLSerializer; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Source +{ + /** @var DOMElement */ + private $context; + public function __construct(DOMElement $context) { - $this->printer->write(\sprintf("done [%s]\n", $this->timer->stop()->asString())); + $this->context = $context; } - private function codeCoverageGenerationFailed(\Exception $e) : void + public function setSourceCode(string $source) : void { - $this->printer->write(\sprintf("failed [%s]\n%s\n", $this->timer->stop()->asString(), $e->getMessage())); + $context = $this->context; + $tokens = (new Tokenizer())->parse($source); + $srcDom = (new XMLSerializer(new NamespaceUri($context->namespaceURI)))->toDom($tokens); + $context->parentNode->replaceChild($context->ownerDocument->importNode($srcDom->documentElement, \true), $context); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; +use const DIRECTORY_SEPARATOR; use const PHP_EOL; -use function array_map; -use function array_reverse; use function count; -use function floor; -use function implode; -use function in_array; -use function is_int; -use function max; -use function preg_split; +use function dirname; +use function file_get_contents; +use function file_put_contents; +use function is_array; +use function is_dir; +use function is_file; +use function is_writable; +use function libxml_clear_errors; +use function libxml_get_errors; +use function libxml_use_internal_errors; use function sprintf; -use function str_pad; -use function str_repeat; use function strlen; -use function vsprintf; -use PHPUnit\Framework\AssertionFailedError; -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\InvalidArgumentException; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestFailure; -use PHPUnit\Framework\TestResult; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Framework\Warning; -use PHPUnit\Runner\PhptTestCase; -use PHPUnit\Util\Color; -use PHPUnit\Util\Printer; -use PHPUnit\SebastianBergmann\Environment\Console; -use PHPUnit\SebastianBergmann\Timer\ResourceUsageFormatter; -use PHPUnit\SebastianBergmann\Timer\Timer; -use Throwable; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -class DefaultResultPrinter extends \PHPUnit\Util\Printer implements \PHPUnit\TextUI\ResultPrinter +use function substr; +use DateTimeImmutable; +use DOMDocument; +use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; +use PHPUnit\SebastianBergmann\CodeCoverage\Directory as DirectoryUtil; +use PHPUnit\SebastianBergmann\CodeCoverage\Driver\PathExistsButIsNotDirectoryException; +use PHPUnit\SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\AbstractNode; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\File as FileNode; +use PHPUnit\SebastianBergmann\CodeCoverage\Version; +use PHPUnit\SebastianBergmann\CodeCoverage\XmlException; +use PHPUnit\SebastianBergmann\Environment\Runtime; +final class Facade { - public const EVENT_TEST_START = 0; - public const EVENT_TEST_END = 1; - public const EVENT_TESTSUITE_START = 2; - public const EVENT_TESTSUITE_END = 3; - public const COLOR_NEVER = 'never'; - public const COLOR_AUTO = 'auto'; - public const COLOR_ALWAYS = 'always'; - public const COLOR_DEFAULT = self::COLOR_NEVER; - private const AVAILABLE_COLORS = [self::COLOR_NEVER, self::COLOR_AUTO, self::COLOR_ALWAYS]; - /** - * @var int - */ - protected $column = 0; - /** - * @var int - */ - protected $maxColumn; - /** - * @var bool - */ - protected $lastTestFailed = \false; - /** - * @var int - */ - protected $numAssertions = 0; - /** - * @var int - */ - protected $numTests = -1; - /** - * @var int - */ - protected $numTestsRun = 0; - /** - * @var int - */ - protected $numTestsWidth; - /** - * @var bool - */ - protected $colors = \false; - /** - * @var bool - */ - protected $debug = \false; - /** - * @var bool - */ - protected $verbose = \false; - /** - * @var int - */ - private $numberOfColumns; /** - * @var bool + * @var string */ - private $reverse; + private $target; /** - * @var bool + * @var Project */ - private $defectListPrinted = \false; + private $project; /** - * @var Timer + * @var string */ - private $timer; + private $phpUnitVersion; + public function __construct(string $version) + { + $this->phpUnitVersion = $version; + } /** - * Constructor. - * - * @param null|resource|string $out - * @param int|string $numberOfColumns - * - * @throws Exception + * @throws XmlException */ - public function __construct($out = null, bool $verbose = \false, string $colors = self::COLOR_DEFAULT, bool $debug = \false, $numberOfColumns = 80, bool $reverse = \false) + public function process(CodeCoverage $coverage, string $target) : void { - parent::__construct($out); - if (!\in_array($colors, self::AVAILABLE_COLORS, \true)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(3, \vsprintf('value from "%s", "%s" or "%s"', self::AVAILABLE_COLORS)); - } - if (!\is_int($numberOfColumns) && $numberOfColumns !== 'max') { - throw \PHPUnit\Framework\InvalidArgumentException::create(5, 'integer or "max"'); - } - $console = new \PHPUnit\SebastianBergmann\Environment\Console(); - $maxNumberOfColumns = $console->getNumberOfColumns(); - if ($numberOfColumns === 'max' || $numberOfColumns !== 80 && $numberOfColumns > $maxNumberOfColumns) { - $numberOfColumns = $maxNumberOfColumns; - } - $this->numberOfColumns = $numberOfColumns; - $this->verbose = $verbose; - $this->debug = $debug; - $this->reverse = $reverse; - if ($colors === self::COLOR_AUTO && $console->hasColorSupport()) { - $this->colors = \true; - } else { - $this->colors = self::COLOR_ALWAYS === $colors; + if (substr($target, -1, 1) !== \DIRECTORY_SEPARATOR) { + $target .= \DIRECTORY_SEPARATOR; } - $this->timer = new \PHPUnit\SebastianBergmann\Timer\Timer(); - $this->timer->start(); + $this->target = $target; + $this->initTargetDirectory($target); + $report = $coverage->getReport(); + $this->project = new Project($coverage->getReport()->name()); + $this->setBuildInformation(); + $this->processTests($coverage->getTests()); + $this->processDirectory($report, $this->project); + $this->saveDocument($this->project->asDom(), 'index'); } - public function printResult(\PHPUnit\Framework\TestResult $result) : void + private function setBuildInformation() : void { - $this->printHeader($result); - $this->printErrors($result); - $this->printWarnings($result); - $this->printFailures($result); - $this->printRisky($result); - if ($this->verbose) { - $this->printIncompletes($result); - $this->printSkipped($result); - } - $this->printFooter($result); + $buildNode = $this->project->buildInformation(); + $buildNode->setRuntimeInformation(new Runtime()); + $buildNode->setBuildTime(new DateTimeImmutable()); + $buildNode->setGeneratorVersions($this->phpUnitVersion, Version::id()); } /** - * An error occurred. + * @throws PathExistsButIsNotDirectoryException + * @throws WriteOperationFailedException */ - public function addError(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + private function initTargetDirectory(string $directory) : void { - $this->writeProgressWithColor('fg-red, bold', 'E'); - $this->lastTestFailed = \true; + if (is_file($directory)) { + if (!is_dir($directory)) { + throw new PathExistsButIsNotDirectoryException($directory); + } + if (!is_writable($directory)) { + throw new WriteOperationFailedException($directory); + } + } + DirectoryUtil::create($directory); } /** - * A failure occurred. + * @throws XmlException */ - public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time) : void + private function processDirectory(DirectoryNode $directory, Node $context) : void { - $this->writeProgressWithColor('bg-red, fg-white', 'F'); - $this->lastTestFailed = \true; + $directoryName = $directory->name(); + if ($this->project->projectSourceDirectory() === $directoryName) { + $directoryName = '/'; + } + $directoryObject = $context->addDirectory($directoryName); + $this->setTotals($directory, $directoryObject->totals()); + foreach ($directory->directories() as $node) { + $this->processDirectory($node, $directoryObject); + } + foreach ($directory->files() as $node) { + $this->processFile($node, $directoryObject); + } } /** - * A warning occurred. + * @throws XmlException */ - public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time) : void + private function processFile(FileNode $file, Directory $context) : void { - $this->writeProgressWithColor('fg-yellow, bold', 'W'); - $this->lastTestFailed = \true; + $fileObject = $context->addFile($file->name(), $file->id() . '.xml'); + $this->setTotals($file, $fileObject->totals()); + $path = substr($file->pathAsString(), strlen($this->project->projectSourceDirectory())); + $fileReport = new Report($path); + $this->setTotals($file, $fileReport->totals()); + foreach ($file->classesAndTraits() as $unit) { + $this->processUnit($unit, $fileReport); + } + foreach ($file->functions() as $function) { + $this->processFunction($function, $fileReport); + } + foreach ($file->lineCoverageData() as $line => $tests) { + if (!is_array($tests) || count($tests) === 0) { + continue; + } + $coverage = $fileReport->lineCoverage((string) $line); + foreach ($tests as $test) { + $coverage->addTest($test); + } + $coverage->finalize(); + } + $fileReport->source()->setSourceCode(file_get_contents($file->pathAsString())); + $this->saveDocument($fileReport->asDom(), $file->id()); } - /** - * Incomplete test. - */ - public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + private function processUnit(array $unit, Report $report) : void { - $this->writeProgressWithColor('fg-yellow, bold', 'I'); - $this->lastTestFailed = \true; + if (isset($unit['className'])) { + $unitObject = $report->classObject($unit['className']); + } else { + $unitObject = $report->traitObject($unit['traitName']); + } + $unitObject->setLines($unit['startLine'], $unit['executableLines'], $unit['executedLines']); + $unitObject->setCrap((float) $unit['crap']); + $unitObject->setNamespace($unit['namespace']); + foreach ($unit['methods'] as $method) { + $methodObject = $unitObject->addMethod($method['methodName']); + $methodObject->setSignature($method['signature']); + $methodObject->setLines((string) $method['startLine'], (string) $method['endLine']); + $methodObject->setCrap($method['crap']); + $methodObject->setTotals((string) $method['executableLines'], (string) $method['executedLines'], (string) $method['coverage']); + } } - /** - * Risky test. - */ - public function addRiskyTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + private function processFunction(array $function, Report $report) : void { - $this->writeProgressWithColor('fg-yellow, bold', 'R'); - $this->lastTestFailed = \true; + $functionObject = $report->functionObject($function['functionName']); + $functionObject->setSignature($function['signature']); + $functionObject->setLines((string) $function['startLine']); + $functionObject->setCrap($function['crap']); + $functionObject->setTotals((string) $function['executableLines'], (string) $function['executedLines'], (string) $function['coverage']); } - /** - * Skipped test. - */ - public function addSkippedTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + private function processTests(array $tests) : void { - $this->writeProgressWithColor('fg-cyan, bold', 'S'); - $this->lastTestFailed = \true; + $testsObject = $this->project->tests(); + foreach ($tests as $test => $result) { + $testsObject->addTest($test, $result); + } } - /** - * A testsuite started. - */ - public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + private function setTotals(AbstractNode $node, Totals $totals) : void { - if ($this->numTests == -1) { - $this->numTests = \count($suite); - $this->numTestsWidth = \strlen((string) $this->numTests); - $this->maxColumn = $this->numberOfColumns - \strlen(' / (XXX%)') - 2 * $this->numTestsWidth; - } + $loc = $node->linesOfCode(); + $totals->setNumLines($loc->linesOfCode(), $loc->commentLinesOfCode(), $loc->nonCommentLinesOfCode(), $node->numberOfExecutableLines(), $node->numberOfExecutedLines()); + $totals->setNumClasses($node->numberOfClasses(), $node->numberOfTestedClasses()); + $totals->setNumTraits($node->numberOfTraits(), $node->numberOfTestedTraits()); + $totals->setNumMethods($node->numberOfMethods(), $node->numberOfTestedMethods()); + $totals->setNumFunctions($node->numberOfFunctions(), $node->numberOfTestedFunctions()); } - /** - * A testsuite ended. - */ - public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + private function targetDirectory() : string { + return $this->target; } /** - * A test started. + * @throws XmlException */ - public function startTest(\PHPUnit\Framework\Test $test) : void + private function saveDocument(DOMDocument $document, string $name) : void { - if ($this->debug) { - $this->write(\sprintf("Test '%s' started\n", \PHPUnit\Util\Test::describeAsString($test))); - } + $filename = sprintf('%s/%s.xml', $this->targetDirectory(), $name); + $document->formatOutput = \true; + $document->preserveWhiteSpace = \false; + $this->initTargetDirectory(dirname($filename)); + file_put_contents($filename, $this->documentAsString($document)); } /** - * A test ended. + * @throws XmlException + * + * @see https://bugs.php.net/bug.php?id=79191 */ - public function endTest(\PHPUnit\Framework\Test $test, float $time) : void - { - if ($this->debug) { - $this->write(\sprintf("Test '%s' ended\n", \PHPUnit\Util\Test::describeAsString($test))); - } - if (!$this->lastTestFailed) { - $this->writeProgress('.'); - } - if ($test instanceof \PHPUnit\Framework\TestCase) { - $this->numAssertions += $test->getNumAssertions(); - } elseif ($test instanceof \PHPUnit\Runner\PhptTestCase) { - $this->numAssertions++; - } - $this->lastTestFailed = \false; - if ($test instanceof \PHPUnit\Framework\TestCase && !$test->hasExpectationOnOutput()) { - $this->write($test->getActualOutput()); - } - } - protected function printDefects(array $defects, string $type) : void + private function documentAsString(DOMDocument $document) : string { - $count = \count($defects); - if ($count == 0) { - return; - } - if ($this->defectListPrinted) { - $this->write("\n--\n\n"); - } - $this->write(\sprintf("There %s %d %s%s:\n", $count == 1 ? 'was' : 'were', $count, $type, $count == 1 ? '' : 's')); - $i = 1; - if ($this->reverse) { - $defects = \array_reverse($defects); - } - foreach ($defects as $defect) { - $this->printDefect($defect, $i++); + $xmlErrorHandling = libxml_use_internal_errors(\true); + $xml = $document->saveXML(); + if ($xml === \false) { + $message = 'Unable to generate the XML'; + foreach (libxml_get_errors() as $error) { + $message .= \PHP_EOL . $error->message; + } + throw new XmlException($message); } - $this->defectListPrinted = \true; + libxml_clear_errors(); + libxml_use_internal_errors($xmlErrorHandling); + return $xml; } - protected function printDefect(\PHPUnit\Framework\TestFailure $defect, int $count) : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; + +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Method +{ + /** + * @var DOMElement + */ + private $contextNode; + public function __construct(DOMElement $context, string $name) { - $this->printDefectHeader($defect, $count); - $this->printDefectTrace($defect); + $this->contextNode = $context; + $this->setName($name); } - protected function printDefectHeader(\PHPUnit\Framework\TestFailure $defect, int $count) : void + public function setSignature(string $signature) : void { - $this->write(\sprintf("\n%d) %s\n", $count, $defect->getTestName())); + $this->contextNode->setAttribute('signature', $signature); } - protected function printDefectTrace(\PHPUnit\Framework\TestFailure $defect) : void + public function setLines(string $start, ?string $end = null) : void { - $e = $defect->thrownException(); - $this->write((string) $e); - while ($e = $e->getPrevious()) { - $this->write("\nCaused by\n" . $e); + $this->contextNode->setAttribute('start', $start); + if ($end !== null) { + $this->contextNode->setAttribute('end', $end); } } - protected function printErrors(\PHPUnit\Framework\TestResult $result) : void - { - $this->printDefects($result->errors(), 'error'); - } - protected function printFailures(\PHPUnit\Framework\TestResult $result) : void - { - $this->printDefects($result->failures(), 'failure'); - } - protected function printWarnings(\PHPUnit\Framework\TestResult $result) : void - { - $this->printDefects($result->warnings(), 'warning'); - } - protected function printIncompletes(\PHPUnit\Framework\TestResult $result) : void + public function setTotals(string $executable, string $executed, string $coverage) : void { - $this->printDefects($result->notImplemented(), 'incomplete test'); + $this->contextNode->setAttribute('executable', $executable); + $this->contextNode->setAttribute('executed', $executed); + $this->contextNode->setAttribute('coverage', $coverage); } - protected function printRisky(\PHPUnit\Framework\TestResult $result) : void + public function setCrap(string $crap) : void { - $this->printDefects($result->risky(), 'risky test'); + $this->contextNode->setAttribute('crap', $crap); } - protected function printSkipped(\PHPUnit\Framework\TestResult $result) : void + private function setName(string $name) : void { - $this->printDefects($result->skipped(), 'skipped test'); + $this->contextNode->setAttribute('name', $name); } - protected function printHeader(\PHPUnit\Framework\TestResult $result) : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; + +use function basename; +use function dirname; +use DOMDocument; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Report extends File +{ + public function __construct(string $name) { - if (\count($result) > 0) { - $this->write(\PHP_EOL . \PHP_EOL . (new \PHPUnit\SebastianBergmann\Timer\ResourceUsageFormatter())->resourceUsage($this->timer->stop()) . \PHP_EOL . \PHP_EOL); - } + $dom = new DOMDocument(); + $dom->loadXML(''); + $contextNode = $dom->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'file')->item(0); + parent::__construct($contextNode); + $this->setName($name); } - protected function printFooter(\PHPUnit\Framework\TestResult $result) : void + public function asDom() : DOMDocument { - if (\count($result) === 0) { - $this->writeWithColor('fg-black, bg-yellow', 'No tests executed!'); - return; - } - if ($result->wasSuccessfulAndNoTestIsRiskyOrSkippedOrIncomplete()) { - $this->writeWithColor('fg-black, bg-green', \sprintf('OK (%d test%s, %d assertion%s)', \count($result), \count($result) === 1 ? '' : 's', $this->numAssertions, $this->numAssertions === 1 ? '' : 's')); - return; - } - $color = 'fg-black, bg-yellow'; - if ($result->wasSuccessful()) { - if ($this->verbose || !$result->allHarmless()) { - $this->write("\n"); - } - $this->writeWithColor($color, 'OK, but incomplete, skipped, or risky tests!'); - } else { - $this->write("\n"); - if ($result->errorCount()) { - $color = 'fg-white, bg-red'; - $this->writeWithColor($color, 'ERRORS!'); - } elseif ($result->failureCount()) { - $color = 'fg-white, bg-red'; - $this->writeWithColor($color, 'FAILURES!'); - } elseif ($result->warningCount()) { - $color = 'fg-black, bg-yellow'; - $this->writeWithColor($color, 'WARNINGS!'); - } - } - $this->writeCountString(\count($result), 'Tests', $color, \true); - $this->writeCountString($this->numAssertions, 'Assertions', $color, \true); - $this->writeCountString($result->errorCount(), 'Errors', $color); - $this->writeCountString($result->failureCount(), 'Failures', $color); - $this->writeCountString($result->warningCount(), 'Warnings', $color); - $this->writeCountString($result->skippedCount(), 'Skipped', $color); - $this->writeCountString($result->notImplementedCount(), 'Incomplete', $color); - $this->writeCountString($result->riskyCount(), 'Risky', $color); - $this->writeWithColor($color, '.'); + return $this->dom(); } - protected function writeProgress(string $progress) : void + public function functionObject($name) : Method { - if ($this->debug) { - return; - } - $this->write($progress); - $this->column++; - $this->numTestsRun++; - if ($this->column == $this->maxColumn || $this->numTestsRun == $this->numTests) { - if ($this->numTestsRun == $this->numTests) { - $this->write(\str_repeat(' ', $this->maxColumn - $this->column)); - } - $this->write(\sprintf(' %' . $this->numTestsWidth . 'd / %' . $this->numTestsWidth . 'd (%3s%%)', $this->numTestsRun, $this->numTests, \floor($this->numTestsRun / $this->numTests * 100))); - if ($this->column == $this->maxColumn) { - $this->writeNewLine(); - } - } + $node = $this->contextNode()->appendChild($this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', 'function')); + return new Method($node, $name); } - protected function writeNewLine() : void + public function classObject($name) : Unit { - $this->column = 0; - $this->write("\n"); + return $this->unitObject('class', $name); } - /** - * Formats a buffer with a specified ANSI color sequence if colors are - * enabled. - */ - protected function colorizeTextBox(string $color, string $buffer) : string + public function traitObject($name) : Unit { - if (!$this->colors) { - return $buffer; - } - $lines = \preg_split('/\\r\\n|\\r|\\n/', $buffer); - $padding = \max(\array_map('\\strlen', $lines)); - $styledLines = []; - foreach ($lines as $line) { - $styledLines[] = \PHPUnit\Util\Color::colorize($color, \str_pad($line, $padding)); - } - return \implode(\PHP_EOL, $styledLines); + return $this->unitObject('trait', $name); } - /** - * Writes a buffer out with a color sequence if colors are enabled. - */ - protected function writeWithColor(string $color, string $buffer, bool $lf = \true) : void + public function source() : Source { - $this->write($this->colorizeTextBox($color, $buffer)); - if ($lf) { - $this->write(\PHP_EOL); + $source = $this->contextNode()->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'source')->item(0); + if (!$source) { + $source = $this->contextNode()->appendChild($this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', 'source')); } + return new Source($source); } - /** - * Writes progress with a color sequence if colors are enabled. - */ - protected function writeProgressWithColor(string $color, string $buffer) : void + private function setName(string $name) : void { - $buffer = $this->colorizeTextBox($color, $buffer); - $this->writeProgress($buffer); + $this->contextNode()->setAttribute('name', basename($name)); + $this->contextNode()->setAttribute('path', dirname($name)); } - private function writeCountString(int $count, string $name, string $color, bool $always = \false) : void + private function unitObject(string $tagName, $name) : Unit { - static $first = \true; - if ($always || $count > 0) { - $this->writeWithColor($color, \sprintf('%s%s: %d', !$first ? ', ' : '', $name, $count), \false); - $first = \false; - } + $node = $this->contextNode()->appendChild($this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', $tagName)); + return new Unit($node, $name); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; -use const PHP_EOL; -use function count; -use function explode; -use function max; -use function preg_replace_callback; -use function str_pad; -use function str_repeat; -use function strlen; -use function wordwrap; -use PHPUnit\Util\Color; -use PHPUnit\SebastianBergmann\Environment\Console; +use DOMDocument; +use DOMElement; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -final class Help +class File { - private const LEFT_MARGIN = ' '; - private const HELP_TEXT = ['Usage' => [['text' => 'phpunit [options] UnitTest.php'], ['text' => 'phpunit [options] ']], 'Code Coverage Options' => [['arg' => '--coverage-clover ', 'desc' => 'Generate code coverage report in Clover XML format'], ['arg' => '--coverage-cobertura ', 'desc' => 'Generate code coverage report in Cobertura XML format'], ['arg' => '--coverage-crap4j ', 'desc' => 'Generate code coverage report in Crap4J XML format'], ['arg' => '--coverage-html ', 'desc' => 'Generate code coverage report in HTML format'], ['arg' => '--coverage-php ', 'desc' => 'Export PHP_CodeCoverage object to file'], ['arg' => '--coverage-text ', 'desc' => 'Generate code coverage report in text format [default: standard output]'], ['arg' => '--coverage-xml ', 'desc' => 'Generate code coverage report in PHPUnit XML format'], ['arg' => '--coverage-cache ', 'desc' => 'Cache static analysis results'], ['arg' => '--warm-coverage-cache', 'desc' => 'Warm static analysis cache'], ['arg' => '--coverage-filter ', 'desc' => 'Include in code coverage analysis'], ['arg' => '--path-coverage', 'desc' => 'Perform path coverage analysis'], ['arg' => '--disable-coverage-ignore', 'desc' => 'Disable annotations for ignoring code coverage'], ['arg' => '--no-coverage', 'desc' => 'Ignore code coverage configuration']], 'Logging Options' => [['arg' => '--log-junit ', 'desc' => 'Log test execution in JUnit XML format to file'], ['arg' => '--log-teamcity ', 'desc' => 'Log test execution in TeamCity format to file'], ['arg' => '--testdox-html ', 'desc' => 'Write agile documentation in HTML format to file'], ['arg' => '--testdox-text ', 'desc' => 'Write agile documentation in Text format to file'], ['arg' => '--testdox-xml ', 'desc' => 'Write agile documentation in XML format to file'], ['arg' => '--reverse-list', 'desc' => 'Print defects in reverse order'], ['arg' => '--no-logging', 'desc' => 'Ignore logging configuration']], 'Test Selection Options' => [['arg' => '--list-suites', 'desc' => 'List available test suites'], ['arg' => '--testsuite ', 'desc' => 'Filter which testsuite to run'], ['arg' => '--list-groups', 'desc' => 'List available test groups'], ['arg' => '--group ', 'desc' => 'Only runs tests from the specified group(s)'], ['arg' => '--exclude-group ', 'desc' => 'Exclude tests from the specified group(s)'], ['arg' => '--covers ', 'desc' => 'Only runs tests annotated with "@covers "'], ['arg' => '--uses ', 'desc' => 'Only runs tests annotated with "@uses "'], ['arg' => '--list-tests', 'desc' => 'List available tests'], ['arg' => '--list-tests-xml ', 'desc' => 'List available tests in XML format'], ['arg' => '--filter ', 'desc' => 'Filter which tests to run'], ['arg' => '--test-suffix ', 'desc' => 'Only search for test in files with specified suffix(es). Default: Test.php,.phpt']], 'Test Execution Options' => [['arg' => '--dont-report-useless-tests', 'desc' => 'Do not report tests that do not test anything'], ['arg' => '--strict-coverage', 'desc' => 'Be strict about @covers annotation usage'], ['arg' => '--strict-global-state', 'desc' => 'Be strict about changes to global state'], ['arg' => '--disallow-test-output', 'desc' => 'Be strict about output during tests'], ['arg' => '--disallow-resource-usage', 'desc' => 'Be strict about resource usage during small tests'], ['arg' => '--enforce-time-limit', 'desc' => 'Enforce time limit based on test size'], ['arg' => '--default-time-limit ', 'desc' => 'Timeout in seconds for tests without @small, @medium or @large'], ['arg' => '--disallow-todo-tests', 'desc' => 'Disallow @todo-annotated tests'], ['spacer' => ''], ['arg' => '--process-isolation', 'desc' => 'Run each test in a separate PHP process'], ['arg' => '--globals-backup', 'desc' => 'Backup and restore $GLOBALS for each test'], ['arg' => '--static-backup', 'desc' => 'Backup and restore static attributes for each test'], ['spacer' => ''], ['arg' => '--colors ', 'desc' => 'Use colors in output ("never", "auto" or "always")'], ['arg' => '--columns ', 'desc' => 'Number of columns to use for progress output'], ['arg' => '--columns max', 'desc' => 'Use maximum number of columns for progress output'], ['arg' => '--stderr', 'desc' => 'Write to STDERR instead of STDOUT'], ['arg' => '--stop-on-defect', 'desc' => 'Stop execution upon first not-passed test'], ['arg' => '--stop-on-error', 'desc' => 'Stop execution upon first error'], ['arg' => '--stop-on-failure', 'desc' => 'Stop execution upon first error or failure'], ['arg' => '--stop-on-warning', 'desc' => 'Stop execution upon first warning'], ['arg' => '--stop-on-risky', 'desc' => 'Stop execution upon first risky test'], ['arg' => '--stop-on-skipped', 'desc' => 'Stop execution upon first skipped test'], ['arg' => '--stop-on-incomplete', 'desc' => 'Stop execution upon first incomplete test'], ['arg' => '--fail-on-incomplete', 'desc' => 'Treat incomplete tests as failures'], ['arg' => '--fail-on-risky', 'desc' => 'Treat risky tests as failures'], ['arg' => '--fail-on-skipped', 'desc' => 'Treat skipped tests as failures'], ['arg' => '--fail-on-warning', 'desc' => 'Treat tests with warnings as failures'], ['arg' => '-v|--verbose', 'desc' => 'Output more verbose information'], ['arg' => '--debug', 'desc' => 'Display debugging information'], ['spacer' => ''], ['arg' => '--repeat ', 'desc' => 'Runs the test(s) repeatedly'], ['arg' => '--teamcity', 'desc' => 'Report test execution progress in TeamCity format'], ['arg' => '--testdox', 'desc' => 'Report test execution progress in TestDox format'], ['arg' => '--testdox-group', 'desc' => 'Only include tests from the specified group(s)'], ['arg' => '--testdox-exclude-group', 'desc' => 'Exclude tests from the specified group(s)'], ['arg' => '--no-interaction', 'desc' => 'Disable TestDox progress animation'], ['arg' => '--printer ', 'desc' => 'TestListener implementation to use'], ['spacer' => ''], ['arg' => '--order-by ', 'desc' => 'Run tests in order: default|defects|duration|no-depends|random|reverse|size'], ['arg' => '--random-order-seed ', 'desc' => 'Use a specific random seed for random order'], ['arg' => '--cache-result', 'desc' => 'Write test results to cache file'], ['arg' => '--do-not-cache-result', 'desc' => 'Do not write test results to cache file']], 'Configuration Options' => [['arg' => '--prepend ', 'desc' => 'A PHP script that is included as early as possible'], ['arg' => '--bootstrap ', 'desc' => 'A PHP script that is included before the tests run'], ['arg' => '-c|--configuration ', 'desc' => 'Read configuration from XML file'], ['arg' => '--no-configuration', 'desc' => 'Ignore default configuration file (phpunit.xml)'], ['arg' => '--extensions ', 'desc' => 'A comma separated list of PHPUnit extensions to load'], ['arg' => '--no-extensions', 'desc' => 'Do not load PHPUnit extensions'], ['arg' => '--include-path ', 'desc' => 'Prepend PHP\'s include_path with given path(s)'], ['arg' => '-d ', 'desc' => 'Sets a php.ini value'], ['arg' => '--cache-result-file ', 'desc' => 'Specify result cache path and filename'], ['arg' => '--generate-configuration', 'desc' => 'Generate configuration file with suggested settings'], ['arg' => '--migrate-configuration', 'desc' => 'Migrate configuration file to current format']], 'Miscellaneous Options' => [['arg' => '-h|--help', 'desc' => 'Prints this usage information'], ['arg' => '--version', 'desc' => 'Prints the version and exits'], ['arg' => '--atleast-version ', 'desc' => 'Checks that version is greater than min and exits'], ['arg' => '--check-version', 'desc' => 'Check whether PHPUnit is the latest version']]]; - /** - * @var int Number of columns required to write the longest option name to the console - */ - private $maxArgLength = 0; /** - * @var int Number of columns left for the description field after padding and option + * @var DOMDocument */ - private $maxDescLength; + private $dom; /** - * @var bool Use color highlights for sections, options and parameters + * @var DOMElement */ - private $hasColor = \false; - public function __construct(?int $width = null, ?bool $withColor = null) + private $contextNode; + public function __construct(DOMElement $context) { - if ($width === null) { - $width = (new \PHPUnit\SebastianBergmann\Environment\Console())->getNumberOfColumns(); - } - if ($withColor === null) { - $this->hasColor = (new \PHPUnit\SebastianBergmann\Environment\Console())->hasColorSupport(); - } else { - $this->hasColor = $withColor; - } - foreach (self::HELP_TEXT as $options) { - foreach ($options as $option) { - if (isset($option['arg'])) { - $this->maxArgLength = \max($this->maxArgLength, isset($option['arg']) ? \strlen($option['arg']) : 0); - } - } - } - $this->maxDescLength = $width - $this->maxArgLength - 4; + $this->dom = $context->ownerDocument; + $this->contextNode = $context; } - /** - * Write the help file to the CLI, adapting width and colors to the console. - */ - public function writeToConsole() : void + public function totals() : Totals { - if ($this->hasColor) { - $this->writeWithColor(); - } else { - $this->writePlaintext(); + $totalsContainer = $this->contextNode->firstChild; + if (!$totalsContainer) { + $totalsContainer = $this->contextNode->appendChild($this->dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'totals')); } + return new Totals($totalsContainer); } - private function writePlaintext() : void + public function lineCoverage(string $line) : Coverage { - foreach (self::HELP_TEXT as $section => $options) { - print "{$section}:" . \PHP_EOL; - if ($section !== 'Usage') { - print \PHP_EOL; - } - foreach ($options as $option) { - if (isset($option['spacer'])) { - print \PHP_EOL; - } - if (isset($option['text'])) { - print self::LEFT_MARGIN . $option['text'] . \PHP_EOL; - } - if (isset($option['arg'])) { - $arg = \str_pad($option['arg'], $this->maxArgLength); - print self::LEFT_MARGIN . $arg . ' ' . $option['desc'] . \PHP_EOL; - } - } - print \PHP_EOL; + $coverage = $this->contextNode->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'coverage')->item(0); + if (!$coverage) { + $coverage = $this->contextNode->appendChild($this->dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'coverage')); } + $lineNode = $coverage->appendChild($this->dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'line')); + return new Coverage($lineNode, $line); } - private function writeWithColor() : void + protected function contextNode() : DOMElement { - foreach (self::HELP_TEXT as $section => $options) { - print \PHPUnit\Util\Color::colorize('fg-yellow', "{$section}:") . \PHP_EOL; - foreach ($options as $option) { - if (isset($option['spacer'])) { - print \PHP_EOL; - } - if (isset($option['text'])) { - print self::LEFT_MARGIN . $option['text'] . \PHP_EOL; - } - if (isset($option['arg'])) { - $arg = \PHPUnit\Util\Color::colorize('fg-green', \str_pad($option['arg'], $this->maxArgLength)); - $arg = \preg_replace_callback('/(<[^>]+>)/', static function ($matches) { - return \PHPUnit\Util\Color::colorize('fg-cyan', $matches[0]); - }, $arg); - $desc = \explode(\PHP_EOL, \wordwrap($option['desc'], $this->maxDescLength, \PHP_EOL)); - print self::LEFT_MARGIN . $arg . ' ' . $desc[0] . \PHP_EOL; - for ($i = 1; $i < \count($desc); $i++) { - print \str_repeat(' ', $this->maxArgLength + 3) . $desc[$i] . \PHP_EOL; - } - } - } - print \PHP_EOL; - } + return $this->contextNode; + } + protected function dom() : DOMDocument + { + return $this->dom; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; -use const PATH_SEPARATOR; -use const PHP_EOL; -use const STDIN; -use function array_keys; -use function assert; -use function class_exists; -use function copy; -use function extension_loaded; -use function fgets; -use function file_get_contents; -use function file_put_contents; -use function getcwd; -use function ini_get; -use function ini_set; -use function is_callable; -use function is_dir; -use function is_file; -use function is_string; -use function printf; -use function realpath; -use function sort; use function sprintf; -use function stream_resolve_include_path; -use function strpos; -use function trim; -use function version_compare; -use PHPUnit\PharIo\Manifest\ApplicationName; -use PHPUnit\PharIo\Manifest\Exception as ManifestException; -use PHPUnit\PharIo\Manifest\ManifestLoader; -use PHPUnit\PharIo\Version\Version as PharIoVersion; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Runner\StandardTestSuiteLoader; -use PHPUnit\Runner\TestSuiteLoader; -use PHPUnit\Runner\Version; -use PHPUnit\TextUI\CliArguments\Builder; -use PHPUnit\TextUI\CliArguments\Configuration; -use PHPUnit\TextUI\CliArguments\Exception as ArgumentsException; -use PHPUnit\TextUI\CliArguments\Mapper; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\FilterMapper; -use PHPUnit\TextUI\XmlConfiguration\Generator; -use PHPUnit\TextUI\XmlConfiguration\Loader; -use PHPUnit\TextUI\XmlConfiguration\Migrator; -use PHPUnit\TextUI\XmlConfiguration\PhpHandler; -use PHPUnit\TextUI\XmlConfiguration\TestSuiteMapper; -use PHPUnit\Util\FileLoader; -use PHPUnit\Util\Filesystem; -use PHPUnit\Util\Printer; -use PHPUnit\Util\TextTestListRenderer; -use PHPUnit\Util\Xml\SchemaDetector; -use PHPUnit\Util\XmlTestListRenderer; -use ReflectionClass; -use ReflectionException; -use PHPUnit\SebastianBergmann\CodeCoverage\Filter; -use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CacheWarmer; -use PHPUnit\SebastianBergmann\FileIterator\Facade as FileIteratorFacade; -use PHPUnit\SebastianBergmann\Timer\Timer; -use Throwable; +use DOMElement; +use DOMNode; +use PHPUnit\SebastianBergmann\CodeCoverage\Percentage; /** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -class Command +final class Totals { /** - * @var array - */ - protected $arguments = []; - /** - * @var array + * @var DOMNode */ - protected $longOptions = []; + private $container; /** - * @var bool + * @var DOMElement */ - private $versionStringPrinted = \false; + private $linesNode; /** - * @psalm-var list + * @var DOMElement */ - private $warnings = []; + private $methodsNode; /** - * @throws Exception + * @var DOMElement */ - public static function main(bool $exit = \true) : int - { - try { - return (new static())->run($_SERVER['argv'], $exit); - } catch (\Throwable $t) { - throw new \PHPUnit\TextUI\Exception($t->getMessage(), (int) $t->getCode(), $t); - } - } + private $functionsNode; /** - * @throws Exception + * @var DOMElement */ - public function run(array $argv, bool $exit = \true) : int - { - $this->handleArguments($argv); - $runner = $this->createRunner(); - if ($this->arguments['test'] instanceof \PHPUnit\Framework\TestSuite) { - $suite = $this->arguments['test']; - } else { - $suite = $runner->getTest($this->arguments['test'], $this->arguments['testSuffixes']); - } - if ($this->arguments['listGroups']) { - return $this->handleListGroups($suite, $exit); - } - if ($this->arguments['listSuites']) { - return $this->handleListSuites($exit); - } - if ($this->arguments['listTests']) { - return $this->handleListTests($suite, $exit); - } - if ($this->arguments['listTestsXml']) { - return $this->handleListTestsXml($suite, $this->arguments['listTestsXml'], $exit); - } - unset($this->arguments['test'], $this->arguments['testFile']); - try { - $result = $runner->run($suite, $this->arguments, $this->warnings, $exit); - } catch (\PHPUnit\TextUI\Exception $e) { - print $e->getMessage() . \PHP_EOL; - } - $return = \PHPUnit\TextUI\TestRunner::FAILURE_EXIT; - if (isset($result) && $result->wasSuccessful()) { - $return = \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; - } elseif (!isset($result) || $result->errorCount() > 0) { - $return = \PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT; - } - if ($exit) { - exit($return); - } - return $return; - } + private $classesNode; /** - * Create a TestRunner, override in subclasses. + * @var DOMElement */ - protected function createRunner() : \PHPUnit\TextUI\TestRunner + private $traitsNode; + public function __construct(DOMElement $container) { - return new \PHPUnit\TextUI\TestRunner($this->arguments['loader']); + $this->container = $container; + $dom = $container->ownerDocument; + $this->linesNode = $dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'lines'); + $this->methodsNode = $dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'methods'); + $this->functionsNode = $dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'functions'); + $this->classesNode = $dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'classes'); + $this->traitsNode = $dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'traits'); + $container->appendChild($this->linesNode); + $container->appendChild($this->methodsNode); + $container->appendChild($this->functionsNode); + $container->appendChild($this->classesNode); + $container->appendChild($this->traitsNode); } - /** - * Handles the command-line arguments. - * - * A child class of PHPUnit\TextUI\Command can hook into the argument - * parsing by adding the switch(es) to the $longOptions array and point to a - * callback method that handles the switch(es) in the child class like this - * - * - * longOptions['my-switch'] = 'myHandler'; - * // my-secondswitch will accept a value - note the equals sign - * $this->longOptions['my-secondswitch='] = 'myOtherHandler'; - * } - * - * // --my-switch -> myHandler() - * protected function myHandler() - * { - * } - * - * // --my-secondswitch foo -> myOtherHandler('foo') - * protected function myOtherHandler ($value) - * { - * } - * - * // You will also need this - the static keyword in the - * // PHPUnit\TextUI\Command will mean that it'll be - * // PHPUnit\TextUI\Command that gets instantiated, - * // not MyCommand - * public static function main($exit = true) - * { - * $command = new static; - * - * return $command->run($_SERVER['argv'], $exit); - * } - * - * } - * - * - * @throws Exception - */ - protected function handleArguments(array $argv) : void + public function container() : DOMNode { - try { - $arguments = (new \PHPUnit\TextUI\CliArguments\Builder())->fromParameters($argv, \array_keys($this->longOptions)); - } catch (\PHPUnit\TextUI\CliArguments\Exception $e) { - $this->exitWithErrorMessage($e->getMessage()); - } - \assert(isset($arguments) && $arguments instanceof \PHPUnit\TextUI\CliArguments\Configuration); - if ($arguments->hasGenerateConfiguration() && $arguments->generateConfiguration()) { - $this->generateConfiguration(); - } - if ($arguments->hasAtLeastVersion()) { - if (\version_compare(\PHPUnit\Runner\Version::id(), $arguments->atLeastVersion(), '>=')) { - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - exit(\PHPUnit\TextUI\TestRunner::FAILURE_EXIT); - } - if ($arguments->hasVersion() && $arguments->version()) { - $this->printVersionString(); - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - if ($arguments->hasCheckVersion() && $arguments->checkVersion()) { - $this->handleVersionCheck(); - } - if ($arguments->hasHelp()) { - $this->showHelp(); - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - if ($arguments->hasUnrecognizedOrderBy()) { - $this->exitWithErrorMessage(\sprintf('unrecognized --order-by option: %s', $arguments->unrecognizedOrderBy())); - } - if ($arguments->hasIniSettings()) { - foreach ($arguments->iniSettings() as $name => $value) { - \ini_set($name, $value); - } - } - if ($arguments->hasIncludePath()) { - \ini_set('include_path', $arguments->includePath() . \PATH_SEPARATOR . \ini_get('include_path')); - } - $this->arguments = (new \PHPUnit\TextUI\CliArguments\Mapper())->mapToLegacyArray($arguments); - $this->handleCustomOptions($arguments->unrecognizedOptions()); - $this->handleCustomTestSuite(); - if (!isset($this->arguments['testSuffixes'])) { - $this->arguments['testSuffixes'] = ['Test.php', '.phpt']; - } - if (!isset($this->arguments['test']) && $arguments->hasArgument()) { - $this->arguments['test'] = \realpath($arguments->argument()); - if ($this->arguments['test'] === \false) { - $this->exitWithErrorMessage(\sprintf('Cannot open file "%s".', $arguments->argument())); - } - } - if ($this->arguments['loader'] !== null) { - $this->arguments['loader'] = $this->handleLoader($this->arguments['loader']); - } - if (isset($this->arguments['configuration'])) { - if (\is_dir($this->arguments['configuration'])) { - $candidate = $this->configurationFileInDirectory($this->arguments['configuration']); - if ($candidate !== null) { - $this->arguments['configuration'] = $candidate; - } - } - } elseif ($this->arguments['useDefaultConfiguration']) { - $candidate = $this->configurationFileInDirectory(\getcwd()); - if ($candidate !== null) { - $this->arguments['configuration'] = $candidate; - } - } - if ($arguments->hasMigrateConfiguration() && $arguments->migrateConfiguration()) { - if (!isset($this->arguments['configuration'])) { - print 'No configuration file found to migrate.' . \PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); - } - $this->migrateConfiguration(\realpath($this->arguments['configuration'])); - } - if (isset($this->arguments['configuration'])) { - try { - $this->arguments['configurationObject'] = (new \PHPUnit\TextUI\XmlConfiguration\Loader())->load($this->arguments['configuration']); - } catch (\Throwable $e) { - print $e->getMessage() . \PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::FAILURE_EXIT); - } - $phpunitConfiguration = $this->arguments['configurationObject']->phpunit(); - (new \PHPUnit\TextUI\XmlConfiguration\PhpHandler())->handle($this->arguments['configurationObject']->php()); - if (isset($this->arguments['bootstrap'])) { - $this->handleBootstrap($this->arguments['bootstrap']); - } elseif ($phpunitConfiguration->hasBootstrap()) { - $this->handleBootstrap($phpunitConfiguration->bootstrap()); - } - if (!isset($this->arguments['stderr'])) { - $this->arguments['stderr'] = $phpunitConfiguration->stderr(); - } - if (!isset($this->arguments['noExtensions']) && $phpunitConfiguration->hasExtensionsDirectory() && \extension_loaded('phar')) { - $this->handleExtensions($phpunitConfiguration->extensionsDirectory()); - } - if (!isset($this->arguments['columns'])) { - $this->arguments['columns'] = $phpunitConfiguration->columns(); - } - if (!isset($this->arguments['printer']) && $phpunitConfiguration->hasPrinterClass()) { - $file = $phpunitConfiguration->hasPrinterFile() ? $phpunitConfiguration->printerFile() : ''; - $this->arguments['printer'] = $this->handlePrinter($phpunitConfiguration->printerClass(), $file); - } - if ($phpunitConfiguration->hasTestSuiteLoaderClass()) { - $file = $phpunitConfiguration->hasTestSuiteLoaderFile() ? $phpunitConfiguration->testSuiteLoaderFile() : ''; - $this->arguments['loader'] = $this->handleLoader($phpunitConfiguration->testSuiteLoaderClass(), $file); - } - if (!isset($this->arguments['testsuite']) && $phpunitConfiguration->hasDefaultTestSuite()) { - $this->arguments['testsuite'] = $phpunitConfiguration->defaultTestSuite(); - } - if (!isset($this->arguments['test'])) { - $this->arguments['test'] = (new \PHPUnit\TextUI\XmlConfiguration\TestSuiteMapper())->map($this->arguments['configurationObject']->testSuite(), $this->arguments['testsuite'] ?? ''); - } - } elseif (isset($this->arguments['bootstrap'])) { - $this->handleBootstrap($this->arguments['bootstrap']); - } - if (isset($this->arguments['printer']) && \is_string($this->arguments['printer'])) { - $this->arguments['printer'] = $this->handlePrinter($this->arguments['printer']); - } - if (isset($this->arguments['configurationObject'], $this->arguments['warmCoverageCache'])) { - $this->handleWarmCoverageCache($this->arguments['configurationObject']); - } - if (!isset($this->arguments['test'])) { - $this->showHelp(); - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); - } + return $this->container; } - /** - * Handles the loading of the PHPUnit\Runner\TestSuiteLoader implementation. - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 - */ - protected function handleLoader(string $loaderClass, string $loaderFile = '') : ?\PHPUnit\Runner\TestSuiteLoader + public function setNumLines(int $loc, int $cloc, int $ncloc, int $executable, int $executed) : void { - $this->warnings[] = 'Using a custom test suite loader is deprecated'; - if (!\class_exists($loaderClass, \false)) { - if ($loaderFile == '') { - $loaderFile = \PHPUnit\Util\Filesystem::classNameToFilename($loaderClass); - } - $loaderFile = \stream_resolve_include_path($loaderFile); - if ($loaderFile) { - require $loaderFile; - } - } - if (\class_exists($loaderClass, \false)) { - try { - $class = new \ReflectionClass($loaderClass); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\TextUI\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($class->implementsInterface(\PHPUnit\Runner\TestSuiteLoader::class) && $class->isInstantiable()) { - $object = $class->newInstance(); - \assert($object instanceof \PHPUnit\Runner\TestSuiteLoader); - return $object; - } - } - if ($loaderClass == \PHPUnit\Runner\StandardTestSuiteLoader::class) { - return null; - } - $this->exitWithErrorMessage(\sprintf('Could not use "%s" as loader.', $loaderClass)); - return null; + $this->linesNode->setAttribute('total', (string) $loc); + $this->linesNode->setAttribute('comments', (string) $cloc); + $this->linesNode->setAttribute('code', (string) $ncloc); + $this->linesNode->setAttribute('executable', (string) $executable); + $this->linesNode->setAttribute('executed', (string) $executed); + $this->linesNode->setAttribute('percent', $executable === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($executed, $executable)->asFloat())); } - /** - * Handles the loading of the PHPUnit\Util\Printer implementation. - * - * @return null|Printer|string - */ - protected function handlePrinter(string $printerClass, string $printerFile = '') + public function setNumClasses(int $count, int $tested) : void { - if (!\class_exists($printerClass, \false)) { - if ($printerFile === '') { - $printerFile = \PHPUnit\Util\Filesystem::classNameToFilename($printerClass); - } - $printerFile = \stream_resolve_include_path($printerFile); - if ($printerFile) { - require $printerFile; - } - } - if (!\class_exists($printerClass)) { - $this->exitWithErrorMessage(\sprintf('Could not use "%s" as printer: class does not exist', $printerClass)); - } - try { - $class = new \ReflectionClass($printerClass); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\TextUI\Exception($e->getMessage(), (int) $e->getCode(), $e); - // @codeCoverageIgnoreEnd - } - if (!$class->implementsInterface(\PHPUnit\TextUI\ResultPrinter::class)) { - $this->exitWithErrorMessage(\sprintf('Could not use "%s" as printer: class does not implement %s', $printerClass, \PHPUnit\TextUI\ResultPrinter::class)); - } - if (!$class->isInstantiable()) { - $this->exitWithErrorMessage(\sprintf('Could not use "%s" as printer: class cannot be instantiated', $printerClass)); - } - if ($class->isSubclassOf(\PHPUnit\TextUI\ResultPrinter::class)) { - return $printerClass; - } - $outputStream = isset($this->arguments['stderr']) ? 'php://stderr' : null; - return $class->newInstance($outputStream); + $this->classesNode->setAttribute('count', (string) $count); + $this->classesNode->setAttribute('tested', (string) $tested); + $this->classesNode->setAttribute('percent', $count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat())); } - /** - * Loads a bootstrap file. - */ - protected function handleBootstrap(string $filename) : void + public function setNumTraits(int $count, int $tested) : void { - try { - \PHPUnit\Util\FileLoader::checkAndLoad($filename); - } catch (\Throwable $t) { - $this->exitWithErrorMessage($t->getMessage()); - } + $this->traitsNode->setAttribute('count', (string) $count); + $this->traitsNode->setAttribute('tested', (string) $tested); + $this->traitsNode->setAttribute('percent', $count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat())); } - protected function handleVersionCheck() : void + public function setNumMethods(int $count, int $tested) : void { - $this->printVersionString(); - $latestVersion = \file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit'); - $isOutdated = \version_compare($latestVersion, \PHPUnit\Runner\Version::id(), '>'); - if ($isOutdated) { - \printf('You are not using the latest version of PHPUnit.' . \PHP_EOL . 'The latest version is PHPUnit %s.' . \PHP_EOL, $latestVersion); - } else { - print 'You are using the latest version of PHPUnit.' . \PHP_EOL; - } - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + $this->methodsNode->setAttribute('count', (string) $count); + $this->methodsNode->setAttribute('tested', (string) $tested); + $this->methodsNode->setAttribute('percent', $count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat())); } - /** - * Show the help message. - */ - protected function showHelp() : void + public function setNumFunctions(int $count, int $tested) : void { - $this->printVersionString(); - (new \PHPUnit\TextUI\Help())->writeToConsole(); + $this->functionsNode->setAttribute('count', (string) $count); + $this->functionsNode->setAttribute('tested', (string) $tested); + $this->functionsNode->setAttribute('percent', $count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat())); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; + +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Tests +{ + private $contextNode; + private $codeMap = [ + -1 => 'UNKNOWN', + // PHPUnit_Runner_BaseTestRunner::STATUS_UNKNOWN + 0 => 'PASSED', + // PHPUnit_Runner_BaseTestRunner::STATUS_PASSED + 1 => 'SKIPPED', + // PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED + 2 => 'INCOMPLETE', + // PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE + 3 => 'FAILURE', + // PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE + 4 => 'ERROR', + // PHPUnit_Runner_BaseTestRunner::STATUS_ERROR + 5 => 'RISKY', + // PHPUnit_Runner_BaseTestRunner::STATUS_RISKY + 6 => 'WARNING', + ]; + public function __construct(DOMElement $context) + { + $this->contextNode = $context; + } + public function addTest(string $test, array $result) : void + { + $node = $this->contextNode->appendChild($this->contextNode->ownerDocument->createElementNS('https://schema.phpunit.de/coverage/1.0', 'test')); + $node->setAttribute('name', $test); + $node->setAttribute('size', $result['size']); + $node->setAttribute('result', (string) $result['status']); + $node->setAttribute('status', $this->codeMap[(int) $result['status']]); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; + +use DOMDocument; +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +abstract class Node +{ /** - * Custom callback for test suite discovery. + * @var DOMDocument */ - protected function handleCustomTestSuite() : void + private $dom; + /** + * @var DOMElement + */ + private $contextNode; + public function __construct(DOMElement $context) { + $this->setContextNode($context); } - private function printVersionString() : void + public function dom() : DOMDocument { - if ($this->versionStringPrinted) { - return; + return $this->dom; + } + public function totals() : Totals + { + $totalsContainer = $this->contextNode()->firstChild; + if (!$totalsContainer) { + $totalsContainer = $this->contextNode()->appendChild($this->dom->createElementNS('https://schema.phpunit.de/coverage/1.0', 'totals')); } - print \PHPUnit\Runner\Version::getVersionString() . \PHP_EOL . \PHP_EOL; - $this->versionStringPrinted = \true; + return new Totals($totalsContainer); } - private function exitWithErrorMessage(string $message) : void + public function addDirectory(string $name) : Directory { - $this->printVersionString(); - print $message . \PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::FAILURE_EXIT); + $dirNode = $this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', 'directory'); + $dirNode->setAttribute('name', $name); + $this->contextNode()->appendChild($dirNode); + return new Directory($dirNode); } - private function handleExtensions(string $directory) : void + public function addFile(string $name, string $href) : File { - foreach ((new \PHPUnit\SebastianBergmann\FileIterator\Facade())->getFilesAsArray($directory, '.phar') as $file) { - if (!\is_file('phar://' . $file . '/manifest.xml')) { - $this->arguments['notLoadedExtensions'][] = $file . ' is not an extension for PHPUnit'; - continue; - } - try { - $applicationName = new \PHPUnit\PharIo\Manifest\ApplicationName('phpunit/phpunit'); - $version = new \PHPUnit\PharIo\Version\Version(\PHPUnit\Runner\Version::series()); - $manifest = \PHPUnit\PharIo\Manifest\ManifestLoader::fromFile('phar://' . $file . '/manifest.xml'); - if (!$manifest->isExtensionFor($applicationName)) { - $this->arguments['notLoadedExtensions'][] = $file . ' is not an extension for PHPUnit'; - continue; - } - if (!$manifest->isExtensionFor($applicationName, $version)) { - $this->arguments['notLoadedExtensions'][] = $file . ' is not compatible with this version of PHPUnit'; - continue; - } - } catch (\PHPUnit\PharIo\Manifest\Exception $e) { - $this->arguments['notLoadedExtensions'][] = $file . ': ' . $e->getMessage(); - continue; - } - require $file; - $this->arguments['loadedExtensions'][] = $manifest->getName()->asString() . ' ' . $manifest->getVersion()->getVersionString(); - } + $fileNode = $this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', 'file'); + $fileNode->setAttribute('name', $name); + $fileNode->setAttribute('href', $href); + $this->contextNode()->appendChild($fileNode); + return new File($fileNode); } - private function handleListGroups(\PHPUnit\Framework\TestSuite $suite, bool $exit) : int + protected function setContextNode(DOMElement $context) : void { - $this->printVersionString(); - print 'Available test group(s):' . \PHP_EOL; - $groups = $suite->getGroups(); - \sort($groups); - foreach ($groups as $group) { - if (\strpos($group, '__phpunit_') === 0) { - continue; - } - \printf(' - %s' . \PHP_EOL, $group); - } - if ($exit) { - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; + $this->dom = $context->ownerDocument; + $this->contextNode = $context; } - /** - * @throws \PHPUnit\Framework\Exception - * @throws \PHPUnit\TextUI\XmlConfiguration\Exception - */ - private function handleListSuites(bool $exit) : int + protected function contextNode() : DOMElement { - $this->printVersionString(); - print 'Available test suite(s):' . \PHP_EOL; - foreach ($this->arguments['configurationObject']->testSuite() as $testSuite) { - \printf(' - %s' . \PHP_EOL, $testSuite->name()); - } - if ($exit) { - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; + return $this->contextNode; } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - private function handleListTests(\PHPUnit\Framework\TestSuite $suite, bool $exit) : int +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; + +use DOMDocument; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Project extends Node +{ + public function __construct(string $directory) { - $this->printVersionString(); - $renderer = new \PHPUnit\Util\TextTestListRenderer(); - print $renderer->render($suite); - if ($exit) { - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; + $this->init(); + $this->setProjectSourceDirectory($directory); } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - private function handleListTestsXml(\PHPUnit\Framework\TestSuite $suite, string $target, bool $exit) : int + public function projectSourceDirectory() : string { - $this->printVersionString(); - $renderer = new \PHPUnit\Util\XmlTestListRenderer(); - \file_put_contents($target, $renderer->render($suite)); - \printf('Wrote list of tests that would have been run to %s' . \PHP_EOL, $target); - if ($exit) { - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); - } - return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; + return $this->contextNode()->getAttribute('source'); } - private function generateConfiguration() : void + public function buildInformation() : BuildInformation { - $this->printVersionString(); - print 'Generating phpunit.xml in ' . \getcwd() . \PHP_EOL . \PHP_EOL; - print 'Bootstrap script (relative to path shown above; default: vendor/autoload.php): '; - $bootstrapScript = \trim(\fgets(\STDIN)); - print 'Tests directory (relative to path shown above; default: tests): '; - $testsDirectory = \trim(\fgets(\STDIN)); - print 'Source directory (relative to path shown above; default: src): '; - $src = \trim(\fgets(\STDIN)); - print 'Cache directory (relative to path shown above; default: .phpunit.cache): '; - $cacheDirectory = \trim(\fgets(\STDIN)); - if ($bootstrapScript === '') { - $bootstrapScript = 'vendor/autoload.php'; - } - if ($testsDirectory === '') { - $testsDirectory = 'tests'; - } - if ($src === '') { - $src = 'src'; - } - if ($cacheDirectory === '') { - $cacheDirectory = '.phpunit.cache'; + $buildNode = $this->dom()->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'build')->item(0); + if (!$buildNode) { + $buildNode = $this->dom()->documentElement->appendChild($this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', 'build')); } - $generator = new \PHPUnit\TextUI\XmlConfiguration\Generator(); - \file_put_contents('phpunit.xml', $generator->generateDefaultConfiguration(\PHPUnit\Runner\Version::series(), $bootstrapScript, $testsDirectory, $src, $cacheDirectory)); - print \PHP_EOL . 'Generated phpunit.xml in ' . \getcwd() . '.' . \PHP_EOL; - print 'Make sure to exclude the ' . $cacheDirectory . ' directory from version control.' . \PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + return new BuildInformation($buildNode); } - private function migrateConfiguration(string $filename) : void + public function tests() : Tests { - $this->printVersionString(); - if (!(new \PHPUnit\Util\Xml\SchemaDetector())->detect($filename)->detected()) { - print $filename . ' does not need to be migrated.' . \PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); - } - \copy($filename, $filename . '.bak'); - print 'Created backup: ' . $filename . '.bak' . \PHP_EOL; - try { - \file_put_contents($filename, (new \PHPUnit\TextUI\XmlConfiguration\Migrator())->migrate($filename)); - print 'Migrated configuration: ' . $filename . \PHP_EOL; - } catch (\Throwable $t) { - print 'Migration failed: ' . $t->getMessage() . \PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + $testsNode = $this->contextNode()->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'tests')->item(0); + if (!$testsNode) { + $testsNode = $this->contextNode()->appendChild($this->dom()->createElementNS('https://schema.phpunit.de/coverage/1.0', 'tests')); } - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + return new Tests($testsNode); } - private function handleCustomOptions(array $unrecognizedOptions) : void + public function asDom() : DOMDocument { - foreach ($unrecognizedOptions as $name => $value) { - if (isset($this->longOptions[$name])) { - $handler = $this->longOptions[$name]; - } - $name .= '='; - if (isset($this->longOptions[$name])) { - $handler = $this->longOptions[$name]; - } - if (isset($handler) && \is_callable([$this, $handler])) { - $this->{$handler}($value); - unset($handler); - } - } + return $this->dom(); } - private function handleWarmCoverageCache(\PHPUnit\TextUI\XmlConfiguration\Configuration $configuration) : void + private function init() : void { - $this->printVersionString(); - if (isset($this->arguments['coverageCacheDirectory'])) { - $cacheDirectory = $this->arguments['coverageCacheDirectory']; - } elseif ($configuration->codeCoverage()->hasCacheDirectory()) { - $cacheDirectory = $configuration->codeCoverage()->cacheDirectory()->path(); - } else { - print 'Cache for static analysis has not been configured' . \PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); - } - $filter = new \PHPUnit\SebastianBergmann\CodeCoverage\Filter(); - if ($configuration->codeCoverage()->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) { - (new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\FilterMapper())->map($filter, $configuration->codeCoverage()); - } elseif (isset($this->arguments['coverageFilter'])) { - if (!\is_array($this->arguments['coverageFilter'])) { - $coverageFilterDirectories = [$this->arguments['coverageFilter']]; - } else { - $coverageFilterDirectories = $this->arguments['coverageFilter']; - } - foreach ($coverageFilterDirectories as $coverageFilterDirectory) { - $filter->includeDirectory($coverageFilterDirectory); - } - } else { - print 'Filter for code coverage has not been configured' . \PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); - } - $timer = new \PHPUnit\SebastianBergmann\Timer\Timer(); - $timer->start(); - print 'Warming cache for static analysis ... '; - (new \PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CacheWarmer())->warmCache($cacheDirectory, !$configuration->codeCoverage()->disableCodeCoverageIgnore(), $configuration->codeCoverage()->ignoreDeprecatedCodeUnits(), $filter); - print 'done [' . $timer->stop()->asString() . ']' . \PHP_EOL; - exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + $dom = new DOMDocument(); + $dom->loadXML(''); + $this->setContextNode($dom->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'project')->item(0)); } - private function configurationFileInDirectory(string $directory) : ?string + private function setProjectSourceDirectory(string $name) : void { - $candidates = [$directory . '/phpunit.xml', $directory . '/phpunit.xml.dist']; - foreach ($candidates as $candidate) { - if (\is_file($candidate)) { - return \realpath($candidate); - } - } - return null; + $this->contextNode()->setAttribute('source', $name); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; -use RuntimeException; +use DOMElement; +use PHPUnit\SebastianBergmann\CodeCoverage\ReportAlreadyFinalizedException; +use XMLWriter; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -final class Exception extends \RuntimeException implements \PHPUnit\Exception +final class Coverage { + /** + * @var XMLWriter + */ + private $writer; + /** + * @var DOMElement + */ + private $contextNode; + /** + * @var bool + */ + private $finalized = \false; + public function __construct(DOMElement $context, string $line) + { + $this->contextNode = $context; + $this->writer = new XMLWriter(); + $this->writer->openMemory(); + $this->writer->startElementNS(null, $context->nodeName, 'https://schema.phpunit.de/coverage/1.0'); + $this->writer->writeAttribute('nr', $line); + } + /** + * @throws ReportAlreadyFinalizedException + */ + public function addTest(string $test) : void + { + if ($this->finalized) { + throw new ReportAlreadyFinalizedException(); + } + $this->writer->startElement('covered'); + $this->writer->writeAttribute('by', $test); + $this->writer->endElement(); + } + public function finalize() : void + { + $this->writer->endElement(); + $fragment = $this->contextNode->ownerDocument->createDocumentFragment(); + $fragment->appendXML($this->writer->outputMemory()); + $this->contextNode->parentNode->replaceChild($fragment, $this->contextNode); + $this->finalized = \true; + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\CliArguments; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; -use function array_map; -use function array_merge; -use function class_exists; -use function explode; -use function is_numeric; -use function str_replace; -use PHPUnit\Runner\TestSuiteSorter; -use PHPUnit\TextUI\DefaultResultPrinter; -use PHPUnit\TextUI\XmlConfiguration\Extension; -use PHPUnit\Util\Log\TeamCity; -use PHPUnit\Util\TestDox\CliTestDoxPrinter; -use PHPUnit\SebastianBergmann\CliParser\Exception as CliParserException; -use PHPUnit\SebastianBergmann\CliParser\Parser as CliParser; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -final class Builder +final class Directory extends Node { - private const LONG_OPTIONS = ['atleast-version=', 'prepend=', 'bootstrap=', 'cache-result', 'do-not-cache-result', 'cache-result-file=', 'check-version', 'colors==', 'columns=', 'configuration=', 'coverage-cache=', 'warm-coverage-cache', 'coverage-filter=', 'coverage-clover=', 'coverage-cobertura=', 'coverage-crap4j=', 'coverage-html=', 'coverage-php=', 'coverage-text==', 'coverage-xml=', 'path-coverage', 'debug', 'disallow-test-output', 'disallow-resource-usage', 'disallow-todo-tests', 'default-time-limit=', 'enforce-time-limit', 'exclude-group=', 'extensions=', 'filter=', 'generate-configuration', 'globals-backup', 'group=', 'covers=', 'uses=', 'help', 'resolve-dependencies', 'ignore-dependencies', 'include-path=', 'list-groups', 'list-suites', 'list-tests', 'list-tests-xml=', 'loader=', 'log-junit=', 'log-teamcity=', 'migrate-configuration', 'no-configuration', 'no-coverage', 'no-logging', 'no-interaction', 'no-extensions', 'order-by=', 'printer=', 'process-isolation', 'repeat=', 'dont-report-useless-tests', 'random-order', 'random-order-seed=', 'reverse-order', 'reverse-list', 'static-backup', 'stderr', 'stop-on-defect', 'stop-on-error', 'stop-on-failure', 'stop-on-warning', 'stop-on-incomplete', 'stop-on-risky', 'stop-on-skipped', 'fail-on-empty-test-suite', 'fail-on-incomplete', 'fail-on-risky', 'fail-on-skipped', 'fail-on-warning', 'strict-coverage', 'disable-coverage-ignore', 'strict-global-state', 'teamcity', 'testdox', 'testdox-group=', 'testdox-exclude-group=', 'testdox-html=', 'testdox-text=', 'testdox-xml=', 'test-suffix=', 'testsuite=', 'verbose', 'version', 'whitelist=', 'dump-xdebug-filter=']; - private const SHORT_OPTIONS = 'd:c:hv'; - public function fromParameters(array $parameters, array $additionalLongOptions) : \PHPUnit\TextUI\CliArguments\Configuration - { - try { - $options = (new \PHPUnit\SebastianBergmann\CliParser\Parser())->parse($parameters, self::SHORT_OPTIONS, \array_merge(self::LONG_OPTIONS, $additionalLongOptions)); - } catch (\PHPUnit\SebastianBergmann\CliParser\Exception $e) { - throw new \PHPUnit\TextUI\CliArguments\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - $argument = null; - $atLeastVersion = null; - $backupGlobals = null; - $backupStaticAttributes = null; - $beStrictAboutChangesToGlobalState = null; - $beStrictAboutResourceUsageDuringSmallTests = null; - $bootstrap = null; - $cacheResult = null; - $cacheResultFile = null; - $checkVersion = null; - $colors = null; - $columns = null; - $configuration = null; - $coverageCacheDirectory = null; - $warmCoverageCache = null; - $coverageFilter = null; - $coverageClover = null; - $coverageCobertura = null; - $coverageCrap4J = null; - $coverageHtml = null; - $coveragePhp = null; - $coverageText = null; - $coverageTextShowUncoveredFiles = null; - $coverageTextShowOnlySummary = null; - $coverageXml = null; - $pathCoverage = null; - $debug = null; - $defaultTimeLimit = null; - $disableCodeCoverageIgnore = null; - $disallowTestOutput = null; - $disallowTodoAnnotatedTests = null; - $enforceTimeLimit = null; - $excludeGroups = null; - $executionOrder = null; - $executionOrderDefects = null; - $extensions = []; - $unavailableExtensions = []; - $failOnEmptyTestSuite = null; - $failOnIncomplete = null; - $failOnRisky = null; - $failOnSkipped = null; - $failOnWarning = null; - $filter = null; - $generateConfiguration = null; - $migrateConfiguration = null; - $groups = null; - $testsCovering = null; - $testsUsing = null; - $help = null; - $includePath = null; - $iniSettings = []; - $junitLogfile = null; - $listGroups = null; - $listSuites = null; - $listTests = null; - $listTestsXml = null; - $loader = null; - $noCoverage = null; - $noExtensions = null; - $noInteraction = null; - $noLogging = null; - $printer = null; - $processIsolation = null; - $randomOrderSeed = null; - $repeat = null; - $reportUselessTests = null; - $resolveDependencies = null; - $reverseList = null; - $stderr = null; - $strictCoverage = null; - $stopOnDefect = null; - $stopOnError = null; - $stopOnFailure = null; - $stopOnIncomplete = null; - $stopOnRisky = null; - $stopOnSkipped = null; - $stopOnWarning = null; - $teamcityLogfile = null; - $testdoxExcludeGroups = null; - $testdoxGroups = null; - $testdoxHtmlFile = null; - $testdoxTextFile = null; - $testdoxXmlFile = null; - $testSuffixes = null; - $testSuite = null; - $unrecognizedOptions = []; - $unrecognizedOrderBy = null; - $useDefaultConfiguration = null; - $verbose = null; - $version = null; - $xdebugFilterFile = null; - if (isset($options[1][0])) { - $argument = $options[1][0]; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml; + +use DOMElement; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Unit +{ + /** + * @var DOMElement + */ + private $contextNode; + public function __construct(DOMElement $context, string $name) + { + $this->contextNode = $context; + $this->setName($name); + } + public function setLines(int $start, int $executable, int $executed) : void + { + $this->contextNode->setAttribute('start', (string) $start); + $this->contextNode->setAttribute('executable', (string) $executable); + $this->contextNode->setAttribute('executed', (string) $executed); + } + public function setCrap(float $crap) : void + { + $this->contextNode->setAttribute('crap', (string) $crap); + } + public function setNamespace(string $namespace) : void + { + $node = $this->contextNode->getElementsByTagNameNS('https://schema.phpunit.de/coverage/1.0', 'namespace')->item(0); + if (!$node) { + $node = $this->contextNode->appendChild($this->contextNode->ownerDocument->createElementNS('https://schema.phpunit.de/coverage/1.0', 'namespace')); } - foreach ($options[0] as $option) { - switch ($option[0]) { - case '--colors': - $colors = $option[1] ?: \PHPUnit\TextUI\DefaultResultPrinter::COLOR_AUTO; - break; - case '--bootstrap': - $bootstrap = $option[1]; - break; - case '--cache-result': - $cacheResult = \true; - break; - case '--do-not-cache-result': - $cacheResult = \false; - break; - case '--cache-result-file': - $cacheResultFile = $option[1]; - break; - case '--columns': - if (\is_numeric($option[1])) { - $columns = (int) $option[1]; - } elseif ($option[1] === 'max') { - $columns = 'max'; - } - break; - case 'c': - case '--configuration': - $configuration = $option[1]; - break; - case '--coverage-cache': - $coverageCacheDirectory = $option[1]; - break; - case '--warm-coverage-cache': - $warmCoverageCache = \true; - break; - case '--coverage-clover': - $coverageClover = $option[1]; - break; - case '--coverage-cobertura': - $coverageCobertura = $option[1]; - break; - case '--coverage-crap4j': - $coverageCrap4J = $option[1]; - break; - case '--coverage-html': - $coverageHtml = $option[1]; - break; - case '--coverage-php': - $coveragePhp = $option[1]; - break; - case '--coverage-text': - if ($option[1] === null) { - $option[1] = 'php://stdout'; - } - $coverageText = $option[1]; - $coverageTextShowUncoveredFiles = \false; - $coverageTextShowOnlySummary = \false; - break; - case '--coverage-xml': - $coverageXml = $option[1]; - break; - case '--path-coverage': - $pathCoverage = \true; - break; - case 'd': - $tmp = \explode('=', $option[1]); - if (isset($tmp[0])) { - if (isset($tmp[1])) { - $iniSettings[$tmp[0]] = $tmp[1]; - } else { - $iniSettings[$tmp[0]] = '1'; - } + $node->setAttribute('name', $namespace); + } + public function addMethod(string $name) : Method + { + $node = $this->contextNode->appendChild($this->contextNode->ownerDocument->createElementNS('https://schema.phpunit.de/coverage/1.0', 'method')); + return new Method($node, $name); + } + private function setName(string $name) : void + { + $this->contextNode->setAttribute('name', $name); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report; + +use function count; +use function dirname; +use function file_put_contents; +use function is_string; +use function ksort; +use function max; +use function range; +use function time; +use DOMDocument; +use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; +use PHPUnit\SebastianBergmann\CodeCoverage\Directory; +use PHPUnit\SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\File; +final class Clover +{ + /** + * @throws WriteOperationFailedException + */ + public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null) : string + { + $time = (string) time(); + $xmlDocument = new DOMDocument('1.0', 'UTF-8'); + $xmlDocument->formatOutput = \true; + $xmlCoverage = $xmlDocument->createElement('coverage'); + $xmlCoverage->setAttribute('generated', $time); + $xmlDocument->appendChild($xmlCoverage); + $xmlProject = $xmlDocument->createElement('project'); + $xmlProject->setAttribute('timestamp', $time); + if (is_string($name)) { + $xmlProject->setAttribute('name', $name); + } + $xmlCoverage->appendChild($xmlProject); + $packages = []; + $report = $coverage->getReport(); + foreach ($report as $item) { + if (!$item instanceof File) { + continue; + } + /* @var File $item */ + $xmlFile = $xmlDocument->createElement('file'); + $xmlFile->setAttribute('name', $item->pathAsString()); + $classes = $item->classesAndTraits(); + $coverageData = $item->lineCoverageData(); + $lines = []; + $namespace = 'global'; + foreach ($classes as $className => $class) { + $classStatements = 0; + $coveredClassStatements = 0; + $coveredMethods = 0; + $classMethods = 0; + foreach ($class['methods'] as $methodName => $method) { + if ($method['executableLines'] == 0) { + continue; } - break; - case '--debug': - $debug = \true; - break; - case 'h': - case '--help': - $help = \true; - break; - case '--filter': - $filter = $option[1]; - break; - case '--testsuite': - $testSuite = $option[1]; - break; - case '--generate-configuration': - $generateConfiguration = \true; - break; - case '--migrate-configuration': - $migrateConfiguration = \true; - break; - case '--group': - $groups = \explode(',', $option[1]); - break; - case '--exclude-group': - $excludeGroups = \explode(',', $option[1]); - break; - case '--covers': - $testsCovering = \array_map('strtolower', \explode(',', $option[1])); - break; - case '--uses': - $testsUsing = \array_map('strtolower', \explode(',', $option[1])); - break; - case '--test-suffix': - $testSuffixes = \explode(',', $option[1]); - break; - case '--include-path': - $includePath = $option[1]; - break; - case '--list-groups': - $listGroups = \true; - break; - case '--list-suites': - $listSuites = \true; - break; - case '--list-tests': - $listTests = \true; - break; - case '--list-tests-xml': - $listTestsXml = $option[1]; - break; - case '--printer': - $printer = $option[1]; - break; - case '--loader': - $loader = $option[1]; - break; - case '--log-junit': - $junitLogfile = $option[1]; - break; - case '--log-teamcity': - $teamcityLogfile = $option[1]; - break; - case '--order-by': - foreach (\explode(',', $option[1]) as $order) { - switch ($order) { - case 'default': - $executionOrder = \PHPUnit\Runner\TestSuiteSorter::ORDER_DEFAULT; - $executionOrderDefects = \PHPUnit\Runner\TestSuiteSorter::ORDER_DEFAULT; - $resolveDependencies = \true; - break; - case 'defects': - $executionOrderDefects = \PHPUnit\Runner\TestSuiteSorter::ORDER_DEFECTS_FIRST; - break; - case 'depends': - $resolveDependencies = \true; - break; - case 'duration': - $executionOrder = \PHPUnit\Runner\TestSuiteSorter::ORDER_DURATION; - break; - case 'no-depends': - $resolveDependencies = \false; - break; - case 'random': - $executionOrder = \PHPUnit\Runner\TestSuiteSorter::ORDER_RANDOMIZED; - break; - case 'reverse': - $executionOrder = \PHPUnit\Runner\TestSuiteSorter::ORDER_REVERSED; - break; - case 'size': - $executionOrder = \PHPUnit\Runner\TestSuiteSorter::ORDER_SIZE; - break; - default: - $unrecognizedOrderBy = $order; - } + $classMethods++; + $classStatements += $method['executableLines']; + $coveredClassStatements += $method['executedLines']; + if ($method['coverage'] == 100) { + $coveredMethods++; } - break; - case '--process-isolation': - $processIsolation = \true; - break; - case '--repeat': - $repeat = (int) $option[1]; - break; - case '--stderr': - $stderr = \true; - break; - case '--stop-on-defect': - $stopOnDefect = \true; - break; - case '--stop-on-error': - $stopOnError = \true; - break; - case '--stop-on-failure': - $stopOnFailure = \true; - break; - case '--stop-on-warning': - $stopOnWarning = \true; - break; - case '--stop-on-incomplete': - $stopOnIncomplete = \true; - break; - case '--stop-on-risky': - $stopOnRisky = \true; - break; - case '--stop-on-skipped': - $stopOnSkipped = \true; - break; - case '--fail-on-empty-test-suite': - $failOnEmptyTestSuite = \true; - break; - case '--fail-on-incomplete': - $failOnIncomplete = \true; - break; - case '--fail-on-risky': - $failOnRisky = \true; - break; - case '--fail-on-skipped': - $failOnSkipped = \true; - break; - case '--fail-on-warning': - $failOnWarning = \true; - break; - case '--teamcity': - $printer = \PHPUnit\Util\Log\TeamCity::class; - break; - case '--testdox': - $printer = \PHPUnit\Util\TestDox\CliTestDoxPrinter::class; - break; - case '--testdox-group': - $testdoxGroups = \explode(',', $option[1]); - break; - case '--testdox-exclude-group': - $testdoxExcludeGroups = \explode(',', $option[1]); - break; - case '--testdox-html': - $testdoxHtmlFile = $option[1]; - break; - case '--testdox-text': - $testdoxTextFile = $option[1]; - break; - case '--testdox-xml': - $testdoxXmlFile = $option[1]; - break; - case '--no-configuration': - $useDefaultConfiguration = \false; - break; - case '--extensions': - foreach (\explode(',', $option[1]) as $extensionClass) { - if (!\class_exists($extensionClass)) { - $unavailableExtensions[] = $extensionClass; - continue; + $methodCount = 0; + foreach (range($method['startLine'], $method['endLine']) as $line) { + if (isset($coverageData[$line]) && $coverageData[$line] !== null) { + $methodCount = max($methodCount, count($coverageData[$line])); } - $extensions[] = new \PHPUnit\TextUI\XmlConfiguration\Extension($extensionClass, '', []); - } - break; - case '--no-extensions': - $noExtensions = \true; - break; - case '--no-coverage': - $noCoverage = \true; - break; - case '--no-logging': - $noLogging = \true; - break; - case '--no-interaction': - $noInteraction = \true; - break; - case '--globals-backup': - $backupGlobals = \true; - break; - case '--static-backup': - $backupStaticAttributes = \true; - break; - case 'v': - case '--verbose': - $verbose = \true; - break; - case '--atleast-version': - $atLeastVersion = $option[1]; - break; - case '--version': - $version = \true; - break; - case '--dont-report-useless-tests': - $reportUselessTests = \false; - break; - case '--strict-coverage': - $strictCoverage = \true; - break; - case '--disable-coverage-ignore': - $disableCodeCoverageIgnore = \true; - break; - case '--strict-global-state': - $beStrictAboutChangesToGlobalState = \true; - break; - case '--disallow-test-output': - $disallowTestOutput = \true; - break; - case '--disallow-resource-usage': - $beStrictAboutResourceUsageDuringSmallTests = \true; - break; - case '--default-time-limit': - $defaultTimeLimit = (int) $option[1]; - break; - case '--enforce-time-limit': - $enforceTimeLimit = \true; - break; - case '--disallow-todo-tests': - $disallowTodoAnnotatedTests = \true; - break; - case '--reverse-list': - $reverseList = \true; - break; - case '--check-version': - $checkVersion = \true; - break; - case '--coverage-filter': - case '--whitelist': - if ($coverageFilter === null) { - $coverageFilter = []; } - $coverageFilter[] = $option[1]; - break; - case '--random-order': - $executionOrder = \PHPUnit\Runner\TestSuiteSorter::ORDER_RANDOMIZED; - break; - case '--random-order-seed': - $randomOrderSeed = (int) $option[1]; - break; - case '--resolve-dependencies': - $resolveDependencies = \true; - break; - case '--ignore-dependencies': - $resolveDependencies = \false; - break; - case '--reverse-order': - $executionOrder = \PHPUnit\Runner\TestSuiteSorter::ORDER_REVERSED; - break; - case '--dump-xdebug-filter': - $xdebugFilterFile = $option[1]; - break; - default: - $unrecognizedOptions[\str_replace('--', '', $option[0])] = $option[1]; + $lines[$method['startLine']] = ['ccn' => $method['ccn'], 'count' => $methodCount, 'crap' => $method['crap'], 'type' => 'method', 'visibility' => $method['visibility'], 'name' => $methodName]; + } + if (!empty($class['package']['namespace'])) { + $namespace = $class['package']['namespace']; + } + $xmlClass = $xmlDocument->createElement('class'); + $xmlClass->setAttribute('name', $className); + $xmlClass->setAttribute('namespace', $namespace); + if (!empty($class['package']['fullPackage'])) { + $xmlClass->setAttribute('fullPackage', $class['package']['fullPackage']); + } + if (!empty($class['package']['category'])) { + $xmlClass->setAttribute('category', $class['package']['category']); + } + if (!empty($class['package']['package'])) { + $xmlClass->setAttribute('package', $class['package']['package']); + } + if (!empty($class['package']['subpackage'])) { + $xmlClass->setAttribute('subpackage', $class['package']['subpackage']); + } + $xmlFile->appendChild($xmlClass); + $xmlMetrics = $xmlDocument->createElement('metrics'); + $xmlMetrics->setAttribute('complexity', (string) $class['ccn']); + $xmlMetrics->setAttribute('methods', (string) $classMethods); + $xmlMetrics->setAttribute('coveredmethods', (string) $coveredMethods); + $xmlMetrics->setAttribute('conditionals', (string) $class['executableBranches']); + $xmlMetrics->setAttribute('coveredconditionals', (string) $class['executedBranches']); + $xmlMetrics->setAttribute('statements', (string) $classStatements); + $xmlMetrics->setAttribute('coveredstatements', (string) $coveredClassStatements); + $xmlMetrics->setAttribute('elements', (string) ($classMethods + $classStatements + $class['executableBranches'])); + $xmlMetrics->setAttribute('coveredelements', (string) ($coveredMethods + $coveredClassStatements + $class['executedBranches'])); + $xmlClass->appendChild($xmlMetrics); + } + foreach ($coverageData as $line => $data) { + if ($data === null || isset($lines[$line])) { + continue; + } + $lines[$line] = ['count' => count($data), 'type' => 'stmt']; + } + ksort($lines); + foreach ($lines as $line => $data) { + $xmlLine = $xmlDocument->createElement('line'); + $xmlLine->setAttribute('num', (string) $line); + $xmlLine->setAttribute('type', $data['type']); + if (isset($data['name'])) { + $xmlLine->setAttribute('name', $data['name']); + } + if (isset($data['visibility'])) { + $xmlLine->setAttribute('visibility', $data['visibility']); + } + if (isset($data['ccn'])) { + $xmlLine->setAttribute('complexity', (string) $data['ccn']); + } + if (isset($data['crap'])) { + $xmlLine->setAttribute('crap', (string) $data['crap']); + } + $xmlLine->setAttribute('count', (string) $data['count']); + $xmlFile->appendChild($xmlLine); + } + $linesOfCode = $item->linesOfCode(); + $xmlMetrics = $xmlDocument->createElement('metrics'); + $xmlMetrics->setAttribute('loc', (string) $linesOfCode->linesOfCode()); + $xmlMetrics->setAttribute('ncloc', (string) $linesOfCode->nonCommentLinesOfCode()); + $xmlMetrics->setAttribute('classes', (string) $item->numberOfClassesAndTraits()); + $xmlMetrics->setAttribute('methods', (string) $item->numberOfMethods()); + $xmlMetrics->setAttribute('coveredmethods', (string) $item->numberOfTestedMethods()); + $xmlMetrics->setAttribute('conditionals', (string) $item->numberOfExecutableBranches()); + $xmlMetrics->setAttribute('coveredconditionals', (string) $item->numberOfExecutedBranches()); + $xmlMetrics->setAttribute('statements', (string) $item->numberOfExecutableLines()); + $xmlMetrics->setAttribute('coveredstatements', (string) $item->numberOfExecutedLines()); + $xmlMetrics->setAttribute('elements', (string) ($item->numberOfMethods() + $item->numberOfExecutableLines() + $item->numberOfExecutableBranches())); + $xmlMetrics->setAttribute('coveredelements', (string) ($item->numberOfTestedMethods() + $item->numberOfExecutedLines() + $item->numberOfExecutedBranches())); + $xmlFile->appendChild($xmlMetrics); + if ($namespace === 'global') { + $xmlProject->appendChild($xmlFile); + } else { + if (!isset($packages[$namespace])) { + $packages[$namespace] = $xmlDocument->createElement('package'); + $packages[$namespace]->setAttribute('name', $namespace); + $xmlProject->appendChild($packages[$namespace]); + } + $packages[$namespace]->appendChild($xmlFile); } } - if (empty($extensions)) { - $extensions = null; - } - if (empty($unavailableExtensions)) { - $unavailableExtensions = null; - } - if (empty($iniSettings)) { - $iniSettings = null; - } - if (empty($coverageFilter)) { - $coverageFilter = null; + $linesOfCode = $report->linesOfCode(); + $xmlMetrics = $xmlDocument->createElement('metrics'); + $xmlMetrics->setAttribute('files', (string) count($report)); + $xmlMetrics->setAttribute('loc', (string) $linesOfCode->linesOfCode()); + $xmlMetrics->setAttribute('ncloc', (string) $linesOfCode->nonCommentLinesOfCode()); + $xmlMetrics->setAttribute('classes', (string) $report->numberOfClassesAndTraits()); + $xmlMetrics->setAttribute('methods', (string) $report->numberOfMethods()); + $xmlMetrics->setAttribute('coveredmethods', (string) $report->numberOfTestedMethods()); + $xmlMetrics->setAttribute('conditionals', (string) $report->numberOfExecutableBranches()); + $xmlMetrics->setAttribute('coveredconditionals', (string) $report->numberOfExecutedBranches()); + $xmlMetrics->setAttribute('statements', (string) $report->numberOfExecutableLines()); + $xmlMetrics->setAttribute('coveredstatements', (string) $report->numberOfExecutedLines()); + $xmlMetrics->setAttribute('elements', (string) ($report->numberOfMethods() + $report->numberOfExecutableLines() + $report->numberOfExecutableBranches())); + $xmlMetrics->setAttribute('coveredelements', (string) ($report->numberOfTestedMethods() + $report->numberOfExecutedLines() + $report->numberOfExecutedBranches())); + $xmlProject->appendChild($xmlMetrics); + $buffer = $xmlDocument->saveXML(); + if ($target !== null) { + Directory::create(dirname($target)); + if (@file_put_contents($target, $buffer) === \false) { + throw new WriteOperationFailedException($target); + } } - return new \PHPUnit\TextUI\CliArguments\Configuration($argument, $atLeastVersion, $backupGlobals, $backupStaticAttributes, $beStrictAboutChangesToGlobalState, $beStrictAboutResourceUsageDuringSmallTests, $bootstrap, $cacheResult, $cacheResultFile, $checkVersion, $colors, $columns, $configuration, $coverageClover, $coverageCobertura, $coverageCrap4J, $coverageHtml, $coveragePhp, $coverageText, $coverageTextShowUncoveredFiles, $coverageTextShowOnlySummary, $coverageXml, $pathCoverage, $coverageCacheDirectory, $warmCoverageCache, $debug, $defaultTimeLimit, $disableCodeCoverageIgnore, $disallowTestOutput, $disallowTodoAnnotatedTests, $enforceTimeLimit, $excludeGroups, $executionOrder, $executionOrderDefects, $extensions, $unavailableExtensions, $failOnEmptyTestSuite, $failOnIncomplete, $failOnRisky, $failOnSkipped, $failOnWarning, $filter, $generateConfiguration, $migrateConfiguration, $groups, $testsCovering, $testsUsing, $help, $includePath, $iniSettings, $junitLogfile, $listGroups, $listSuites, $listTests, $listTestsXml, $loader, $noCoverage, $noExtensions, $noInteraction, $noLogging, $printer, $processIsolation, $randomOrderSeed, $repeat, $reportUselessTests, $resolveDependencies, $reverseList, $stderr, $strictCoverage, $stopOnDefect, $stopOnError, $stopOnFailure, $stopOnIncomplete, $stopOnRisky, $stopOnSkipped, $stopOnWarning, $teamcityLogfile, $testdoxExcludeGroups, $testdoxGroups, $testdoxHtmlFile, $testdoxTextFile, $testdoxXmlFile, $testSuffixes, $testSuite, $unrecognizedOptions, $unrecognizedOrderBy, $useDefaultConfiguration, $verbose, $version, $coverageFilter, $xdebugFilterFile); + return $buffer; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\CliArguments; - -use RuntimeException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Exception extends \RuntimeException implements \PHPUnit\Exception -{ -} - * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI\CliArguments; +namespace PHPUnit\SebastianBergmann\CodeCoverage\Report; -use PHPUnit\TextUI\XmlConfiguration\Extension; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * @psalm-immutable - */ -final class Configuration +use function count; +use function dirname; +use function file_put_contents; +use function range; +use function time; +use DOMImplementation; +use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; +use PHPUnit\SebastianBergmann\CodeCoverage\Directory; +use PHPUnit\SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException; +use PHPUnit\SebastianBergmann\CodeCoverage\Node\File; +final class Cobertura { /** - * @var ?string - */ - private $argument; - /** - * @var ?string - */ - private $atLeastVersion; - /** - * @var ?bool - */ - private $backupGlobals; - /** - * @var ?bool - */ - private $backupStaticAttributes; - /** - * @var ?bool - */ - private $beStrictAboutChangesToGlobalState; - /** - * @var ?bool - */ - private $beStrictAboutResourceUsageDuringSmallTests; - /** - * @var ?string - */ - private $bootstrap; - /** - * @var ?bool - */ - private $cacheResult; - /** - * @var ?string - */ - private $cacheResultFile; - /** - * @var ?bool - */ - private $checkVersion; - /** - * @var ?string - */ - private $colors; - /** - * @var null|int|string - */ - private $columns; - /** - * @var ?string - */ - private $configuration; - /** - * @var null|string[] - */ - private $coverageFilter; - /** - * @var ?string - */ - private $coverageClover; - /** - * @var ?string - */ - private $coverageCobertura; - /** - * @var ?string - */ - private $coverageCrap4J; - /** - * @var ?string - */ - private $coverageHtml; - /** - * @var ?string - */ - private $coveragePhp; - /** - * @var ?string - */ - private $coverageText; - /** - * @var ?bool - */ - private $coverageTextShowUncoveredFiles; - /** - * @var ?bool - */ - private $coverageTextShowOnlySummary; - /** - * @var ?string - */ - private $coverageXml; - /** - * @var ?bool - */ - private $pathCoverage; - /** - * @var ?string - */ - private $coverageCacheDirectory; - /** - * @var ?bool - */ - private $warmCoverageCache; - /** - * @var ?bool - */ - private $debug; - /** - * @var ?int - */ - private $defaultTimeLimit; - /** - * @var ?bool - */ - private $disableCodeCoverageIgnore; - /** - * @var ?bool - */ - private $disallowTestOutput; - /** - * @var ?bool - */ - private $disallowTodoAnnotatedTests; - /** - * @var ?bool - */ - private $enforceTimeLimit; - /** - * @var null|string[] - */ - private $excludeGroups; - /** - * @var ?int - */ - private $executionOrder; - /** - * @var ?int - */ - private $executionOrderDefects; - /** - * @var null|Extension[] - */ - private $extensions; - /** - * @var null|string[] - */ - private $unavailableExtensions; - /** - * @var ?bool - */ - private $failOnEmptyTestSuite; - /** - * @var ?bool - */ - private $failOnIncomplete; - /** - * @var ?bool - */ - private $failOnRisky; - /** - * @var ?bool - */ - private $failOnSkipped; - /** - * @var ?bool - */ - private $failOnWarning; - /** - * @var ?string - */ - private $filter; - /** - * @var ?bool - */ - private $generateConfiguration; - /** - * @var ?bool - */ - private $migrateConfiguration; - /** - * @var null|string[] - */ - private $groups; - /** - * @var null|string[] - */ - private $testsCovering; - /** - * @var null|string[] - */ - private $testsUsing; - /** - * @var ?bool - */ - private $help; - /** - * @var ?string - */ - private $includePath; - /** - * @var null|string[] - */ - private $iniSettings; - /** - * @var ?string - */ - private $junitLogfile; - /** - * @var ?bool - */ - private $listGroups; - /** - * @var ?bool - */ - private $listSuites; - /** - * @var ?bool - */ - private $listTests; - /** - * @var ?string - */ - private $listTestsXml; - /** - * @var ?string - */ - private $loader; - /** - * @var ?bool - */ - private $noCoverage; - /** - * @var ?bool - */ - private $noExtensions; - /** - * @var ?bool - */ - private $noInteraction; - /** - * @var ?bool - */ - private $noLogging; - /** - * @var ?string - */ - private $printer; - /** - * @var ?bool - */ - private $processIsolation; - /** - * @var ?int - */ - private $randomOrderSeed; - /** - * @var ?int - */ - private $repeat; - /** - * @var ?bool - */ - private $reportUselessTests; - /** - * @var ?bool - */ - private $resolveDependencies; - /** - * @var ?bool - */ - private $reverseList; - /** - * @var ?bool - */ - private $stderr; - /** - * @var ?bool - */ - private $strictCoverage; - /** - * @var ?bool - */ - private $stopOnDefect; - /** - * @var ?bool - */ - private $stopOnError; - /** - * @var ?bool - */ - private $stopOnFailure; - /** - * @var ?bool - */ - private $stopOnIncomplete; - /** - * @var ?bool - */ - private $stopOnRisky; - /** - * @var ?bool - */ - private $stopOnSkipped; - /** - * @var ?bool - */ - private $stopOnWarning; - /** - * @var ?string - */ - private $teamcityLogfile; - /** - * @var null|string[] - */ - private $testdoxExcludeGroups; - /** - * @var null|string[] - */ - private $testdoxGroups; - /** - * @var ?string - */ - private $testdoxHtmlFile; - /** - * @var ?string - */ - private $testdoxTextFile; - /** - * @var ?string - */ - private $testdoxXmlFile; - /** - * @var null|string[] - */ - private $testSuffixes; - /** - * @var ?string - */ - private $testSuite; - /** - * @var string[] - */ - private $unrecognizedOptions; - /** - * @var ?string - */ - private $unrecognizedOrderBy; - /** - * @var ?bool - */ - private $useDefaultConfiguration; - /** - * @var ?bool + * @throws WriteOperationFailedException */ - private $verbose; + public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null) : string + { + $time = (string) time(); + $report = $coverage->getReport(); + $implementation = new DOMImplementation(); + $documentType = $implementation->createDocumentType('coverage', '', 'http://cobertura.sourceforge.net/xml/coverage-04.dtd'); + $document = $implementation->createDocument('', '', $documentType); + $document->xmlVersion = '1.0'; + $document->encoding = 'UTF-8'; + $document->formatOutput = \true; + $coverageElement = $document->createElement('coverage'); + $linesValid = $report->numberOfExecutableLines(); + $linesCovered = $report->numberOfExecutedLines(); + $lineRate = $linesValid === 0 ? 0 : $linesCovered / $linesValid; + $coverageElement->setAttribute('line-rate', (string) $lineRate); + $branchesValid = $report->numberOfExecutableBranches(); + $branchesCovered = $report->numberOfExecutedBranches(); + $branchRate = $branchesValid === 0 ? 0 : $branchesCovered / $branchesValid; + $coverageElement->setAttribute('branch-rate', (string) $branchRate); + $coverageElement->setAttribute('lines-covered', (string) $report->numberOfExecutedLines()); + $coverageElement->setAttribute('lines-valid', (string) $report->numberOfExecutableLines()); + $coverageElement->setAttribute('branches-covered', (string) $report->numberOfExecutedBranches()); + $coverageElement->setAttribute('branches-valid', (string) $report->numberOfExecutableBranches()); + $coverageElement->setAttribute('complexity', ''); + $coverageElement->setAttribute('version', '0.4'); + $coverageElement->setAttribute('timestamp', $time); + $document->appendChild($coverageElement); + $sourcesElement = $document->createElement('sources'); + $coverageElement->appendChild($sourcesElement); + $sourceElement = $document->createElement('source', $report->pathAsString()); + $sourcesElement->appendChild($sourceElement); + $packagesElement = $document->createElement('packages'); + $coverageElement->appendChild($packagesElement); + $complexity = 0; + foreach ($report as $item) { + if (!$item instanceof File) { + continue; + } + $packageElement = $document->createElement('package'); + $packageComplexity = 0; + $packageName = $name ?? ''; + $packageElement->setAttribute('name', $packageName); + $linesValid = $item->numberOfExecutableLines(); + $linesCovered = $item->numberOfExecutedLines(); + $lineRate = $linesValid === 0 ? 0 : $linesCovered / $linesValid; + $packageElement->setAttribute('line-rate', (string) $lineRate); + $branchesValid = $item->numberOfExecutableBranches(); + $branchesCovered = $item->numberOfExecutedBranches(); + $branchRate = $branchesValid === 0 ? 0 : $branchesCovered / $branchesValid; + $packageElement->setAttribute('branch-rate', (string) $branchRate); + $packageElement->setAttribute('complexity', ''); + $packagesElement->appendChild($packageElement); + $classesElement = $document->createElement('classes'); + $packageElement->appendChild($classesElement); + $classes = $item->classesAndTraits(); + $coverageData = $item->lineCoverageData(); + foreach ($classes as $className => $class) { + $complexity += $class['ccn']; + $packageComplexity += $class['ccn']; + if (!empty($class['package']['namespace'])) { + $className = $class['package']['namespace'] . '\\' . $className; + } + $linesValid = $class['executableLines']; + $linesCovered = $class['executedLines']; + $lineRate = $linesValid === 0 ? 0 : $linesCovered / $linesValid; + $branchesValid = $class['executableBranches']; + $branchesCovered = $class['executedBranches']; + $branchRate = $branchesValid === 0 ? 0 : $branchesCovered / $branchesValid; + $classElement = $document->createElement('class'); + $classElement->setAttribute('name', $className); + $classElement->setAttribute('filename', \str_replace($report->pathAsString() . \DIRECTORY_SEPARATOR, '', $item->pathAsString())); + $classElement->setAttribute('line-rate', (string) $lineRate); + $classElement->setAttribute('branch-rate', (string) $branchRate); + $classElement->setAttribute('complexity', (string) $class['ccn']); + $classesElement->appendChild($classElement); + $methodsElement = $document->createElement('methods'); + $classElement->appendChild($methodsElement); + $classLinesElement = $document->createElement('lines'); + $classElement->appendChild($classLinesElement); + foreach ($class['methods'] as $methodName => $method) { + if ($method['executableLines'] === 0) { + continue; + } + \preg_match("/\\((.*?)\\)/", $method['signature'], $signature); + $linesValid = $method['executableLines']; + $linesCovered = $method['executedLines']; + $lineRate = $linesValid === 0 ? 0 : $linesCovered / $linesValid; + $branchesValid = $method['executableBranches']; + $branchesCovered = $method['executedBranches']; + $branchRate = $branchesValid === 0 ? 0 : $branchesCovered / $branchesValid; + $methodElement = $document->createElement('method'); + $methodElement->setAttribute('name', $methodName); + $methodElement->setAttribute('signature', $signature[1]); + $methodElement->setAttribute('line-rate', (string) $lineRate); + $methodElement->setAttribute('branch-rate', (string) $branchRate); + $methodElement->setAttribute('complexity', (string) $method['ccn']); + $methodLinesElement = $document->createElement('lines'); + $methodElement->appendChild($methodLinesElement); + foreach (range($method['startLine'], $method['endLine']) as $line) { + if (!isset($coverageData[$line]) || $coverageData[$line] === null) { + continue; + } + $methodLineElement = $document->createElement('line'); + $methodLineElement->setAttribute('number', (string) $line); + $methodLineElement->setAttribute('hits', (string) count($coverageData[$line])); + $methodLinesElement->appendChild($methodLineElement); + $classLineElement = $methodLineElement->cloneNode(); + $classLinesElement->appendChild($classLineElement); + } + $methodsElement->appendChild($methodElement); + } + } + if ($report->numberOfFunctions() === 0) { + $packageElement->setAttribute('complexity', (string) $packageComplexity); + continue; + } + $functionsComplexity = 0; + $functionsLinesValid = 0; + $functionsLinesCovered = 0; + $functionsBranchesValid = 0; + $functionsBranchesCovered = 0; + $classElement = $document->createElement('class'); + $classElement->setAttribute('name', \basename($item->pathAsString())); + $classElement->setAttribute('filename', \str_replace($report->pathAsString() . \DIRECTORY_SEPARATOR, '', $item->pathAsString())); + $methodsElement = $document->createElement('methods'); + $classElement->appendChild($methodsElement); + $classLinesElement = $document->createElement('lines'); + $classElement->appendChild($classLinesElement); + $functions = $report->functions(); + foreach ($functions as $functionName => $function) { + if ($function['executableLines'] === 0) { + continue; + } + $complexity += $function['ccn']; + $packageComplexity += $function['ccn']; + $functionsComplexity += $function['ccn']; + $linesValid = $function['executableLines']; + $linesCovered = $function['executedLines']; + $lineRate = $linesValid === 0 ? 0 : $linesCovered / $linesValid; + $functionsLinesValid += $linesValid; + $functionsLinesCovered += $linesCovered; + $branchesValid = $function['executableBranches']; + $branchesCovered = $function['executedBranches']; + $branchRate = $branchesValid === 0 ? 0 : $branchesCovered / $branchesValid; + $functionsBranchesValid += $branchesValid; + $functionsBranchesCovered += $branchesValid; + $methodElement = $document->createElement('method'); + $methodElement->setAttribute('name', $functionName); + $methodElement->setAttribute('signature', $function['signature']); + $methodElement->setAttribute('line-rate', (string) $lineRate); + $methodElement->setAttribute('branch-rate', (string) $branchRate); + $methodElement->setAttribute('complexity', (string) $function['ccn']); + $methodLinesElement = $document->createElement('lines'); + $methodElement->appendChild($methodLinesElement); + foreach (range($function['startLine'], $function['endLine']) as $line) { + if (!isset($coverageData[$line]) || $coverageData[$line] === null) { + continue; + } + $methodLineElement = $document->createElement('line'); + $methodLineElement->setAttribute('number', (string) $line); + $methodLineElement->setAttribute('hits', (string) count($coverageData[$line])); + $methodLinesElement->appendChild($methodLineElement); + $classLineElement = $methodLineElement->cloneNode(); + $classLinesElement->appendChild($classLineElement); + } + $methodsElement->appendChild($methodElement); + } + $packageElement->setAttribute('complexity', (string) $packageComplexity); + if ($functionsLinesValid === 0) { + continue; + } + $lineRate = $functionsLinesCovered / $functionsLinesValid; + $branchRate = $functionsBranchesValid === 0 ? 0 : $functionsBranchesCovered / $functionsBranchesValid; + $classElement->setAttribute('line-rate', (string) $lineRate); + $classElement->setAttribute('branch-rate', (string) $branchRate); + $classElement->setAttribute('complexity', (string) $functionsComplexity); + $classesElement->appendChild($classElement); + } + $coverageElement->setAttribute('complexity', (string) $complexity); + $buffer = $document->saveXML(); + if ($target !== null) { + Directory::create(dirname($target)); + if (@file_put_contents($target, $buffer) === \false) { + throw new WriteOperationFailedException($target); + } + } + return $buffer; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage; + +use function array_diff; +use function array_diff_key; +use function array_flip; +use function array_intersect; +use function array_intersect_key; +use function count; +use function file; +use function in_array; +use function range; +use PHPUnit\SebastianBergmann\CodeCoverage\Driver\Driver; +use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\UncoveredFileAnalyser; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class RawCodeCoverageData +{ /** - * @var ?bool + * @var array> */ - private $version; + private static $emptyLineCache = []; /** - * @var ?string + * @var array + * + * @see https://xdebug.org/docs/code_coverage for format */ - private $xdebugFilterFile; + private $lineCoverage; /** - * @param null|int|string $columns + * @var array + * + * @see https://xdebug.org/docs/code_coverage for format */ - public function __construct(?string $argument, ?string $atLeastVersion, ?bool $backupGlobals, ?bool $backupStaticAttributes, ?bool $beStrictAboutChangesToGlobalState, ?bool $beStrictAboutResourceUsageDuringSmallTests, ?string $bootstrap, ?bool $cacheResult, ?string $cacheResultFile, ?bool $checkVersion, ?string $colors, $columns, ?string $configuration, ?string $coverageClover, ?string $coverageCobertura, ?string $coverageCrap4J, ?string $coverageHtml, ?string $coveragePhp, ?string $coverageText, ?bool $coverageTextShowUncoveredFiles, ?bool $coverageTextShowOnlySummary, ?string $coverageXml, ?bool $pathCoverage, ?string $coverageCacheDirectory, ?bool $warmCoverageCache, ?bool $debug, ?int $defaultTimeLimit, ?bool $disableCodeCoverageIgnore, ?bool $disallowTestOutput, ?bool $disallowTodoAnnotatedTests, ?bool $enforceTimeLimit, ?array $excludeGroups, ?int $executionOrder, ?int $executionOrderDefects, ?array $extensions, ?array $unavailableExtensions, ?bool $failOnEmptyTestSuite, ?bool $failOnIncomplete, ?bool $failOnRisky, ?bool $failOnSkipped, ?bool $failOnWarning, ?string $filter, ?bool $generateConfiguration, ?bool $migrateConfiguration, ?array $groups, ?array $testsCovering, ?array $testsUsing, ?bool $help, ?string $includePath, ?array $iniSettings, ?string $junitLogfile, ?bool $listGroups, ?bool $listSuites, ?bool $listTests, ?string $listTestsXml, ?string $loader, ?bool $noCoverage, ?bool $noExtensions, ?bool $noInteraction, ?bool $noLogging, ?string $printer, ?bool $processIsolation, ?int $randomOrderSeed, ?int $repeat, ?bool $reportUselessTests, ?bool $resolveDependencies, ?bool $reverseList, ?bool $stderr, ?bool $strictCoverage, ?bool $stopOnDefect, ?bool $stopOnError, ?bool $stopOnFailure, ?bool $stopOnIncomplete, ?bool $stopOnRisky, ?bool $stopOnSkipped, ?bool $stopOnWarning, ?string $teamcityLogfile, ?array $testdoxExcludeGroups, ?array $testdoxGroups, ?string $testdoxHtmlFile, ?string $testdoxTextFile, ?string $testdoxXmlFile, ?array $testSuffixes, ?string $testSuite, array $unrecognizedOptions, ?string $unrecognizedOrderBy, ?bool $useDefaultConfiguration, ?bool $verbose, ?bool $version, ?array $coverageFilter, ?string $xdebugFilterFile) - { - $this->argument = $argument; - $this->atLeastVersion = $atLeastVersion; - $this->backupGlobals = $backupGlobals; - $this->backupStaticAttributes = $backupStaticAttributes; - $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; - $this->beStrictAboutResourceUsageDuringSmallTests = $beStrictAboutResourceUsageDuringSmallTests; - $this->bootstrap = $bootstrap; - $this->cacheResult = $cacheResult; - $this->cacheResultFile = $cacheResultFile; - $this->checkVersion = $checkVersion; - $this->colors = $colors; - $this->columns = $columns; - $this->configuration = $configuration; - $this->coverageFilter = $coverageFilter; - $this->coverageClover = $coverageClover; - $this->coverageCobertura = $coverageCobertura; - $this->coverageCrap4J = $coverageCrap4J; - $this->coverageHtml = $coverageHtml; - $this->coveragePhp = $coveragePhp; - $this->coverageText = $coverageText; - $this->coverageTextShowUncoveredFiles = $coverageTextShowUncoveredFiles; - $this->coverageTextShowOnlySummary = $coverageTextShowOnlySummary; - $this->coverageXml = $coverageXml; - $this->pathCoverage = $pathCoverage; - $this->coverageCacheDirectory = $coverageCacheDirectory; - $this->warmCoverageCache = $warmCoverageCache; - $this->debug = $debug; - $this->defaultTimeLimit = $defaultTimeLimit; - $this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore; - $this->disallowTestOutput = $disallowTestOutput; - $this->disallowTodoAnnotatedTests = $disallowTodoAnnotatedTests; - $this->enforceTimeLimit = $enforceTimeLimit; - $this->excludeGroups = $excludeGroups; - $this->executionOrder = $executionOrder; - $this->executionOrderDefects = $executionOrderDefects; - $this->extensions = $extensions; - $this->unavailableExtensions = $unavailableExtensions; - $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; - $this->failOnIncomplete = $failOnIncomplete; - $this->failOnRisky = $failOnRisky; - $this->failOnSkipped = $failOnSkipped; - $this->failOnWarning = $failOnWarning; - $this->filter = $filter; - $this->generateConfiguration = $generateConfiguration; - $this->migrateConfiguration = $migrateConfiguration; - $this->groups = $groups; - $this->testsCovering = $testsCovering; - $this->testsUsing = $testsUsing; - $this->help = $help; - $this->includePath = $includePath; - $this->iniSettings = $iniSettings; - $this->junitLogfile = $junitLogfile; - $this->listGroups = $listGroups; - $this->listSuites = $listSuites; - $this->listTests = $listTests; - $this->listTestsXml = $listTestsXml; - $this->loader = $loader; - $this->noCoverage = $noCoverage; - $this->noExtensions = $noExtensions; - $this->noInteraction = $noInteraction; - $this->noLogging = $noLogging; - $this->printer = $printer; - $this->processIsolation = $processIsolation; - $this->randomOrderSeed = $randomOrderSeed; - $this->repeat = $repeat; - $this->reportUselessTests = $reportUselessTests; - $this->resolveDependencies = $resolveDependencies; - $this->reverseList = $reverseList; - $this->stderr = $stderr; - $this->strictCoverage = $strictCoverage; - $this->stopOnDefect = $stopOnDefect; - $this->stopOnError = $stopOnError; - $this->stopOnFailure = $stopOnFailure; - $this->stopOnIncomplete = $stopOnIncomplete; - $this->stopOnRisky = $stopOnRisky; - $this->stopOnSkipped = $stopOnSkipped; - $this->stopOnWarning = $stopOnWarning; - $this->teamcityLogfile = $teamcityLogfile; - $this->testdoxExcludeGroups = $testdoxExcludeGroups; - $this->testdoxGroups = $testdoxGroups; - $this->testdoxHtmlFile = $testdoxHtmlFile; - $this->testdoxTextFile = $testdoxTextFile; - $this->testdoxXmlFile = $testdoxXmlFile; - $this->testSuffixes = $testSuffixes; - $this->testSuite = $testSuite; - $this->unrecognizedOptions = $unrecognizedOptions; - $this->unrecognizedOrderBy = $unrecognizedOrderBy; - $this->useDefaultConfiguration = $useDefaultConfiguration; - $this->verbose = $verbose; - $this->version = $version; - $this->xdebugFilterFile = $xdebugFilterFile; - } - public function hasArgument() : bool + private $functionCoverage; + public static function fromXdebugWithoutPathCoverage(array $rawCoverage) : self { - return $this->argument !== null; + return new self($rawCoverage, []); } - /** - * @throws Exception - */ - public function argument() : string + public static function fromXdebugWithPathCoverage(array $rawCoverage) : self { - if ($this->argument === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + $lineCoverage = []; + $functionCoverage = []; + foreach ($rawCoverage as $file => $fileCoverageData) { + $lineCoverage[$file] = $fileCoverageData['lines']; + $functionCoverage[$file] = $fileCoverageData['functions']; } - return $this->argument; + return new self($lineCoverage, $functionCoverage); } - public function hasAtLeastVersion() : bool + public static function fromXdebugWithMixedCoverage(array $rawCoverage) : self { - return $this->atLeastVersion !== null; + $lineCoverage = []; + $functionCoverage = []; + foreach ($rawCoverage as $file => $fileCoverageData) { + if (!isset($fileCoverageData['functions'])) { + // Current file does not have functions, so line coverage + // is stored in $fileCoverageData, not in $fileCoverageData['lines'] + $lineCoverage[$file] = $fileCoverageData; + continue; + } + $lineCoverage[$file] = $fileCoverageData['lines']; + $functionCoverage[$file] = $fileCoverageData['functions']; + } + return new self($lineCoverage, $functionCoverage); } - /** - * @throws Exception - */ - public function atLeastVersion() : string + public static function fromUncoveredFile(string $filename, UncoveredFileAnalyser $uncoveredFileAnalyser) : self { - if ($this->atLeastVersion === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + $lineCoverage = []; + foreach ($uncoveredFileAnalyser->executableLinesIn($filename) as $line) { + $lineCoverage[$line] = Driver::LINE_NOT_EXECUTED; } - return $this->atLeastVersion; + return new self([$filename => $lineCoverage], []); } - public function hasBackupGlobals() : bool + private function __construct(array $lineCoverage, array $functionCoverage) { - return $this->backupGlobals !== null; + $this->lineCoverage = $lineCoverage; + $this->functionCoverage = $functionCoverage; + $this->skipEmptyLines(); } - /** - * @throws Exception - */ - public function backupGlobals() : bool + public function clear() : void { - if ($this->backupGlobals === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->backupGlobals; + $this->lineCoverage = $this->functionCoverage = []; } - public function hasBackupStaticAttributes() : bool + public function lineCoverage() : array { - return $this->backupStaticAttributes !== null; + return $this->lineCoverage; } - /** - * @throws Exception - */ - public function backupStaticAttributes() : bool + public function functionCoverage() : array { - if ($this->backupStaticAttributes === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->backupStaticAttributes; + return $this->functionCoverage; } - public function hasBeStrictAboutChangesToGlobalState() : bool + public function removeCoverageDataForFile(string $filename) : void { - return $this->beStrictAboutChangesToGlobalState !== null; + unset($this->lineCoverage[$filename], $this->functionCoverage[$filename]); } /** - * @throws Exception + * @param int[] $lines */ - public function beStrictAboutChangesToGlobalState() : bool + public function keepCoverageDataOnlyForLines(string $filename, array $lines) : void { - if ($this->beStrictAboutChangesToGlobalState === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (!isset($this->lineCoverage[$filename])) { + return; + } + $this->lineCoverage[$filename] = array_intersect_key($this->lineCoverage[$filename], array_flip($lines)); + if (isset($this->functionCoverage[$filename])) { + foreach ($this->functionCoverage[$filename] as $functionName => $functionData) { + foreach ($functionData['branches'] as $branchId => $branch) { + if (count(array_diff(range($branch['line_start'], $branch['line_end']), $lines)) > 0) { + unset($this->functionCoverage[$filename][$functionName]['branches'][$branchId]); + foreach ($functionData['paths'] as $pathId => $path) { + if (in_array($branchId, $path['path'], \true)) { + unset($this->functionCoverage[$filename][$functionName]['paths'][$pathId]); + } + } + } + } + } } - return $this->beStrictAboutChangesToGlobalState; - } - public function hasBeStrictAboutResourceUsageDuringSmallTests() : bool - { - return $this->beStrictAboutResourceUsageDuringSmallTests !== null; } /** - * @throws Exception + * @param int[] $lines */ - public function beStrictAboutResourceUsageDuringSmallTests() : bool + public function removeCoverageDataForLines(string $filename, array $lines) : void { - if ($this->beStrictAboutResourceUsageDuringSmallTests === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (empty($lines)) { + return; + } + if (!isset($this->lineCoverage[$filename])) { + return; + } + $this->lineCoverage[$filename] = array_diff_key($this->lineCoverage[$filename], array_flip($lines)); + if (isset($this->functionCoverage[$filename])) { + foreach ($this->functionCoverage[$filename] as $functionName => $functionData) { + foreach ($functionData['branches'] as $branchId => $branch) { + if (count(array_intersect($lines, range($branch['line_start'], $branch['line_end']))) > 0) { + unset($this->functionCoverage[$filename][$functionName]['branches'][$branchId]); + foreach ($functionData['paths'] as $pathId => $path) { + if (in_array($branchId, $path['path'], \true)) { + unset($this->functionCoverage[$filename][$functionName]['paths'][$pathId]); + } + } + } + } + } } - return $this->beStrictAboutResourceUsageDuringSmallTests; - } - public function hasBootstrap() : bool - { - return $this->bootstrap !== null; } /** - * @throws Exception + * At the end of a file, the PHP interpreter always sees an implicit return. Where this occurs in a file that has + * e.g. a class definition, that line cannot be invoked from a test and results in confusing coverage. This engine + * implementation detail therefore needs to be masked which is done here by simply ensuring that all empty lines + * are skipped over for coverage purposes. + * + * @see https://github.com/sebastianbergmann/php-code-coverage/issues/799 */ - public function bootstrap() : string + private function skipEmptyLines() : void { - if ($this->bootstrap === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + foreach ($this->lineCoverage as $filename => $coverage) { + foreach ($this->getEmptyLinesForFile($filename) as $emptyLine) { + unset($this->lineCoverage[$filename][$emptyLine]); + } } - return $this->bootstrap; } - public function hasCacheResult() : bool + private function getEmptyLinesForFile(string $filename) : array { - return $this->cacheResult !== null; + if (!isset(self::$emptyLineCache[$filename])) { + self::$emptyLineCache[$filename] = []; + if (\is_file($filename)) { + $sourceLines = \explode("\n", \file_get_contents($filename)); + foreach ($sourceLines as $line => $source) { + if (\trim($source) === '') { + self::$emptyLineCache[$filename][] = $line + 1; + } + } + } + } + return self::$emptyLineCache[$filename]; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class CrapIndex +{ /** - * @throws Exception + * @var int */ - public function cacheResult() : bool - { - if ($this->cacheResult === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->cacheResult; - } - public function hasCacheResultFile() : bool - { - return $this->cacheResultFile !== null; - } + private $cyclomaticComplexity; /** - * @throws Exception + * @var float */ - public function cacheResultFile() : string + private $codeCoverage; + public static function fromCyclomaticComplexityAndCoveragePercentage(int $cyclomaticComplexity, float $codeCoverage) : self { - if ($this->cacheResultFile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->cacheResultFile; + return new self($cyclomaticComplexity, $codeCoverage); } - public function hasCheckVersion() : bool + public function __construct(int $cyclomaticComplexity, float $codeCoverage) { - return $this->checkVersion !== null; + $this->cyclomaticComplexity = $cyclomaticComplexity; + $this->codeCoverage = $codeCoverage; } - /** - * @throws Exception - */ - public function checkVersion() : bool + public function asString() : string { - if ($this->checkVersion === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->codeCoverage === 0.0) { + return (string) ($this->cyclomaticComplexity ** 2 + $this->cyclomaticComplexity); } - return $this->checkVersion; - } - public function hasColors() : bool - { - return $this->colors !== null; + if ($this->codeCoverage >= 95) { + return (string) $this->cyclomaticComplexity; + } + return sprintf('%01.2F', $this->cyclomaticComplexity ** 2 * (1 - $this->codeCoverage / 100) ** 3 + $this->cyclomaticComplexity); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Node; + +use function array_filter; +use function count; +use function range; +use PHPUnit\SebastianBergmann\CodeCoverage\CrapIndex; +use PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class File extends AbstractNode +{ + /** + * @var array + */ + private $lineCoverageData; + /** + * @var array + */ + private $functionCoverageData; + /** + * @var array + */ + private $testData; + /** + * @var int + */ + private $numExecutableLines = 0; + /** + * @var int + */ + private $numExecutedLines = 0; + /** + * @var int + */ + private $numExecutableBranches = 0; + /** + * @var int + */ + private $numExecutedBranches = 0; + /** + * @var int + */ + private $numExecutablePaths = 0; + /** + * @var int + */ + private $numExecutedPaths = 0; + /** + * @var array + */ + private $classes = []; + /** + * @var array + */ + private $traits = []; + /** + * @var array + */ + private $functions = []; + /** + * @var LinesOfCode + */ + private $linesOfCode; /** - * @throws Exception + * @var int */ - public function colors() : string - { - if ($this->colors === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->colors; - } - public function hasColumns() : bool - { - return $this->columns !== null; - } + private $numClasses; /** - * @throws Exception + * @var int */ - public function columns() - { - if ($this->columns === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->columns; - } - public function hasConfiguration() : bool - { - return $this->configuration !== null; - } + private $numTestedClasses = 0; /** - * @throws Exception + * @var int */ - public function configuration() : string - { - if ($this->configuration === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->configuration; - } - public function hasCoverageFilter() : bool - { - return $this->coverageFilter !== null; - } + private $numTraits; /** - * @throws Exception + * @var int */ - public function coverageFilter() : array - { - if ($this->coverageFilter === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageFilter; - } - public function hasCoverageClover() : bool - { - return $this->coverageClover !== null; - } + private $numTestedTraits = 0; /** - * @throws Exception + * @var int */ - public function coverageClover() : string - { - if ($this->coverageClover === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageClover; - } - public function hasCoverageCobertura() : bool - { - return $this->coverageCobertura !== null; - } + private $numMethods; /** - * @throws Exception + * @var int */ - public function coverageCobertura() : string - { - if ($this->coverageCobertura === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageCobertura; - } - public function hasCoverageCrap4J() : bool - { - return $this->coverageCrap4J !== null; - } + private $numTestedMethods; /** - * @throws Exception + * @var int */ - public function coverageCrap4J() : string - { - if ($this->coverageCrap4J === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageCrap4J; - } - public function hasCoverageHtml() : bool - { - return $this->coverageHtml !== null; - } + private $numTestedFunctions; /** - * @throws Exception + * @var array */ - public function coverageHtml() : string - { - if ($this->coverageHtml === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageHtml; - } - public function hasCoveragePhp() : bool + private $codeUnitsByLine = []; + public function __construct(string $name, AbstractNode $parent, array $lineCoverageData, array $functionCoverageData, array $testData, array $classes, array $traits, array $functions, LinesOfCode $linesOfCode) { - return $this->coveragePhp !== null; + parent::__construct($name, $parent); + $this->lineCoverageData = $lineCoverageData; + $this->functionCoverageData = $functionCoverageData; + $this->testData = $testData; + $this->linesOfCode = $linesOfCode; + $this->calculateStatistics($classes, $traits, $functions); } - /** - * @throws Exception - */ - public function coveragePhp() : string + public function count() : int { - if ($this->coveragePhp === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coveragePhp; + return 1; } - public function hasCoverageText() : bool + public function lineCoverageData() : array { - return $this->coverageText !== null; + return $this->lineCoverageData; } - /** - * @throws Exception - */ - public function coverageText() : string + public function functionCoverageData() : array { - if ($this->coverageText === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageText; + return $this->functionCoverageData; } - public function hasCoverageTextShowUncoveredFiles() : bool + public function testData() : array { - return $this->coverageTextShowUncoveredFiles !== null; + return $this->testData; } - /** - * @throws Exception - */ - public function coverageTextShowUncoveredFiles() : bool + public function classes() : array { - if ($this->coverageTextShowUncoveredFiles === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageTextShowUncoveredFiles; + return $this->classes; } - public function hasCoverageTextShowOnlySummary() : bool + public function traits() : array { - return $this->coverageTextShowOnlySummary !== null; + return $this->traits; } - /** - * @throws Exception - */ - public function coverageTextShowOnlySummary() : bool + public function functions() : array { - if ($this->coverageTextShowOnlySummary === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageTextShowOnlySummary; + return $this->functions; } - public function hasCoverageXml() : bool + public function linesOfCode() : LinesOfCode { - return $this->coverageXml !== null; + return $this->linesOfCode; } - /** - * @throws Exception - */ - public function coverageXml() : string + public function numberOfExecutableLines() : int { - if ($this->coverageXml === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageXml; + return $this->numExecutableLines; } - public function hasPathCoverage() : bool + public function numberOfExecutedLines() : int { - return $this->pathCoverage !== null; + return $this->numExecutedLines; } - /** - * @throws Exception - */ - public function pathCoverage() : bool + public function numberOfExecutableBranches() : int { - if ($this->pathCoverage === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->pathCoverage; + return $this->numExecutableBranches; } - public function hasCoverageCacheDirectory() : bool + public function numberOfExecutedBranches() : int { - return $this->coverageCacheDirectory !== null; + return $this->numExecutedBranches; } - /** - * @throws Exception - */ - public function coverageCacheDirectory() : string + public function numberOfExecutablePaths() : int { - if ($this->coverageCacheDirectory === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->coverageCacheDirectory; + return $this->numExecutablePaths; } - public function hasWarmCoverageCache() : bool + public function numberOfExecutedPaths() : int { - return $this->warmCoverageCache !== null; + return $this->numExecutedPaths; } - /** - * @throws Exception - */ - public function warmCoverageCache() : bool + public function numberOfClasses() : int { - if ($this->warmCoverageCache === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->numClasses === null) { + $this->numClasses = 0; + foreach ($this->classes as $class) { + foreach ($class['methods'] as $method) { + if ($method['executableLines'] > 0) { + $this->numClasses++; + continue 2; + } + } + } } - return $this->warmCoverageCache; + return $this->numClasses; } - public function hasDebug() : bool + public function numberOfTestedClasses() : int { - return $this->debug !== null; + return $this->numTestedClasses; } - /** - * @throws Exception - */ - public function debug() : bool + public function numberOfTraits() : int { - if ($this->debug === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->numTraits === null) { + $this->numTraits = 0; + foreach ($this->traits as $trait) { + foreach ($trait['methods'] as $method) { + if ($method['executableLines'] > 0) { + $this->numTraits++; + continue 2; + } + } + } } - return $this->debug; + return $this->numTraits; } - public function hasDefaultTimeLimit() : bool + public function numberOfTestedTraits() : int { - return $this->defaultTimeLimit !== null; + return $this->numTestedTraits; } - /** - * @throws Exception - */ - public function defaultTimeLimit() : int + public function numberOfMethods() : int { - if ($this->defaultTimeLimit === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->numMethods === null) { + $this->numMethods = 0; + foreach ($this->classes as $class) { + foreach ($class['methods'] as $method) { + if ($method['executableLines'] > 0) { + $this->numMethods++; + } + } + } + foreach ($this->traits as $trait) { + foreach ($trait['methods'] as $method) { + if ($method['executableLines'] > 0) { + $this->numMethods++; + } + } + } } - return $this->defaultTimeLimit; - } - public function hasDisableCodeCoverageIgnore() : bool - { - return $this->disableCodeCoverageIgnore !== null; + return $this->numMethods; } - /** - * @throws Exception - */ - public function disableCodeCoverageIgnore() : bool + public function numberOfTestedMethods() : int { - if ($this->disableCodeCoverageIgnore === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->numTestedMethods === null) { + $this->numTestedMethods = 0; + foreach ($this->classes as $class) { + foreach ($class['methods'] as $method) { + if ($method['executableLines'] > 0 && $method['coverage'] === 100) { + $this->numTestedMethods++; + } + } + } + foreach ($this->traits as $trait) { + foreach ($trait['methods'] as $method) { + if ($method['executableLines'] > 0 && $method['coverage'] === 100) { + $this->numTestedMethods++; + } + } + } } - return $this->disableCodeCoverageIgnore; + return $this->numTestedMethods; } - public function hasDisallowTestOutput() : bool + public function numberOfFunctions() : int { - return $this->disallowTestOutput !== null; + return count($this->functions); } - /** - * @throws Exception - */ - public function disallowTestOutput() : bool + public function numberOfTestedFunctions() : int { - if ($this->disallowTestOutput === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->numTestedFunctions === null) { + $this->numTestedFunctions = 0; + foreach ($this->functions as $function) { + if ($function['executableLines'] > 0 && $function['coverage'] === 100) { + $this->numTestedFunctions++; + } + } } - return $this->disallowTestOutput; + return $this->numTestedFunctions; } - public function hasDisallowTodoAnnotatedTests() : bool + private function calculateStatistics(array $classes, array $traits, array $functions) : void { - return $this->disallowTodoAnnotatedTests !== null; + foreach (range(1, $this->linesOfCode->linesOfCode()) as $lineNumber) { + $this->codeUnitsByLine[$lineNumber] = []; + } + $this->processClasses($classes); + $this->processTraits($traits); + $this->processFunctions($functions); + foreach (range(1, $this->linesOfCode->linesOfCode()) as $lineNumber) { + if (isset($this->lineCoverageData[$lineNumber])) { + foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) { + $codeUnit['executableLines']++; + } + unset($codeUnit); + $this->numExecutableLines++; + if (count($this->lineCoverageData[$lineNumber]) > 0) { + foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) { + $codeUnit['executedLines']++; + } + unset($codeUnit); + $this->numExecutedLines++; + } + } + } + foreach ($this->traits as &$trait) { + foreach ($trait['methods'] as &$method) { + $methodLineCoverage = $method['executableLines'] ? $method['executedLines'] / $method['executableLines'] * 100 : 100; + $methodBranchCoverage = $method['executableBranches'] ? $method['executedBranches'] / $method['executableBranches'] * 100 : 0; + $methodPathCoverage = $method['executablePaths'] ? $method['executedPaths'] / $method['executablePaths'] * 100 : 0; + $method['coverage'] = $methodBranchCoverage ?: $methodLineCoverage; + $method['crap'] = (new CrapIndex($method['ccn'], $methodPathCoverage ?: $methodLineCoverage))->asString(); + $trait['ccn'] += $method['ccn']; + } + unset($method); + $traitLineCoverage = $trait['executableLines'] ? $trait['executedLines'] / $trait['executableLines'] * 100 : 100; + $traitBranchCoverage = $trait['executableBranches'] ? $trait['executedBranches'] / $trait['executableBranches'] * 100 : 0; + $traitPathCoverage = $trait['executablePaths'] ? $trait['executedPaths'] / $trait['executablePaths'] * 100 : 0; + $trait['coverage'] = $traitBranchCoverage ?: $traitLineCoverage; + $trait['crap'] = (new CrapIndex($trait['ccn'], $traitPathCoverage ?: $traitLineCoverage))->asString(); + if ($trait['executableLines'] > 0 && $trait['coverage'] === 100) { + $this->numTestedClasses++; + } + } + unset($trait); + foreach ($this->classes as &$class) { + foreach ($class['methods'] as &$method) { + $methodLineCoverage = $method['executableLines'] ? $method['executedLines'] / $method['executableLines'] * 100 : 100; + $methodBranchCoverage = $method['executableBranches'] ? $method['executedBranches'] / $method['executableBranches'] * 100 : 0; + $methodPathCoverage = $method['executablePaths'] ? $method['executedPaths'] / $method['executablePaths'] * 100 : 0; + $method['coverage'] = $methodBranchCoverage ?: $methodLineCoverage; + $method['crap'] = (new CrapIndex($method['ccn'], $methodPathCoverage ?: $methodLineCoverage))->asString(); + $class['ccn'] += $method['ccn']; + } + unset($method); + $classLineCoverage = $class['executableLines'] ? $class['executedLines'] / $class['executableLines'] * 100 : 100; + $classBranchCoverage = $class['executableBranches'] ? $class['executedBranches'] / $class['executableBranches'] * 100 : 0; + $classPathCoverage = $class['executablePaths'] ? $class['executedPaths'] / $class['executablePaths'] * 100 : 0; + $class['coverage'] = $classBranchCoverage ?: $classLineCoverage; + $class['crap'] = (new CrapIndex($class['ccn'], $classPathCoverage ?: $classLineCoverage))->asString(); + if ($class['executableLines'] > 0 && $class['coverage'] === 100) { + $this->numTestedClasses++; + } + } + unset($class); + foreach ($this->functions as &$function) { + $functionLineCoverage = $function['executableLines'] ? $function['executedLines'] / $function['executableLines'] * 100 : 100; + $functionBranchCoverage = $function['executableBranches'] ? $function['executedBranches'] / $function['executableBranches'] * 100 : 0; + $functionPathCoverage = $function['executablePaths'] ? $function['executedPaths'] / $function['executablePaths'] * 100 : 0; + $function['coverage'] = $functionBranchCoverage ?: $functionLineCoverage; + $function['crap'] = (new CrapIndex($function['ccn'], $functionPathCoverage ?: $functionLineCoverage))->asString(); + if ($function['coverage'] === 100) { + $this->numTestedFunctions++; + } + } } - /** - * @throws Exception - */ - public function disallowTodoAnnotatedTests() : bool + private function processClasses(array $classes) : void { - if ($this->disallowTodoAnnotatedTests === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + $link = $this->id() . '.html#'; + foreach ($classes as $className => $class) { + $this->classes[$className] = ['className' => $className, 'namespace' => $class['namespace'], 'methods' => [], 'startLine' => $class['startLine'], 'executableLines' => 0, 'executedLines' => 0, 'executableBranches' => 0, 'executedBranches' => 0, 'executablePaths' => 0, 'executedPaths' => 0, 'ccn' => 0, 'coverage' => 0, 'crap' => 0, 'link' => $link . $class['startLine']]; + foreach ($class['methods'] as $methodName => $method) { + $methodData = $this->newMethod($className, $methodName, $method, $link); + $this->classes[$className]['methods'][$methodName] = $methodData; + $this->classes[$className]['executableBranches'] += $methodData['executableBranches']; + $this->classes[$className]['executedBranches'] += $methodData['executedBranches']; + $this->classes[$className]['executablePaths'] += $methodData['executablePaths']; + $this->classes[$className]['executedPaths'] += $methodData['executedPaths']; + $this->numExecutableBranches += $methodData['executableBranches']; + $this->numExecutedBranches += $methodData['executedBranches']; + $this->numExecutablePaths += $methodData['executablePaths']; + $this->numExecutedPaths += $methodData['executedPaths']; + foreach (range($method['startLine'], $method['endLine']) as $lineNumber) { + $this->codeUnitsByLine[$lineNumber] = [&$this->classes[$className], &$this->classes[$className]['methods'][$methodName]]; + } + } } - return $this->disallowTodoAnnotatedTests; } - public function hasEnforceTimeLimit() : bool + private function processTraits(array $traits) : void { - return $this->enforceTimeLimit !== null; + $link = $this->id() . '.html#'; + foreach ($traits as $traitName => $trait) { + $this->traits[$traitName] = ['traitName' => $traitName, 'namespace' => $trait['namespace'], 'methods' => [], 'startLine' => $trait['startLine'], 'executableLines' => 0, 'executedLines' => 0, 'executableBranches' => 0, 'executedBranches' => 0, 'executablePaths' => 0, 'executedPaths' => 0, 'ccn' => 0, 'coverage' => 0, 'crap' => 0, 'link' => $link . $trait['startLine']]; + foreach ($trait['methods'] as $methodName => $method) { + $methodData = $this->newMethod($traitName, $methodName, $method, $link); + $this->traits[$traitName]['methods'][$methodName] = $methodData; + $this->traits[$traitName]['executableBranches'] += $methodData['executableBranches']; + $this->traits[$traitName]['executedBranches'] += $methodData['executedBranches']; + $this->traits[$traitName]['executablePaths'] += $methodData['executablePaths']; + $this->traits[$traitName]['executedPaths'] += $methodData['executedPaths']; + $this->numExecutableBranches += $methodData['executableBranches']; + $this->numExecutedBranches += $methodData['executedBranches']; + $this->numExecutablePaths += $methodData['executablePaths']; + $this->numExecutedPaths += $methodData['executedPaths']; + foreach (range($method['startLine'], $method['endLine']) as $lineNumber) { + $this->codeUnitsByLine[$lineNumber] = [&$this->traits[$traitName], &$this->traits[$traitName]['methods'][$methodName]]; + } + } + } } - /** - * @throws Exception - */ - public function enforceTimeLimit() : bool + private function processFunctions(array $functions) : void { - if ($this->enforceTimeLimit === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + $link = $this->id() . '.html#'; + foreach ($functions as $functionName => $function) { + $this->functions[$functionName] = ['functionName' => $functionName, 'namespace' => $function['namespace'], 'signature' => $function['signature'], 'startLine' => $function['startLine'], 'endLine' => $function['endLine'], 'executableLines' => 0, 'executedLines' => 0, 'executableBranches' => 0, 'executedBranches' => 0, 'executablePaths' => 0, 'executedPaths' => 0, 'ccn' => $function['ccn'], 'coverage' => 0, 'crap' => 0, 'link' => $link . $function['startLine']]; + foreach (range($function['startLine'], $function['endLine']) as $lineNumber) { + $this->codeUnitsByLine[$lineNumber] = [&$this->functions[$functionName]]; + } + if (isset($this->functionCoverageData[$functionName]['branches'])) { + $this->functions[$functionName]['executableBranches'] = count($this->functionCoverageData[$functionName]['branches']); + $this->functions[$functionName]['executedBranches'] = count(array_filter($this->functionCoverageData[$functionName]['branches'], static function (array $branch) { + return (bool) $branch['hit']; + })); + } + if (isset($this->functionCoverageData[$functionName]['paths'])) { + $this->functions[$functionName]['executablePaths'] = count($this->functionCoverageData[$functionName]['paths']); + $this->functions[$functionName]['executedPaths'] = count(array_filter($this->functionCoverageData[$functionName]['paths'], static function (array $path) { + return (bool) $path['hit']; + })); + } + $this->numExecutableBranches += $this->functions[$functionName]['executableBranches']; + $this->numExecutedBranches += $this->functions[$functionName]['executedBranches']; + $this->numExecutablePaths += $this->functions[$functionName]['executablePaths']; + $this->numExecutedPaths += $this->functions[$functionName]['executedPaths']; } - return $this->enforceTimeLimit; } - public function hasExcludeGroups() : bool + private function newMethod(string $className, string $methodName, array $method, string $link) : array { - return $this->excludeGroups !== null; + $methodData = ['methodName' => $methodName, 'visibility' => $method['visibility'], 'signature' => $method['signature'], 'startLine' => $method['startLine'], 'endLine' => $method['endLine'], 'executableLines' => 0, 'executedLines' => 0, 'executableBranches' => 0, 'executedBranches' => 0, 'executablePaths' => 0, 'executedPaths' => 0, 'ccn' => $method['ccn'], 'coverage' => 0, 'crap' => 0, 'link' => $link . $method['startLine']]; + $key = $className . '->' . $methodName; + if (isset($this->functionCoverageData[$key]['branches'])) { + $methodData['executableBranches'] = count($this->functionCoverageData[$key]['branches']); + $methodData['executedBranches'] = count(array_filter($this->functionCoverageData[$key]['branches'], static function (array $branch) { + return (bool) $branch['hit']; + })); + } + if (isset($this->functionCoverageData[$key]['paths'])) { + $methodData['executablePaths'] = count($this->functionCoverageData[$key]['paths']); + $methodData['executedPaths'] = count(array_filter($this->functionCoverageData[$key]['paths'], static function (array $path) { + return (bool) $path['hit']; + })); + } + return $methodData; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Node; + +use const DIRECTORY_SEPARATOR; +use function array_shift; +use function basename; +use function count; +use function dirname; +use function explode; +use function implode; +use function is_file; +use function str_replace; +use function strpos; +use function substr; +use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; +use PHPUnit\SebastianBergmann\CodeCoverage\ProcessedCodeCoverageData; +use PHPUnit\SebastianBergmann\CodeCoverage\StaticAnalysis\CoveredFileAnalyser; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Builder +{ /** - * @throws Exception + * @var CoveredFileAnalyser */ - public function excludeGroups() : array + private $coveredFileAnalyser; + public function __construct(CoveredFileAnalyser $coveredFileAnalyser) { - if ($this->excludeGroups === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->excludeGroups; + $this->coveredFileAnalyser = $coveredFileAnalyser; } - public function hasExecutionOrder() : bool + public function build(CodeCoverage $coverage) : Directory { - return $this->executionOrder !== null; + $data = clone $coverage->getData(); + // clone because path munging is destructive to the original data + $commonPath = $this->reducePaths($data); + $root = new Directory($commonPath, null); + $this->addItems($root, $this->buildDirectoryStructure($data), $coverage->getTests()); + return $root; } - /** - * @throws Exception - */ - public function executionOrder() : int + private function addItems(Directory $root, array $items, array $tests) : void { - if ($this->executionOrder === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + foreach ($items as $key => $value) { + $key = (string) $key; + if (substr($key, -2) === '/f') { + $key = substr($key, 0, -2); + $filename = $root->pathAsString() . \DIRECTORY_SEPARATOR . $key; + if (is_file($filename)) { + $root->addFile(new File($key, $root, $value['lineCoverage'], $value['functionCoverage'], $tests, $this->coveredFileAnalyser->classesIn($filename), $this->coveredFileAnalyser->traitsIn($filename), $this->coveredFileAnalyser->functionsIn($filename), $this->coveredFileAnalyser->linesOfCodeFor($filename))); + } + } else { + $child = $root->addDirectory($key); + $this->addItems($child, $value, $tests); + } } - return $this->executionOrder; - } - public function hasExecutionOrderDefects() : bool - { - return $this->executionOrderDefects !== null; } /** - * @throws Exception + * Builds an array representation of the directory structure. + * + * For instance, + * + * + * Array + * ( + * [Money.php] => Array + * ( + * ... + * ) + * + * [MoneyBag.php] => Array + * ( + * ... + * ) + * ) + * + * + * is transformed into + * + * + * Array + * ( + * [.] => Array + * ( + * [Money.php] => Array + * ( + * ... + * ) + * + * [MoneyBag.php] => Array + * ( + * ... + * ) + * ) + * ) + * */ - public function executionOrderDefects() : int + private function buildDirectoryStructure(ProcessedCodeCoverageData $data) : array { - if ($this->executionOrderDefects === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + $result = []; + foreach ($data->coveredFiles() as $originalPath) { + $path = explode(\DIRECTORY_SEPARATOR, $originalPath); + $pointer =& $result; + $max = count($path); + for ($i = 0; $i < $max; $i++) { + $type = ''; + if ($i === $max - 1) { + $type = '/f'; + } + $pointer =& $pointer[$path[$i] . $type]; + } + $pointer = ['lineCoverage' => $data->lineCoverage()[$originalPath] ?? [], 'functionCoverage' => $data->functionCoverage()[$originalPath] ?? []]; } - return $this->executionOrderDefects; - } - public function hasFailOnEmptyTestSuite() : bool - { - return $this->failOnEmptyTestSuite !== null; + return $result; } /** - * @throws Exception + * Reduces the paths by cutting the longest common start path. + * + * For instance, + * + * + * Array + * ( + * [/home/sb/Money/Money.php] => Array + * ( + * ... + * ) + * + * [/home/sb/Money/MoneyBag.php] => Array + * ( + * ... + * ) + * ) + * + * + * is reduced to + * + * + * Array + * ( + * [Money.php] => Array + * ( + * ... + * ) + * + * [MoneyBag.php] => Array + * ( + * ... + * ) + * ) + * */ - public function failOnEmptyTestSuite() : bool + private function reducePaths(ProcessedCodeCoverageData $coverage) : string { - if ($this->failOnEmptyTestSuite === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (empty($coverage->coveredFiles())) { + return '.'; } - return $this->failOnEmptyTestSuite; - } - public function hasFailOnIncomplete() : bool - { - return $this->failOnIncomplete !== null; + $commonPath = ''; + $paths = $coverage->coveredFiles(); + if (count($paths) === 1) { + $commonPath = dirname($paths[0]) . \DIRECTORY_SEPARATOR; + $coverage->renameFile($paths[0], basename($paths[0])); + return $commonPath; + } + $max = count($paths); + for ($i = 0; $i < $max; $i++) { + // strip phar:// prefixes + if (strpos($paths[$i], 'phar://') === 0) { + $paths[$i] = substr($paths[$i], 7); + $paths[$i] = str_replace('/', \DIRECTORY_SEPARATOR, $paths[$i]); + } + $paths[$i] = explode(\DIRECTORY_SEPARATOR, $paths[$i]); + if (empty($paths[$i][0])) { + $paths[$i][0] = \DIRECTORY_SEPARATOR; + } + } + $done = \false; + $max = count($paths); + while (!$done) { + for ($i = 0; $i < $max - 1; $i++) { + if (!isset($paths[$i][0]) || !isset($paths[$i + 1][0]) || $paths[$i][0] !== $paths[$i + 1][0]) { + $done = \true; + break; + } + } + if (!$done) { + $commonPath .= $paths[0][0]; + if ($paths[0][0] !== \DIRECTORY_SEPARATOR) { + $commonPath .= \DIRECTORY_SEPARATOR; + } + for ($i = 0; $i < $max; $i++) { + array_shift($paths[$i]); + } + } + } + $original = $coverage->coveredFiles(); + $max = count($original); + for ($i = 0; $i < $max; $i++) { + $coverage->renameFile($original[$i], implode(\DIRECTORY_SEPARATOR, $paths[$i])); + } + return substr($commonPath, 0, -1); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Node; + +use function count; +use RecursiveIterator; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Iterator implements RecursiveIterator +{ /** - * @throws Exception + * @var int */ - public function failOnIncomplete() : bool - { - if ($this->failOnIncomplete === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->failOnIncomplete; - } - public function hasFailOnRisky() : bool - { - return $this->failOnRisky !== null; - } + private $position; /** - * @throws Exception + * @var AbstractNode[] */ - public function failOnRisky() : bool - { - if ($this->failOnRisky === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->failOnRisky; - } - public function hasFailOnSkipped() : bool + private $nodes; + public function __construct(Directory $node) { - return $this->failOnSkipped !== null; + $this->nodes = $node->children(); } /** - * @throws Exception + * Rewinds the Iterator to the first element. */ - public function failOnSkipped() : bool - { - if ($this->failOnSkipped === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->failOnSkipped; - } - public function hasFailOnWarning() : bool + public function rewind() : void { - return $this->failOnWarning !== null; + $this->position = 0; } /** - * @throws Exception + * Checks if there is a current element after calls to rewind() or next(). */ - public function failOnWarning() : bool - { - if ($this->failOnWarning === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->failOnWarning; - } - public function hasFilter() : bool + public function valid() : bool { - return $this->filter !== null; + return $this->position < count($this->nodes); } /** - * @throws Exception + * Returns the key of the current element. */ - public function filter() : string - { - if ($this->filter === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->filter; - } - public function hasGenerateConfiguration() : bool + public function key() : int { - return $this->generateConfiguration !== null; + return $this->position; } /** - * @throws Exception + * Returns the current element. */ - public function generateConfiguration() : bool - { - if ($this->generateConfiguration === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->generateConfiguration; - } - public function hasMigrateConfiguration() : bool + public function current() : ?AbstractNode { - return $this->migrateConfiguration !== null; + return $this->valid() ? $this->nodes[$this->position] : null; } /** - * @throws Exception + * Moves forward to next element. */ - public function migrateConfiguration() : bool - { - if ($this->migrateConfiguration === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->migrateConfiguration; - } - public function hasGroups() : bool + public function next() : void { - return $this->groups !== null; + $this->position++; } /** - * @throws Exception + * Returns the sub iterator for the current element. + * + * @return Iterator */ - public function groups() : array - { - if ($this->groups === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->groups; - } - public function hasTestsCovering() : bool + public function getChildren() : self { - return $this->testsCovering !== null; + return new self($this->nodes[$this->position]); } /** - * @throws Exception + * Checks whether the current element has children. */ - public function testsCovering() : array - { - if ($this->testsCovering === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testsCovering; - } - public function hasTestsUsing() : bool + public function hasChildren() : bool { - return $this->testsUsing !== null; + return $this->nodes[$this->position] instanceof Directory; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Node; + +use function array_merge; +use function count; +use IteratorAggregate; +use RecursiveIteratorIterator; +use PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Directory extends AbstractNode implements IteratorAggregate +{ /** - * @throws Exception + * @var AbstractNode[] */ - public function testsUsing() : array - { - if ($this->testsUsing === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testsUsing; - } - public function hasHelp() : bool - { - return $this->help !== null; - } + private $children = []; /** - * @throws Exception + * @var Directory[] */ - public function help() : bool - { - if ($this->help === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->help; - } - public function hasIncludePath() : bool - { - return $this->includePath !== null; - } + private $directories = []; /** - * @throws Exception + * @var File[] */ - public function includePath() : string - { - if ($this->includePath === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->includePath; - } - public function hasIniSettings() : bool - { - return $this->iniSettings !== null; - } + private $files = []; /** - * @throws Exception + * @var array */ - public function iniSettings() : array - { - if ($this->iniSettings === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->iniSettings; - } - public function hasJunitLogfile() : bool - { - return $this->junitLogfile !== null; - } + private $classes; /** - * @throws Exception + * @var array */ - public function junitLogfile() : string - { - if ($this->junitLogfile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->junitLogfile; - } - public function hasListGroups() : bool - { - return $this->listGroups !== null; - } + private $traits; /** - * @throws Exception + * @var array */ - public function listGroups() : bool - { - if ($this->listGroups === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->listGroups; - } - public function hasListSuites() : bool - { - return $this->listSuites !== null; - } + private $functions; /** - * @throws Exception + * @var LinesOfCode */ - public function listSuites() : bool - { - if ($this->listSuites === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->listSuites; - } - public function hasListTests() : bool - { - return $this->listTests !== null; - } + private $linesOfCode; /** - * @throws Exception + * @var int */ - public function listTests() : bool - { - if ($this->listTests === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->listTests; - } - public function hasListTestsXml() : bool - { - return $this->listTestsXml !== null; - } + private $numFiles = -1; /** - * @throws Exception + * @var int */ - public function listTestsXml() : string - { - if ($this->listTestsXml === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->listTestsXml; - } - public function hasLoader() : bool - { - return $this->loader !== null; - } + private $numExecutableLines = -1; /** - * @throws Exception + * @var int */ - public function loader() : string - { - if ($this->loader === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->loader; - } - public function hasNoCoverage() : bool - { - return $this->noCoverage !== null; - } + private $numExecutedLines = -1; /** - * @throws Exception + * @var int */ - public function noCoverage() : bool - { - if ($this->noCoverage === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->noCoverage; - } - public function hasNoExtensions() : bool - { - return $this->noExtensions !== null; - } + private $numExecutableBranches = -1; /** - * @throws Exception + * @var int */ - public function noExtensions() : bool - { - if ($this->noExtensions === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->noExtensions; - } - public function hasExtensions() : bool - { - return $this->extensions !== null; - } + private $numExecutedBranches = -1; + /** + * @var int + */ + private $numExecutablePaths = -1; + /** + * @var int + */ + private $numExecutedPaths = -1; /** - * @throws Exception + * @var int */ - public function extensions() : array - { - if ($this->extensions === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->extensions; - } - public function hasUnavailableExtensions() : bool - { - return $this->unavailableExtensions !== null; - } + private $numClasses = -1; /** - * @throws Exception + * @var int */ - public function unavailableExtensions() : array - { - if ($this->unavailableExtensions === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->unavailableExtensions; - } - public function hasNoInteraction() : bool - { - return $this->noInteraction !== null; - } + private $numTestedClasses = -1; /** - * @throws Exception + * @var int */ - public function noInteraction() : bool - { - if ($this->noInteraction === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->noInteraction; - } - public function hasNoLogging() : bool - { - return $this->noLogging !== null; - } + private $numTraits = -1; /** - * @throws Exception + * @var int */ - public function noLogging() : bool - { - if ($this->noLogging === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->noLogging; - } - public function hasPrinter() : bool - { - return $this->printer !== null; - } + private $numTestedTraits = -1; /** - * @throws Exception + * @var int */ - public function printer() : string - { - if ($this->printer === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->printer; - } - public function hasProcessIsolation() : bool - { - return $this->processIsolation !== null; - } + private $numMethods = -1; /** - * @throws Exception + * @var int */ - public function processIsolation() : bool - { - if ($this->processIsolation === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->processIsolation; - } - public function hasRandomOrderSeed() : bool - { - return $this->randomOrderSeed !== null; - } + private $numTestedMethods = -1; /** - * @throws Exception + * @var int */ - public function randomOrderSeed() : int - { - if ($this->randomOrderSeed === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->randomOrderSeed; - } - public function hasRepeat() : bool - { - return $this->repeat !== null; - } + private $numFunctions = -1; /** - * @throws Exception + * @var int */ - public function repeat() : int + private $numTestedFunctions = -1; + public function count() : int { - if ($this->repeat === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->numFiles === -1) { + $this->numFiles = 0; + foreach ($this->children as $child) { + $this->numFiles += count($child); + } } - return $this->repeat; - } - public function hasReportUselessTests() : bool - { - return $this->reportUselessTests !== null; + return $this->numFiles; } - /** - * @throws Exception - */ - public function reportUselessTests() : bool + public function getIterator() : RecursiveIteratorIterator { - if ($this->reportUselessTests === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->reportUselessTests; + return new RecursiveIteratorIterator(new Iterator($this), RecursiveIteratorIterator::SELF_FIRST); } - public function hasResolveDependencies() : bool + public function addDirectory(string $name) : self { - return $this->resolveDependencies !== null; + $directory = new self($name, $this); + $this->children[] = $directory; + $this->directories[] =& $this->children[count($this->children) - 1]; + return $directory; } - /** - * @throws Exception - */ - public function resolveDependencies() : bool + public function addFile(File $file) : void { - if ($this->resolveDependencies === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->resolveDependencies; + $this->children[] = $file; + $this->files[] =& $this->children[count($this->children) - 1]; + $this->numExecutableLines = -1; + $this->numExecutedLines = -1; } - public function hasReverseList() : bool + public function directories() : array { - return $this->reverseList !== null; + return $this->directories; } - /** - * @throws Exception - */ - public function reverseList() : bool + public function files() : array { - if ($this->reverseList === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->reverseList; + return $this->files; } - public function hasStderr() : bool + public function children() : array { - return $this->stderr !== null; + return $this->children; } - /** - * @throws Exception - */ - public function stderr() : bool + public function classes() : array { - if ($this->stderr === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->classes === null) { + $this->classes = []; + foreach ($this->children as $child) { + $this->classes = array_merge($this->classes, $child->classes()); + } } - return $this->stderr; + return $this->classes; } - public function hasStrictCoverage() : bool + public function traits() : array { - return $this->strictCoverage !== null; + if ($this->traits === null) { + $this->traits = []; + foreach ($this->children as $child) { + $this->traits = array_merge($this->traits, $child->traits()); + } + } + return $this->traits; } - /** - * @throws Exception - */ - public function strictCoverage() : bool + public function functions() : array { - if ($this->strictCoverage === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->functions === null) { + $this->functions = []; + foreach ($this->children as $child) { + $this->functions = array_merge($this->functions, $child->functions()); + } } - return $this->strictCoverage; + return $this->functions; } - public function hasStopOnDefect() : bool + public function linesOfCode() : LinesOfCode { - return $this->stopOnDefect !== null; + if ($this->linesOfCode === null) { + $this->linesOfCode = new LinesOfCode(0, 0, 0, 0); + foreach ($this->children as $child) { + $this->linesOfCode = $this->linesOfCode->plus($child->linesOfCode()); + } + } + return $this->linesOfCode; } - /** - * @throws Exception - */ - public function stopOnDefect() : bool + public function numberOfExecutableLines() : int { - if ($this->stopOnDefect === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->numExecutableLines === -1) { + $this->numExecutableLines = 0; + foreach ($this->children as $child) { + $this->numExecutableLines += $child->numberOfExecutableLines(); + } } - return $this->stopOnDefect; + return $this->numExecutableLines; } - public function hasStopOnError() : bool + public function numberOfExecutedLines() : int { - return $this->stopOnError !== null; + if ($this->numExecutedLines === -1) { + $this->numExecutedLines = 0; + foreach ($this->children as $child) { + $this->numExecutedLines += $child->numberOfExecutedLines(); + } + } + return $this->numExecutedLines; } - /** - * @throws Exception - */ - public function stopOnError() : bool + public function numberOfExecutableBranches() : int { - if ($this->stopOnError === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->numExecutableBranches === -1) { + $this->numExecutableBranches = 0; + foreach ($this->children as $child) { + $this->numExecutableBranches += $child->numberOfExecutableBranches(); + } } - return $this->stopOnError; + return $this->numExecutableBranches; } - public function hasStopOnFailure() : bool + public function numberOfExecutedBranches() : int { - return $this->stopOnFailure !== null; + if ($this->numExecutedBranches === -1) { + $this->numExecutedBranches = 0; + foreach ($this->children as $child) { + $this->numExecutedBranches += $child->numberOfExecutedBranches(); + } + } + return $this->numExecutedBranches; } - /** - * @throws Exception - */ - public function stopOnFailure() : bool + public function numberOfExecutablePaths() : int { - if ($this->stopOnFailure === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->numExecutablePaths === -1) { + $this->numExecutablePaths = 0; + foreach ($this->children as $child) { + $this->numExecutablePaths += $child->numberOfExecutablePaths(); + } } - return $this->stopOnFailure; + return $this->numExecutablePaths; } - public function hasStopOnIncomplete() : bool + public function numberOfExecutedPaths() : int { - return $this->stopOnIncomplete !== null; + if ($this->numExecutedPaths === -1) { + $this->numExecutedPaths = 0; + foreach ($this->children as $child) { + $this->numExecutedPaths += $child->numberOfExecutedPaths(); + } + } + return $this->numExecutedPaths; } - /** - * @throws Exception - */ - public function stopOnIncomplete() : bool + public function numberOfClasses() : int { - if ($this->stopOnIncomplete === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->numClasses === -1) { + $this->numClasses = 0; + foreach ($this->children as $child) { + $this->numClasses += $child->numberOfClasses(); + } } - return $this->stopOnIncomplete; + return $this->numClasses; } - public function hasStopOnRisky() : bool + public function numberOfTestedClasses() : int { - return $this->stopOnRisky !== null; + if ($this->numTestedClasses === -1) { + $this->numTestedClasses = 0; + foreach ($this->children as $child) { + $this->numTestedClasses += $child->numberOfTestedClasses(); + } + } + return $this->numTestedClasses; } - /** - * @throws Exception - */ - public function stopOnRisky() : bool + public function numberOfTraits() : int { - if ($this->stopOnRisky === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->numTraits === -1) { + $this->numTraits = 0; + foreach ($this->children as $child) { + $this->numTraits += $child->numberOfTraits(); + } } - return $this->stopOnRisky; + return $this->numTraits; } - public function hasStopOnSkipped() : bool + public function numberOfTestedTraits() : int { - return $this->stopOnSkipped !== null; + if ($this->numTestedTraits === -1) { + $this->numTestedTraits = 0; + foreach ($this->children as $child) { + $this->numTestedTraits += $child->numberOfTestedTraits(); + } + } + return $this->numTestedTraits; } - /** - * @throws Exception - */ - public function stopOnSkipped() : bool + public function numberOfMethods() : int { - if ($this->stopOnSkipped === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->numMethods === -1) { + $this->numMethods = 0; + foreach ($this->children as $child) { + $this->numMethods += $child->numberOfMethods(); + } } - return $this->stopOnSkipped; + return $this->numMethods; } - public function hasStopOnWarning() : bool + public function numberOfTestedMethods() : int { - return $this->stopOnWarning !== null; + if ($this->numTestedMethods === -1) { + $this->numTestedMethods = 0; + foreach ($this->children as $child) { + $this->numTestedMethods += $child->numberOfTestedMethods(); + } + } + return $this->numTestedMethods; } - /** - * @throws Exception - */ - public function stopOnWarning() : bool + public function numberOfFunctions() : int { - if ($this->stopOnWarning === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->numFunctions === -1) { + $this->numFunctions = 0; + foreach ($this->children as $child) { + $this->numFunctions += $child->numberOfFunctions(); + } } - return $this->stopOnWarning; + return $this->numFunctions; } - public function hasTeamcityLogfile() : bool + public function numberOfTestedFunctions() : int { - return $this->teamcityLogfile !== null; + if ($this->numTestedFunctions === -1) { + $this->numTestedFunctions = 0; + foreach ($this->children as $child) { + $this->numTestedFunctions += $child->numberOfTestedFunctions(); + } + } + return $this->numTestedFunctions; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage\Node; + +use const DIRECTORY_SEPARATOR; +use function array_merge; +use function str_replace; +use function substr; +use Countable; +use PHPUnit\SebastianBergmann\CodeCoverage\Percentage; +use PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +abstract class AbstractNode implements Countable +{ /** - * @throws Exception + * @var string */ - public function teamcityLogfile() : string - { - if ($this->teamcityLogfile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->teamcityLogfile; - } - public function hasTestdoxExcludeGroups() : bool - { - return $this->testdoxExcludeGroups !== null; - } + private $name; /** - * @throws Exception + * @var string */ - public function testdoxExcludeGroups() : array - { - if ($this->testdoxExcludeGroups === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testdoxExcludeGroups; - } - public function hasTestdoxGroups() : bool - { - return $this->testdoxGroups !== null; - } + private $pathAsString; /** - * @throws Exception + * @var array */ - public function testdoxGroups() : array - { - if ($this->testdoxGroups === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testdoxGroups; - } - public function hasTestdoxHtmlFile() : bool - { - return $this->testdoxHtmlFile !== null; - } + private $pathAsArray; /** - * @throws Exception + * @var AbstractNode */ - public function testdoxHtmlFile() : string + private $parent; + /** + * @var string + */ + private $id; + public function __construct(string $name, self $parent = null) { - if ($this->testdoxHtmlFile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if (substr($name, -1) === \DIRECTORY_SEPARATOR) { + $name = substr($name, 0, -1); } - return $this->testdoxHtmlFile; + $this->name = $name; + $this->parent = $parent; } - public function hasTestdoxTextFile() : bool + public function name() : string { - return $this->testdoxTextFile !== null; + return $this->name; } - /** - * @throws Exception - */ - public function testdoxTextFile() : string + public function id() : string { - if ($this->testdoxTextFile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->id === null) { + $parent = $this->parent(); + if ($parent === null) { + $this->id = 'index'; + } else { + $parentId = $parent->id(); + if ($parentId === 'index') { + $this->id = str_replace(':', '_', $this->name); + } else { + $this->id = $parentId . '/' . $this->name; + } + } } - return $this->testdoxTextFile; + return $this->id; } - public function hasTestdoxXmlFile() : bool + public function pathAsString() : string { - return $this->testdoxXmlFile !== null; + if ($this->pathAsString === null) { + if ($this->parent === null) { + $this->pathAsString = $this->name; + } else { + $this->pathAsString = $this->parent->pathAsString() . \DIRECTORY_SEPARATOR . $this->name; + } + } + return $this->pathAsString; } - /** - * @throws Exception - */ - public function testdoxXmlFile() : string + public function pathAsArray() : array { - if ($this->testdoxXmlFile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + if ($this->pathAsArray === null) { + if ($this->parent === null) { + $this->pathAsArray = []; + } else { + $this->pathAsArray = $this->parent->pathAsArray(); + } + $this->pathAsArray[] = $this; } - return $this->testdoxXmlFile; + return $this->pathAsArray; } - public function hasTestSuffixes() : bool + public function parent() : ?self { - return $this->testSuffixes !== null; + return $this->parent; } - /** - * @throws Exception - */ - public function testSuffixes() : array + public function percentageOfTestedClasses() : Percentage + { + return Percentage::fromFractionAndTotal($this->numberOfTestedClasses(), $this->numberOfClasses()); + } + public function percentageOfTestedTraits() : Percentage { - if ($this->testSuffixes === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testSuffixes; + return Percentage::fromFractionAndTotal($this->numberOfTestedTraits(), $this->numberOfTraits()); } - public function hasTestSuite() : bool + public function percentageOfTestedClassesAndTraits() : Percentage { - return $this->testSuite !== null; + return Percentage::fromFractionAndTotal($this->numberOfTestedClassesAndTraits(), $this->numberOfClassesAndTraits()); } - /** - * @throws Exception - */ - public function testSuite() : string + public function percentageOfTestedFunctions() : Percentage { - if ($this->testSuite === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->testSuite; + return Percentage::fromFractionAndTotal($this->numberOfTestedFunctions(), $this->numberOfFunctions()); } - public function unrecognizedOptions() : array + public function percentageOfTestedMethods() : Percentage { - return $this->unrecognizedOptions; + return Percentage::fromFractionAndTotal($this->numberOfTestedMethods(), $this->numberOfMethods()); } - public function hasUnrecognizedOrderBy() : bool + public function percentageOfTestedFunctionsAndMethods() : Percentage { - return $this->unrecognizedOrderBy !== null; + return Percentage::fromFractionAndTotal($this->numberOfTestedFunctionsAndMethods(), $this->numberOfFunctionsAndMethods()); } - /** - * @throws Exception - */ - public function unrecognizedOrderBy() : string + public function percentageOfExecutedLines() : Percentage { - if ($this->unrecognizedOrderBy === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->unrecognizedOrderBy; + return Percentage::fromFractionAndTotal($this->numberOfExecutedLines(), $this->numberOfExecutableLines()); } - public function hasUseDefaultConfiguration() : bool + public function percentageOfExecutedBranches() : Percentage { - return $this->useDefaultConfiguration !== null; + return Percentage::fromFractionAndTotal($this->numberOfExecutedBranches(), $this->numberOfExecutableBranches()); } - /** - * @throws Exception - */ - public function useDefaultConfiguration() : bool + public function percentageOfExecutedPaths() : Percentage { - if ($this->useDefaultConfiguration === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->useDefaultConfiguration; + return Percentage::fromFractionAndTotal($this->numberOfExecutedPaths(), $this->numberOfExecutablePaths()); } - public function hasVerbose() : bool + public function numberOfClassesAndTraits() : int { - return $this->verbose !== null; + return $this->numberOfClasses() + $this->numberOfTraits(); } - /** - * @throws Exception - */ - public function verbose() : bool + public function numberOfTestedClassesAndTraits() : int { - if ($this->verbose === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->verbose; + return $this->numberOfTestedClasses() + $this->numberOfTestedTraits(); } - public function hasVersion() : bool + public function classesAndTraits() : array { - return $this->version !== null; + return array_merge($this->classes(), $this->traits()); } - /** - * @throws Exception - */ - public function version() : bool + public function numberOfFunctionsAndMethods() : int { - if ($this->version === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); - } - return $this->version; + return $this->numberOfFunctions() + $this->numberOfMethods(); } - public function hasXdebugFilterFile() : bool + public function numberOfTestedFunctionsAndMethods() : int { - return $this->xdebugFilterFile !== null; + return $this->numberOfTestedFunctions() + $this->numberOfTestedMethods(); } + public abstract function classes() : array; + public abstract function traits() : array; + public abstract function functions() : array; + public abstract function linesOfCode() : LinesOfCode; + public abstract function numberOfExecutableLines() : int; + public abstract function numberOfExecutedLines() : int; + public abstract function numberOfExecutableBranches() : int; + public abstract function numberOfExecutedBranches() : int; + public abstract function numberOfExecutablePaths() : int; + public abstract function numberOfExecutedPaths() : int; + public abstract function numberOfClasses() : int; + public abstract function numberOfTestedClasses() : int; + public abstract function numberOfTraits() : int; + public abstract function numberOfTestedTraits() : int; + public abstract function numberOfMethods() : int; + public abstract function numberOfTestedMethods() : int; + public abstract function numberOfFunctions() : int; + public abstract function numberOfTestedFunctions() : int; +} +php-code-coverage + +Copyright (c) 2009-2021, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeCoverage; + +use function is_dir; +use function mkdir; +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Directory +{ /** - * @throws Exception + * @throws DirectoryCouldNotBeCreatedException */ - public function xdebugFilterFile() : string + public static function create(string $directory) : void { - if ($this->xdebugFilterFile === null) { - throw new \PHPUnit\TextUI\CliArguments\Exception(); + $success = !(!is_dir($directory) && !@mkdir($directory, 0777, \true) && !is_dir($directory)); + if (!$success) { + throw new DirectoryCouldNotBeCreatedException(sprintf('Directory "%s" could not be created', $directory)); } - return $this->xdebugFilterFile; } } [], 'listGroups' => \false, 'listSuites' => \false, 'listTests' => \false, 'listTestsXml' => \false, 'loader' => null, 'useDefaultConfiguration' => \true, 'loadedExtensions' => [], 'unavailableExtensions' => [], 'notLoadedExtensions' => []]; - if ($arguments->hasColors()) { - $result['colors'] = $arguments->colors(); - } - if ($arguments->hasBootstrap()) { - $result['bootstrap'] = $arguments->bootstrap(); - } - if ($arguments->hasCacheResult()) { - $result['cacheResult'] = $arguments->cacheResult(); - } - if ($arguments->hasCacheResultFile()) { - $result['cacheResultFile'] = $arguments->cacheResultFile(); - } - if ($arguments->hasColumns()) { - $result['columns'] = $arguments->columns(); - } - if ($arguments->hasConfiguration()) { - $result['configuration'] = $arguments->configuration(); - } - if ($arguments->hasCoverageCacheDirectory()) { - $result['coverageCacheDirectory'] = $arguments->coverageCacheDirectory(); - } - if ($arguments->hasWarmCoverageCache()) { - $result['warmCoverageCache'] = $arguments->warmCoverageCache(); - } - if ($arguments->hasCoverageClover()) { - $result['coverageClover'] = $arguments->coverageClover(); - } - if ($arguments->hasCoverageCobertura()) { - $result['coverageCobertura'] = $arguments->coverageCobertura(); - } - if ($arguments->hasCoverageCrap4J()) { - $result['coverageCrap4J'] = $arguments->coverageCrap4J(); - } - if ($arguments->hasCoverageHtml()) { - $result['coverageHtml'] = $arguments->coverageHtml(); - } - if ($arguments->hasCoveragePhp()) { - $result['coveragePHP'] = $arguments->coveragePhp(); - } - if ($arguments->hasCoverageText()) { - $result['coverageText'] = $arguments->coverageText(); - } - if ($arguments->hasCoverageTextShowUncoveredFiles()) { - $result['coverageTextShowUncoveredFiles'] = $arguments->hasCoverageTextShowUncoveredFiles(); - } - if ($arguments->hasCoverageTextShowOnlySummary()) { - $result['coverageTextShowOnlySummary'] = $arguments->coverageTextShowOnlySummary(); - } - if ($arguments->hasCoverageXml()) { - $result['coverageXml'] = $arguments->coverageXml(); - } - if ($arguments->hasPathCoverage()) { - $result['pathCoverage'] = $arguments->pathCoverage(); - } - if ($arguments->hasDebug()) { - $result['debug'] = $arguments->debug(); - } - if ($arguments->hasHelp()) { - $result['help'] = $arguments->help(); - } - if ($arguments->hasFilter()) { - $result['filter'] = $arguments->filter(); - } - if ($arguments->hasTestSuite()) { - $result['testsuite'] = $arguments->testSuite(); - } - if ($arguments->hasGroups()) { - $result['groups'] = $arguments->groups(); - } - if ($arguments->hasExcludeGroups()) { - $result['excludeGroups'] = $arguments->excludeGroups(); - } - if ($arguments->hasTestsCovering()) { - $result['testsCovering'] = $arguments->testsCovering(); - } - if ($arguments->hasTestsUsing()) { - $result['testsUsing'] = $arguments->testsUsing(); - } - if ($arguments->hasTestSuffixes()) { - $result['testSuffixes'] = $arguments->testSuffixes(); - } - if ($arguments->hasIncludePath()) { - $result['includePath'] = $arguments->includePath(); - } - if ($arguments->hasListGroups()) { - $result['listGroups'] = $arguments->listGroups(); - } - if ($arguments->hasListSuites()) { - $result['listSuites'] = $arguments->listSuites(); - } - if ($arguments->hasListTests()) { - $result['listTests'] = $arguments->listTests(); - } - if ($arguments->hasListTestsXml()) { - $result['listTestsXml'] = $arguments->listTestsXml(); - } - if ($arguments->hasPrinter()) { - $result['printer'] = $arguments->printer(); - } - if ($arguments->hasLoader()) { - $result['loader'] = $arguments->loader(); - } - if ($arguments->hasJunitLogfile()) { - $result['junitLogfile'] = $arguments->junitLogfile(); - } - if ($arguments->hasTeamcityLogfile()) { - $result['teamcityLogfile'] = $arguments->teamcityLogfile(); - } - if ($arguments->hasExecutionOrder()) { - $result['executionOrder'] = $arguments->executionOrder(); - } - if ($arguments->hasExecutionOrderDefects()) { - $result['executionOrderDefects'] = $arguments->executionOrderDefects(); - } - if ($arguments->hasExtensions()) { - $result['extensions'] = $arguments->extensions(); - } - if ($arguments->hasUnavailableExtensions()) { - $result['unavailableExtensions'] = $arguments->unavailableExtensions(); - } - if ($arguments->hasResolveDependencies()) { - $result['resolveDependencies'] = $arguments->resolveDependencies(); - } - if ($arguments->hasProcessIsolation()) { - $result['processIsolation'] = $arguments->processIsolation(); - } - if ($arguments->hasRepeat()) { - $result['repeat'] = $arguments->repeat(); - } - if ($arguments->hasStderr()) { - $result['stderr'] = $arguments->stderr(); - } - if ($arguments->hasStopOnDefect()) { - $result['stopOnDefect'] = $arguments->stopOnDefect(); - } - if ($arguments->hasStopOnError()) { - $result['stopOnError'] = $arguments->stopOnError(); - } - if ($arguments->hasStopOnFailure()) { - $result['stopOnFailure'] = $arguments->stopOnFailure(); - } - if ($arguments->hasStopOnWarning()) { - $result['stopOnWarning'] = $arguments->stopOnWarning(); - } - if ($arguments->hasStopOnIncomplete()) { - $result['stopOnIncomplete'] = $arguments->stopOnIncomplete(); - } - if ($arguments->hasStopOnRisky()) { - $result['stopOnRisky'] = $arguments->stopOnRisky(); - } - if ($arguments->hasStopOnSkipped()) { - $result['stopOnSkipped'] = $arguments->stopOnSkipped(); - } - if ($arguments->hasFailOnEmptyTestSuite()) { - $result['failOnEmptyTestSuite'] = $arguments->failOnEmptyTestSuite(); - } - if ($arguments->hasFailOnIncomplete()) { - $result['failOnIncomplete'] = $arguments->failOnIncomplete(); - } - if ($arguments->hasFailOnRisky()) { - $result['failOnRisky'] = $arguments->failOnRisky(); - } - if ($arguments->hasFailOnSkipped()) { - $result['failOnSkipped'] = $arguments->failOnSkipped(); - } - if ($arguments->hasFailOnWarning()) { - $result['failOnWarning'] = $arguments->failOnWarning(); - } - if ($arguments->hasTestdoxGroups()) { - $result['testdoxGroups'] = $arguments->testdoxGroups(); - } - if ($arguments->hasTestdoxExcludeGroups()) { - $result['testdoxExcludeGroups'] = $arguments->testdoxExcludeGroups(); - } - if ($arguments->hasTestdoxHtmlFile()) { - $result['testdoxHTMLFile'] = $arguments->testdoxHtmlFile(); - } - if ($arguments->hasTestdoxTextFile()) { - $result['testdoxTextFile'] = $arguments->testdoxTextFile(); - } - if ($arguments->hasTestdoxXmlFile()) { - $result['testdoxXMLFile'] = $arguments->testdoxXmlFile(); - } - if ($arguments->hasUseDefaultConfiguration()) { - $result['useDefaultConfiguration'] = $arguments->useDefaultConfiguration(); - } - if ($arguments->hasNoExtensions()) { - $result['noExtensions'] = $arguments->noExtensions(); - } - if ($arguments->hasNoCoverage()) { - $result['noCoverage'] = $arguments->noCoverage(); - } - if ($arguments->hasNoLogging()) { - $result['noLogging'] = $arguments->noLogging(); - } - if ($arguments->hasNoInteraction()) { - $result['noInteraction'] = $arguments->noInteraction(); - } - if ($arguments->hasBackupGlobals()) { - $result['backupGlobals'] = $arguments->backupGlobals(); - } - if ($arguments->hasBackupStaticAttributes()) { - $result['backupStaticAttributes'] = $arguments->backupStaticAttributes(); - } - if ($arguments->hasVerbose()) { - $result['verbose'] = $arguments->verbose(); - } - if ($arguments->hasReportUselessTests()) { - $result['reportUselessTests'] = $arguments->reportUselessTests(); - } - if ($arguments->hasStrictCoverage()) { - $result['strictCoverage'] = $arguments->strictCoverage(); + return (new DOMDocument())->importNode($element, \true); + } + /** + * @deprecated Only used by assertEqualXMLStructure() + */ + public static function removeCharacterDataNodes(DOMNode $node) : void + { + if ($node->hasChildNodes()) { + for ($i = $node->childNodes->length - 1; $i >= 0; $i--) { + if (($child = $node->childNodes->item($i)) instanceof DOMCharacterData) { + $node->removeChild($child); + } + } } - if ($arguments->hasDisableCodeCoverageIgnore()) { - $result['disableCodeCoverageIgnore'] = $arguments->disableCodeCoverageIgnore(); + } + /** + * Escapes a string for the use in XML documents. + * + * Any Unicode character is allowed, excluding the surrogate blocks, FFFE, + * and FFFF (not even as character reference). + * + * @see https://www.w3.org/TR/xml/#charsets + */ + public static function prepareString(string $string) : string + { + return preg_replace('/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/', '', htmlspecialchars(self::convertToUtf8($string), \ENT_QUOTES)); + } + /** + * "Convert" a DOMElement object into a PHP variable. + */ + public static function xmlToVariable(DOMElement $element) + { + $variable = null; + switch ($element->tagName) { + case 'array': + $variable = []; + foreach ($element->childNodes as $entry) { + if (!$entry instanceof DOMElement || $entry->tagName !== 'element') { + continue; + } + $item = $entry->childNodes->item(0); + if ($item instanceof DOMText) { + $item = $entry->childNodes->item(1); + } + $value = self::xmlToVariable($item); + if ($entry->hasAttribute('key')) { + $variable[(string) $entry->getAttribute('key')] = $value; + } else { + $variable[] = $value; + } + } + break; + case 'object': + $className = $element->getAttribute('class'); + if ($element->hasChildNodes()) { + $arguments = $element->childNodes->item(0)->childNodes; + $constructorArgs = []; + foreach ($arguments as $argument) { + if ($argument instanceof DOMElement) { + $constructorArgs[] = self::xmlToVariable($argument); + } + } + try { + assert(class_exists($className)); + $variable = (new ReflectionClass($className))->newInstanceArgs($constructorArgs); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Util\Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } else { + $variable = new $className(); + } + break; + case 'boolean': + $variable = $element->textContent === 'true'; + break; + case 'integer': + case 'double': + case 'string': + $variable = $element->textContent; + settype($variable, $element->tagName); + break; } - if ($arguments->hasBeStrictAboutChangesToGlobalState()) { - $result['beStrictAboutChangesToGlobalState'] = $arguments->beStrictAboutChangesToGlobalState(); + return $variable; + } + private static function convertToUtf8(string $string) : string + { + if (!self::isUtf8($string)) { + $string = mb_convert_encoding($string, 'UTF-8'); } - if ($arguments->hasDisallowTestOutput()) { - $result['disallowTestOutput'] = $arguments->disallowTestOutput(); + return $string; + } + private static function isUtf8(string $string) : bool + { + $length = strlen($string); + for ($i = 0; $i < $length; $i++) { + if (ord($string[$i]) < 0x80) { + $n = 0; + } elseif ((ord($string[$i]) & 0xe0) === 0xc0) { + $n = 1; + } elseif ((ord($string[$i]) & 0xf0) === 0xe0) { + $n = 2; + } elseif ((ord($string[$i]) & 0xf0) === 0xf0) { + $n = 3; + } else { + return \false; + } + for ($j = 0; $j < $n; $j++) { + if (++$i === $length || (ord($string[$i]) & 0xc0) !== 0x80) { + return \false; + } + } } - if ($arguments->hasBeStrictAboutResourceUsageDuringSmallTests()) { - $result['beStrictAboutResourceUsageDuringSmallTests'] = $arguments->beStrictAboutResourceUsageDuringSmallTests(); + return \true; + } +} + + * + * 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_keys; +use function array_reverse; +use function defined; +use function get_defined_constants; +use function get_included_files; +use function in_array; +use function ini_get_all; +use function is_array; +use function is_file; +use function is_scalar; +use function preg_match; +use function serialize; +use function sprintf; +use function strpos; +use function var_export; +use Closure; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class GlobalState +{ + /** + * @var string[] + */ + private const SUPER_GLOBAL_ARRAYS = ['_ENV', '_POST', '_GET', '_COOKIE', '_SERVER', '_FILES', '_REQUEST']; + /** + * @throws Exception + */ + public static function getIncludedFilesAsString() : string + { + return self::processIncludedFilesAsString(get_included_files()); + } + /** + * @param string[] $files + * + * @throws Exception + */ + public static function processIncludedFilesAsString(array $files) : string + { + $excludeList = new \PHPUnit\Util\ExcludeList(); + $prefix = \false; + $result = ''; + if (defined('__PHPUNIT_PHAR__')) { + $prefix = 'phar://' . __PHPUNIT_PHAR__ . '/'; } - if ($arguments->hasDefaultTimeLimit()) { - $result['defaultTimeLimit'] = $arguments->defaultTimeLimit(); + // Do not process bootstrap script + unset($files[0]); + foreach (array_reverse($files) as $file) { + if (!empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) && in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], \true)) { + continue; + } + if ($prefix !== \false && strpos($file, $prefix) === 0) { + continue; + } + // Skip virtual file system protocols + if (preg_match('/^(vfs|phpvfs[a-z0-9]+):/', $file)) { + continue; + } + if (!$excludeList->isExcluded($file) && is_file($file)) { + $result = 'require_once \'' . $file . "';\n" . $result; + } } - if ($arguments->hasEnforceTimeLimit()) { - $result['enforceTimeLimit'] = $arguments->enforceTimeLimit(); + return $result; + } + public static function getIniSettingsAsString() : string + { + $result = ''; + foreach (ini_get_all(null, \false) as $key => $value) { + $result .= sprintf('@ini_set(%s, %s);' . "\n", self::exportVariable($key), self::exportVariable((string) $value)); } - if ($arguments->hasDisallowTodoAnnotatedTests()) { - $result['disallowTodoAnnotatedTests'] = $arguments->disallowTodoAnnotatedTests(); + return $result; + } + public static function getConstantsAsString() : string + { + $constants = get_defined_constants(\true); + $result = ''; + if (isset($constants['user'])) { + foreach ($constants['user'] as $name => $value) { + $result .= sprintf('if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", $name, $name, self::exportVariable($value)); + } } - if ($arguments->hasReverseList()) { - $result['reverseList'] = $arguments->reverseList(); + return $result; + } + public static function getGlobalsAsString() : string + { + $result = ''; + foreach (self::SUPER_GLOBAL_ARRAYS as $superGlobalArray) { + if (isset($GLOBALS[$superGlobalArray]) && is_array($GLOBALS[$superGlobalArray])) { + foreach (array_keys($GLOBALS[$superGlobalArray]) as $key) { + if ($GLOBALS[$superGlobalArray][$key] instanceof Closure) { + continue; + } + $result .= sprintf('$GLOBALS[\'%s\'][\'%s\'] = %s;' . "\n", $superGlobalArray, $key, self::exportVariable($GLOBALS[$superGlobalArray][$key])); + } + } } - if ($arguments->hasCoverageFilter()) { - $result['coverageFilter'] = $arguments->coverageFilter(); + $excludeList = self::SUPER_GLOBAL_ARRAYS; + $excludeList[] = 'GLOBALS'; + foreach (array_keys($GLOBALS) as $key) { + if (!$GLOBALS[$key] instanceof Closure && !in_array($key, $excludeList, \true)) { + $result .= sprintf('$GLOBALS[\'%s\'] = %s;' . "\n", $key, self::exportVariable($GLOBALS[$key])); + } } - if ($arguments->hasRandomOrderSeed()) { - $result['randomOrderSeed'] = $arguments->randomOrderSeed(); + return $result; + } + private static function exportVariable($variable) : string + { + if (is_scalar($variable) || $variable === null || is_array($variable) && self::arrayOnlyContainsScalars($variable)) { + return var_export($variable, \true); } - if ($arguments->hasXdebugFilterFile()) { - $result['xdebugFilterFile'] = $arguments->xdebugFilterFile(); + return 'unserialize(' . var_export(serialize($variable), \true) . ')'; + } + private static function arrayOnlyContainsScalars(array $array) : bool + { + $result = \true; + foreach ($array as $element) { + if (is_array($element)) { + $result = self::arrayOnlyContainsScalars($element); + } elseif (!is_scalar($element) && $element !== null) { + $result = \false; + } + if (!$result) { + break; + } } return $result; } @@ -47079,17 +50867,14 @@ declare (strict_types=1); * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI; +namespace PHPUnit\Util; -use PHPUnit\Framework\TestListener; -use PHPUnit\Framework\TestResult; +use RuntimeException; /** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -interface ResultPrinter extends \PHPUnit\Framework\TestListener +final class InvalidDataSetException extends RuntimeException implements \PHPUnit\Exception { - public function printResult(\PHPUnit\Framework\TestResult $result) : void; - public function write(string $buffer) : void; } document = new DOMDocument('1.0', 'UTF-8'); + $this->document->formatOutput = \true; + $this->root = $this->document->createElement('testsuites'); + $this->document->appendChild($this->root); + parent::__construct($out); + $this->reportRiskyTests = $reportRiskyTests; + } + /** + * Flush buffer and close output. + */ + public function flush() : void + { + $this->write($this->getXML()); + parent::flush(); + } + /** + * An error occurred. + */ + public function addError(Test $test, Throwable $t, float $time) : void + { + $this->doAddFault($test, $t, 'error'); + $this->testSuiteErrors[$this->testSuiteLevel]++; + } + /** + * A warning occurred. + */ + public function addWarning(Test $test, Warning $e, float $time) : void + { + $this->doAddFault($test, $e, 'warning'); + $this->testSuiteWarnings[$this->testSuiteLevel]++; + } + /** + * A failure occurred. + */ + public function addFailure(Test $test, AssertionFailedError $e, float $time) : void + { + $this->doAddFault($test, $e, 'failure'); + $this->testSuiteFailures[$this->testSuiteLevel]++; + } + /** + * Incomplete test. + */ + public function addIncompleteTest(Test $test, Throwable $t, float $time) : void + { + $this->doAddSkipped(); + } + /** + * Risky test. + */ + public function addRiskyTest(Test $test, Throwable $t, float $time) : void + { + if (!$this->reportRiskyTests) { + return; + } + $this->doAddFault($test, $t, 'error'); + $this->testSuiteErrors[$this->testSuiteLevel]++; + } + /** + * Skipped test. + */ + public function addSkippedTest(Test $test, Throwable $t, float $time) : void + { + $this->doAddSkipped(); + } + /** + * A testsuite started. + */ + public function startTestSuite(TestSuite $suite) : void + { + $testSuite = $this->document->createElement('testsuite'); + $testSuite->setAttribute('name', $suite->getName()); + if (class_exists($suite->getName(), \false)) { + try { + $class = new ReflectionClass($suite->getName()); + $testSuite->setAttribute('file', $class->getFileName()); + } catch (ReflectionException $e) { + } + } + if ($this->testSuiteLevel > 0) { + $this->testSuites[$this->testSuiteLevel]->appendChild($testSuite); + } else { + $this->root->appendChild($testSuite); + } + $this->testSuiteLevel++; + $this->testSuites[$this->testSuiteLevel] = $testSuite; + $this->testSuiteTests[$this->testSuiteLevel] = 0; + $this->testSuiteAssertions[$this->testSuiteLevel] = 0; + $this->testSuiteErrors[$this->testSuiteLevel] = 0; + $this->testSuiteWarnings[$this->testSuiteLevel] = 0; + $this->testSuiteFailures[$this->testSuiteLevel] = 0; + $this->testSuiteSkipped[$this->testSuiteLevel] = 0; + $this->testSuiteTimes[$this->testSuiteLevel] = 0; + } + /** + * A testsuite ended. + */ + public function endTestSuite(TestSuite $suite) : void + { + $this->testSuites[$this->testSuiteLevel]->setAttribute('tests', (string) $this->testSuiteTests[$this->testSuiteLevel]); + $this->testSuites[$this->testSuiteLevel]->setAttribute('assertions', (string) $this->testSuiteAssertions[$this->testSuiteLevel]); + $this->testSuites[$this->testSuiteLevel]->setAttribute('errors', (string) $this->testSuiteErrors[$this->testSuiteLevel]); + $this->testSuites[$this->testSuiteLevel]->setAttribute('warnings', (string) $this->testSuiteWarnings[$this->testSuiteLevel]); + $this->testSuites[$this->testSuiteLevel]->setAttribute('failures', (string) $this->testSuiteFailures[$this->testSuiteLevel]); + $this->testSuites[$this->testSuiteLevel]->setAttribute('skipped', (string) $this->testSuiteSkipped[$this->testSuiteLevel]); + $this->testSuites[$this->testSuiteLevel]->setAttribute('time', sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel])); + if ($this->testSuiteLevel > 1) { + $this->testSuiteTests[$this->testSuiteLevel - 1] += $this->testSuiteTests[$this->testSuiteLevel]; + $this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel]; + $this->testSuiteErrors[$this->testSuiteLevel - 1] += $this->testSuiteErrors[$this->testSuiteLevel]; + $this->testSuiteWarnings[$this->testSuiteLevel - 1] += $this->testSuiteWarnings[$this->testSuiteLevel]; + $this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel]; + $this->testSuiteSkipped[$this->testSuiteLevel - 1] += $this->testSuiteSkipped[$this->testSuiteLevel]; + $this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel]; + } + $this->testSuiteLevel--; + } + /** + * A test started. + */ + public function startTest(Test $test) : void + { + $usesDataprovider = \false; + if (method_exists($test, 'usesDataProvider')) { + $usesDataprovider = $test->usesDataProvider(); + } + $testCase = $this->document->createElement('testcase'); + $testCase->setAttribute('name', $test->getName()); + try { + $class = new ReflectionClass($test); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $methodName = $test->getName(!$usesDataprovider); + if ($class->hasMethod($methodName)) { + try { + $method = $class->getMethod($methodName); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $testCase->setAttribute('class', $class->getName()); + $testCase->setAttribute('classname', str_replace('\\', '.', $class->getName())); + $testCase->setAttribute('file', $class->getFileName()); + $testCase->setAttribute('line', (string) $method->getStartLine()); + } + $this->currentTestCase = $testCase; + } + /** + * A test ended. + */ + public function endTest(Test $test, float $time) : void + { + $numAssertions = 0; + if (method_exists($test, 'getNumAssertions')) { + $numAssertions = $test->getNumAssertions(); + } + $this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions; + $this->currentTestCase->setAttribute('assertions', (string) $numAssertions); + $this->currentTestCase->setAttribute('time', sprintf('%F', $time)); + $this->testSuites[$this->testSuiteLevel]->appendChild($this->currentTestCase); + $this->testSuiteTests[$this->testSuiteLevel]++; + $this->testSuiteTimes[$this->testSuiteLevel] += $time; + $testOutput = ''; + if (method_exists($test, 'hasOutput') && method_exists($test, 'getActualOutput')) { + $testOutput = $test->hasOutput() ? $test->getActualOutput() : ''; + } + if (!empty($testOutput)) { + $systemOut = $this->document->createElement('system-out', Xml::prepareString($testOutput)); + $this->currentTestCase->appendChild($systemOut); + } + $this->currentTestCase = null; + } + /** + * Returns the XML as a string. + */ + public function getXML() : string + { + return $this->document->saveXML(); + } + private function doAddFault(Test $test, Throwable $t, string $type) : void + { + if ($this->currentTestCase === null) { + return; + } + if ($test instanceof SelfDescribing) { + $buffer = $test->toString() . "\n"; + } else { + $buffer = ''; + } + $buffer .= trim(TestFailure::exceptionToString($t) . "\n" . Filter::getFilteredStacktrace($t)); + $fault = $this->document->createElement($type, Xml::prepareString($buffer)); + if ($t instanceof ExceptionWrapper) { + $fault->setAttribute('type', $t->getClassName()); + } else { + $fault->setAttribute('type', get_class($t)); + } + $this->currentTestCase->appendChild($fault); + } + private function doAddSkipped() : void + { + if ($this->currentTestCase === null) { + return; + } + $skipped = $this->document->createElement('skipped'); + $this->currentTestCase->appendChild($skipped); + $this->testSuiteSkipped[$this->testSuiteLevel]++; + } } printHeader($result); + $this->printFooter($result); + } + /** + * An error occurred. + */ + public function addError(Test $test, Throwable $t, float $time) : void + { + $this->printEvent('testFailed', ['name' => $test->getName(), 'message' => self::getMessage($t), 'details' => self::getDetails($t), 'duration' => self::toMilliseconds($time)]); + } + /** + * A warning occurred. + */ + public function addWarning(Test $test, Warning $e, float $time) : void + { + $this->write(self::getMessage($e) . \PHP_EOL); + } + /** + * A failure occurred. + */ + public function addFailure(Test $test, AssertionFailedError $e, float $time) : void + { + $parameters = ['name' => $test->getName(), 'message' => self::getMessage($e), 'details' => self::getDetails($e), 'duration' => self::toMilliseconds($time)]; + if ($e instanceof ExpectationFailedException) { + $comparisonFailure = $e->getComparisonFailure(); + if ($comparisonFailure instanceof ComparisonFailure) { + $expectedString = $comparisonFailure->getExpectedAsString(); + if ($expectedString === null || empty($expectedString)) { + $expectedString = self::getPrimitiveValueAsString($comparisonFailure->getExpected()); + } + $actualString = $comparisonFailure->getActualAsString(); + if ($actualString === null || empty($actualString)) { + $actualString = self::getPrimitiveValueAsString($comparisonFailure->getActual()); + } + if ($actualString !== null && $expectedString !== null) { + $parameters['type'] = 'comparisonFailure'; + $parameters['actual'] = $actualString; + $parameters['expected'] = $expectedString; + } + } + } + $this->printEvent('testFailed', $parameters); + } + /** + * Incomplete test. + */ + public function addIncompleteTest(Test $test, Throwable $t, float $time) : void + { + $this->printIgnoredTest($test->getName(), $t, $time); + } + /** + * Risky test. + */ + public function addRiskyTest(Test $test, Throwable $t, float $time) : void + { + $this->addError($test, $t, $time); + } + /** + * Skipped test. + */ + public function addSkippedTest(Test $test, Throwable $t, float $time) : void + { + $testName = $test->getName(); + if ($this->startedTestName !== $testName) { + $this->startTest($test); + $this->printIgnoredTest($testName, $t, $time); + $this->endTest($test, $time); + } else { + $this->printIgnoredTest($testName, $t, $time); + } + } + public function printIgnoredTest(string $testName, Throwable $t, float $time) : void + { + $this->printEvent('testIgnored', ['name' => $testName, 'message' => self::getMessage($t), 'details' => self::getDetails($t), 'duration' => self::toMilliseconds($time)]); + } + /** + * A testsuite started. + */ + public function startTestSuite(TestSuite $suite) : void + { + if (stripos(ini_get('disable_functions'), 'getmypid') === \false) { + $this->flowId = getmypid(); + } else { + $this->flowId = \false; + } + if (!$this->isSummaryTestCountPrinted) { + $this->isSummaryTestCountPrinted = \true; + $this->printEvent('testCount', ['count' => count($suite)]); + } + $suiteName = $suite->getName(); + if (empty($suiteName)) { + return; + } + $parameters = ['name' => $suiteName]; + if (class_exists($suiteName, \false)) { + $fileName = self::getFileName($suiteName); + $parameters['locationHint'] = "php_qn://{$fileName}::\\{$suiteName}"; + } else { + $split = explode('::', $suiteName); + if (count($split) === 2 && class_exists($split[0]) && method_exists($split[0], $split[1])) { + $fileName = self::getFileName($split[0]); + $parameters['locationHint'] = "php_qn://{$fileName}::\\{$suiteName}"; + $parameters['name'] = $split[1]; + } + } + $this->printEvent('testSuiteStarted', $parameters); + } + /** + * A testsuite ended. + */ + public function endTestSuite(TestSuite $suite) : void + { + $suiteName = $suite->getName(); + if (empty($suiteName)) { + return; + } + $parameters = ['name' => $suiteName]; + if (!class_exists($suiteName, \false)) { + $split = explode('::', $suiteName); + if (count($split) === 2 && class_exists($split[0]) && method_exists($split[0], $split[1])) { + $parameters['name'] = $split[1]; + } + } + $this->printEvent('testSuiteFinished', $parameters); + } + /** + * A test started. */ - private $nodes = []; - public static function fromNodeList(\DOMNodeList $list) : self + public function startTest(Test $test) : void { - $snapshot = new self(); - foreach ($list as $node) { - $snapshot->nodes[] = $node; + $testName = $test->getName(); + $this->startedTestName = $testName; + $params = ['name' => $testName]; + if ($test instanceof TestCase) { + $className = get_class($test); + $fileName = self::getFileName($className); + $params['locationHint'] = "php_qn://{$fileName}::\\{$className}::{$testName}"; } - return $snapshot; + $this->printEvent('testStarted', $params); } - public function count() : int + /** + * A test ended. + */ + public function endTest(Test $test, float $time) : void { - return \count($this->nodes); + parent::endTest($test, $time); + $this->printEvent('testFinished', ['name' => $test->getName(), 'duration' => self::toMilliseconds($time)]); } - public function getIterator() : \ArrayIterator + protected function writeProgress(string $progress) : void { - return new \ArrayIterator($this->nodes); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -use function sprintf; -use function trim; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class ValidationResult -{ - /** - * @psalm-var array> - */ - private $validationErrors = []; - /** - * @psalm-param array $errors - */ - public static function fromArray(array $errors) : self + private function printEvent(string $eventName, array $params = []) : void { - $validationErrors = []; - foreach ($errors as $error) { - if (!isset($validationErrors[$error->line])) { - $validationErrors[$error->line] = []; - } - $validationErrors[$error->line][] = \trim($error->message); + $this->write("\n##teamcity[{$eventName}"); + if ($this->flowId) { + $params['flowId'] = $this->flowId; } - return new self($validationErrors); + foreach ($params as $key => $value) { + $escapedValue = self::escapeValue((string) $value); + $this->write(" {$key}='{$escapedValue}'"); + } + $this->write("]\n"); } - private function __construct(array $validationErrors) + private static function getMessage(Throwable $t) : string { - $this->validationErrors = $validationErrors; + $message = ''; + if ($t instanceof ExceptionWrapper) { + if ($t->getClassName() !== '') { + $message .= $t->getClassName(); + } + if ($message !== '' && $t->getMessage() !== '') { + $message .= ' : '; + } + } + return $message . $t->getMessage(); } - public function hasValidationErrors() : bool + private static function getDetails(Throwable $t) : string { - return !empty($this->validationErrors); + $stackTrace = Filter::getFilteredStacktrace($t); + $previous = $t instanceof ExceptionWrapper ? $t->getPreviousWrapped() : $t->getPrevious(); + while ($previous) { + $stackTrace .= "\nCaused by\n" . TestFailure::exceptionToString($previous) . "\n" . Filter::getFilteredStacktrace($previous); + $previous = $previous instanceof ExceptionWrapper ? $previous->getPreviousWrapped() : $previous->getPrevious(); + } + return ' ' . str_replace("\n", "\n ", $stackTrace); } - public function asString() : string + private static function getPrimitiveValueAsString($value) : ?string { - $buffer = ''; - foreach ($this->validationErrors as $line => $validationErrorsOnLine) { - $buffer .= \sprintf(\PHP_EOL . ' Line %d:' . \PHP_EOL, $line); - foreach ($validationErrorsOnLine as $validationError) { - $buffer .= \sprintf(' - %s' . \PHP_EOL, $validationError); - } + if ($value === null) { + return 'null'; } - return $buffer; + if (is_bool($value)) { + return $value ? 'true' : 'false'; + } + if (is_scalar($value)) { + return print_r($value, \true); + } + return null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -use function libxml_clear_errors; -use function libxml_get_errors; -use function libxml_use_internal_errors; -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Validator -{ - public function validate(\DOMDocument $document, string $xsdFilename) : \PHPUnit\Util\Xml\ValidationResult + private static function escapeValue(string $text) : string { - $originalErrorHandling = \libxml_use_internal_errors(\true); - $document->schemaValidate($xsdFilename); - $errors = \libxml_get_errors(); - \libxml_clear_errors(); - \libxml_use_internal_errors($originalErrorHandling); - return \PHPUnit\Util\Xml\ValidationResult::fromArray($errors); + return str_replace(['|', "'", "\n", "\r", ']', '['], ['||', "|'", '|n', '|r', '|]', '|['], $text); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -use RuntimeException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Exception extends \RuntimeException implements \PHPUnit\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class SchemaDetector -{ /** - * @throws Exception + * @param string $className */ - public function detect(string $filename) : \PHPUnit\Util\Xml\SchemaDetectionResult + private static function getFileName($className) : string { - $document = (new \PHPUnit\Util\Xml\Loader())->loadFile($filename, \false, \true, \true); - foreach (['9.2', '8.5'] as $candidate) { - $schema = (new \PHPUnit\Util\Xml\SchemaFinder())->find($candidate); - if (!(new \PHPUnit\Util\Xml\Validator())->validate($document, $schema)->hasValidationErrors()) { - return new \PHPUnit\Util\Xml\SuccessfulSchemaDetectionResult($candidate); - } + try { + return (new ReflectionClass($className))->getFileName(); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), (int) $e->getCode(), $e); } - return new \PHPUnit\Util\Xml\FailedSchemaDetectionResult(); + // @codeCoverageIgnoreEnd + } + /** + * @param float $time microseconds + */ + private static function toMilliseconds(float $time) : int + { + return (int) round($time * 1000); } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; +namespace PHPUnit\Util; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @deprecated Use ExcludeList instead * - * @psalm-immutable + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -abstract class SchemaDetectionResult +final class Blacklist { - public function detected() : bool + public static function addDirectory(string $directory) : void { - return \false; + \PHPUnit\Util\ExcludeList::addDirectory($directory); } /** * @throws Exception + * + * @return string[] */ - public function version() : string + public function getBlacklistedDirectories() : array { - throw new \PHPUnit\Util\Xml\Exception('No supported schema was detected'); + return (new \PHPUnit\Util\ExcludeList())->getExcludedDirectories(); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @psalm-immutable - */ -final class SuccessfulSchemaDetectionResult extends \PHPUnit\Util\Xml\SchemaDetectionResult -{ /** - * @var string + * @throws Exception */ - private $version; - public function __construct(string $version) - { - $this->version = $version; - } - public function detected() : bool - { - return \true; - } - public function version() : string + public function isBlacklisted(string $file) : bool { - return $this->version; + return (new \PHPUnit\Util\ExcludeList())->isExcluded($file); } } path() . 'phpunit.xsd'; + $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 { - $filename = $this->path() . 'schema/' . $version . '.xsd'; + if ($t->getPrevious()) { + $t = $t->getPrevious(); + } + $eTrace = $t->getTrace(); + $eFile = $t->getFile(); + $eLine = $t->getLine(); } - if (!\is_file($filename)) { - throw new \PHPUnit\Util\Xml\Exception(\sprintf('Schema for PHPUnit %s is not available', $version)); + if (!self::frameExists($eTrace, $eFile, $eLine)) { + array_unshift($eTrace, ['file' => $eFile, 'line' => $eLine]); } - return $filename; + $prefix = defined('__PHPUNIT_PHAR_ROOT__') ? __PHPUNIT_PHAR_ROOT__ : \false; + $excludeList = new \PHPUnit\Util\ExcludeList(); + foreach ($eTrace as $frame) { + if (self::shouldPrintFrame($frame, $prefix, $excludeList)) { + $filteredStacktrace .= sprintf("%s:%s\n", $frame['file'], $frame['line'] ?? '?'); + } + } + return $filteredStacktrace; } - private function path() : string + private static function shouldPrintFrame(array $frame, $prefix, \PHPUnit\Util\ExcludeList $excludeList) : bool { - if (\defined('__PHPUNIT_PHAR_ROOT__')) { - return __PHPUNIT_PHAR_ROOT__ . '/'; + if (!isset($frame['file'])) { + return \false; } - return __DIR__ . '/../../../'; + $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; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Xml; - -use function chdir; -use function dirname; -use function error_reporting; -use function file_get_contents; -use function getcwd; -use function libxml_get_errors; -use function libxml_use_internal_errors; -use function sprintf; -use DOMDocument; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Loader -{ - /** - * @throws Exception - */ - public function loadFile(string $filename, bool $isHtml = \false, bool $xinclude = \false, bool $strict = \false) : \DOMDocument + private static function fileIsExcluded(string $file, \PHPUnit\Util\ExcludeList $excludeList) : bool { - $reporting = \error_reporting(0); - $contents = \file_get_contents($filename); - \error_reporting($reporting); - if ($contents === \false) { - throw new \PHPUnit\Util\Xml\Exception(\sprintf('Could not read "%s".', $filename)); - } - return $this->load($contents, $isHtml, $filename, $xinclude, $strict); + return (empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) || !in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], \true)) && !$excludeList->isExcluded($file); } - /** - * @throws Exception - */ - public function load(string $actual, bool $isHtml = \false, string $filename = '', bool $xinclude = \false, bool $strict = \false) : \DOMDocument + private static function frameExists(array $trace, string $file, int $line) : bool { - if ($actual === '') { - throw new \PHPUnit\Util\Xml\Exception('Could not load XML from empty string'); - } - // Required for XInclude on Windows. - if ($xinclude) { - $cwd = \getcwd(); - @\chdir(\dirname($filename)); - } - $document = new \DOMDocument(); - $document->preserveWhiteSpace = \false; - $internal = \libxml_use_internal_errors(\true); - $message = ''; - $reporting = \error_reporting(0); - if ($filename !== '') { - // Required for XInclude - $document->documentURI = $filename; - } - if ($isHtml) { - $loaded = $document->loadHTML($actual); - } else { - $loaded = $document->loadXML($actual); - } - if (!$isHtml && $xinclude) { - $document->xinclude(); - } - foreach (\libxml_get_errors() as $error) { - $message .= "\n" . $error->message; - } - \libxml_use_internal_errors($internal); - \error_reporting($reporting); - if (isset($cwd)) { - @\chdir($cwd); - } - if ($loaded === \false || $strict && $message !== '') { - if ($filename !== '') { - throw new \PHPUnit\Util\Xml\Exception(\sprintf('Could not load "%s".%s', $filename, $message !== '' ? "\n" . $message : '')); - } - if ($message === '') { - $message = 'Could not load XML for unknown reason'; + foreach ($trace as $frame) { + if (isset($frame['file'], $frame['line']) && $frame['file'] === $file && $frame['line'] === $line) { + return \true; } - throw new \PHPUnit\Util\Xml\Exception($message); } - return $document; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use function preg_match; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class RegularExpression -{ - /** - * @return false|int - */ - public static function safeMatch(string $pattern, string $subject) - { - return \PHPUnit\Util\ErrorHandler::invokeIgnoringWarnings(static function () use($pattern, $subject) { - return \preg_match($pattern, $subject); - }); + return \false; } } getName()]; + if ($test instanceof TestCase) { + return [get_class($test), $test->getName()]; } - if ($test instanceof \PHPUnit\Framework\SelfDescribing) { + if ($test instanceof SelfDescribing) { return ['', $test->toString()]; } - return ['', \get_class($test)]; + return ['', get_class($test)]; } public static function describeAsString(\PHPUnit\Framework\Test $test) : string { - if ($test instanceof \PHPUnit\Framework\SelfDescribing) { + if ($test instanceof SelfDescribing) { return $test->toString(); } - return \get_class($test); + return get_class($test); } /** * @throws CodeCoverageException @@ -47680,9 +51728,9 @@ final class Test { return self::getLinesToBeCoveredOrUsed($className, $methodName, 'uses'); } - public static function requiresCodeCoverageDataCollection(\PHPUnit\Framework\TestCase $test) : bool + public static function requiresCodeCoverageDataCollection(TestCase $test) : bool { - $annotations = $test->getAnnotations(); + $annotations = self::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); // If there is no @covers annotation but a @coversNothing annotation on // the test method then code coverage data does not need to be collected if (isset($annotations['method']['coversNothing'])) { @@ -47708,7 +51756,7 @@ final class Test */ public static function getRequirements(string $className, string $methodName) : array { - return self::mergeArraysRecursively(\PHPUnit\Util\Annotation\Registry::getInstance()->forClassName($className)->requirements(), \PHPUnit\Util\Annotation\Registry::getInstance()->forMethod($className, $methodName)->requirements()); + return self::mergeArraysRecursively(Registry::getInstance()->forClassName($className)->requirements(), Registry::getInstance()->forMethod($className, $methodName)->requirements()); } /** * Returns the missing requirements for a test. @@ -47724,59 +51772,59 @@ final class Test $hint = null; if (!empty($required['PHP'])) { $operator = new \PHPUnit\Util\VersionComparisonOperator(empty($required['PHP']['operator']) ? '>=' : $required['PHP']['operator']); - if (!\version_compare(\PHP_VERSION, $required['PHP']['version'], $operator->asString())) { - $missing[] = \sprintf('PHP %s %s is required.', $operator->asString(), $required['PHP']['version']); + if (!version_compare(\PHP_VERSION, $required['PHP']['version'], $operator->asString())) { + $missing[] = sprintf('PHP %s %s is required.', $operator->asString(), $required['PHP']['version']); $hint = 'PHP'; } } elseif (!empty($required['PHP_constraint'])) { $version = new \PHPUnit\PharIo\Version\Version(self::sanitizeVersionNumber(\PHP_VERSION)); if (!$required['PHP_constraint']['constraint']->complies($version)) { - $missing[] = \sprintf('PHP version does not match the required constraint %s.', $required['PHP_constraint']['constraint']->asString()); + $missing[] = sprintf('PHP version does not match the required constraint %s.', $required['PHP_constraint']['constraint']->asString()); $hint = 'PHP_constraint'; } } if (!empty($required['PHPUnit'])) { - $phpunitVersion = \PHPUnit\Runner\Version::id(); + $phpunitVersion = Version::id(); $operator = new \PHPUnit\Util\VersionComparisonOperator(empty($required['PHPUnit']['operator']) ? '>=' : $required['PHPUnit']['operator']); - if (!\version_compare($phpunitVersion, $required['PHPUnit']['version'], $operator->asString())) { - $missing[] = \sprintf('PHPUnit %s %s is required.', $operator->asString(), $required['PHPUnit']['version']); + if (!version_compare($phpunitVersion, $required['PHPUnit']['version'], $operator->asString())) { + $missing[] = sprintf('PHPUnit %s %s is required.', $operator->asString(), $required['PHPUnit']['version']); $hint = $hint ?? 'PHPUnit'; } } elseif (!empty($required['PHPUnit_constraint'])) { - $phpunitVersion = new \PHPUnit\PharIo\Version\Version(self::sanitizeVersionNumber(\PHPUnit\Runner\Version::id())); + $phpunitVersion = new \PHPUnit\PharIo\Version\Version(self::sanitizeVersionNumber(Version::id())); if (!$required['PHPUnit_constraint']['constraint']->complies($phpunitVersion)) { - $missing[] = \sprintf('PHPUnit version does not match the required constraint %s.', $required['PHPUnit_constraint']['constraint']->asString()); + $missing[] = sprintf('PHPUnit version does not match the required constraint %s.', $required['PHPUnit_constraint']['constraint']->asString()); $hint = $hint ?? 'PHPUnit_constraint'; } } - if (!empty($required['OSFAMILY']) && $required['OSFAMILY'] !== (new \PHPUnit\SebastianBergmann\Environment\OperatingSystem())->getFamily()) { - $missing[] = \sprintf('Operating system %s is required.', $required['OSFAMILY']); + if (!empty($required['OSFAMILY']) && $required['OSFAMILY'] !== (new OperatingSystem())->getFamily()) { + $missing[] = sprintf('Operating system %s is required.', $required['OSFAMILY']); $hint = $hint ?? 'OSFAMILY'; } if (!empty($required['OS'])) { - $requiredOsPattern = \sprintf('/%s/i', \addcslashes($required['OS'], '/')); - if (!\preg_match($requiredOsPattern, \PHP_OS)) { - $missing[] = \sprintf('Operating system matching %s is required.', $requiredOsPattern); + $requiredOsPattern = sprintf('/%s/i', addcslashes($required['OS'], '/')); + if (!preg_match($requiredOsPattern, \PHP_OS)) { + $missing[] = sprintf('Operating system matching %s is required.', $requiredOsPattern); $hint = $hint ?? 'OS'; } } if (!empty($required['functions'])) { foreach ($required['functions'] as $function) { - $pieces = \explode('::', $function); - if (\count($pieces) === 2 && \class_exists($pieces[0]) && \method_exists($pieces[0], $pieces[1])) { + $pieces = explode('::', $function); + if (count($pieces) === 2 && class_exists($pieces[0]) && method_exists($pieces[0], $pieces[1])) { continue; } - if (\function_exists($function)) { + if (function_exists($function)) { continue; } - $missing[] = \sprintf('Function %s is required.', $function); + $missing[] = sprintf('Function %s is required.', $function); $hint = $hint ?? 'function_' . $function; } } if (!empty($required['setting'])) { foreach ($required['setting'] as $setting => $value) { - if (\ini_get($setting) !== $value) { - $missing[] = \sprintf('Setting "%s" must be "%s".', $setting, $value); + if (ini_get($setting) !== $value) { + $missing[] = sprintf('Setting "%s" must be "%s".', $setting, $value); $hint = $hint ?? '__SETTING_' . $setting; } } @@ -47786,25 +51834,25 @@ final class Test if (isset($required['extension_versions'][$extension])) { continue; } - if (!\extension_loaded($extension)) { - $missing[] = \sprintf('Extension %s is required.', $extension); + if (!extension_loaded($extension)) { + $missing[] = sprintf('Extension %s is required.', $extension); $hint = $hint ?? 'extension_' . $extension; } } } if (!empty($required['extension_versions'])) { foreach ($required['extension_versions'] as $extension => $req) { - $actualVersion = \phpversion($extension); + $actualVersion = phpversion($extension); $operator = new \PHPUnit\Util\VersionComparisonOperator(empty($req['operator']) ? '>=' : $req['operator']); - if ($actualVersion === \false || !\version_compare($actualVersion, $req['version'], $operator->asString())) { - $missing[] = \sprintf('Extension %s %s %s is required.', $extension, $operator->asString(), $req['version']); + if ($actualVersion === \false || !version_compare($actualVersion, $req['version'], $operator->asString())) { + $missing[] = sprintf('Extension %s %s %s is required.', $extension, $operator->asString(), $req['version']); $hint = $hint ?? 'extension_' . $extension; } } } if ($hint && isset($required['__OFFSET'])) { - \array_unshift($missing, '__OFFSET_FILE=' . $required['__OFFSET']['__FILE']); - \array_unshift($missing, '__OFFSET_LINE=' . ($required['__OFFSET'][$hint] ?? 1)); + array_unshift($missing, '__OFFSET_FILE=' . $required['__OFFSET']['__FILE']); + array_unshift($missing, '__OFFSET_LINE=' . ($required['__OFFSET'][$hint] ?? 1)); } return $missing; } @@ -47816,14 +51864,14 @@ final class Test */ public static function getProvidedData(string $className, string $methodName) : ?array { - return \PHPUnit\Util\Annotation\Registry::getInstance()->forMethod($className, $methodName)->getProvidedData(); + return Registry::getInstance()->forMethod($className, $methodName)->getProvidedData(); } /** * @psalm-param class-string $className */ public static function parseTestMethodAnnotations(string $className, ?string $methodName = '') : array { - $registry = \PHPUnit\Util\Annotation\Registry::getInstance(); + $registry = Registry::getInstance(); if ($methodName !== null) { try { return ['method' => $registry->forMethod($className, $methodName)->symbolAnnotations(), 'class' => $registry->forClassName($className)->symbolAnnotations()]; @@ -47838,7 +51886,7 @@ final class Test */ public static function getInlineAnnotations(string $className, string $methodName) : array { - return \PHPUnit\Util\Annotation\Registry::getInstance()->forMethod($className, $methodName)->getInlineAnnotations(); + return Registry::getInstance()->forMethod($className, $methodName)->getInlineAnnotations(); } /** @psalm-param class-string $className */ public static function getBackupSettings(string $className, string $methodName) : array @@ -47855,14 +51903,14 @@ final class Test $annotations = self::parseTestMethodAnnotations($className, $methodName); $dependsAnnotations = $annotations['class']['depends'] ?? []; if (isset($annotations['method']['depends'])) { - $dependsAnnotations = \array_merge($dependsAnnotations, $annotations['method']['depends']); + $dependsAnnotations = array_merge($dependsAnnotations, $annotations['method']['depends']); } // Normalize dependency name to className::methodName $dependencies = []; foreach ($dependsAnnotations as $value) { - $dependencies[] = \PHPUnit\Framework\ExecutionOrderDependency::createFromDependsAnnotation($className, $value); + $dependencies[] = ExecutionOrderDependency::createFromDependsAnnotation($className, $value); } - return \array_unique($dependencies); + return array_unique($dependencies); } /** @psalm-param class-string $className */ public static function getGroups(string $className, ?string $methodName = '') : array @@ -47906,12 +51954,12 @@ final class Test } } } - return \array_unique(\array_merge([], ...$groups)); + return array_unique(array_merge([], ...$groups)); } /** @psalm-param class-string $className */ public static function getSize(string $className, ?string $methodName) : int { - $groups = \array_flip(self::getGroups($className, $methodName)); + $groups = array_flip(self::getGroups($className, $methodName)); if (isset($groups['large'])) { return self::LARGE; } @@ -47943,33 +51991,33 @@ final class Test /** @psalm-param class-string $className */ public static function getHookMethods(string $className) : array { - if (!\class_exists($className, \false)) { + if (!class_exists($className, \false)) { return self::emptyHookMethodsArray(); } if (!isset(self::$hookMethods[$className])) { self::$hookMethods[$className] = self::emptyHookMethodsArray(); try { - foreach ((new \ReflectionClass($className))->getMethods() as $method) { - if ($method->getDeclaringClass()->getName() === \PHPUnit\Framework\Assert::class) { + foreach ((new ReflectionClass($className))->getMethods() as $method) { + if ($method->getDeclaringClass()->getName() === Assert::class) { continue; } - if ($method->getDeclaringClass()->getName() === \PHPUnit\Framework\TestCase::class) { + if ($method->getDeclaringClass()->getName() === TestCase::class) { continue; } - $docBlock = \PHPUnit\Util\Annotation\Registry::getInstance()->forMethod($className, $method->getName()); + $docBlock = Registry::getInstance()->forMethod($className, $method->getName()); if ($method->isStatic()) { if ($docBlock->isHookToBeExecutedBeforeClass()) { - \array_unshift(self::$hookMethods[$className]['beforeClass'], $method->getName()); + array_unshift(self::$hookMethods[$className]['beforeClass'], $method->getName()); } if ($docBlock->isHookToBeExecutedAfterClass()) { self::$hookMethods[$className]['afterClass'][] = $method->getName(); } } if ($docBlock->isToBeExecutedBeforeTest()) { - \array_unshift(self::$hookMethods[$className]['before'], $method->getName()); + array_unshift(self::$hookMethods[$className]['before'], $method->getName()); } if ($docBlock->isToBeExecutedAsPreCondition()) { - \array_unshift(self::$hookMethods[$className]['preCondition'], $method->getName()); + array_unshift(self::$hookMethods[$className]['preCondition'], $method->getName()); } if ($docBlock->isToBeExecutedAsPostCondition()) { self::$hookMethods[$className]['postCondition'][] = $method->getName(); @@ -47978,20 +52026,20 @@ final class Test self::$hookMethods[$className]['after'][] = $method->getName(); } } - } catch (\ReflectionException $e) { + } catch (ReflectionException $e) { } } return self::$hookMethods[$className]; } - public static function isTestMethod(\ReflectionMethod $method) : bool + public static function isTestMethod(ReflectionMethod $method) : bool { if (!$method->isPublic()) { return \false; } - if (\strpos($method->getName(), 'test') === 0) { + if (strpos($method->getName(), 'test') === 0) { return \true; } - return \array_key_exists('test', \PHPUnit\Util\Annotation\Registry::getInstance()->forMethod($method->getDeclaringClass()->getName(), $method->getName())->symbolAnnotations()); + return array_key_exists('test', Registry::getInstance()->forMethod($method->getDeclaringClass()->getName(), $method->getName())->symbolAnnotations()); } /** * @throws CodeCoverageException @@ -48002,31 +52050,31 @@ final class Test $annotations = self::parseTestMethodAnnotations($className, $methodName); $classShortcut = null; if (!empty($annotations['class'][$mode . 'DefaultClass'])) { - if (\count($annotations['class'][$mode . 'DefaultClass']) > 1) { - throw new \PHPUnit\Framework\CodeCoverageException(\sprintf('More than one @%sClass annotation in class or interface "%s".', $mode, $className)); + if (count($annotations['class'][$mode . 'DefaultClass']) > 1) { + throw new CodeCoverageException(sprintf('More than one @%sClass annotation in class or interface "%s".', $mode, $className)); } $classShortcut = $annotations['class'][$mode . 'DefaultClass'][0]; } $list = $annotations['class'][$mode] ?? []; if (isset($annotations['method'][$mode])) { - $list = \array_merge($list, $annotations['method'][$mode]); + $list = array_merge($list, $annotations['method'][$mode]); } - $codeUnits = \PHPUnit\SebastianBergmann\CodeUnit\CodeUnitCollection::fromArray([]); - $mapper = new \PHPUnit\SebastianBergmann\CodeUnit\Mapper(); - foreach (\array_unique($list) as $element) { - if ($classShortcut && \strncmp($element, '::', 2) === 0) { + $codeUnits = CodeUnitCollection::fromArray([]); + $mapper = new Mapper(); + foreach (array_unique($list) as $element) { + if ($classShortcut && strncmp($element, '::', 2) === 0) { $element = $classShortcut . $element; } - $element = \preg_replace('/[\\s()]+$/', '', $element); - $element = \explode(' ', $element); + $element = preg_replace('/[\\s()]+$/', '', $element); + $element = explode(' ', $element); $element = $element[0]; - if ($mode === 'covers' && \interface_exists($element)) { - throw new \PHPUnit\Framework\InvalidCoversTargetException(\sprintf('Trying to @cover interface "%s".', $element)); + if ($mode === 'covers' && interface_exists($element)) { + throw new InvalidCoversTargetException(sprintf('Trying to @cover interface "%s".', $element)); } try { $codeUnits = $codeUnits->mergeWith($mapper->stringToCodeUnits($element)); - } catch (\PHPUnit\SebastianBergmann\CodeUnit\InvalidCodeUnitException $e) { - throw new \PHPUnit\Framework\InvalidCoversTargetException(\sprintf('"@%s %s" is invalid', $mode, $element), (int) $e->getCode(), $e); + } catch (InvalidCodeUnitException $e) { + throw new InvalidCoversTargetException(sprintf('"@%s %s" is invalid', $mode, $element), (int) $e->getCode(), $e); } } return $mapper->codeUnitsToSourceLines($codeUnits); @@ -48063,7 +52111,7 @@ final class Test */ private static function sanitizeVersionNumber(string $version) { - return \preg_replace('/^(\\d+\\.\\d+(?:.\\d+)?).*$/', '$1', $version); + return preg_replace('/^(\\d+\\.\\d+(?:.\\d+)?).*$/', '$1', $version); } private static function shouldCoversAnnotationBeUsed(array $annotations) : bool { @@ -48089,7 +52137,7 @@ final class Test * * Zend Framework (http://framework.zend.com/) * - * @link http://github.com/zendframework/zf2 for the canonical source repository + * @see http://github.com/zendframework/zf2 for the canonical source repository * * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License @@ -48097,164 +52145,23 @@ final class Test private static function mergeArraysRecursively(array $a, array $b) : array { foreach ($b as $key => $value) { - if (\array_key_exists($key, $a)) { - if (\is_int($key)) { + if (array_key_exists($key, $a)) { + if (is_int($key)) { $a[] = $value; - } elseif (\is_array($value) && \is_array($a[$key])) { + } elseif (is_array($value) && is_array($a[$key])) { $a[$key] = self::mergeArraysRecursively($a[$key], $value); } else { - $a[$key] = $value; - } - } else { - $a[$key] = $value; - } - } - return $a; - } - private static function canonicalizeName(string $name) : string - { - return \strtolower(\trim($name, '\\')); - } -} - - * - * 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_keys; -use function array_reverse; -use function defined; -use function get_defined_constants; -use function get_included_files; -use function in_array; -use function ini_get_all; -use function is_array; -use function is_file; -use function is_scalar; -use function preg_match; -use function serialize; -use function sprintf; -use function strpos; -use function var_export; -use Closure; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class GlobalState -{ - /** - * @var string[] - */ - private const SUPER_GLOBAL_ARRAYS = ['_ENV', '_POST', '_GET', '_COOKIE', '_SERVER', '_FILES', '_REQUEST']; - /** - * @throws Exception - */ - public static function getIncludedFilesAsString() : string - { - return self::processIncludedFilesAsString(\get_included_files()); - } - /** - * @param string[] $files - * - * @throws Exception - */ - public static function processIncludedFilesAsString(array $files) : string - { - $excludeList = new \PHPUnit\Util\ExcludeList(); - $prefix = \false; - $result = ''; - if (\defined('__PHPUNIT_PHAR__')) { - $prefix = 'phar://' . __PHPUNIT_PHAR__ . '/'; - } - // Do not process bootstrap script - unset($files[0]); - foreach (\array_reverse($files) as $file) { - if (!empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) && \in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], \true)) { - continue; - } - if ($prefix !== \false && \strpos($file, $prefix) === 0) { - continue; - } - // Skip virtual file system protocols - if (\preg_match('/^(vfs|phpvfs[a-z0-9]+):/', $file)) { - continue; - } - if (!$excludeList->isExcluded($file) && \is_file($file)) { - $result = 'require_once \'' . $file . "';\n" . $result; - } - } - return $result; - } - public static function getIniSettingsAsString() : string - { - $result = ''; - foreach (\ini_get_all(null, \false) as $key => $value) { - $result .= \sprintf('@ini_set(%s, %s);' . "\n", self::exportVariable($key), self::exportVariable((string) $value)); - } - return $result; - } - public static function getConstantsAsString() : string - { - $constants = \get_defined_constants(\true); - $result = ''; - if (isset($constants['user'])) { - foreach ($constants['user'] as $name => $value) { - $result .= \sprintf('if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", $name, $name, self::exportVariable($value)); - } - } - return $result; - } - public static function getGlobalsAsString() : string - { - $result = ''; - foreach (self::SUPER_GLOBAL_ARRAYS as $superGlobalArray) { - if (isset($GLOBALS[$superGlobalArray]) && \is_array($GLOBALS[$superGlobalArray])) { - foreach (\array_keys($GLOBALS[$superGlobalArray]) as $key) { - if ($GLOBALS[$superGlobalArray][$key] instanceof \Closure) { - continue; - } - $result .= \sprintf('$GLOBALS[\'%s\'][\'%s\'] = %s;' . "\n", $superGlobalArray, $key, self::exportVariable($GLOBALS[$superGlobalArray][$key])); + $a[$key] = $value; } + } else { + $a[$key] = $value; } } - $excludeList = self::SUPER_GLOBAL_ARRAYS; - $excludeList[] = 'GLOBALS'; - foreach (\array_keys($GLOBALS) as $key) { - if (!$GLOBALS[$key] instanceof \Closure && !\in_array($key, $excludeList, \true)) { - $result .= \sprintf('$GLOBALS[\'%s\'] = %s;' . "\n", $key, self::exportVariable($GLOBALS[$key])); - } - } - return $result; - } - private static function exportVariable($variable) : string - { - if (\is_scalar($variable) || $variable === null || \is_array($variable) && self::arrayOnlyContainsScalars($variable)) { - return \var_export($variable, \true); - } - return 'unserialize(' . \var_export(\serialize($variable), \true) . ')'; + return $a; } - private static function arrayOnlyContainsScalars(array $array) : bool + private static function canonicalizeName(string $name) : string { - $result = \true; - foreach ($array as $element) { - if (\is_array($element)) { - $result = self::arrayOnlyContainsScalars($element); - } elseif (!\is_scalar($element) && $element !== null) { - $result = \false; - } - if (!$result) { - break; - } - } - return $result; + return strtolower(trim($name, '\\')); } } write($this->currentTestClassPrettified . "\n"); + $includePathFilename = stream_resolve_include_path($filename); + $localFile = __DIR__ . \DIRECTORY_SEPARATOR . $filename; + if (!$includePathFilename || $includePathFilename === $localFile || !self::isReadable($includePathFilename)) { + throw new \PHPUnit\Util\Exception(sprintf('Cannot open file "%s".' . "\n", $filename)); + } + self::load($includePathFilename); + return $includePathFilename; } /** - * Handler for 'on test' event. + * Loads a PHP sourcefile. */ - protected function onTest(string $name, bool $success = \true) : void + public static function load(string $filename) : void { - if ($success) { - $this->write(' [x] '); - } else { - $this->write(' [ ] '); + $oldVariableNames = array_keys(get_defined_vars()); + /** + * @noinspection PhpIncludeInspection + * @psalm-suppress UnresolvableInclude + */ + include_once $filename; + $newVariables = get_defined_vars(); + foreach (array_diff(array_keys($newVariables), $oldVariableNames) as $variableName) { + if ($variableName !== 'oldVariableNames') { + $GLOBALS[$variableName] = $newVariables[$variableName]; + } } - $this->write($name . "\n"); } /** - * Handler for 'end class' event. + * @see https://github.com/sebastianbergmann/phpunit/pull/2751 */ - protected function endClass(string $name) : void + private static function isReadable(string $filename) : bool { - $this->write("\n"); + return @fopen($filename, 'r') !== \false; } } Buffer for test results - */ - protected $testResults = []; - /** - * @var array Lookup table for testname to testResults[index] - */ - protected $testNameResultIndex = []; - /** - * @var bool - */ - protected $enableOutputBuffer = \false; - /** - * @var array array - */ - protected $originalExecutionOrder = []; - /** - * @var int - */ - protected $spinState = 0; - /** - * @var bool - */ - protected $showProgress = \true; - /** - * @param null|resource|string $out - * @param int|string $numberOfColumns - * - * @throws \PHPUnit\Framework\Exception + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function __construct($out = null, bool $verbose = \false, string $colors = self::COLOR_DEFAULT, bool $debug = \false, $numberOfColumns = 80, bool $reverse = \false) - { - parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns, $reverse); - $this->prettifier = new \PHPUnit\Util\TestDox\NamePrettifier($this->colors); - } - public function setOriginalExecutionOrder(array $order) : void - { - $this->originalExecutionOrder = $order; - $this->enableOutputBuffer = !empty($order); - } - public function setShowProgressAnimation(bool $showProgress) : void + public function render(TestSuite $suite) : string { - $this->showProgress = $showProgress; + $writer = new XMLWriter(); + $writer->openMemory(); + $writer->setIndent(\true); + $writer->startDocument(); + $writer->startElement('tests'); + $currentTestCase = null; + foreach (new RecursiveIteratorIterator($suite->getIterator()) as $test) { + if ($test instanceof TestCase) { + if (get_class($test) !== $currentTestCase) { + if ($currentTestCase !== null) { + $writer->endElement(); + } + $writer->startElement('testCaseClass'); + $writer->writeAttribute('name', get_class($test)); + $currentTestCase = get_class($test); + } + $writer->startElement('testCaseMethod'); + $writer->writeAttribute('name', $test->getName(\false)); + $writer->writeAttribute('groups', implode(',', $test->getGroups())); + if (!empty($test->getDataSetAsString(\false))) { + $writer->writeAttribute('dataSet', str_replace(' with data set ', '', $test->getDataSetAsString(\false))); + } + $writer->endElement(); + } elseif ($test instanceof PhptTestCase) { + if ($currentTestCase !== null) { + $writer->endElement(); + $currentTestCase = null; + } + $writer->startElement('phptFile'); + $writer->writeAttribute('path', $test->getName()); + $writer->endElement(); + } + } + if ($currentTestCase !== null) { + $writer->endElement(); + } + $writer->endElement(); + return $writer->outputMemory(); } - public function printResult(\PHPUnit\Framework\TestResult $result) : void +} + + * + * 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 addslashes; +use function array_map; +use function implode; +use function is_string; +use function realpath; +use function sprintf; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage as FilterConfiguration; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @deprecated + */ +final class XdebugFilterScriptGenerator +{ + public function generate(FilterConfiguration $filter) : string { + $files = array_map(static function ($item) { + return sprintf(" '%s'", $item); + }, $this->getItems($filter)); + $files = implode(",\n", $files); + return <<testHasPassed()) { - $this->registerTestResult($test, null, \PHPUnit\Runner\BaseTestRunner::STATUS_PASSED, $time, \false); + $files = []; + foreach ($filter->directories() as $directory) { + $path = realpath($directory->path()); + if (is_string($path)) { + $files[] = sprintf(addslashes('%s' . \DIRECTORY_SEPARATOR), $path); + } } - if ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof \PHPUnit\Runner\PhptTestCase) { - $this->testIndex++; + foreach ($filter->files() as $file) { + $files[] = $file->path(); } - parent::endTest($test, $time); + return $files; } +} + + * + * 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 is_dir; +use function mkdir; +use function str_replace; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Filesystem +{ /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * Maps class names to source file names. + * + * - PEAR CS: Foo_Bar_Baz -> Foo/Bar/Baz.php + * - Namespace: Foo\Bar\Baz -> Foo/Bar/Baz.php */ - public function addError(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + public static function classNameToFilename(string $className) : string { - $this->registerTestResult($test, $t, \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR, $time, \true); + return str_replace(['_', '\\'], \DIRECTORY_SEPARATOR, $className) . '.php'; } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time) : void + public static function createDirectory(string $directory) : bool { - $this->registerTestResult($test, $e, \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING, $time, \true); + return !(!is_dir($directory) && !@mkdir($directory, 0777, \true) && !is_dir($directory)); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use function in_array; +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class VersionComparisonOperator +{ /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @psalm-var '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' */ - public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time) : void + private $operator; + public function __construct(string $operator) { - $this->registerTestResult($test, $e, \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE, $time, \true); + $this->ensureOperatorIsValid($operator); + $this->operator = $operator; } /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @return '!='|'<'|'<='|'<>'|'='|'=='|'>'|'>='|'eq'|'ge'|'gt'|'le'|'lt'|'ne' */ - public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + public function asString() : string { - $this->registerTestResult($test, $t, \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE, $time, \false); + return $this->operator; } /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * + * @psalm-assert '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' $operator */ - public function addRiskyTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + private function ensureOperatorIsValid(string $operator) : void { - $this->registerTestResult($test, $t, \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY, $time, \false); + if (!in_array($operator, ['<', 'lt', '<=', 'le', '>', 'gt', '>=', 'ge', '==', '=', 'eq', '!=', '<>', 'ne'], \true)) { + throw new \PHPUnit\Util\Exception(sprintf('"%s" is not a valid version_compare() operator', $operator)); + } } +} + + * + * 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 +{ /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var array */ - public function addSkippedTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void - { - $this->registerTestResult($test, $t, \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED, $time, \false); - } - public function writeProgress(string $progress) : void - { - $this->flushOutputBuffer(); - } - public function flush() : void - { - $this->flushOutputBuffer(\true); - } + private const WHITESPACE_MAP = [' ' => '·', "\t" => '⇥']; /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var array */ - protected function registerTestResult(\PHPUnit\Framework\Test $test, ?\Throwable $t, int $status, float $time, bool $verbose) : void - { - $testName = $test instanceof \PHPUnit\Framework\Reorderable ? $test->sortId() : $test->getName(); - $result = ['className' => $this->formatClassName($test), 'testName' => $testName, 'testMethod' => $this->formatTestName($test), 'message' => '', 'status' => $status, 'time' => $time, 'verbose' => $verbose]; - if ($t !== null) { - $result['message'] = $this->formatTestResultMessage($t, $result); - } - $this->testResults[$this->testIndex] = $result; - $this->testNameResultIndex[$testName] = $this->testIndex; - } - protected function formatTestName(\PHPUnit\Framework\Test $test) : string - { - return \method_exists($test, 'getName') ? $test->getName() : ''; - } - protected function formatClassName(\PHPUnit\Framework\Test $test) : string - { - return \get_class($test); - } - protected function testHasPassed() : bool - { - if (!isset($this->testResults[$this->testIndex]['status'])) { - return \true; - } - if ($this->testResults[$this->testIndex]['status'] === \PHPUnit\Runner\BaseTestRunner::STATUS_PASSED) { - return \true; - } - return \false; - } - protected function flushOutputBuffer(bool $forceFlush = \false) : void + 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 ($this->testFlushIndex === $this->testIndex) { - return; + if (trim($buffer) === '') { + return $buffer; } - if ($this->testFlushIndex > 0) { - if ($this->enableOutputBuffer) { - $prevResult = $this->getTestResultByName($this->originalExecutionOrder[$this->testFlushIndex - 1]); - } else { - $prevResult = $this->testResults[$this->testFlushIndex - 1]; + $codes = array_map('\\trim', explode(',', $color)); + $styles = []; + foreach ($codes as $code) { + if (isset(self::$ansiCodes[$code])) { + $styles[] = self::$ansiCodes[$code] ?? ''; } - } else { - $prevResult = $this->getEmptyTestResult(); - } - if (!$this->enableOutputBuffer) { - $this->writeTestResult($prevResult, $this->testResults[$this->testFlushIndex++]); - } else { - do { - $flushed = \false; - if (!$forceFlush && isset($this->originalExecutionOrder[$this->testFlushIndex])) { - $result = $this->getTestResultByName($this->originalExecutionOrder[$this->testFlushIndex]); - } else { - // This test(name) cannot found in original execution order, - // flush result to output stream right away - $result = $this->testResults[$this->testFlushIndex]; - } - if (!empty($result)) { - $this->hideSpinner(); - $this->writeTestResult($prevResult, $result); - $this->testFlushIndex++; - $prevResult = $result; - $flushed = \true; - } else { - $this->showSpinner(); - } - } while ($flushed && $this->testFlushIndex < $this->testIndex); - } - } - protected function showSpinner() : void - { - if (!$this->showProgress) { - return; } - if ($this->spinState) { - $this->undrawSpinner(); + if (empty($styles)) { + return $buffer; } - $this->spinState++; - $this->drawSpinner(); + return self::optimizeColor(sprintf("\33[%sm", implode(';', $styles)) . $buffer . "\33[0m"); } - protected function hideSpinner() : void + public static function colorizePath(string $path, ?string $prevPath = null, bool $colorizeFilename = \false) : string { - if (!$this->showProgress) { - return; + if ($prevPath === null) { + $prevPath = ''; } - if ($this->spinState) { - $this->undrawSpinner(); + $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]); + } } - $this->spinState = 0; - } - protected function drawSpinner() : void - { - // optional for CLI printers: show the user a 'buffering output' spinner - } - protected function undrawSpinner() : void - { - // remove the spinner from the current line - } - protected function writeTestResult(array $prevResult, array $result) : void - { - } - protected function getEmptyTestResult() : array - { - return ['className' => '', 'testName' => '', 'message' => '', 'failed' => '', 'verbose' => '']; - } - protected function getTestResultByName(?string $testName) : array - { - if (isset($this->testNameResultIndex[$testName])) { - return $this->testResults[$this->testNameResultIndex[$testName]]; + if ($colorizeFilename) { + $last = count($path) - 1; + $path[$last] = preg_replace_callback('/([\\-_\\.]+|phpt$)/', static function ($matches) { + return self::dim($matches[0]); + }, $path[$last]); } - return []; + return self::optimizeColor(implode(self::dim(\DIRECTORY_SEPARATOR), $path)); } - protected function formatThrowable(\Throwable $t, ?int $status = null) : string + public static function dim(string $buffer) : string { - $message = \trim(\PHPUnit\Framework\TestFailure::exceptionToString($t)); - if ($message) { - $message .= \PHP_EOL . \PHP_EOL . $this->formatStacktrace($t); - } else { - $message = $this->formatStacktrace($t); + if (trim($buffer) === '') { + return $buffer; } - return $message; + return "\33[2m{$buffer}\33[22m"; } - protected function formatStacktrace(\Throwable $t) : string + public static function visualizeWhitespace(string $buffer, bool $visualizeEOL = \false) : string { - return \PHPUnit\Util\Filter::getFilteredStacktrace($t); + $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); } - protected function formatTestResultMessage(\Throwable $t, array $result, string $prefix = '│') : string + private static function optimizeColor(string $buffer) : string { - $message = $this->formatThrowable($t, $result['status']); - if ($message === '') { - return ''; - } - if (!($this->verbose || $result['verbose'])) { - return ''; - } - return $this->prefixLines($prefix, $message); + $patterns = ["/\33\\[22m\33\\[2m/" => '', "/\33\\[([^m]*)m\33\\[([1-9][0-9;]*)m/" => "\33[\$1;\$2m", "/(\33\\[[^m]*m)+(\33\\[0m)/" => '$2']; + return preg_replace(array_keys($patterns), array_values($patterns), $buffer); } - protected function prefixLines(string $prefix, string $message) : string +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use const PHP_EOL; +use function get_class; +use function sprintf; +use function str_replace; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Runner\PhptTestCase; +use RecursiveIteratorIterator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TextTestListRenderer +{ + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function render(TestSuite $suite) : string { - $message = \trim($message); - return \implode(\PHP_EOL, \array_map(static function (string $text) use($prefix) { - return ' ' . $prefix . ($text ? ' ' . $text : ''); - }, \preg_split('/\\r\\n|\\r|\\n/', $message))); + $buffer = 'Available test(s):' . \PHP_EOL; + foreach (new RecursiveIteratorIterator($suite->getIterator()) as $test) { + if ($test instanceof TestCase) { + $name = sprintf('%s::%s', get_class($test), str_replace(' with data set ', '', $test->getName())); + } elseif ($test instanceof PhptTestCase) { + $name = $test->getName(); + } else { + continue; + } + $buffer .= sprintf(' - %s' . \PHP_EOL, $name); + } + return $buffer; } } - - - - Test Documentation - - - -EOT; - /** - * @var string - */ - private const CLASS_HEADER = <<<'EOT' - -

          %s

          -
            - -EOT; - /** - * @var string - */ - private const CLASS_FOOTER = <<<'EOT' -
          -EOT; - /** - * @var string - */ - private const PAGE_FOOTER = <<<'EOT' - - - -EOT; - public function printResult(\PHPUnit\Framework\TestResult $result) : void - { - } - /** - * Handler for 'start run' event. - */ - protected function startRun() : void - { - $this->write(self::PAGE_HEADER); - } - /** - * Handler for 'start class' event. - */ - protected function startClass(string $name) : void - { - $this->write(\sprintf(self::CLASS_HEADER, $name, $this->currentTestClassPrettified)); - } - /** - * Handler for 'on test' event. - */ - protected function onTest(string $name, bool $success = \true) : void - { - $this->write(\sprintf("
        • %s %s
        • \n", $success ? '#555753' : '#ef2929', $success ? '✓' : '❌', $name)); - } - /** - * Handler for 'end class' event. - */ - protected function endClass(string $name) : void - { - $this->write(self::CLASS_FOOTER); - } - /** - * Handler for 'end run' event. + * @return false|int */ - protected function endRun() : void + public static function safeMatch(string $pattern, string $subject) { - $this->write(self::PAGE_FOOTER); + return \PHPUnit\Util\ErrorHandler::invokeIgnoringWarnings(static function () use($pattern, $subject) { + return preg_match($pattern, $subject); + }); } } '│', 'start' => '│', 'message' => '│', 'diff' => '│', 'trace' => '│', 'last' => '│']; + private $convertDeprecationsToExceptions; /** - * Colored Testdox use box-drawing for a more textured map of the message. + * @var bool */ - private const PREFIX_DECORATED = ['default' => '│', 'start' => '┐', 'message' => '├', 'diff' => '┊', 'trace' => '╵', 'last' => '┴']; - private const SPINNER_ICONS = [" \33[36m◐\33[0m running tests", " \33[36m◓\33[0m running tests", " \33[36m◑\33[0m running tests", " \33[36m◒\33[0m running tests"]; - private const STATUS_STYLES = [\PHPUnit\Runner\BaseTestRunner::STATUS_PASSED => ['symbol' => '✔', 'color' => 'fg-green'], \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR => ['symbol' => '✘', 'color' => 'fg-yellow', 'message' => 'bg-yellow,fg-black'], \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE => ['symbol' => '✘', 'color' => 'fg-red', 'message' => 'bg-red,fg-white'], \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED => ['symbol' => '↩', 'color' => 'fg-cyan', 'message' => 'fg-cyan'], \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY => ['symbol' => '☢', 'color' => 'fg-yellow', 'message' => 'fg-yellow'], \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE => ['symbol' => '∅', 'color' => 'fg-yellow', 'message' => 'fg-yellow'], \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING => ['symbol' => '⚠', 'color' => 'fg-yellow', 'message' => 'fg-yellow'], \PHPUnit\Runner\BaseTestRunner::STATUS_UNKNOWN => ['symbol' => '?', 'color' => 'fg-blue', 'message' => 'fg-white,bg-blue']]; + private $convertErrorsToExceptions; /** - * @var int[] + * @var bool */ - private $nonSuccessfulTestResults = []; + private $convertNoticesToExceptions; /** - * @var Timer + * @var bool */ - private $timer; + private $convertWarningsToExceptions; /** - * @param null|resource|string $out - * @param int|string $numberOfColumns - * - * @throws \PHPUnit\Framework\Exception + * @var bool */ - public function __construct($out = null, bool $verbose = \false, string $colors = self::COLOR_DEFAULT, bool $debug = \false, $numberOfColumns = 80, bool $reverse = \false) - { - parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns, $reverse); - $this->timer = new \PHPUnit\SebastianBergmann\Timer\Timer(); - $this->timer->start(); - } - public function printResult(\PHPUnit\Framework\TestResult $result) : void + private $registered = \false; + public static function invokeIgnoringWarnings(callable $callable) { - $this->printHeader($result); - $this->printNonSuccessfulTestsSummary($result->count()); - $this->printFooter($result); + set_error_handler(static function ($errorNumber, $errorString) { + if ($errorNumber === \E_WARNING) { + return; + } + return \false; + }); + $result = $callable(); + restore_error_handler(); + return $result; } - protected function printHeader(\PHPUnit\Framework\TestResult $result) : void + public function __construct(bool $convertDeprecationsToExceptions, bool $convertErrorsToExceptions, bool $convertNoticesToExceptions, bool $convertWarningsToExceptions) { - $this->write("\n" . (new \PHPUnit\SebastianBergmann\Timer\ResourceUsageFormatter())->resourceUsage($this->timer->stop()) . "\n\n"); + $this->convertDeprecationsToExceptions = $convertDeprecationsToExceptions; + $this->convertErrorsToExceptions = $convertErrorsToExceptions; + $this->convertNoticesToExceptions = $convertNoticesToExceptions; + $this->convertWarningsToExceptions = $convertWarningsToExceptions; } - protected function formatClassName(\PHPUnit\Framework\Test $test) : string + public function __invoke(int $errorNumber, string $errorString, string $errorFile, int $errorLine) : bool { - if ($test instanceof \PHPUnit\Framework\TestCase) { - return $this->prettifier->prettifyTestClass(\get_class($test)); + /* + * 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; } - return \get_class($test); - } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - protected function registerTestResult(\PHPUnit\Framework\Test $test, ?\Throwable $t, int $status, float $time, bool $verbose) : void - { - if ($status !== \PHPUnit\Runner\BaseTestRunner::STATUS_PASSED) { - $this->nonSuccessfulTestResults[] = $this->testIndex; + 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); } - parent::registerTestResult($test, $t, $status, $time, $verbose); } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - protected function formatTestName(\PHPUnit\Framework\Test $test) : string + public function register() : void { - if ($test instanceof \PHPUnit\Framework\TestCase) { - return $this->prettifier->prettifyTestCase($test); + if ($this->registered) { + return; } - return parent::formatTestName($test); + $oldErrorHandler = set_error_handler($this); + if ($oldErrorHandler !== null) { + restore_error_handler(); + return; + } + $this->registered = \true; } - protected function writeTestResult(array $prevResult, array $result) : void + public function unregister() : void { - // spacer line for new suite headers and after verbose messages - if ($prevResult['testName'] !== '' && (!empty($prevResult['message']) || $prevResult['className'] !== $result['className'])) { - $this->write(\PHP_EOL); - } - // suite header - if ($prevResult['className'] !== $result['className']) { - $this->write($this->colorizeTextBox('underlined', $result['className']) . \PHP_EOL); - } - // test result line - if ($this->colors && $result['className'] === \PHPUnit\Runner\PhptTestCase::class) { - $testName = \PHPUnit\Util\Color::colorizePath($result['testName'], $prevResult['testName'], \true); - } else { - $testName = $result['testMethod']; + if (!$this->registered) { + return; } - $style = self::STATUS_STYLES[$result['status']]; - $line = \sprintf(' %s %s%s' . \PHP_EOL, $this->colorizeTextBox($style['color'], $style['symbol']), $testName, $this->verbose ? ' ' . $this->formatRuntime($result['time'], $style['color']) : ''); - $this->write($line); - // additional information when verbose - $this->write($result['message']); + restore_error_handler(); } - protected function formatThrowable(\Throwable $t, ?int $status = null) : string - { - return \trim(\PHPUnit\Framework\TestFailure::exceptionToString($t)); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Exception extends RuntimeException implements \PHPUnit\Exception +{ +} +colorizeTextBox($style, \implode(\PHP_EOL, $message)); + + $result = new PHPUnit\Framework\TestResult; + + if ({collectCodeCoverageInformation}) { + $filter = unserialize('{codeCoverageFilter}'); + + $codeCoverage = new CodeCoverage( + (new Selector)->{driverMethod}($filter), + $filter + ); + + if ({cachesStaticAnalysis}) { + $codeCoverage->cacheStaticAnalysis(unserialize('{codeCoverageCacheDirectory}')); } - return [$message, $diff]; + + $result->setCodeCoverage($codeCoverage); } - protected function formatStacktrace(\Throwable $t) : string - { - $trace = \PHPUnit\Util\Filter::getFilteredStacktrace($t); - if (!$this->colors) { - return $trace; - } - $lines = []; - $prevPath = ''; - foreach (\explode(\PHP_EOL, $trace) as $line) { - if (\preg_match('/^(.*):(\\d+)$/', $line, $matches)) { - $lines[] = \PHPUnit\Util\Color::colorizePath($matches[1], $prevPath) . \PHPUnit\Util\Color::dim(':') . \PHPUnit\Util\Color::colorize('fg-blue', $matches[2]) . "\n"; - $prevPath = $matches[1]; - } else { - $lines[] = $line; - $prevPath = ''; - } - } - return \implode('', $lines); + + $result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything}); + $result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests}); + $result->enforceTimeLimit({enforcesTimeLimit}); + $result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests}); + $result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests}); + + $test = new {className}('{methodName}', unserialize('{data}'), '{dataName}'); + \assert($test instanceof TestCase); + + $test->setDependencyInput(unserialize('{dependencyInput}')); + $test->setInIsolation(true); + + ob_end_clean(); + $test->run($result); + $output = ''; + if (!$test->hasExpectationOnOutput()) { + $output = $test->getActualOutput(); } - protected function formatTestResultMessage(\Throwable $t, array $result, ?string $prefix = null) : string - { - $message = $this->formatThrowable($t, $result['status']); - $diff = ''; - if (!($this->verbose || $result['verbose'])) { - return ''; - } - if ($message && $this->colors) { - $style = self::STATUS_STYLES[$result['status']]['message'] ?? ''; - [$message, $diff] = $this->colorizeMessageAndDiff($style, $message); - } - if ($prefix === null || !$this->colors) { - $prefix = self::PREFIX_SIMPLE; - } - if ($this->colors) { - $color = self::STATUS_STYLES[$result['status']]['color'] ?? ''; - $prefix = \array_map(static function ($p) use($color) { - return \PHPUnit\Util\Color::colorize($color, $p); - }, self::PREFIX_DECORATED); - } - $trace = $this->formatStacktrace($t); - $out = $this->prefixLines($prefix['start'], \PHP_EOL) . \PHP_EOL; - if ($message) { - $out .= $this->prefixLines($prefix['message'], $message . \PHP_EOL) . \PHP_EOL; - } - if ($diff) { - $out .= $this->prefixLines($prefix['diff'], $diff . \PHP_EOL) . \PHP_EOL; + + ini_set('xdebug.scream', '0'); + @rewind(STDOUT); /* @ as not every STDOUT target stream is rewindable */ + if ($stdout = @stream_get_contents(STDOUT)) { + $output = $stdout . $output; + $streamMetaData = stream_get_meta_data(STDOUT); + if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { + @ftruncate(STDOUT, 0); + @rewind(STDOUT); } - if ($trace) { - if ($message || $diff) { - $out .= $this->prefixLines($prefix['default'], \PHP_EOL) . \PHP_EOL; - } - $out .= $this->prefixLines($prefix['trace'], $trace . \PHP_EOL) . \PHP_EOL; + } + + print serialize( + [ + 'testResult' => $test->getResult(), + 'numAssertions' => $test->getNumAssertions(), + 'result' => $result, + 'output' => $output + ] + ); +} + +$configurationFilePath = '{configurationFilePath}'; + +if ('' !== $configurationFilePath) { + $configuration = (new Loader)->load($configurationFilePath); + + (new PhpHandler)->handle($configuration->php()); + + unset($configuration); +} + +function __phpunit_error_handler($errno, $errstr, $errfile, $errline) +{ + return true; +} + +set_error_handler('__phpunit_error_handler'); + +{constants} +{included_files} +{globals} + +restore_error_handler(); + +if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; + unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); +} + +__phpunit_run_isolated_test(); +{driverMethod}($filter), + $filter + ); + + if ({cachesStaticAnalysis}) { + $codeCoverage->cacheStaticAnalysis(unserialize('{codeCoverageCacheDirectory}')); } - $out .= $this->prefixLines($prefix['last'], \PHP_EOL) . \PHP_EOL; - return $out; + + $result->setCodeCoverage($codeCoverage); } - protected function drawSpinner() : void - { - if ($this->colors) { - $id = $this->spinState % \count(self::SPINNER_ICONS); - $this->write(self::SPINNER_ICONS[$id]); - } + + $result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything}); + $result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests}); + $result->enforceTimeLimit({enforcesTimeLimit}); + $result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests}); + $result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests}); + + $test = new {className}('{name}', unserialize('{data}'), '{dataName}'); + $test->setDependencyInput(unserialize('{dependencyInput}')); + $test->setInIsolation(TRUE); + + ob_end_clean(); + $test->run($result); + $output = ''; + if (!$test->hasExpectationOnOutput()) { + $output = $test->getActualOutput(); } - protected function undrawSpinner() : void - { - if ($this->colors) { - $id = $this->spinState % \count(self::SPINNER_ICONS); - $this->write("\33[1K\33[" . \strlen(self::SPINNER_ICONS[$id]) . 'D'); + + ini_set('xdebug.scream', '0'); + @rewind(STDOUT); /* @ as not every STDOUT target stream is rewindable */ + if ($stdout = @stream_get_contents(STDOUT)) { + $output = $stdout . $output; + $streamMetaData = stream_get_meta_data(STDOUT); + if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { + @ftruncate(STDOUT, 0); + @rewind(STDOUT); } } - private function formatRuntime(float $time, string $color = '') : string - { - if (!$this->colors) { - return \sprintf('[%.2f ms]', $time * 1000); - } - if ($time > 1) { - $color = 'fg-magenta'; - } - return \PHPUnit\Util\Color::colorize($color, ' ' . (int) \ceil($time * 1000) . ' ' . \PHPUnit\Util\Color::dim('ms')); + + print serialize( + [ + 'testResult' => $test->getResult(), + 'numAssertions' => $test->getNumAssertions(), + 'result' => $result, + 'output' => $output + ] + ); +} + +$configurationFilePath = '{configurationFilePath}'; + +if ('' !== $configurationFilePath) { + $configuration = (new Loader)->load($configurationFilePath); + + (new PhpHandler)->handle($configuration->php()); + + unset($configuration); +} + +function __phpunit_error_handler($errno, $errstr, $errfile, $errline) +{ + return true; +} + +set_error_handler('__phpunit_error_handler'); + +{constants} +{included_files} +{globals} + +restore_error_handler(); + +if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; + unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); +} + +__phpunit_run_isolated_test(); +{driverMethod}($filter), + $filter + ); + + if ({codeCoverageCacheDirectory}) { + $coverage->cacheStaticAnalysis({codeCoverageCacheDirectory}); } - private function printNonSuccessfulTestsSummary(int $numberOfExecutedTests) : void - { - if (empty($this->nonSuccessfulTestResults)) { - return; - } - if (\count($this->nonSuccessfulTestResults) / $numberOfExecutedTests >= 0.7) { - return; - } - $this->write("Summary of non-successful tests:\n\n"); - $prevResult = $this->getEmptyTestResult(); - foreach ($this->nonSuccessfulTestResults as $testIndex) { - $result = $this->testResults[$testIndex]; - $this->writeTestResult($prevResult, $result); - $prevResult = $result; + + $coverage->start(__FILE__); +} + +register_shutdown_function( + function() use ($coverage) { + $output = null; + + if ($coverage) { + $output = $coverage->stop(); } + + file_put_contents('{coverageFile}', serialize($output)); } -} +); + +ob_end_clean(); + +require '{job}'; useColor = $useColor; - } - /** - * Prettifies the name of a test class. - * - * @psalm-param class-string $className - */ - public function prettifyTestClass(string $className) : string - { - try { - $annotations = \PHPUnit\Util\Test::parseTestMethodAnnotations($className); - if (isset($annotations['class']['testdox'][0])) { - return $annotations['class']['testdox'][0]; - } - } catch (\PHPUnit\Util\Exception $e) { - // ignore, determine className by parsing the provided name - } - $parts = \explode('\\', $className); - $className = \array_pop($parts); - if (\substr($className, -1 * \strlen('Test')) === 'Test') { - $className = \substr($className, 0, \strlen($className) - \strlen('Test')); - } - if (\strpos($className, 'Tests') === 0) { - $className = \substr($className, \strlen('Tests')); - } elseif (\strpos($className, 'Test') === 0) { - $className = \substr($className, \strlen('Test')); - } - if (empty($className)) { - $className = 'UnnamedTests'; - } - if (!empty($parts)) { - $parts[] = $className; - $fullyQualifiedName = \implode('\\', $parts); - } else { - $fullyQualifiedName = $className; - } - $result = ''; - $wasLowerCase = \false; - foreach (\range(0, \strlen($className) - 1) as $i) { - $isLowerCase = \mb_strtolower($className[$i], 'UTF-8') === $className[$i]; - if ($wasLowerCase && !$isLowerCase) { - $result .= ' '; - } - $result .= $className[$i]; - if ($isLowerCase) { - $wasLowerCase = \true; - } else { - $wasLowerCase = \false; - } - } - if ($fullyQualifiedName !== $className) { - return $result . ' (' . $fullyQualifiedName . ')'; - } - return $result; - } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function prettifyTestCase(\PHPUnit\Framework\TestCase $test) : string - { - $annotations = $test->getAnnotations(); - $annotationWithPlaceholders = \false; - $callback = static function (string $variable) : string { - return \sprintf('/%s(?=\\b)/', \preg_quote($variable, '/')); - }; - if (isset($annotations['method']['testdox'][0])) { - $result = $annotations['method']['testdox'][0]; - if (\strpos($result, '$') !== \false) { - $annotation = $annotations['method']['testdox'][0]; - $providedData = $this->mapTestMethodParameterNamesToProvidedDataValues($test); - $variables = \array_map($callback, \array_keys($providedData)); - $result = \trim(\preg_replace($variables, $providedData, $annotation)); - $annotationWithPlaceholders = \true; - } - } else { - $result = $this->prettifyTestMethod($test->getName(\false)); - } - if (!$annotationWithPlaceholders && $test->usesDataProvider()) { - $result .= $this->prettifyDataSet($test); - } - return $result; - } - public function prettifyDataSet(\PHPUnit\Framework\TestCase $test) : string + public function getCommand(array $settings, string $file = null) : string { - if (!$this->useColor) { - return $test->getDataSetAsString(\false); - } - if (\is_int($test->dataName())) { - $data = \PHPUnit\Util\Color::dim(' with data set ') . \PHPUnit\Util\Color::colorize('fg-cyan', (string) $test->dataName()); - } else { - $data = \PHPUnit\Util\Color::dim(' with ') . \PHPUnit\Util\Color::colorize('fg-cyan', \PHPUnit\Util\Color::visualizeWhitespace((string) $test->dataName())); + if (\PHP_MAJOR_VERSION < 8) { + return '"' . parent::getCommand($settings, $file) . '"'; } - return $data; + return parent::getCommand($settings, $file); } /** - * Prettifies the name of a test method. + * @throws Exception */ - public function prettifyTestMethod(string $name) : string + protected function getHandles() : array { - $buffer = ''; - if ($name === '') { - return $buffer; - } - $string = (string) \preg_replace('#\\d+$#', '', $name, -1, $count); - if (\in_array($string, $this->strings, \true)) { - $name = $string; - } elseif ($count === 0) { - $this->strings[] = $string; - } - if (\strpos($name, 'test_') === 0) { - $name = \substr($name, 5); - } elseif (\strpos($name, 'test') === 0) { - $name = \substr($name, 4); - } - if ($name === '') { - return $buffer; - } - $name[0] = \strtoupper($name[0]); - if (\strpos($name, '_') !== \false) { - return \trim(\str_replace('_', ' ', $name)); - } - $wasNumeric = \false; - foreach (\range(0, \strlen($name) - 1) as $i) { - if ($i > 0 && \ord($name[$i]) >= 65 && \ord($name[$i]) <= 90) { - $buffer .= ' ' . \strtolower($name[$i]); - } else { - $isNumeric = \is_numeric($name[$i]); - if (!$wasNumeric && $isNumeric) { - $buffer .= ' '; - $wasNumeric = \true; - } - if ($wasNumeric && !$isNumeric) { - $wasNumeric = \false; - } - $buffer .= $name[$i]; - } + if (\false === ($stdout_handle = tmpfile())) { + throw new Exception('A temporary file could not be created; verify that your TEMP environment variable is writable'); } - return $buffer; + return [1 => $stdout_handle]; } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - private function mapTestMethodParameterNamesToProvidedDataValues(\PHPUnit\Framework\TestCase $test) : array + protected function useTemporaryFile() : bool { - try { - $reflector = new \ReflectionMethod(\get_class($test), $test->getName(\false)); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Util\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $providedData = []; - $providedDataValues = \array_values($test->getProvidedData()); - $i = 0; - $providedData['$_dataName'] = $test->dataName(); - foreach ($reflector->getParameters() as $parameter) { - if (!\array_key_exists($i, $providedDataValues) && $parameter->isDefaultValueAvailable()) { - try { - $providedDataValues[$i] = $parameter->getDefaultValue(); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Util\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } - $value = $providedDataValues[$i++] ?? null; - if (\is_object($value)) { - $reflector = new \ReflectionObject($value); - if ($reflector->hasMethod('__toString')) { - $value = (string) $value; - } else { - $value = \get_class($value); - } - } - if (!\is_scalar($value)) { - $value = \gettype($value); - } - if (\is_bool($value) || \is_int($value) || \is_float($value)) { - $value = (new \PHPUnit\SebastianBergmann\Exporter\Exporter())->export($value); - } - if (\is_string($value) && $value === '') { - if ($this->useColor) { - $value = \PHPUnit\Util\Color::colorize('dim,underlined', 'empty'); - } else { - $value = "''"; - } - } - $providedData['$' . $parameter->getName()] = $value; - } - if ($this->useColor) { - $providedData = \array_map(static function ($value) { - return \PHPUnit\Util\Color::colorize('fg-cyan', \PHPUnit\Util\Color::visualizeWhitespace((string) $value, \true)); - }, $providedData); - } - return $providedData; + return \true; } } document = new \DOMDocument('1.0', 'UTF-8'); - $this->document->formatOutput = \true; - $this->root = $this->document->createElement('tests'); - $this->document->appendChild($this->root); - $this->prettifier = new \PHPUnit\Util\TestDox\NamePrettifier(); - parent::__construct($out); - } - /** - * Flush buffer and close output. - */ - public function flush() : void - { - $this->write($this->document->saveXML()); - parent::flush(); - } - /** - * An error occurred. - */ - public function addError(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void - { - $this->exception = $t; - } - /** - * A warning occurred. - */ - public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time) : void - { - } - /** - * A failure occurred. - */ - public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time) : void - { - $this->exception = $e; - } - /** - * Incomplete test. - */ - public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void - { - } - /** - * Risky test. - */ - public function addRiskyTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void - { - } - /** - * Skipped test. - */ - public function addSkippedTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void - { - } - /** - * A test suite started. - */ - public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) : void - { - } - /** - * A test suite ended. - */ - public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + public function runJob(string $job, array $settings = []) : array { + if ($this->stdin || $this->useTemporaryFile()) { + if (!($this->tempFile = tempnam(sys_get_temp_dir(), 'PHPUnit')) || file_put_contents($this->tempFile, $job) === \false) { + throw new Exception('Unable to write temporary file'); + } + $job = $this->stdin; + } + return $this->runProcess($job, $settings); } /** - * A test started. + * Returns an array of file handles to be used in place of pipes. */ - public function startTest(\PHPUnit\Framework\Test $test) : void + protected function getHandles() : array { - $this->exception = null; + return []; } /** - * A test ended. + * Handles creating the child process and returning the STDOUT and STDERR. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception */ - public function endTest(\PHPUnit\Framework\Test $test, float $time) : void + protected function runProcess(string $job, array $settings) : array { - if (!$test instanceof \PHPUnit\Framework\TestCase) { - return; - } - $groups = \array_filter($test->getGroups(), static function ($group) { - return !($group === 'small' || $group === 'medium' || $group === 'large' || \strpos($group, '__phpunit_') === 0); - }); - $testNode = $this->document->createElement('test'); - $testNode->setAttribute('className', \get_class($test)); - $testNode->setAttribute('methodName', $test->getName()); - $testNode->setAttribute('prettifiedClassName', $this->prettifier->prettifyTestClass(\get_class($test))); - $testNode->setAttribute('prettifiedMethodName', $this->prettifier->prettifyTestCase($test)); - $testNode->setAttribute('status', (string) $test->getStatus()); - $testNode->setAttribute('time', (string) $time); - $testNode->setAttribute('size', (string) $test->getSize()); - $testNode->setAttribute('groups', \implode(',', $groups)); - foreach ($groups as $group) { - $groupNode = $this->document->createElement('group'); - $groupNode->setAttribute('name', $group); - $testNode->appendChild($groupNode); - } - $annotations = $test->getAnnotations(); - foreach (['class', 'method'] as $type) { - foreach ($annotations[$type] as $annotation => $values) { - if ($annotation !== 'covers' && $annotation !== 'uses') { - continue; - } - foreach ($values as $value) { - $coversNode = $this->document->createElement($annotation); - $coversNode->setAttribute('target', $value); - $testNode->appendChild($coversNode); + $handles = $this->getHandles(); + $env = null; + if ($this->env) { + $env = $_SERVER ?? []; + unset($env['argv'], $env['argc']); + $env = array_merge($env, $this->env); + foreach ($env as $envKey => $envVar) { + if (is_array($envVar)) { + unset($env[$envKey]); } } } - foreach ($test->doubledTypes() as $doubledType) { - $testDoubleNode = $this->document->createElement('testDouble'); - $testDoubleNode->setAttribute('type', $doubledType); - $testNode->appendChild($testDoubleNode); + $pipeSpec = [0 => $handles[0] ?? ['pipe', 'r'], 1 => $handles[1] ?? ['pipe', 'w'], 2 => $handles[2] ?? ['pipe', 'w']]; + $process = proc_open($this->getCommand($settings, $this->tempFile), $pipeSpec, $pipes, null, $env); + if (!is_resource($process)) { + throw new Exception('Unable to spawn worker process'); } - $inlineAnnotations = \PHPUnit\Util\Test::getInlineAnnotations(\get_class($test), $test->getName(\false)); - if (isset($inlineAnnotations['given'], $inlineAnnotations['when'], $inlineAnnotations['then'])) { - $testNode->setAttribute('given', $inlineAnnotations['given']['value']); - $testNode->setAttribute('givenStartLine', (string) $inlineAnnotations['given']['line']); - $testNode->setAttribute('when', $inlineAnnotations['when']['value']); - $testNode->setAttribute('whenStartLine', (string) $inlineAnnotations['when']['line']); - $testNode->setAttribute('then', $inlineAnnotations['then']['value']); - $testNode->setAttribute('thenStartLine', (string) $inlineAnnotations['then']['line']); + if ($job) { + $this->process($pipes[0], $job); } - if ($this->exception !== null) { - if ($this->exception instanceof \PHPUnit\Framework\Exception) { - $steps = $this->exception->getSerializableTrace(); - } else { - $steps = $this->exception->getTrace(); - } - try { - $file = (new \ReflectionClass($test))->getFileName(); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - foreach ($steps as $step) { - if (isset($step['file']) && $step['file'] === $file) { - $testNode->setAttribute('exceptionLine', (string) $step['line']); + fclose($pipes[0]); + $stderr = $stdout = ''; + if ($this->timeout) { + unset($pipes[0]); + while (\true) { + $r = $pipes; + $w = null; + $e = null; + $n = @stream_select($r, $w, $e, $this->timeout); + if ($n === \false) { break; } + if ($n === 0) { + proc_terminate($process, 9); + throw new Exception(sprintf('Job execution aborted after %d seconds', $this->timeout)); + } + if ($n > 0) { + foreach ($r as $pipe) { + $pipeOffset = 0; + foreach ($pipes as $i => $origPipe) { + if ($pipe === $origPipe) { + $pipeOffset = $i; + break; + } + } + if (!$pipeOffset) { + break; + } + $line = fread($pipe, 8192); + if ($line === '' || $line === \false) { + fclose($pipes[$pipeOffset]); + unset($pipes[$pipeOffset]); + } elseif ($pipeOffset === 1) { + $stdout .= $line; + } else { + $stderr .= $line; + } + } + if (empty($pipes)) { + break; + } + } + } + } else { + if (isset($pipes[1])) { + $stdout = stream_get_contents($pipes[1]); + fclose($pipes[1]); + } + if (isset($pipes[2])) { + $stderr = stream_get_contents($pipes[2]); + fclose($pipes[2]); } - $testNode->setAttribute('exceptionMessage', $this->exception->getMessage()); } - $this->root->appendChild($testNode); + if (isset($handles[1])) { + rewind($handles[1]); + $stdout = stream_get_contents($handles[1]); + fclose($handles[1]); + } + if (isset($handles[2])) { + rewind($handles[2]); + $stderr = stream_get_contents($handles[2]); + fclose($handles[2]); + } + proc_close($process); + $this->cleanup(); + return ['stdout' => $stdout, 'stderr' => $stderr]; + } + /** + * @param resource $pipe + */ + protected function process($pipe, string $job) : void + { + fwrite($pipe, $job); + } + protected function cleanup() : void + { + if ($this->tempFile) { + unlink($this->tempFile); + } + } + protected function useTemporaryFile() : bool + { + return \false; } } */ - protected $skipped = 0; + protected $env = []; /** * @var int */ - protected $incomplete = 0; - /** - * @var null|string - */ - protected $currentTestClassPrettified; - /** - * @var null|string - */ - protected $currentTestMethodPrettified; - /** - * @var array - */ - private $groups; - /** - * @var array - */ - private $excludeGroups; + protected $timeout = 0; + public static function factory() : self + { + if (\DIRECTORY_SEPARATOR === '\\') { + return new \PHPUnit\Util\PHP\WindowsPhpProcess(); + } + return new \PHPUnit\Util\PHP\DefaultPhpProcess(); + } + public function __construct() + { + $this->runtime = new Runtime(); + } /** - * @param resource $out + * Defines if should use STDERR redirection or not. * - * @throws \PHPUnit\Framework\Exception + * Then $stderrRedirection is TRUE, STDERR is redirected to STDOUT. */ - public function __construct($out = null, array $groups = [], array $excludeGroups = []) + public function setUseStderrRedirection(bool $stderrRedirection) : void { - parent::__construct($out); - $this->groups = $groups; - $this->excludeGroups = $excludeGroups; - $this->prettifier = new \PHPUnit\Util\TestDox\NamePrettifier(); - $this->startRun(); + $this->stderrRedirection = $stderrRedirection; } /** - * Flush buffer and close output. + * Returns TRUE if uses STDERR redirection or FALSE if not. */ - public function flush() : void + public function useStderrRedirection() : bool { - $this->doEndClass(); - $this->endRun(); - parent::flush(); + return $this->stderrRedirection; } /** - * An error occurred. + * Sets the input string to be sent via STDIN. */ - public function addError(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + public function setStdin(string $stdin) : void { - if (!$this->isOfInterest($test)) { - return; - } - $this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR; - $this->failed++; + $this->stdin = $stdin; } /** - * A warning occurred. + * Returns the input string to be sent via STDIN. */ - public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time) : void + public function getStdin() : string { - if (!$this->isOfInterest($test)) { - return; - } - $this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING; - $this->warned++; + return $this->stdin; } /** - * A failure occurred. + * Sets the string of arguments to pass to the php job. */ - public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time) : void + public function setArgs(string $args) : void { - if (!$this->isOfInterest($test)) { - return; - } - $this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE; - $this->failed++; + $this->args = $args; } /** - * Incomplete test. + * Returns the string of arguments to pass to the php job. */ - public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + public function getArgs() : string { - if (!$this->isOfInterest($test)) { - return; - } - $this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE; - $this->incomplete++; + return $this->args; } /** - * Risky test. + * Sets the array of environment variables to start the child process with. + * + * @param array $env */ - public function addRiskyTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + public function setEnv(array $env) : void { - if (!$this->isOfInterest($test)) { - return; - } - $this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY; - $this->risky++; + $this->env = $env; } /** - * Skipped test. + * Returns the array of environment variables to start the child process with. */ - public function addSkippedTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + public function getEnv() : array { - if (!$this->isOfInterest($test)) { - return; - } - $this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED; - $this->skipped++; + return $this->env; } /** - * A testsuite started. + * Sets the amount of seconds to wait before timing out. */ - public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + public function setTimeout(int $timeout) : void { + $this->timeout = $timeout; } /** - * A testsuite ended. + * Returns the amount of seconds to wait before timing out. */ - public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + public function getTimeout() : int { + return $this->timeout; } /** - * A test started. + * Runs a single test in a separate PHP process. * * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function startTest(\PHPUnit\Framework\Test $test) : void + public function runTestJob(string $job, Test $test, TestResult $result) : void { - if (!$this->isOfInterest($test)) { - return; - } - $class = \get_class($test); - if ($this->testClass !== $class) { - if ($this->testClass !== '') { - $this->doEndClass(); - } - $this->currentTestClassPrettified = $this->prettifier->prettifyTestClass($class); - $this->testClass = $class; - $this->tests = []; - $this->startClass($class); - } - if ($test instanceof \PHPUnit\Framework\TestCase) { - $this->currentTestMethodPrettified = $this->prettifier->prettifyTestCase($test); - } - $this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_PASSED; + $result->startTest($test); + $_result = $this->runJob($job); + $this->processChildResult($test, $result, $_result['stdout'], $_result['stderr']); } /** - * A test ended. + * Returns the command based into the configurations. */ - public function endTest(\PHPUnit\Framework\Test $test, float $time) : void + public function getCommand(array $settings, string $file = null) : string { - if (!$this->isOfInterest($test)) { - return; + $command = $this->runtime->getBinary(); + if ($this->runtime->hasPCOV()) { + $settings = array_merge($settings, $this->runtime->getCurrentSettings(array_keys(ini_get_all('pcov')))); + } elseif ($this->runtime->hasXdebug()) { + $settings = array_merge($settings, $this->runtime->getCurrentSettings(array_keys(ini_get_all('xdebug')))); } - $this->tests[] = [$this->currentTestMethodPrettified, $this->testStatus]; - $this->currentTestClassPrettified = null; - $this->currentTestMethodPrettified = null; - } - protected function doEndClass() : void - { - foreach ($this->tests as $test) { - $this->onTest($test[0], $test[1] === \PHPUnit\Runner\BaseTestRunner::STATUS_PASSED); + $command .= $this->settingsToParameters($settings); + if (\PHP_SAPI === 'phpdbg') { + $command .= ' -qrr'; + if (!$file) { + $command .= 's='; + } } - $this->endClass($this->testClass); - } - /** - * Handler for 'start run' event. - */ - protected function startRun() : void - { - } - /** - * Handler for 'start class' event. - */ - protected function startClass(string $name) : void - { - } - /** - * Handler for 'on test' event. - */ - protected function onTest(string $name, bool $success = \true) : void - { + if ($file) { + $command .= ' ' . escapeshellarg($file); + } + if ($this->args) { + if (!$file) { + $command .= ' --'; + } + $command .= ' ' . $this->args; + } + if ($this->stderrRedirection) { + $command .= ' 2>&1'; + } + return $command; } /** - * Handler for 'end class' event. + * Runs a single job (PHP code) using a separate PHP process. */ - protected function endClass(string $name) : void + public abstract function runJob(string $job, array $settings = []) : array; + protected function settingsToParameters(array $settings) : string { + $buffer = ''; + foreach ($settings as $setting) { + $buffer .= ' -d ' . escapeshellarg($setting); + } + return $buffer; } /** - * Handler for 'end run' event. + * Processes the TestResult object from an isolated process. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - protected function endRun() : void - { - } - private function isOfInterest(\PHPUnit\Framework\Test $test) : bool + private function processChildResult(Test $test, TestResult $result, string $stdout, string $stderr) : void { - if (!$test instanceof \PHPUnit\Framework\TestCase) { - return \false; - } - if ($test instanceof \PHPUnit\Framework\WarningTestCase) { - return \false; - } - if (!empty($this->groups)) { - foreach ($test->getGroups() as $group) { - if (\in_array($group, $this->groups, \true)) { - return \true; + $time = 0; + if (!empty($stderr)) { + $result->addError($test, new Exception(trim($stderr)), $time); + } else { + set_error_handler( + /** + * @throws ErrorException + */ + static function ($errno, $errstr, $errfile, $errline) : void { + throw new ErrorException($errstr, $errno, $errno, $errfile, $errline); + } + ); + try { + if (strpos($stdout, "#!/usr/bin/env php\n") === 0) { + $stdout = substr($stdout, 19); + } + $childResult = unserialize(str_replace("#!/usr/bin/env php\n", '', $stdout)); + restore_error_handler(); + if ($childResult === \false) { + $result->addFailure($test, new AssertionFailedError('Test was run in child process and ended unexpectedly'), $time); } + } catch (ErrorException $e) { + restore_error_handler(); + $childResult = \false; + $result->addError($test, new Exception(trim($stdout), 0, $e), $time); } - return \false; - } - if (!empty($this->excludeGroups)) { - foreach ($test->getGroups() as $group) { - if (\in_array($group, $this->excludeGroups, \true)) { - return \false; + if ($childResult !== \false) { + if (!empty($childResult['output'])) { + $output = $childResult['output']; + } + /* @var TestCase $test */ + $test->setResult($childResult['testResult']); + $test->addToAssertionCount($childResult['numAssertions']); + $childResult = $childResult['result']; + assert($childResult instanceof TestResult); + if ($result->getCollectCodeCoverageInformation()) { + $result->getCodeCoverage()->merge($childResult->getCodeCoverage()); + } + $time = $childResult->time(); + $notImplemented = $childResult->notImplemented(); + $risky = $childResult->risky(); + $skipped = $childResult->skipped(); + $errors = $childResult->errors(); + $warnings = $childResult->warnings(); + $failures = $childResult->failures(); + if (!empty($notImplemented)) { + $result->addError($test, $this->getException($notImplemented[0]), $time); + } elseif (!empty($risky)) { + $result->addError($test, $this->getException($risky[0]), $time); + } elseif (!empty($skipped)) { + $result->addError($test, $this->getException($skipped[0]), $time); + } elseif (!empty($errors)) { + $result->addError($test, $this->getException($errors[0]), $time); + } elseif (!empty($warnings)) { + $result->addWarning($test, $this->getException($warnings[0]), $time); + } elseif (!empty($failures)) { + $result->addFailure($test, $this->getException($failures[0]), $time); } } - return \true; } - return \true; + $result->endTest($test, $time); + if (!empty($output)) { + print $output; + } } -} - - * - * 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 is_dir; -use function mkdir; -use function str_replace; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Filesystem -{ /** - * Maps class names to source file names. + * Gets the thrown exception from a PHPUnit\Framework\TestFailure. * - * - PEAR CS: Foo_Bar_Baz -> Foo/Bar/Baz.php - * - Namespace: Foo\Bar\Baz -> Foo/Bar/Baz.php + * @see https://github.com/sebastianbergmann/phpunit/issues/74 */ - public static function classNameToFilename(string $className) : string - { - return \str_replace(['_', '\\'], \DIRECTORY_SEPARATOR, $className) . '.php'; - } - public static function createDirectory(string $directory) : bool + private function getException(TestFailure $error) : Exception { - return !(!\is_dir($directory) && !@\mkdir($directory, 0777, \true) && !\is_dir($directory)); + $exception = $error->thrownException(); + if ($exception instanceof __PHP_Incomplete_Class) { + $exceptionArray = []; + foreach ((array) $exception as $key => $value) { + $key = substr($key, strrpos($key, "\0") + 1); + $exceptionArray[$key] = $value; + } + $exception = new SyntheticError(sprintf('%s: %s', $exceptionArray['_PHP_Incomplete_Class_Name'], $exceptionArray['message']), $exceptionArray['code'], $exceptionArray['file'], $exceptionArray['line'], $exceptionArray['trace']); + } + return $exception; } } stream = $out; return; } - if (!\is_string($out)) { + if (!is_string($out)) { return; } - if (\strpos($out, 'socket://') === 0) { - $tmp = \explode(':', \str_replace('socket://', '', $out)); - if (\count($tmp) !== 2) { - throw new \PHPUnit\Util\Exception(\sprintf('"%s" does not match "socket://hostname:port" format', $out)); + if (strpos($out, 'socket://') === 0) { + $tmp = explode(':', str_replace('socket://', '', $out)); + if (count($tmp) !== 2) { + throw new \PHPUnit\Util\Exception(sprintf('"%s" does not match "socket://hostname:port" format', $out)); } - $this->stream = \fsockopen($tmp[0], (int) $tmp[1]); + $this->stream = fsockopen($tmp[0], (int) $tmp[1]); return; } - if (\strpos($out, 'php://') === \false && !\PHPUnit\Util\Filesystem::createDirectory(\dirname($out))) { - throw new \PHPUnit\Util\Exception(\sprintf('Directory "%s" was not created', \dirname($out))); + if (strpos($out, 'php://') === \false && !\PHPUnit\Util\Filesystem::createDirectory(dirname($out))) { + throw new \PHPUnit\Util\Exception(sprintf('Directory "%s" was not created', dirname($out))); } - $this->stream = \fopen($out, 'wb'); - $this->isPhpStream = \strncmp($out, 'php://', 6) !== 0; + $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); + assert(is_resource($this->stream)); + fwrite($this->stream, $buffer); } else { if (\PHP_SAPI !== 'cli' && \PHP_SAPI !== 'phpdbg') { - $buffer = \htmlspecialchars($buffer, \ENT_COMPAT | \ENT_SUBSTITUTE); + $buffer = htmlspecialchars($buffer, \ENT_COMPAT | \ENT_SUBSTITUTE); } print $buffer; } @@ -49869,9 +53682,59 @@ class Printer public function flush() : void { if ($this->stream && $this->isPhpStream) { - \assert(\is_resource($this->stream)); - \fclose($this->stream); + assert(is_resource($this->stream)); + fclose($this->stream); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use Throwable; +/** + * @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; + } + } + public static function isCloneable(object $object) : bool + { + try { + $clone = clone $object; + } catch (Throwable $t) { + return \false; } + return $clone instanceof $object; } } 0) { + $json = (array) $json; + } else { + return; + } + } + ksort($json); + foreach ($json as $key => &$value) { + self::recursiveSort($value); + } } } getName(); return new self((string) $class->getDocComment(), \false, self::extractAnnotationsFromReflector($class), $class->getStartLine(), $class->getEndLine(), $class->getFileName(), $className, $className); @@ -50057,7 +53940,7 @@ final class DocBlock /** * @psalm-param class-string $classNameInHierarchy */ - public static function ofMethod(\ReflectionMethod $method, string $classNameInHierarchy) : self + public static function ofMethod(ReflectionMethod $method, string $classNameInHierarchy) : self { return new self((string) $method->getDocComment(), \true, self::extractAnnotationsFromReflector($method), $method->getStartLine(), $method->getEndLine(), $method->getFileName(), $method->getName(), $classNameInHierarchy); } @@ -50100,38 +53983,37 @@ final class DocBlock $requires = []; $recordedSettings = []; $extensionVersions = []; - $recordedOffsets = ['__FILE' => \realpath($this->fileName)]; - // Split docblock into lines and rewind offset to start of docblock - $lines = \preg_split('/\\r\\n|\\r|\\n/', $this->docComment); - $offset -= \count($lines); + $recordedOffsets = ['__FILE' => realpath($this->fileName)]; + // Trim docblock markers, split it into lines and rewind offset to start of docblock + $lines = preg_replace(['#^/\\*{2}#', '#\\*/$#'], '', preg_split('/\\r\\n|\\r|\\n/', $this->docComment)); + $offset -= count($lines); foreach ($lines as $line) { - if (\preg_match(self::REGEX_REQUIRES_OS, $line, $matches)) { + if (preg_match(self::REGEX_REQUIRES_OS, $line, $matches)) { $requires[$matches['name']] = $matches['value']; $recordedOffsets[$matches['name']] = $offset; } - if (\preg_match(self::REGEX_REQUIRES_VERSION, $line, $matches)) { + if (preg_match(self::REGEX_REQUIRES_VERSION, $line, $matches)) { $requires[$matches['name']] = ['version' => $matches['version'], 'operator' => $matches['operator']]; $recordedOffsets[$matches['name']] = $offset; } - if (\preg_match(self::REGEX_REQUIRES_VERSION_CONSTRAINT, $line, $matches)) { + if (preg_match(self::REGEX_REQUIRES_VERSION_CONSTRAINT, $line, $matches)) { if (!empty($requires[$matches['name']])) { $offset++; continue; } try { - $versionConstraintParser = new \PHPUnit\PharIo\Version\VersionConstraintParser(); - $requires[$matches['name'] . '_constraint'] = ['constraint' => $versionConstraintParser->parse(\trim($matches['constraint']))]; + $versionConstraintParser = new VersionConstraintParser(); + $requires[$matches['name'] . '_constraint'] = ['constraint' => $versionConstraintParser->parse(trim($matches['constraint']))]; $recordedOffsets[$matches['name'] . '_constraint'] = $offset; } catch (\PHPUnit\PharIo\Version\Exception $e) { - /* @TODO this catch is currently not valid, see https://github.com/phar-io/version/issues/16 */ - throw new \PHPUnit\Framework\Warning($e->getMessage(), $e->getCode(), $e); + throw new Warning($e->getMessage(), $e->getCode(), $e); } } - if (\preg_match(self::REGEX_REQUIRES_SETTING, $line, $matches)) { + if (preg_match(self::REGEX_REQUIRES_SETTING, $line, $matches)) { $recordedSettings[$matches['setting']] = $matches['value']; $recordedOffsets['__SETTING_' . $matches['setting']] = $offset; } - if (\preg_match(self::REGEX_REQUIRES, $line, $matches)) { + if (preg_match(self::REGEX_REQUIRES, $line, $matches)) { $name = $matches['name'] . 's'; if (!isset($requires[$name])) { $requires[$name] = []; @@ -50144,7 +54026,7 @@ final class DocBlock } $offset++; } - return $this->parsedRequirements = \array_merge($requires, ['__OFFSET' => $recordedOffsets], \array_filter(['setting' => $recordedSettings, 'extension_versions' => $extensionVersions])); + return $this->parsedRequirements = array_merge($requires, ['__OFFSET' => $recordedOffsets], array_filter(['setting' => $recordedSettings, 'extension_versions' => $extensionVersions])); } /** * Returns the provided data for a method. @@ -50159,11 +54041,11 @@ final class DocBlock return null; } if ($data === []) { - throw new \PHPUnit\Framework\SkippedTestError(); + throw new SkippedTestError(); } foreach ($data as $key => $value) { - if (!\is_array($value)) { - throw new \PHPUnit\Util\InvalidDataSetException(\sprintf('Data set %s is invalid.', \is_int($key) ? '#' . $key : '"' . $key . '"')); + if (!is_array($value)) { + throw new InvalidDataSetException(sprintf('Data set %s is invalid.', is_int($key) ? '#' . $key : '"' . $key . '"')); } } return $data; @@ -50173,15 +54055,15 @@ final class DocBlock */ public function getInlineAnnotations() : array { - $code = \file($this->fileName); + $code = file($this->fileName); $lineNumber = $this->startLine; $startLine = $this->startLine - 1; $endLine = $this->endLine - 1; - $codeLines = \array_slice($code, $startLine, $endLine - $startLine + 1); + $codeLines = array_slice($code, $startLine, $endLine - $startLine + 1); $annotations = []; foreach ($codeLines as $line) { - if (\preg_match('#/\\*\\*?\\s*@(?P[A-Za-z_-]+)(?:[ \\t]+(?P.*?))?[ \\t]*\\r?\\*/$#m', $line, $matches)) { - $annotations[\strtolower($matches['name'])] = ['line' => $lineNumber, 'value' => $matches['value']]; + if (preg_match('#/\\*\\*?\\s*@(?P[A-Za-z_-]+)(?:[ \\t]+(?P.*?))?[ \\t]*\\r?\\*/$#m', $line, $matches)) { + $annotations[strtolower($matches['name'])] = ['line' => $lineNumber, 'value' => $matches['value']]; } $lineNumber++; } @@ -50193,27 +54075,27 @@ final class DocBlock } public function isHookToBeExecutedBeforeClass() : bool { - return $this->isMethod && \false !== \strpos($this->docComment, '@beforeClass'); + return $this->isMethod && \false !== strpos($this->docComment, '@beforeClass'); } public function isHookToBeExecutedAfterClass() : bool { - return $this->isMethod && \false !== \strpos($this->docComment, '@afterClass'); + return $this->isMethod && \false !== strpos($this->docComment, '@afterClass'); } public function isToBeExecutedBeforeTest() : bool { - return 1 === \preg_match('/@before\\b/', $this->docComment); + return 1 === preg_match('/@before\\b/', $this->docComment); } public function isToBeExecutedAfterTest() : bool { - return 1 === \preg_match('/@after\\b/', $this->docComment); + return 1 === preg_match('/@after\\b/', $this->docComment); } public function isToBeExecutedAsPreCondition() : bool { - return 1 === \preg_match('/@preCondition\\b/', $this->docComment); + return 1 === preg_match('/@preCondition\\b/', $this->docComment); } public function isToBeExecutedAsPostCondition() : bool { - return 1 === \preg_match('/@postCondition\\b/', $this->docComment); + return 1 === preg_match('/@postCondition\\b/', $this->docComment); } private function getDataFromDataProviderAnnotation(string $docComment) : ?array { @@ -50222,31 +54104,31 @@ final class DocBlock if ($this->isMethod) { $methodName = $this->name; } - if (!\preg_match_all(self::REGEX_DATA_PROVIDER, $docComment, $matches)) { + if (!preg_match_all(self::REGEX_DATA_PROVIDER, $docComment, $matches)) { return null; } $result = []; foreach ($matches[1] as $match) { - $dataProviderMethodNameNamespace = \explode('\\', $match); - $leaf = \explode('::', \array_pop($dataProviderMethodNameNamespace)); - $dataProviderMethodName = \array_pop($leaf); + $dataProviderMethodNameNamespace = explode('\\', $match); + $leaf = explode('::', array_pop($dataProviderMethodNameNamespace)); + $dataProviderMethodName = array_pop($leaf); if (empty($dataProviderMethodNameNamespace)) { $dataProviderMethodNameNamespace = ''; } else { - $dataProviderMethodNameNamespace = \implode('\\', $dataProviderMethodNameNamespace) . '\\'; + $dataProviderMethodNameNamespace = implode('\\', $dataProviderMethodNameNamespace) . '\\'; } if (empty($leaf)) { $dataProviderClassName = $className; } else { /** @psalm-var class-string $dataProviderClassName */ - $dataProviderClassName = $dataProviderMethodNameNamespace . \array_pop($leaf); + $dataProviderClassName = $dataProviderMethodNameNamespace . array_pop($leaf); } try { - $dataProviderClass = new \ReflectionClass($dataProviderClassName); + $dataProviderClass = new ReflectionClass($dataProviderClassName); $dataProviderMethod = $dataProviderClass->getMethod($dataProviderMethodName); // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Util\Exception($e->getMessage(), (int) $e->getCode(), $e); + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), (int) $e->getCode(), $e); // @codeCoverageIgnoreEnd } if ($dataProviderMethod->isStatic()) { @@ -50259,85 +54141,594 @@ final class DocBlock } else { $data = $dataProviderMethod->invoke($object, $methodName); } - if ($data instanceof \Traversable) { + if ($data instanceof Traversable) { $origData = $data; $data = []; foreach ($origData as $key => $value) { - if (\is_int($key)) { + if (is_int($key)) { $data[] = $value; - } elseif (\array_key_exists($key, $data)) { - throw new \PHPUnit\Framework\InvalidDataProviderException(\sprintf('The key "%s" has already been defined in the data provider "%s".', $key, $match)); + } elseif (array_key_exists($key, $data)) { + throw new InvalidDataProviderException(sprintf('The key "%s" has already been defined in the data provider "%s".', $key, $match)); } else { $data[$key] = $value; } } } - if (\is_array($data)) { - $result = \array_merge($result, $data); + if (is_array($data)) { + $result = array_merge($result, $data); } } return $result; } /** - * @throws Exception + * @throws Exception + */ + private function getDataFromTestWithAnnotation(string $docComment) : ?array + { + $docComment = $this->cleanUpMultiLineAnnotation($docComment); + if (!preg_match(self::REGEX_TEST_WITH, $docComment, $matches, \PREG_OFFSET_CAPTURE)) { + return null; + } + $offset = strlen($matches[0][0]) + $matches[0][1]; + $annotationContent = substr($docComment, $offset); + $data = []; + foreach (explode("\n", $annotationContent) as $candidateRow) { + $candidateRow = trim($candidateRow); + if ($candidateRow[0] !== '[') { + break; + } + $dataSet = json_decode($candidateRow, \true); + if (json_last_error() !== \JSON_ERROR_NONE) { + throw new Exception('The data set for the @testWith annotation cannot be parsed: ' . json_last_error_msg()); + } + $data[] = $dataSet; + } + if (!$data) { + throw new Exception('The data set for the @testWith annotation cannot be parsed.'); + } + return $data; + } + private function cleanUpMultiLineAnnotation(string $docComment) : string + { + //removing initial ' * ' for docComment + $docComment = str_replace("\r\n", "\n", $docComment); + $docComment = preg_replace('/' . '\\n' . '\\s*' . '\\*' . '\\s?' . '/', "\n", $docComment); + $docComment = (string) substr($docComment, 0, -1); + return rtrim($docComment, "\n"); + } + /** @return array> */ + private static function parseDocBlock(string $docBlock) : array + { + // Strip away the docblock header and footer to ease parsing of one line annotations + $docBlock = (string) substr($docBlock, 3, -2); + $annotations = []; + if (preg_match_all('/@(?P[A-Za-z_-]+)(?:[ \\t]+(?P.*?))?[ \\t]*\\r?$/m', $docBlock, $matches)) { + $numMatches = count($matches[0]); + for ($i = 0; $i < $numMatches; $i++) { + $annotations[$matches['name'][$i]][] = (string) $matches['value'][$i]; + } + } + return $annotations; + } + /** @param ReflectionClass|ReflectionFunctionAbstract $reflector */ + private static function extractAnnotationsFromReflector(Reflector $reflector) : array + { + $annotations = []; + if ($reflector instanceof ReflectionClass) { + $annotations = array_merge($annotations, ...array_map(static function (ReflectionClass $trait) : array { + return self::parseDocBlock((string) $trait->getDocComment()); + }, array_values($reflector->getTraits()))); + } + return array_merge($annotations, self::parseDocBlock((string) $reflector->getDocComment())); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Annotation; + +use function array_key_exists; +use PHPUnit\Util\Exception; +use ReflectionClass; +use ReflectionException; +use ReflectionMethod; +/** + * Reflection information, and therefore DocBlock information, is static within + * a single PHP process. It is therefore okay to use a Singleton registry here. + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Registry +{ + /** @var null|self */ + private static $instance; + /** @var array indexed by class name */ + private $classDocBlocks = []; + /** @var array> indexed by class name and method name */ + private $methodDocBlocks = []; + public static function getInstance() : self + { + return self::$instance ?? (self::$instance = new self()); + } + private function __construct() + { + } + /** + * @throws Exception + * @psalm-param class-string $class + */ + public function forClassName(string $class) : \PHPUnit\Util\Annotation\DocBlock + { + if (array_key_exists($class, $this->classDocBlocks)) { + return $this->classDocBlocks[$class]; + } + try { + $reflection = new ReflectionClass($class); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + return $this->classDocBlocks[$class] = \PHPUnit\Util\Annotation\DocBlock::ofClass($reflection); + } + /** + * @throws Exception + * @psalm-param class-string $classInHierarchy + */ + public function forMethod(string $classInHierarchy, string $method) : \PHPUnit\Util\Annotation\DocBlock + { + if (isset($this->methodDocBlocks[$classInHierarchy][$method])) { + return $this->methodDocBlocks[$classInHierarchy][$method]; + } + try { + $reflection = new ReflectionMethod($classInHierarchy, $method); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + return $this->methodDocBlocks[$classInHierarchy][$method] = \PHPUnit\Util\Annotation\DocBlock::ofMethod($reflection, $classInHierarchy); + } +} + + * + * 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 class_exists; +use function defined; +use function dirname; +use function is_dir; +use function realpath; +use function sprintf; +use function strpos; +use function sys_get_temp_dir; +use PHPUnit\Composer\Autoload\ClassLoader; +use PHPUnit\DeepCopy\DeepCopy; +use PHPUnit\Doctrine\Instantiator\Instantiator; +use PHPUnit\PharIo\Manifest\Manifest; +use PHPUnit\PharIo\Version\Version as PharIoVersion; +use PHPUnit\phpDocumentor\Reflection\DocBlock; +use PHPUnit\phpDocumentor\Reflection\Project; +use PHPUnit\phpDocumentor\Reflection\Type; +use PHPUnit\PhpParser\Parser; +use PHPUnit\Framework\TestCase; +use Prophecy\Prophet; +use ReflectionClass; +use ReflectionException; +use PHPUnit\SebastianBergmann\CliParser\Parser as CliParser; +use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; +use PHPUnit\SebastianBergmann\CodeUnit\CodeUnit; +use PHPUnit\SebastianBergmann\CodeUnitReverseLookup\Wizard; +use PHPUnit\SebastianBergmann\Comparator\Comparator; +use PHPUnit\SebastianBergmann\Complexity\Calculator; +use PHPUnit\SebastianBergmann\Diff\Diff; +use PHPUnit\SebastianBergmann\Environment\Runtime; +use PHPUnit\SebastianBergmann\Exporter\Exporter; +use PHPUnit\SebastianBergmann\FileIterator\Facade as FileIteratorFacade; +use PHPUnit\SebastianBergmann\GlobalState\Snapshot; +use PHPUnit\SebastianBergmann\Invoker\Invoker; +use PHPUnit\SebastianBergmann\LinesOfCode\Counter; +use PHPUnit\SebastianBergmann\ObjectEnumerator\Enumerator; +use PHPUnit\SebastianBergmann\RecursionContext\Context; +use PHPUnit\SebastianBergmann\ResourceOperations\ResourceOperations; +use PHPUnit\SebastianBergmann\Template\Template; +use PHPUnit\SebastianBergmann\Timer\Timer; +use PHPUnit\SebastianBergmann\Type\TypeName; +use PHPUnit\SebastianBergmann\Version; +use PHPUnit\Symfony\Polyfill\Ctype\Ctype; +use PHPUnit\TheSeer\Tokenizer\Tokenizer; +use PHPUnit\Webmozart\Assert\Assert; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class ExcludeList +{ + /** + * @var array + */ + private const EXCLUDED_CLASS_NAMES = [ + // composer + ClassLoader::class => 1, + // doctrine/instantiator + Instantiator::class => 1, + // myclabs/deepcopy + DeepCopy::class => 1, + // nikic/php-parser + Parser::class => 1, + // phar-io/manifest + Manifest::class => 1, + // phar-io/version + PharIoVersion::class => 1, + // phpdocumentor/reflection-common + Project::class => 1, + // phpdocumentor/reflection-docblock + DocBlock::class => 1, + // phpdocumentor/type-resolver + Type::class => 1, + // phpspec/prophecy + Prophet::class => 1, + // phpunit/phpunit + TestCase::class => 2, + // phpunit/php-code-coverage + CodeCoverage::class => 1, + // phpunit/php-file-iterator + FileIteratorFacade::class => 1, + // phpunit/php-invoker + Invoker::class => 1, + // phpunit/php-text-template + Template::class => 1, + // phpunit/php-timer + Timer::class => 1, + // sebastian/cli-parser + CliParser::class => 1, + // sebastian/code-unit + CodeUnit::class => 1, + // sebastian/code-unit-reverse-lookup + Wizard::class => 1, + // sebastian/comparator + Comparator::class => 1, + // sebastian/complexity + Calculator::class => 1, + // sebastian/diff + Diff::class => 1, + // sebastian/environment + Runtime::class => 1, + // sebastian/exporter + Exporter::class => 1, + // sebastian/global-state + Snapshot::class => 1, + // sebastian/lines-of-code + Counter::class => 1, + // sebastian/object-enumerator + Enumerator::class => 1, + // sebastian/recursion-context + Context::class => 1, + // sebastian/resource-operations + ResourceOperations::class => 1, + // sebastian/type + TypeName::class => 1, + // sebastian/version + Version::class => 1, + // symfony/polyfill-ctype + Ctype::class => 1, + // theseer/tokenizer + Tokenizer::class => 1, + // webmozart/assert + Assert::class => 1, + ]; + /** + * @var string[] + */ + private static $directories; + public static function addDirectory(string $directory) : void + { + if (!is_dir($directory)) { + throw new \PHPUnit\Util\Exception(sprintf('"%s" is not a directory', $directory)); + } + self::$directories[] = realpath($directory); + } + /** + * @throws Exception + * + * @return string[] + */ + public function getExcludedDirectories() : array + { + $this->initialize(); + return self::$directories; + } + /** + * @throws Exception + */ + public function isExcluded(string $file) : bool + { + if (defined('PHPUNIT_TESTSUITE')) { + return \false; + } + $this->initialize(); + foreach (self::$directories as $directory) { + if (strpos($file, $directory) === 0) { + return \true; + } + } + return \false; + } + /** + * @throws Exception + */ + private function initialize() : void + { + if (self::$directories === null) { + self::$directories = []; + foreach (self::EXCLUDED_CLASS_NAMES as $className => $parent) { + if (!class_exists($className)) { + continue; + } + try { + $directory = (new ReflectionClass($className))->getFileName(); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Util\Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + for ($i = 0; $i < $parent; $i++) { + $directory = dirname($directory); + } + self::$directories[] = $directory; + } + // Hide process isolation workaround on Windows. + if (\DIRECTORY_SEPARATOR === '\\') { + // tempnam() prefix is limited to first 3 chars. + // @see https://php.net/manual/en/function.tempnam.php + self::$directories[] = sys_get_temp_dir() . '\\PHP'; + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +use function defined; +use function is_file; +use function sprintf; +use PHPUnit\Runner\Version; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class SchemaFinder +{ + /** + * @throws Exception + */ + public function find(string $version) : string + { + if ($version === Version::series()) { + $filename = $this->path() . 'phpunit.xsd'; + } else { + $filename = $this->path() . 'schema/' . $version . '.xsd'; + } + if (!is_file($filename)) { + throw new \PHPUnit\Util\Xml\Exception(sprintf('Schema for PHPUnit %s is not available', $version)); + } + return $filename; + } + private function path() : string + { + if (defined('__PHPUNIT_PHAR_ROOT__')) { + return __PHPUNIT_PHAR_ROOT__ . '/'; + } + return __DIR__ . '/../../../'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +use function file_get_contents; +use function libxml_clear_errors; +use function libxml_get_errors; +use function libxml_use_internal_errors; +use DOMDocument; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Validator +{ + public function validate(DOMDocument $document, string $xsdFilename) : \PHPUnit\Util\Xml\ValidationResult + { + $originalErrorHandling = libxml_use_internal_errors(\true); + $document->schemaValidateSource(file_get_contents($xsdFilename)); + $errors = libxml_get_errors(); + libxml_clear_errors(); + libxml_use_internal_errors($originalErrorHandling); + return \PHPUnit\Util\Xml\ValidationResult::fromArray($errors); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +use function chdir; +use function dirname; +use function error_reporting; +use function file_get_contents; +use function getcwd; +use function libxml_get_errors; +use function libxml_use_internal_errors; +use function sprintf; +use DOMDocument; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Loader +{ + /** + * @throws Exception + */ + public function loadFile(string $filename, bool $isHtml = \false, bool $xinclude = \false, bool $strict = \false) : DOMDocument + { + $reporting = error_reporting(0); + $contents = file_get_contents($filename); + error_reporting($reporting); + if ($contents === \false) { + throw new \PHPUnit\Util\Xml\Exception(sprintf('Could not read "%s".', $filename)); + } + return $this->load($contents, $isHtml, $filename, $xinclude, $strict); + } + /** + * @throws Exception + */ + public function load(string $actual, bool $isHtml = \false, string $filename = '', bool $xinclude = \false, bool $strict = \false) : DOMDocument + { + if ($actual === '') { + throw new \PHPUnit\Util\Xml\Exception('Could not load XML from empty string'); + } + // Required for XInclude on Windows. + if ($xinclude) { + $cwd = getcwd(); + @chdir(dirname($filename)); + } + $document = new DOMDocument(); + $document->preserveWhiteSpace = \false; + $internal = libxml_use_internal_errors(\true); + $message = ''; + $reporting = error_reporting(0); + if ($filename !== '') { + // Required for XInclude + $document->documentURI = $filename; + } + if ($isHtml) { + $loaded = $document->loadHTML($actual); + } else { + $loaded = $document->loadXML($actual); + } + if (!$isHtml && $xinclude) { + $document->xinclude(); + } + foreach (libxml_get_errors() as $error) { + $message .= "\n" . $error->message; + } + libxml_use_internal_errors($internal); + error_reporting($reporting); + if (isset($cwd)) { + @chdir($cwd); + } + if ($loaded === \false || $strict && $message !== '') { + if ($filename !== '') { + throw new \PHPUnit\Util\Xml\Exception(sprintf('Could not load "%s".%s', $filename, $message !== '' ? "\n" . $message : '')); + } + if ($message === '') { + $message = 'Could not load XML for unknown reason'; + } + throw new \PHPUnit\Util\Xml\Exception($message); + } + return $document; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +use function sprintf; +use function trim; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +final class ValidationResult +{ + /** + * @psalm-var array> + */ + private $validationErrors = []; + /** + * @psalm-param array $errors */ - private function getDataFromTestWithAnnotation(string $docComment) : ?array + public static function fromArray(array $errors) : self { - $docComment = $this->cleanUpMultiLineAnnotation($docComment); - if (!\preg_match(self::REGEX_TEST_WITH, $docComment, $matches, \PREG_OFFSET_CAPTURE)) { - return null; - } - $offset = \strlen($matches[0][0]) + $matches[0][1]; - $annotationContent = \substr($docComment, $offset); - $data = []; - foreach (\explode("\n", $annotationContent) as $candidateRow) { - $candidateRow = \trim($candidateRow); - if ($candidateRow[0] !== '[') { - break; - } - $dataSet = \json_decode($candidateRow, \true); - if (\json_last_error() !== \JSON_ERROR_NONE) { - throw new \PHPUnit\Util\Exception('The data set for the @testWith annotation cannot be parsed: ' . \json_last_error_msg()); + $validationErrors = []; + foreach ($errors as $error) { + if (!isset($validationErrors[$error->line])) { + $validationErrors[$error->line] = []; } - $data[] = $dataSet; - } - if (!$data) { - throw new \PHPUnit\Util\Exception('The data set for the @testWith annotation cannot be parsed.'); + $validationErrors[$error->line][] = trim($error->message); } - return $data; + return new self($validationErrors); } - private function cleanUpMultiLineAnnotation(string $docComment) : string + private function __construct(array $validationErrors) { - //removing initial ' * ' for docComment - $docComment = \str_replace("\r\n", "\n", $docComment); - $docComment = \preg_replace('/' . '\\n' . '\\s*' . '\\*' . '\\s?' . '/', "\n", $docComment); - $docComment = (string) \substr($docComment, 0, -1); - return \rtrim($docComment, "\n"); + $this->validationErrors = $validationErrors; } - /** @return array> */ - private static function parseDocBlock(string $docBlock) : array + public function hasValidationErrors() : bool { - // Strip away the docblock header and footer to ease parsing of one line annotations - $docBlock = (string) \substr($docBlock, 3, -2); - $annotations = []; - if (\preg_match_all('/@(?P[A-Za-z_-]+)(?:[ \\t]+(?P.*?))?[ \\t]*\\r?$/m', $docBlock, $matches)) { - $numMatches = \count($matches[0]); - for ($i = 0; $i < $numMatches; $i++) { - $annotations[$matches['name'][$i]][] = (string) $matches['value'][$i]; - } - } - return $annotations; + return !empty($this->validationErrors); } - /** @param ReflectionClass|ReflectionFunctionAbstract $reflector */ - private static function extractAnnotationsFromReflector(\Reflector $reflector) : array + public function asString() : string { - $annotations = []; - if ($reflector instanceof \ReflectionClass) { - $annotations = \array_merge($annotations, ...\array_map(function (\ReflectionClass $trait) : array { - return self::parseDocBlock((string) $trait->getDocComment()); - }, \array_values($reflector->getTraits()))); + $buffer = ''; + foreach ($this->validationErrors as $line => $validationErrorsOnLine) { + $buffer .= sprintf(\PHP_EOL . ' Line %d:' . \PHP_EOL, $line); + foreach ($validationErrorsOnLine as $validationError) { + $buffer .= sprintf(' - %s' . \PHP_EOL, $validationError); + } } - return \array_merge($annotations, self::parseDocBlock((string) $reflector->getDocComment())); + return $buffer; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +/** * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable */ -final class Registry +final class FailedSchemaDetectionResult extends \PHPUnit\Util\Xml\SchemaDetectionResult { - /** @var null|self */ - private static $instance; - /** @var array indexed by class name */ - private $classDocBlocks = []; - /** @var array> indexed by class name and method name */ - private $methodDocBlocks = []; - public static function getInstance() : self - { - return self::$instance ?? (self::$instance = new self()); - } - private function __construct() +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @psalm-immutable + */ +abstract class SchemaDetectionResult +{ + public function detected() : bool { + return \false; } /** * @throws Exception - * @psalm-param class-string $class */ - public function forClassName(string $class) : \PHPUnit\Util\Annotation\DocBlock + public function version() : string { - if (\array_key_exists($class, $this->classDocBlocks)) { - return $this->classDocBlocks[$class]; - } - try { - $reflection = new \ReflectionClass($class); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Util\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - return $this->classDocBlocks[$class] = \PHPUnit\Util\Annotation\DocBlock::ofClass($reflection); + throw new \PHPUnit\Util\Xml\Exception('No supported schema was detected'); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class SchemaDetector +{ /** * @throws Exception - * @psalm-param class-string $classInHierarchy */ - public function forMethod(string $classInHierarchy, string $method) : \PHPUnit\Util\Annotation\DocBlock + public function detect(string $filename) : \PHPUnit\Util\Xml\SchemaDetectionResult { - if (isset($this->methodDocBlocks[$classInHierarchy][$method])) { - return $this->methodDocBlocks[$classInHierarchy][$method]; - } - try { - $reflection = new \ReflectionMethod($classInHierarchy, $method); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Util\Exception($e->getMessage(), (int) $e->getCode(), $e); + $document = (new \PHPUnit\Util\Xml\Loader())->loadFile($filename, \false, \true, \true); + foreach (['9.2', '8.5'] as $candidate) { + $schema = (new \PHPUnit\Util\Xml\SchemaFinder())->find($candidate); + if (!(new \PHPUnit\Util\Xml\Validator())->validate($document, $schema)->hasValidationErrors()) { + return new \PHPUnit\Util\Xml\SuccessfulSchemaDetectionResult($candidate); + } } - // @codeCoverageIgnoreEnd - return $this->methodDocBlocks[$classInHierarchy][$method] = \PHPUnit\Util\Annotation\DocBlock::ofMethod($reflection, $classInHierarchy); + return new \PHPUnit\Util\Xml\FailedSchemaDetectionResult(); } } - */ - private const WHITESPACE_MAP = [' ' => '·', "\t" => '⇥']; - /** - * @var array + * @var string */ - private const WHITESPACE_EOL_MAP = [' ' => '·', "\t" => '⇥', "\n" => '↵', "\r" => '⟵']; + private $version; + public function __construct(string $version) + { + $this->version = $version; + } + public function detected() : bool + { + return \true; + } + public function version() : string + { + return $this->version; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Xml; + +use ArrayIterator; +use Countable; +use DOMNode; +use DOMNodeList; +use IteratorAggregate; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class SnapshotNodeList implements Countable, IteratorAggregate +{ /** - * @var array + * @var DOMNode[] */ - 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 + private $nodes = []; + public static function fromNodeList(DOMNodeList $list) : self { - 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; + $snapshot = new self(); + foreach ($list as $node) { + $snapshot->nodes[] = $node; } - return self::optimizeColor(\sprintf("\33[%sm", \implode(';', $styles)) . $buffer . "\33[0m"); + return $snapshot; } - public static function colorizePath(string $path, ?string $prevPath = null, bool $colorizeFilename = \false) : string + public function count() : int { - 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)); + return \count($this->nodes); } - public static function dim(string $buffer) : string + public function getIterator() : ArrayIterator { - if (\trim($buffer) === '') { - return $buffer; - } - return "\33[2m{$buffer}\33[22m"; + return new ArrayIterator($this->nodes); } - public static function visualizeWhitespace(string $buffer, bool $visualizeEOL = \false) : string +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\TestDox; + +use PHPUnit\Framework\TestResult; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TextResultPrinter extends \PHPUnit\Util\TestDox\ResultPrinter +{ + public function printResult(TestResult $result) : void { - $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 + /** + * Handler for 'start class' event. + */ + protected function startClass(string $name) : void { - $patterns = ["/\33\\[22m\33\\[2m/" => '', "/\33\\[([^m]*)m\33\\[([1-9][0-9;]*)m/" => "\33[\$1;\$2m", "/(\33\\[[^m]*m)+(\33\\[0m)/" => '$2']; - return \preg_replace(\array_keys($patterns), \array_values($patterns), $buffer); + $this->write($this->currentTestClassPrettified . "\n"); + } + /** + * Handler for 'on test' event. + */ + protected function onTest(string $name, bool $success = \true) : void + { + if ($success) { + $this->write(' [x] '); + } else { + $this->write(' [ ] '); + } + $this->write($name . "\n"); + } + /** + * Handler for 'end class' event. + */ + protected function endClass(string $name) : void + { + $this->write("\n"); } } importNode($element, \true); - } + private $strings = []; /** - * @deprecated Only used by assertEqualXMLStructure() + * @var bool */ - public static function removeCharacterDataNodes(\DOMNode $node) : void + private $useColor; + public function __construct(bool $useColor = \false) { - if ($node->hasChildNodes()) { - for ($i = $node->childNodes->length - 1; $i >= 0; $i--) { - if (($child = $node->childNodes->item($i)) instanceof \DOMCharacterData) { - $node->removeChild($child); - } - } - } + $this->useColor = $useColor; } /** - * Escapes a string for the use in XML documents. - * - * Any Unicode character is allowed, excluding the surrogate blocks, FFFE, - * and FFFF (not even as character reference). + * Prettifies the name of a test class. * - * @see https://www.w3.org/TR/xml/#charsets + * @psalm-param class-string $className */ - public static function prepareString(string $string) : string + public function prettifyTestClass(string $className) : string { - return \preg_replace('/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/', '', \htmlspecialchars(self::convertToUtf8($string), \ENT_QUOTES)); + try { + $annotations = Test::parseTestMethodAnnotations($className); + if (isset($annotations['class']['testdox'][0])) { + return $annotations['class']['testdox'][0]; + } + } catch (UtilException $e) { + // ignore, determine className by parsing the provided name + } + $parts = explode('\\', $className); + $className = array_pop($parts); + if (substr($className, -1 * strlen('Test')) === 'Test') { + $className = substr($className, 0, strlen($className) - strlen('Test')); + } + if (strpos($className, 'Tests') === 0) { + $className = substr($className, strlen('Tests')); + } elseif (strpos($className, 'Test') === 0) { + $className = substr($className, strlen('Test')); + } + if (empty($className)) { + $className = 'UnnamedTests'; + } + if (!empty($parts)) { + $parts[] = $className; + $fullyQualifiedName = implode('\\', $parts); + } else { + $fullyQualifiedName = $className; + } + $result = ''; + $wasLowerCase = \false; + foreach (range(0, strlen($className) - 1) as $i) { + $isLowerCase = mb_strtolower($className[$i], 'UTF-8') === $className[$i]; + if ($wasLowerCase && !$isLowerCase) { + $result .= ' '; + } + $result .= $className[$i]; + if ($isLowerCase) { + $wasLowerCase = \true; + } else { + $wasLowerCase = \false; + } + } + if ($fullyQualifiedName !== $className) { + return $result . ' (' . $fullyQualifiedName . ')'; + } + return $result; } /** - * "Convert" a DOMElement object into a PHP variable. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public static function xmlToVariable(\DOMElement $element) + public function prettifyTestCase(TestCase $test) : string { - $variable = null; - switch ($element->tagName) { - case 'array': - $variable = []; - foreach ($element->childNodes as $entry) { - if (!$entry instanceof \DOMElement || $entry->tagName !== 'element') { - continue; - } - $item = $entry->childNodes->item(0); - if ($item instanceof \DOMText) { - $item = $entry->childNodes->item(1); - } - $value = self::xmlToVariable($item); - if ($entry->hasAttribute('key')) { - $variable[(string) $entry->getAttribute('key')] = $value; - } else { - $variable[] = $value; - } - } - break; - case 'object': - $className = $element->getAttribute('class'); - if ($element->hasChildNodes()) { - $arguments = $element->childNodes->item(0)->childNodes; - $constructorArgs = []; - foreach ($arguments as $argument) { - if ($argument instanceof \DOMElement) { - $constructorArgs[] = self::xmlToVariable($argument); - } - } - try { - \assert(\class_exists($className)); - $variable = (new \ReflectionClass($className))->newInstanceArgs($constructorArgs); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Util\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } else { - $variable = new $className(); - } - break; - case 'boolean': - $variable = $element->textContent === 'true'; - break; - case 'integer': - case 'double': - case 'string': - $variable = $element->textContent; - \settype($variable, $element->tagName); - break; + $annotations = Test::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); + $annotationWithPlaceholders = \false; + $callback = static function (string $variable) : string { + return sprintf('/%s(?=\\b)/', preg_quote($variable, '/')); + }; + if (isset($annotations['method']['testdox'][0])) { + $result = $annotations['method']['testdox'][0]; + if (strpos($result, '$') !== \false) { + $annotation = $annotations['method']['testdox'][0]; + $providedData = $this->mapTestMethodParameterNamesToProvidedDataValues($test); + $variables = array_map($callback, array_keys($providedData)); + $result = trim(preg_replace($variables, $providedData, $annotation)); + $annotationWithPlaceholders = \true; + } + } else { + $result = $this->prettifyTestMethod($test->getName(\false)); } - return $variable; + if (!$annotationWithPlaceholders && $test->usesDataProvider()) { + $result .= $this->prettifyDataSet($test); + } + return $result; } - private static function convertToUtf8(string $string) : string + public function prettifyDataSet(TestCase $test) : string { - if (!self::isUtf8($string)) { - $string = \mb_convert_encoding($string, 'UTF-8'); + if (!$this->useColor) { + return $test->getDataSetAsString(\false); } - return $string; + if (is_int($test->dataName())) { + $data = Color::dim(' with data set ') . Color::colorize('fg-cyan', (string) $test->dataName()); + } else { + $data = Color::dim(' with ') . Color::colorize('fg-cyan', Color::visualizeWhitespace((string) $test->dataName())); + } + return $data; } - private static function isUtf8(string $string) : bool + /** + * Prettifies the name of a test method. + */ + public function prettifyTestMethod(string $name) : string { - $length = \strlen($string); - for ($i = 0; $i < $length; $i++) { - if (\ord($string[$i]) < 0x80) { - $n = 0; - } elseif ((\ord($string[$i]) & 0xe0) === 0xc0) { - $n = 1; - } elseif ((\ord($string[$i]) & 0xf0) === 0xe0) { - $n = 2; - } elseif ((\ord($string[$i]) & 0xf0) === 0xf0) { - $n = 3; - } else { - return \false; - } - for ($j = 0; $j < $n; $j++) { - if (++$i === $length || (\ord($string[$i]) & 0xc0) !== 0x80) { - return \false; + $buffer = ''; + if ($name === '') { + return $buffer; + } + $string = (string) preg_replace('#\\d+$#', '', $name, -1, $count); + if (in_array($string, $this->strings, \true)) { + $name = $string; + } elseif ($count === 0) { + $this->strings[] = $string; + } + if (strpos($name, 'test_') === 0) { + $name = substr($name, 5); + } elseif (strpos($name, 'test') === 0) { + $name = substr($name, 4); + } + if ($name === '') { + return $buffer; + } + $name[0] = strtoupper($name[0]); + if (strpos($name, '_') !== \false) { + return trim(str_replace('_', ' ', $name)); + } + $wasNumeric = \false; + foreach (range(0, strlen($name) - 1) as $i) { + if ($i > 0 && ord($name[$i]) >= 65 && ord($name[$i]) <= 90) { + $buffer .= ' ' . strtolower($name[$i]); + } else { + $isNumeric = is_numeric($name[$i]); + if (!$wasNumeric && $isNumeric) { + $buffer .= ' '; + $wasNumeric = \true; + } + if ($wasNumeric && !$isNumeric) { + $wasNumeric = \false; } + $buffer .= $name[$i]; } } - return \true; - } -} - - * - * 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 addslashes; -use function array_map; -use function implode; -use function is_string; -use function realpath; -use function sprintf; -use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage as FilterConfiguration; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @deprecated - */ -final class XdebugFilterScriptGenerator -{ - public function generate(\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage $filter) : string - { - $files = \array_map(static function ($item) { - return \sprintf(" '%s'", $item); - }, $this->getItems($filter)); - $files = \implode(",\n", $files); - return <<directories() as $directory) { - $path = \realpath($directory->path()); - if (\is_string($path)) { - $files[] = \sprintf(\addslashes('%s' . \DIRECTORY_SEPARATOR), $path); + try { + $reflector = new ReflectionMethod(get_class($test), $test->getName(\false)); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new UtilException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $providedData = []; + $providedDataValues = array_values($test->getProvidedData()); + $i = 0; + $providedData['$_dataName'] = $test->dataName(); + foreach ($reflector->getParameters() as $parameter) { + if (!array_key_exists($i, $providedDataValues) && $parameter->isDefaultValueAvailable()) { + try { + $providedDataValues[$i] = $parameter->getDefaultValue(); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new UtilException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + $value = $providedDataValues[$i++] ?? null; + if (is_object($value)) { + $reflector = new ReflectionObject($value); + if ($reflector->hasMethod('__toString')) { + $value = (string) $value; + } else { + $value = get_class($value); + } + } + if (!is_scalar($value)) { + $value = gettype($value); + } + if (is_bool($value) || is_int($value) || is_float($value)) { + $value = (new Exporter())->export($value); + } + if (is_string($value) && $value === '') { + if ($this->useColor) { + $value = Color::colorize('dim,underlined', 'empty'); + } else { + $value = "''"; + } } + $providedData['$' . $parameter->getName()] = $value; } - foreach ($filter->files() as $file) { - $files[] = $file->path(); + if ($this->useColor) { + $providedData = array_map(static function ($value) { + return Color::colorize('fg-cyan', Color::visualizeWhitespace((string) $value, \true)); + }, $providedData); } - return $files; + return $providedData; } } '│', 'start' => '│', 'message' => '│', 'diff' => '│', 'trace' => '│', 'last' => '│']; /** - * @var int[] + * Colored Testdox use box-drawing for a more textured map of the message. */ - private $testSuiteSkipped = [0]; + private const PREFIX_DECORATED = ['default' => '│', 'start' => '┐', 'message' => '├', 'diff' => '┊', 'trace' => '╵', 'last' => '┴']; + private const SPINNER_ICONS = [" \33[36m◐\33[0m running tests", " \33[36m◓\33[0m running tests", " \33[36m◑\33[0m running tests", " \33[36m◒\33[0m running tests"]; + private const STATUS_STYLES = [BaseTestRunner::STATUS_PASSED => ['symbol' => '✔', 'color' => 'fg-green'], BaseTestRunner::STATUS_ERROR => ['symbol' => '✘', 'color' => 'fg-yellow', 'message' => 'bg-yellow,fg-black'], BaseTestRunner::STATUS_FAILURE => ['symbol' => '✘', 'color' => 'fg-red', 'message' => 'bg-red,fg-white'], BaseTestRunner::STATUS_SKIPPED => ['symbol' => '↩', 'color' => 'fg-cyan', 'message' => 'fg-cyan'], BaseTestRunner::STATUS_RISKY => ['symbol' => '☢', 'color' => 'fg-yellow', 'message' => 'fg-yellow'], BaseTestRunner::STATUS_INCOMPLETE => ['symbol' => '∅', 'color' => 'fg-yellow', 'message' => 'fg-yellow'], BaseTestRunner::STATUS_WARNING => ['symbol' => '⚠', 'color' => 'fg-yellow', 'message' => 'fg-yellow'], BaseTestRunner::STATUS_UNKNOWN => ['symbol' => '?', 'color' => 'fg-blue', 'message' => 'fg-white,bg-blue']]; /** * @var int[] */ - private $testSuiteTimes = [0]; - /** - * @var int - */ - private $testSuiteLevel = 0; + private $nonSuccessfulTestResults = []; /** - * @var DOMElement + * @var Timer */ - private $currentTestCase; + private $timer; /** - * @param null|mixed $out + * @param null|resource|string $out + * @param int|string $numberOfColumns + * + * @throws \PHPUnit\Framework\Exception */ - public function __construct($out = null, bool $reportRiskyTests = \false) + public function __construct($out = null, bool $verbose = \false, string $colors = self::COLOR_DEFAULT, bool $debug = \false, $numberOfColumns = 80, bool $reverse = \false) { - $this->document = new \DOMDocument('1.0', 'UTF-8'); - $this->document->formatOutput = \true; - $this->root = $this->document->createElement('testsuites'); - $this->document->appendChild($this->root); - parent::__construct($out); - $this->reportRiskyTests = $reportRiskyTests; + parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns, $reverse); + $this->timer = new Timer(); + $this->timer->start(); } - /** - * Flush buffer and close output. - */ - public function flush() : void + public function printResult(TestResult $result) : void { - $this->write($this->getXML()); - parent::flush(); + $this->printHeader($result); + $this->printNonSuccessfulTestsSummary($result->count()); + $this->printFooter($result); } - /** - * An error occurred. - */ - public function addError(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + protected function printHeader(TestResult $result) : void { - $this->doAddFault($test, $t, 'error'); - $this->testSuiteErrors[$this->testSuiteLevel]++; + $this->write("\n" . (new ResourceUsageFormatter())->resourceUsage($this->timer->stop()) . "\n\n"); } - /** - * A warning occurred. - */ - public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time) : void + protected function formatClassName(Test $test) : string { - $this->doAddFault($test, $e, 'warning'); - $this->testSuiteWarnings[$this->testSuiteLevel]++; + if ($test instanceof TestCase) { + return $this->prettifier->prettifyTestClass(get_class($test)); + } + return get_class($test); } /** - * A failure occurred. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time) : void + protected function registerTestResult(Test $test, ?Throwable $t, int $status, float $time, bool $verbose) : void { - $this->doAddFault($test, $e, 'failure'); - $this->testSuiteFailures[$this->testSuiteLevel]++; + if ($status !== BaseTestRunner::STATUS_PASSED) { + $this->nonSuccessfulTestResults[] = $this->testIndex; + } + parent::registerTestResult($test, $t, $status, $time, $verbose); } /** - * Incomplete test. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + protected function formatTestName(Test $test) : string { - $this->doAddSkipped(); + if ($test instanceof TestCase) { + return $this->prettifier->prettifyTestCase($test); + } + return parent::formatTestName($test); } - /** - * Risky test. - */ - public function addRiskyTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + protected function writeTestResult(array $prevResult, array $result) : void { - if (!$this->reportRiskyTests) { - return; + // spacer line for new suite headers and after verbose messages + if ($prevResult['testName'] !== '' && (!empty($prevResult['message']) || $prevResult['className'] !== $result['className'])) { + $this->write(\PHP_EOL); } - $this->doAddFault($test, $t, 'error'); - $this->testSuiteErrors[$this->testSuiteLevel]++; + // suite header + if ($prevResult['className'] !== $result['className']) { + $this->write($this->colorizeTextBox('underlined', $result['className']) . \PHP_EOL); + } + // test result line + if ($this->colors && $result['className'] === PhptTestCase::class) { + $testName = Color::colorizePath($result['testName'], $prevResult['testName'], \true); + } else { + $testName = $result['testMethod']; + } + $style = self::STATUS_STYLES[$result['status']]; + $line = sprintf(' %s %s%s' . \PHP_EOL, $this->colorizeTextBox($style['color'], $style['symbol']), $testName, $this->verbose ? ' ' . $this->formatRuntime($result['time'], $style['color']) : ''); + $this->write($line); + // additional information when verbose + $this->write($result['message']); } - /** - * Skipped test. - */ - public function addSkippedTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + protected function formatThrowable(Throwable $t, ?int $status = null) : string { - $this->doAddSkipped(); + return trim(\PHPUnit\Framework\TestFailure::exceptionToString($t)); } - /** - * A testsuite started. - */ - public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + protected function colorizeMessageAndDiff(string $style, string $buffer) : array { - $testSuite = $this->document->createElement('testsuite'); - $testSuite->setAttribute('name', $suite->getName()); - if (\class_exists($suite->getName(), \false)) { - try { - $class = new \ReflectionClass($suite->getName()); - $testSuite->setAttribute('file', $class->getFileName()); - } catch (\ReflectionException $e) { + $lines = $buffer ? array_map('\\rtrim', explode(\PHP_EOL, $buffer)) : []; + $message = []; + $diff = []; + $insideDiff = \false; + foreach ($lines as $line) { + if ($line === '--- Expected') { + $insideDiff = \true; + } + if (!$insideDiff) { + $message[] = $line; + } else { + if (strpos($line, '-') === 0) { + $line = Color::colorize('fg-red', Color::visualizeWhitespace($line, \true)); + } elseif (strpos($line, '+') === 0) { + $line = Color::colorize('fg-green', Color::visualizeWhitespace($line, \true)); + } elseif ($line === '@@ @@') { + $line = Color::colorize('fg-cyan', $line); + } + $diff[] = $line; } } - if ($this->testSuiteLevel > 0) { - $this->testSuites[$this->testSuiteLevel]->appendChild($testSuite); - } else { - $this->root->appendChild($testSuite); + $diff = implode(\PHP_EOL, $diff); + if (!empty($message)) { + $message = $this->colorizeTextBox($style, implode(\PHP_EOL, $message)); } - $this->testSuiteLevel++; - $this->testSuites[$this->testSuiteLevel] = $testSuite; - $this->testSuiteTests[$this->testSuiteLevel] = 0; - $this->testSuiteAssertions[$this->testSuiteLevel] = 0; - $this->testSuiteErrors[$this->testSuiteLevel] = 0; - $this->testSuiteWarnings[$this->testSuiteLevel] = 0; - $this->testSuiteFailures[$this->testSuiteLevel] = 0; - $this->testSuiteSkipped[$this->testSuiteLevel] = 0; - $this->testSuiteTimes[$this->testSuiteLevel] = 0; + return [$message, $diff]; } - /** - * A testsuite ended. - */ - public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + protected function formatStacktrace(Throwable $t) : string { - $this->testSuites[$this->testSuiteLevel]->setAttribute('tests', (string) $this->testSuiteTests[$this->testSuiteLevel]); - $this->testSuites[$this->testSuiteLevel]->setAttribute('assertions', (string) $this->testSuiteAssertions[$this->testSuiteLevel]); - $this->testSuites[$this->testSuiteLevel]->setAttribute('errors', (string) $this->testSuiteErrors[$this->testSuiteLevel]); - $this->testSuites[$this->testSuiteLevel]->setAttribute('warnings', (string) $this->testSuiteWarnings[$this->testSuiteLevel]); - $this->testSuites[$this->testSuiteLevel]->setAttribute('failures', (string) $this->testSuiteFailures[$this->testSuiteLevel]); - $this->testSuites[$this->testSuiteLevel]->setAttribute('skipped', (string) $this->testSuiteSkipped[$this->testSuiteLevel]); - $this->testSuites[$this->testSuiteLevel]->setAttribute('time', \sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel])); - if ($this->testSuiteLevel > 1) { - $this->testSuiteTests[$this->testSuiteLevel - 1] += $this->testSuiteTests[$this->testSuiteLevel]; - $this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel]; - $this->testSuiteErrors[$this->testSuiteLevel - 1] += $this->testSuiteErrors[$this->testSuiteLevel]; - $this->testSuiteWarnings[$this->testSuiteLevel - 1] += $this->testSuiteWarnings[$this->testSuiteLevel]; - $this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel]; - $this->testSuiteSkipped[$this->testSuiteLevel - 1] += $this->testSuiteSkipped[$this->testSuiteLevel]; - $this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel]; + $trace = \PHPUnit\Util\Filter::getFilteredStacktrace($t); + if (!$this->colors) { + return $trace; } - $this->testSuiteLevel--; + $lines = []; + $prevPath = ''; + foreach (explode(\PHP_EOL, $trace) as $line) { + if (preg_match('/^(.*):(\\d+)$/', $line, $matches)) { + $lines[] = Color::colorizePath($matches[1], $prevPath) . Color::dim(':') . Color::colorize('fg-blue', $matches[2]) . "\n"; + $prevPath = $matches[1]; + } else { + $lines[] = $line; + $prevPath = ''; + } + } + return implode('', $lines); } - /** - * A test started. - */ - public function startTest(\PHPUnit\Framework\Test $test) : void + protected function formatTestResultMessage(Throwable $t, array $result, ?string $prefix = null) : string { - $usesDataprovider = \false; - if (\method_exists($test, 'usesDataProvider')) { - $usesDataprovider = $test->usesDataProvider(); + $message = $this->formatThrowable($t, $result['status']); + $diff = ''; + if (!($this->verbose || $result['verbose'])) { + return ''; } - $testCase = $this->document->createElement('testcase'); - $testCase->setAttribute('name', $test->getName()); - try { - $class = new \ReflectionClass($test); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Util\Exception($e->getMessage(), (int) $e->getCode(), $e); + if ($message && $this->colors) { + $style = self::STATUS_STYLES[$result['status']]['message'] ?? ''; + [$message, $diff] = $this->colorizeMessageAndDiff($style, $message); } - // @codeCoverageIgnoreEnd - $methodName = $test->getName(!$usesDataprovider); - if ($class->hasMethod($methodName)) { - try { - $method = $class->getMethod($methodName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Util\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $testCase->setAttribute('class', $class->getName()); - $testCase->setAttribute('classname', \str_replace('\\', '.', $class->getName())); - $testCase->setAttribute('file', $class->getFileName()); - $testCase->setAttribute('line', (string) $method->getStartLine()); + if ($prefix === null || !$this->colors) { + $prefix = self::PREFIX_SIMPLE; } - $this->currentTestCase = $testCase; - } - /** - * A test ended. - */ - public function endTest(\PHPUnit\Framework\Test $test, float $time) : void - { - $numAssertions = 0; - if (\method_exists($test, 'getNumAssertions')) { - $numAssertions = $test->getNumAssertions(); + if ($this->colors) { + $color = self::STATUS_STYLES[$result['status']]['color'] ?? ''; + $prefix = array_map(static function ($p) use($color) { + return Color::colorize($color, $p); + }, self::PREFIX_DECORATED); } - $this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions; - $this->currentTestCase->setAttribute('assertions', (string) $numAssertions); - $this->currentTestCase->setAttribute('time', \sprintf('%F', $time)); - $this->testSuites[$this->testSuiteLevel]->appendChild($this->currentTestCase); - $this->testSuiteTests[$this->testSuiteLevel]++; - $this->testSuiteTimes[$this->testSuiteLevel] += $time; - $testOutput = ''; - if (\method_exists($test, 'hasOutput') && \method_exists($test, 'getActualOutput')) { - $testOutput = $test->hasOutput() ? $test->getActualOutput() : ''; + $trace = $this->formatStacktrace($t); + $out = $this->prefixLines($prefix['start'], \PHP_EOL) . \PHP_EOL; + if ($message) { + $out .= $this->prefixLines($prefix['message'], $message . \PHP_EOL) . \PHP_EOL; } - if (!empty($testOutput)) { - $systemOut = $this->document->createElement('system-out', \PHPUnit\Util\Xml::prepareString($testOutput)); - $this->currentTestCase->appendChild($systemOut); + if ($diff) { + $out .= $this->prefixLines($prefix['diff'], $diff . \PHP_EOL) . \PHP_EOL; } - $this->currentTestCase = null; + if ($trace) { + if ($message || $diff) { + $out .= $this->prefixLines($prefix['default'], \PHP_EOL) . \PHP_EOL; + } + $out .= $this->prefixLines($prefix['trace'], $trace . \PHP_EOL) . \PHP_EOL; + } + $out .= $this->prefixLines($prefix['last'], \PHP_EOL) . \PHP_EOL; + return $out; } - /** - * Returns the XML as a string. - */ - public function getXML() : string + protected function drawSpinner() : void { - return $this->document->saveXML(); + if ($this->colors) { + $id = $this->spinState % count(self::SPINNER_ICONS); + $this->write(self::SPINNER_ICONS[$id]); + } } - private function doAddFault(\PHPUnit\Framework\Test $test, \Throwable $t, string $type) : void + protected function undrawSpinner() : void { - if ($this->currentTestCase === null) { - return; + if ($this->colors) { + $id = $this->spinState % count(self::SPINNER_ICONS); + $this->write("\33[1K\33[" . strlen(self::SPINNER_ICONS[$id]) . 'D'); } - if ($test instanceof \PHPUnit\Framework\SelfDescribing) { - $buffer = $test->toString() . "\n"; - } else { - $buffer = ''; + } + private function formatRuntime(float $time, string $color = '') : string + { + if (!$this->colors) { + return sprintf('[%.2f ms]', $time * 1000); } - $buffer .= \trim(\PHPUnit\Framework\TestFailure::exceptionToString($t) . "\n" . \PHPUnit\Util\Filter::getFilteredStacktrace($t)); - $fault = $this->document->createElement($type, \PHPUnit\Util\Xml::prepareString($buffer)); - if ($t instanceof \PHPUnit\Framework\ExceptionWrapper) { - $fault->setAttribute('type', $t->getClassName()); - } else { - $fault->setAttribute('type', \get_class($t)); + if ($time > 1) { + $color = 'fg-magenta'; } - $this->currentTestCase->appendChild($fault); + return Color::colorize($color, ' ' . (int) ceil($time * 1000) . ' ' . Color::dim('ms')); } - private function doAddSkipped() : void + private function printNonSuccessfulTestsSummary(int $numberOfExecutedTests) : void { - if ($this->currentTestCase === null) { + if (empty($this->nonSuccessfulTestResults)) { return; } - $skipped = $this->document->createElement('skipped'); - $this->currentTestCase->appendChild($skipped); - $this->testSuiteSkipped[$this->testSuiteLevel]++; + if (count($this->nonSuccessfulTestResults) / $numberOfExecutedTests >= 0.7) { + return; + } + $this->write("Summary of non-successful tests:\n\n"); + $prevResult = $this->getEmptyTestResult(); + foreach ($this->nonSuccessfulTestResults as $testIndex) { + $result = $this->testResults[$testIndex]; + $this->writeTestResult($prevResult, $result); + $prevResult = $result; + } } } printHeader($result); - $this->printFooter($result); + parent::__construct($out); + $this->groups = $groups; + $this->excludeGroups = $excludeGroups; + $this->prettifier = new \PHPUnit\Util\TestDox\NamePrettifier(); + $this->startRun(); + } + /** + * Flush buffer and close output. + */ + public function flush() : void + { + $this->doEndClass(); + $this->endRun(); + parent::flush(); } /** * An error occurred. */ - public function addError(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + public function addError(Test $test, Throwable $t, float $time) : void { - $this->printEvent('testFailed', ['name' => $test->getName(), 'message' => self::getMessage($t), 'details' => self::getDetails($t), 'duration' => self::toMilliseconds($time)]); + if (!$this->isOfInterest($test)) { + return; + } + $this->testStatus = BaseTestRunner::STATUS_ERROR; + $this->failed++; } /** * A warning occurred. */ - public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time) : void + public function addWarning(Test $test, Warning $e, float $time) : void { - $this->printEvent('testFailed', ['name' => $test->getName(), 'message' => self::getMessage($e), 'details' => self::getDetails($e), 'duration' => self::toMilliseconds($time)]); + if (!$this->isOfInterest($test)) { + return; + } + $this->testStatus = BaseTestRunner::STATUS_WARNING; + $this->warned++; } /** * A failure occurred. */ - public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time) : void + public function addFailure(Test $test, AssertionFailedError $e, float $time) : void { - $parameters = ['name' => $test->getName(), 'message' => self::getMessage($e), 'details' => self::getDetails($e), 'duration' => self::toMilliseconds($time)]; - if ($e instanceof \PHPUnit\Framework\ExpectationFailedException) { - $comparisonFailure = $e->getComparisonFailure(); - if ($comparisonFailure instanceof \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure) { - $expectedString = $comparisonFailure->getExpectedAsString(); - if ($expectedString === null || empty($expectedString)) { - $expectedString = self::getPrimitiveValueAsString($comparisonFailure->getExpected()); - } - $actualString = $comparisonFailure->getActualAsString(); - if ($actualString === null || empty($actualString)) { - $actualString = self::getPrimitiveValueAsString($comparisonFailure->getActual()); - } - if ($actualString !== null && $expectedString !== null) { - $parameters['type'] = 'comparisonFailure'; - $parameters['actual'] = $actualString; - $parameters['expected'] = $expectedString; - } - } + if (!$this->isOfInterest($test)) { + return; } - $this->printEvent('testFailed', $parameters); + $this->testStatus = BaseTestRunner::STATUS_FAILURE; + $this->failed++; } /** * Incomplete test. */ - public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + public function addIncompleteTest(Test $test, Throwable $t, float $time) : void { - $this->printIgnoredTest($test->getName(), $t, $time); + if (!$this->isOfInterest($test)) { + return; + } + $this->testStatus = BaseTestRunner::STATUS_INCOMPLETE; + $this->incomplete++; } /** * Risky test. */ - public function addRiskyTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + public function addRiskyTest(Test $test, Throwable $t, float $time) : void { - $this->addError($test, $t, $time); + if (!$this->isOfInterest($test)) { + return; + } + $this->testStatus = BaseTestRunner::STATUS_RISKY; + $this->risky++; } /** * Skipped test. */ - public function addSkippedTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + public function addSkippedTest(Test $test, Throwable $t, float $time) : void { - $testName = $test->getName(); - if ($this->startedTestName !== $testName) { - $this->startTest($test); - $this->printIgnoredTest($testName, $t, $time); - $this->endTest($test, $time); - } else { - $this->printIgnoredTest($testName, $t, $time); + if (!$this->isOfInterest($test)) { + return; } - } - public function printIgnoredTest(string $testName, \Throwable $t, float $time) : void - { - $this->printEvent('testIgnored', ['name' => $testName, 'message' => self::getMessage($t), 'details' => self::getDetails($t), 'duration' => self::toMilliseconds($time)]); + $this->testStatus = BaseTestRunner::STATUS_SKIPPED; + $this->skipped++; } /** * A testsuite started. */ - public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + public function startTestSuite(TestSuite $suite) : void { - if (\stripos(\ini_get('disable_functions'), 'getmypid') === \false) { - $this->flowId = \getmypid(); - } else { - $this->flowId = \false; - } - if (!$this->isSummaryTestCountPrinted) { - $this->isSummaryTestCountPrinted = \true; - $this->printEvent('testCount', ['count' => \count($suite)]); - } - $suiteName = $suite->getName(); - if (empty($suiteName)) { - return; - } - $parameters = ['name' => $suiteName]; - if (\class_exists($suiteName, \false)) { - $fileName = self::getFileName($suiteName); - $parameters['locationHint'] = "php_qn://{$fileName}::\\{$suiteName}"; - } else { - $split = \explode('::', $suiteName); - if (\count($split) === 2 && \class_exists($split[0]) && \method_exists($split[0], $split[1])) { - $fileName = self::getFileName($split[0]); - $parameters['locationHint'] = "php_qn://{$fileName}::\\{$suiteName}"; - $parameters['name'] = $split[1]; - } - } - $this->printEvent('testSuiteStarted', $parameters); } /** * A testsuite ended. */ - public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + public function endTestSuite(TestSuite $suite) : void { - $suiteName = $suite->getName(); - if (empty($suiteName)) { - return; - } - $parameters = ['name' => $suiteName]; - if (!\class_exists($suiteName, \false)) { - $split = \explode('::', $suiteName); - if (\count($split) === 2 && \class_exists($split[0]) && \method_exists($split[0], $split[1])) { - $parameters['name'] = $split[1]; - } - } - $this->printEvent('testSuiteFinished', $parameters); } /** * A test started. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function startTest(\PHPUnit\Framework\Test $test) : void + public function startTest(Test $test) : void { - $testName = $test->getName(); - $this->startedTestName = $testName; - $params = ['name' => $testName]; - if ($test instanceof \PHPUnit\Framework\TestCase) { - $className = \get_class($test); - $fileName = self::getFileName($className); - $params['locationHint'] = "php_qn://{$fileName}::\\{$className}::{$testName}"; + if (!$this->isOfInterest($test)) { + return; } - $this->printEvent('testStarted', $params); + $class = get_class($test); + if ($this->testClass !== $class) { + if ($this->testClass !== '') { + $this->doEndClass(); + } + $this->currentTestClassPrettified = $this->prettifier->prettifyTestClass($class); + $this->testClass = $class; + $this->tests = []; + $this->startClass($class); + } + if ($test instanceof TestCase) { + $this->currentTestMethodPrettified = $this->prettifier->prettifyTestCase($test); + } + $this->testStatus = BaseTestRunner::STATUS_PASSED; } /** * A test ended. */ - public function endTest(\PHPUnit\Framework\Test $test, float $time) : void - { - parent::endTest($test, $time); - $this->printEvent('testFinished', ['name' => $test->getName(), 'duration' => self::toMilliseconds($time)]); - } - protected function writeProgress(string $progress) : void - { - } - private function printEvent(string $eventName, array $params = []) : void + public function endTest(Test $test, float $time) : void { - $this->write("\n##teamcity[{$eventName}"); - if ($this->flowId) { - $params['flowId'] = $this->flowId; - } - foreach ($params as $key => $value) { - $escapedValue = self::escapeValue((string) $value); - $this->write(" {$key}='{$escapedValue}'"); + if (!$this->isOfInterest($test)) { + return; } - $this->write("]\n"); + $this->tests[] = [$this->currentTestMethodPrettified, $this->testStatus]; + $this->currentTestClassPrettified = null; + $this->currentTestMethodPrettified = null; } - private static function getMessage(\Throwable $t) : string + protected function doEndClass() : void { - $message = ''; - if ($t instanceof \PHPUnit\Framework\ExceptionWrapper) { - if ($t->getClassName() !== '') { - $message .= $t->getClassName(); - } - if ($message !== '' && $t->getMessage() !== '') { - $message .= ' : '; - } + foreach ($this->tests as $test) { + $this->onTest($test[0], $test[1] === BaseTestRunner::STATUS_PASSED); } - return $message . $t->getMessage(); + $this->endClass($this->testClass); } - private static function getDetails(\Throwable $t) : string + /** + * Handler for 'start run' event. + */ + protected function startRun() : void { - $stackTrace = \PHPUnit\Util\Filter::getFilteredStacktrace($t); - $previous = $t instanceof \PHPUnit\Framework\ExceptionWrapper ? $t->getPreviousWrapped() : $t->getPrevious(); - while ($previous) { - $stackTrace .= "\nCaused by\n" . \PHPUnit\Framework\TestFailure::exceptionToString($previous) . "\n" . \PHPUnit\Util\Filter::getFilteredStacktrace($previous); - $previous = $previous instanceof \PHPUnit\Framework\ExceptionWrapper ? $previous->getPreviousWrapped() : $previous->getPrevious(); - } - return ' ' . \str_replace("\n", "\n ", $stackTrace); } - private static function getPrimitiveValueAsString($value) : ?string + /** + * Handler for 'start class' event. + */ + protected function startClass(string $name) : void { - if ($value === null) { - return 'null'; - } - if (\is_bool($value)) { - return $value ? 'true' : 'false'; - } - if (\is_scalar($value)) { - return \print_r($value, \true); - } - return null; } - private static function escapeValue(string $text) : string + /** + * Handler for 'on test' event. + */ + protected function onTest(string $name, bool $success = \true) : void { - return \str_replace(['|', "'", "\n", "\r", ']', '['], ['||', "|'", '|n', '|r', '|]', '|['], $text); } /** - * @param string $className + * Handler for 'end class' event. */ - private static function getFileName($className) : string + protected function endClass(string $name) : void { - try { - return (new \ReflectionClass($className))->getFileName(); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Util\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd } /** - * @param float $time microseconds + * Handler for 'end run' event. */ - private static function toMilliseconds(float $time) : int + protected function endRun() : void + { + } + private function isOfInterest(Test $test) : bool { - return (int) \round($time * 1000); + if (!$test instanceof TestCase) { + return \false; + } + if ($test instanceof ErrorTestCase || $test instanceof WarningTestCase) { + return \false; + } + if (!empty($this->groups)) { + foreach ($test->getGroups() as $group) { + if (in_array($group, $this->groups, \true)) { + return \true; + } + } + return \false; + } + if (!empty($this->excludeGroups)) { + foreach ($test->getGroups() as $group) { + if (in_array($group, $this->excludeGroups, \true)) { + return \false; + } + } + return \true; + } + return \true; } } '|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' + * @var DOMDocument */ - private $operator; - public function __construct(string $operator) + private $document; + /** + * @var DOMElement + */ + private $root; + /** + * @var NamePrettifier + */ + private $prettifier; + /** + * @var null|Throwable + */ + private $exception; + /** + * @param resource|string $out + * + * @throws Exception + */ + public function __construct($out = null) { - $this->ensureOperatorIsValid($operator); - $this->operator = $operator; + $this->document = new DOMDocument('1.0', 'UTF-8'); + $this->document->formatOutput = \true; + $this->root = $this->document->createElement('tests'); + $this->document->appendChild($this->root); + $this->prettifier = new \PHPUnit\Util\TestDox\NamePrettifier(); + parent::__construct($out); } /** - * @return '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' + * Flush buffer and close output. */ - public function asString() : string + public function flush() : void { - return $this->operator; + $this->write($this->document->saveXML()); + parent::flush(); } /** - * @throws Exception - * - * @psalm-assert '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' $operator + * An error occurred. */ - private function ensureOperatorIsValid(string $operator) : void + public function addError(Test $test, Throwable $t, float $time) : void { - if (!\in_array($operator, ['<', 'lt', '<=', 'le', '>', 'gt', '>=', 'ge', '==', '=', 'eq', '!=', '<>', 'ne'], \true)) { - throw new \PHPUnit\Util\Exception(\sprintf('"%s" is not a valid version_compare() operator', $operator)); - } + $this->exception = $t; } -} - - * - * 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 + * A warning occurred. */ - public static function prettify(string $json) : string + public function addWarning(Test $test, Warning $e, float $time) : void { - $decodedJson = \json_decode($json, \false); - if (\json_last_error()) { - throw new \PHPUnit\Framework\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'. + * A failure occurred. */ - public static function canonicalize(string $json) : array + public function addFailure(Test $test, AssertionFailedError $e, float $time) : void { - $decodedJson = \json_decode($json); - if (\json_last_error()) { - return [\true, null]; - } - self::recursiveSort($decodedJson); - $reencodedJson = \json_encode($decodedJson); - return [\false, $reencodedJson]; + $this->exception = $e; } /** - * JSON object keys are unordered while PHP array keys are ordered. + * Incomplete test. + */ + public function addIncompleteTest(Test $test, Throwable $t, float $time) : void + { + } + /** + * Risky test. + */ + public function addRiskyTest(Test $test, Throwable $t, float $time) : void + { + } + /** + * Skipped test. + */ + public function addSkippedTest(Test $test, Throwable $t, float $time) : void + { + } + /** + * A test suite started. + */ + public function startTestSuite(TestSuite $suite) : void + { + } + /** + * A test suite ended. + */ + public function endTestSuite(TestSuite $suite) : void + { + } + /** + * A test started. + */ + public function startTest(Test $test) : void + { + $this->exception = null; + } + /** + * A test ended. * - * Sort all array keys to ensure both the expected and actual values have - * their keys in the same order. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - private static function recursiveSort(&$json) : void + public function endTest(Test $test, float $time) : 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; + if (!$test instanceof TestCase || $test instanceof WarningTestCase) { + return; + } + $groups = array_filter($test->getGroups(), static function ($group) { + return !($group === 'small' || $group === 'medium' || $group === 'large' || strpos($group, '__phpunit_') === 0); + }); + $testNode = $this->document->createElement('test'); + $testNode->setAttribute('className', get_class($test)); + $testNode->setAttribute('methodName', $test->getName()); + $testNode->setAttribute('prettifiedClassName', $this->prettifier->prettifyTestClass(get_class($test))); + $testNode->setAttribute('prettifiedMethodName', $this->prettifier->prettifyTestCase($test)); + $testNode->setAttribute('status', (string) $test->getStatus()); + $testNode->setAttribute('time', (string) $time); + $testNode->setAttribute('size', (string) $test->getSize()); + $testNode->setAttribute('groups', implode(',', $groups)); + foreach ($groups as $group) { + $groupNode = $this->document->createElement('group'); + $groupNode->setAttribute('name', $group); + $testNode->appendChild($groupNode); + } + $annotations = TestUtil::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); + foreach (['class', 'method'] as $type) { + foreach ($annotations[$type] as $annotation => $values) { + if ($annotation !== 'covers' && $annotation !== 'uses') { + continue; + } + foreach ($values as $value) { + $coversNode = $this->document->createElement($annotation); + $coversNode->setAttribute('target', $value); + $testNode->appendChild($coversNode); + } } } - \ksort($json); - foreach ($json as $key => &$value) { - self::recursiveSort($value); + foreach ($test->doubledTypes() as $doubledType) { + $testDoubleNode = $this->document->createElement('testDouble'); + $testDoubleNode->setAttribute('type', $doubledType); + $testNode->appendChild($testDoubleNode); + } + $inlineAnnotations = \PHPUnit\Util\Test::getInlineAnnotations(get_class($test), $test->getName(\false)); + if (isset($inlineAnnotations['given'], $inlineAnnotations['when'], $inlineAnnotations['then'])) { + $testNode->setAttribute('given', $inlineAnnotations['given']['value']); + $testNode->setAttribute('givenStartLine', (string) $inlineAnnotations['given']['line']); + $testNode->setAttribute('when', $inlineAnnotations['when']['value']); + $testNode->setAttribute('whenStartLine', (string) $inlineAnnotations['when']['line']); + $testNode->setAttribute('then', $inlineAnnotations['then']['value']); + $testNode->setAttribute('thenStartLine', (string) $inlineAnnotations['then']['line']); + } + if ($this->exception !== null) { + if ($this->exception instanceof Exception) { + $steps = $this->exception->getSerializableTrace(); + } else { + $steps = $this->exception->getTrace(); + } + try { + $file = (new ReflectionClass($test))->getFileName(); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + foreach ($steps as $step) { + if (isset($step['file']) && $step['file'] === $file) { + $testNode->setAttribute('exceptionLine', (string) $step['line']); + break; + } + } + $testNode->setAttribute('exceptionMessage', $this->exception->getMessage()); } + $this->root->appendChild($testNode); } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\PHP; +namespace PHPUnit\Util\TestDox; -use const DIRECTORY_SEPARATOR; -use const PHP_SAPI; -use function array_keys; -use function array_merge; -use function assert; -use function escapeshellarg; -use function ini_get_all; -use function restore_error_handler; -use function set_error_handler; -use function sprintf; -use function str_replace; -use function strpos; -use function strrpos; -use function substr; +use const PHP_EOL; +use function array_map; +use function get_class; +use function implode; +use function method_exists; +use function preg_split; use function trim; -use function unserialize; -use __PHP_Incomplete_Class; -use ErrorException; use PHPUnit\Framework\AssertionFailedError; -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\SyntheticError; +use PHPUnit\Framework\Reorderable; use PHPUnit\Framework\Test; use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestFailure; use PHPUnit\Framework\TestResult; -use PHPUnit\SebastianBergmann\Environment\Runtime; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Framework\Warning; +use PHPUnit\Runner\BaseTestRunner; +use PHPUnit\Runner\PhptTestCase; +use PHPUnit\TextUI\DefaultResultPrinter; +use Throwable; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -abstract class AbstractPhpProcess +class TestDoxPrinter extends DefaultResultPrinter { /** - * @var Runtime + * @var NamePrettifier + */ + protected $prettifier; + /** + * @var int The number of test results received from the TestRunner */ - protected $runtime; + protected $testIndex = 0; /** - * @var bool + * @var int The number of test results already sent to the output */ - protected $stderrRedirection = \false; + protected $testFlushIndex = 0; /** - * @var string + * @var array Buffer for test results */ - protected $stdin = ''; + protected $testResults = []; /** - * @var string + * @var array Lookup table for testname to testResults[index] */ - protected $args = ''; + protected $testNameResultIndex = []; /** - * @var array + * @var bool */ - protected $env = []; + protected $enableOutputBuffer = \false; + /** + * @var array array + */ + protected $originalExecutionOrder = []; /** * @var int */ - protected $timeout = 0; - public static function factory() : self - { - if (\DIRECTORY_SEPARATOR === '\\') { - return new \PHPUnit\Util\PHP\WindowsPhpProcess(); - } - return new \PHPUnit\Util\PHP\DefaultPhpProcess(); - } - public function __construct() - { - $this->runtime = new \PHPUnit\SebastianBergmann\Environment\Runtime(); - } + protected $spinState = 0; /** - * Defines if should use STDERR redirection or not. + * @var bool + */ + protected $showProgress = \true; + /** + * @param null|resource|string $out + * @param int|string $numberOfColumns * - * Then $stderrRedirection is TRUE, STDERR is redirected to STDOUT. + * @throws \PHPUnit\Framework\Exception */ - public function setUseStderrRedirection(bool $stderrRedirection) : void + public function __construct($out = null, bool $verbose = \false, string $colors = self::COLOR_DEFAULT, bool $debug = \false, $numberOfColumns = 80, bool $reverse = \false) { - $this->stderrRedirection = $stderrRedirection; + parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns, $reverse); + $this->prettifier = new \PHPUnit\Util\TestDox\NamePrettifier($this->colors); } - /** - * Returns TRUE if uses STDERR redirection or FALSE if not. - */ - public function useStderrRedirection() : bool + public function setOriginalExecutionOrder(array $order) : void { - return $this->stderrRedirection; + $this->originalExecutionOrder = $order; + $this->enableOutputBuffer = !empty($order); } - /** - * Sets the input string to be sent via STDIN. - */ - public function setStdin(string $stdin) : void + public function setShowProgressAnimation(bool $showProgress) : void { - $this->stdin = $stdin; + $this->showProgress = $showProgress; } - /** - * Returns the input string to be sent via STDIN. - */ - public function getStdin() : string + public function printResult(TestResult $result) : void { - return $this->stdin; } /** - * Sets the string of arguments to pass to the php job. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function setArgs(string $args) : void + public function endTest(Test $test, float $time) : void { - $this->args = $args; + if (!$test instanceof TestCase && !$test instanceof PhptTestCase && !$test instanceof TestSuite) { + return; + } + if ($this->testHasPassed()) { + $this->registerTestResult($test, null, BaseTestRunner::STATUS_PASSED, $time, \false); + } + if ($test instanceof TestCase || $test instanceof PhptTestCase) { + $this->testIndex++; + } + parent::endTest($test, $time); } /** - * Returns the string of arguments to pass to the php job. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function getArgs() : string + public function addError(Test $test, Throwable $t, float $time) : void { - return $this->args; + $this->registerTestResult($test, $t, BaseTestRunner::STATUS_ERROR, $time, \true); } /** - * Sets the array of environment variables to start the child process with. - * - * @param array $env + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function setEnv(array $env) : void + public function addWarning(Test $test, Warning $e, float $time) : void { - $this->env = $env; + $this->registerTestResult($test, $e, BaseTestRunner::STATUS_WARNING, $time, \true); } /** - * Returns the array of environment variables to start the child process with. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function getEnv() : array + public function addFailure(Test $test, AssertionFailedError $e, float $time) : void { - return $this->env; + $this->registerTestResult($test, $e, BaseTestRunner::STATUS_FAILURE, $time, \true); } /** - * Sets the amount of seconds to wait before timing out. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function setTimeout(int $timeout) : void + public function addIncompleteTest(Test $test, Throwable $t, float $time) : void { - $this->timeout = $timeout; + $this->registerTestResult($test, $t, BaseTestRunner::STATUS_INCOMPLETE, $time, \false); } /** - * Returns the amount of seconds to wait before timing out. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function getTimeout() : int + public function addRiskyTest(Test $test, Throwable $t, float $time) : void { - return $this->timeout; + $this->registerTestResult($test, $t, BaseTestRunner::STATUS_RISKY, $time, \false); } /** - * Runs a single test in a separate PHP process. - * * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function runTestJob(string $job, \PHPUnit\Framework\Test $test, \PHPUnit\Framework\TestResult $result) : void + public function addSkippedTest(Test $test, Throwable $t, float $time) : void { - $result->startTest($test); - $_result = $this->runJob($job); - $this->processChildResult($test, $result, $_result['stdout'], $_result['stderr']); + $this->registerTestResult($test, $t, BaseTestRunner::STATUS_SKIPPED, $time, \false); } - /** - * Returns the command based into the configurations. - */ - public function getCommand(array $settings, string $file = null) : string + public function writeProgress(string $progress) : void { - $command = $this->runtime->getBinary(); - if ($this->runtime->hasPCOV()) { - $settings = \array_merge($settings, $this->runtime->getCurrentSettings(\array_keys(\ini_get_all('pcov')))); - } elseif ($this->runtime->hasXdebug()) { - $settings = \array_merge($settings, $this->runtime->getCurrentSettings(\array_keys(\ini_get_all('xdebug')))); - } - $command .= $this->settingsToParameters($settings); - if (\PHP_SAPI === 'phpdbg') { - $command .= ' -qrr'; - if (!$file) { - $command .= 's='; - } - } - if ($file) { - $command .= ' ' . \escapeshellarg($file); - } - if ($this->args) { - if (!$file) { - $command .= ' --'; - } - $command .= ' ' . $this->args; - } - if ($this->stderrRedirection) { - $command .= ' 2>&1'; - } - return $command; + $this->flushOutputBuffer(); } - /** - * Runs a single job (PHP code) using a separate PHP process. - */ - public abstract function runJob(string $job, array $settings = []) : array; - protected function settingsToParameters(array $settings) : string + public function flush() : void { - $buffer = ''; - foreach ($settings as $setting) { - $buffer .= ' -d ' . \escapeshellarg($setting); - } - return $buffer; + $this->flushOutputBuffer(\true); } /** - * Processes the TestResult object from an isolated process. - * * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - private function processChildResult(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\TestResult $result, string $stdout, string $stderr) : void + protected function registerTestResult(Test $test, ?Throwable $t, int $status, float $time, bool $verbose) : void { - $time = 0; - if (!empty($stderr)) { - $result->addError($test, new \PHPUnit\Framework\Exception(\trim($stderr)), $time); - } else { - \set_error_handler( - /** - * @throws ErrorException - */ - static function ($errno, $errstr, $errfile, $errline) : void { - throw new \ErrorException($errstr, $errno, $errno, $errfile, $errline); - } - ); - try { - if (\strpos($stdout, "#!/usr/bin/env php\n") === 0) { - $stdout = \substr($stdout, 19); - } - $childResult = \unserialize(\str_replace("#!/usr/bin/env php\n", '', $stdout)); - \restore_error_handler(); - if ($childResult === \false) { - $result->addFailure($test, new \PHPUnit\Framework\AssertionFailedError('Test was run in child process and ended unexpectedly'), $time); - } - } 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'])) { - $output = $childResult['output']; - } - /* @var TestCase $test */ - $test->setResult($childResult['testResult']); - $test->addToAssertionCount($childResult['numAssertions']); - $childResult = $childResult['result']; - \assert($childResult instanceof \PHPUnit\Framework\TestResult); - if ($result->getCollectCodeCoverageInformation()) { - $result->getCodeCoverage()->merge($childResult->getCodeCoverage()); - } - $time = $childResult->time(); - $notImplemented = $childResult->notImplemented(); - $risky = $childResult->risky(); - $skipped = $childResult->skipped(); - $errors = $childResult->errors(); - $warnings = $childResult->warnings(); - $failures = $childResult->failures(); - if (!empty($notImplemented)) { - $result->addError($test, $this->getException($notImplemented[0]), $time); - } elseif (!empty($risky)) { - $result->addError($test, $this->getException($risky[0]), $time); - } elseif (!empty($skipped)) { - $result->addError($test, $this->getException($skipped[0]), $time); - } elseif (!empty($errors)) { - $result->addError($test, $this->getException($errors[0]), $time); - } elseif (!empty($warnings)) { - $result->addWarning($test, $this->getException($warnings[0]), $time); - } elseif (!empty($failures)) { - $result->addFailure($test, $this->getException($failures[0]), $time); - } - } + $testName = $test instanceof Reorderable ? $test->sortId() : $test->getName(); + $result = ['className' => $this->formatClassName($test), 'testName' => $testName, 'testMethod' => $this->formatTestName($test), 'message' => '', 'status' => $status, 'time' => $time, 'verbose' => $verbose]; + if ($t !== null) { + $result['message'] = $this->formatTestResultMessage($t, $result); } - $result->endTest($test, $time); - if (!empty($output)) { - print $output; + $this->testResults[$this->testIndex] = $result; + $this->testNameResultIndex[$testName] = $this->testIndex; + } + protected function formatTestName(Test $test) : string + { + return method_exists($test, 'getName') ? $test->getName() : ''; + } + protected function formatClassName(Test $test) : string + { + return get_class($test); + } + protected function testHasPassed() : bool + { + if (!isset($this->testResults[$this->testIndex]['status'])) { + return \true; + } + if ($this->testResults[$this->testIndex]['status'] === BaseTestRunner::STATUS_PASSED) { + return \true; } + return \false; } - /** - * Gets the thrown exception from a PHPUnit\Framework\TestFailure. - * - * @see https://github.com/sebastianbergmann/phpunit/issues/74 - */ - private function getException(\PHPUnit\Framework\TestFailure $error) : \PHPUnit\Framework\Exception + protected function flushOutputBuffer(bool $forceFlush = \false) : void { - $exception = $error->thrownException(); - if ($exception instanceof \__PHP_Incomplete_Class) { - $exceptionArray = []; - foreach ((array) $exception as $key => $value) { - $key = \substr($key, \strrpos($key, "\0") + 1); - $exceptionArray[$key] = $value; + if ($this->testFlushIndex === $this->testIndex) { + return; + } + if ($this->testFlushIndex > 0) { + if ($this->enableOutputBuffer && isset($this->originalExecutionOrder[$this->testFlushIndex - 1])) { + $prevResult = $this->getTestResultByName($this->originalExecutionOrder[$this->testFlushIndex - 1]); + } else { + $prevResult = $this->testResults[$this->testFlushIndex - 1]; } - $exception = new \PHPUnit\Framework\SyntheticError(\sprintf('%s: %s', $exceptionArray['_PHP_Incomplete_Class_Name'], $exceptionArray['message']), $exceptionArray['code'], $exceptionArray['file'], $exceptionArray['line'], $exceptionArray['trace']); + } else { + $prevResult = $this->getEmptyTestResult(); + } + if (!$this->enableOutputBuffer) { + $this->writeTestResult($prevResult, $this->testResults[$this->testFlushIndex++]); + } else { + do { + $flushed = \false; + if (!$forceFlush && isset($this->originalExecutionOrder[$this->testFlushIndex])) { + $result = $this->getTestResultByName($this->originalExecutionOrder[$this->testFlushIndex]); + } else { + // This test(name) cannot found in original execution order, + // flush result to output stream right away + $result = $this->testResults[$this->testFlushIndex]; + } + if (!empty($result)) { + $this->hideSpinner(); + $this->writeTestResult($prevResult, $result); + $this->testFlushIndex++; + $prevResult = $result; + $flushed = \true; + } else { + $this->showSpinner(); + } + } while ($flushed && $this->testFlushIndex < $this->testIndex); } - return $exception; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\PHP; - -use const PHP_MAJOR_VERSION; -use function tmpfile; -use PHPUnit\Framework\Exception; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @see https://bugs.php.net/bug.php?id=51800 - */ -final class WindowsPhpProcess extends \PHPUnit\Util\PHP\DefaultPhpProcess -{ - public function getCommand(array $settings, string $file = null) : string + protected function showSpinner() : void { - if (\PHP_MAJOR_VERSION < 8) { - return '"' . parent::getCommand($settings, $file) . '"'; + if (!$this->showProgress) { + return; } - return parent::getCommand($settings, $file); + if ($this->spinState) { + $this->undrawSpinner(); + } + $this->spinState++; + $this->drawSpinner(); } - /** - * @throws Exception - */ - protected function getHandles() : array + protected function hideSpinner() : void { - if (\false === ($stdout_handle = \tmpfile())) { - throw new \PHPUnit\Framework\Exception('A temporary file could not be created; verify that your TEMP environment variable is writable'); + if (!$this->showProgress) { + return; } - return [1 => $stdout_handle]; + if ($this->spinState) { + $this->undrawSpinner(); + } + $this->spinState = 0; } - protected function useTemporaryFile() : bool + protected function drawSpinner() : void { - return \true; - } -} -{driverMethod}($filter), - $filter - ); - - if ({cachesStaticAnalysis}) { - $codeCoverage->cacheStaticAnalysis(unserialize('{codeCoverageCacheDirectory}')); - } - - $result->setCodeCoverage($codeCoverage); + // optional for CLI printers: show the user a 'buffering output' spinner } - - $result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything}); - $result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests}); - $result->enforceTimeLimit({enforcesTimeLimit}); - $result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests}); - $result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests}); - - $test = new {className}('{methodName}', unserialize('{data}'), '{dataName}'); - \assert($test instanceof TestCase); - - $test->setDependencyInput(unserialize('{dependencyInput}')); - $test->setInIsolation(true); - - ob_end_clean(); - $test->run($result); - $output = ''; - if (!$test->hasExpectationOnOutput()) { - $output = $test->getActualOutput(); + protected function undrawSpinner() : void + { + // remove the spinner from the current line } - - ini_set('xdebug.scream', '0'); - @rewind(STDOUT); /* @ as not every STDOUT target stream is rewindable */ - if ($stdout = @stream_get_contents(STDOUT)) { - $output = $stdout . $output; - $streamMetaData = stream_get_meta_data(STDOUT); - if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { - @ftruncate(STDOUT, 0); - @rewind(STDOUT); - } + protected function writeTestResult(array $prevResult, array $result) : void + { } - - print serialize( - [ - 'testResult' => $test->getResult(), - 'numAssertions' => $test->getNumAssertions(), - 'result' => $result, - 'output' => $output - ] - ); -} - -$configurationFilePath = '{configurationFilePath}'; - -if ('' !== $configurationFilePath) { - $configuration = (new Loader)->load($configurationFilePath); - - (new PhpHandler)->handle($configuration->php()); - - unset($configuration); -} - -function __phpunit_error_handler($errno, $errstr, $errfile, $errline) -{ - return true; -} - -set_error_handler('__phpunit_error_handler'); - -{constants} -{included_files} -{globals} - -restore_error_handler(); - -if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { - require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; - unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); -} - -__phpunit_run_isolated_test(); - '', 'testName' => '', 'message' => '', 'failed' => '', 'verbose' => '']; } - - $result = new PHPUnit\Framework\TestResult; - - if ({collectCodeCoverageInformation}) { - $filter = unserialize('{codeCoverageFilter}'); - - $codeCoverage = new CodeCoverage( - (new Selector)->{driverMethod}($filter), - $filter - ); - - if ({cachesStaticAnalysis}) { - $codeCoverage->cacheStaticAnalysis(unserialize('{codeCoverageCacheDirectory}')); + protected function getTestResultByName(?string $testName) : array + { + if (isset($this->testNameResultIndex[$testName])) { + return $this->testResults[$this->testNameResultIndex[$testName]]; } - - $result->setCodeCoverage($codeCoverage); + return []; } - - $result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything}); - $result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests}); - $result->enforceTimeLimit({enforcesTimeLimit}); - $result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests}); - $result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests}); - - $test = new {className}('{name}', unserialize('{data}'), '{dataName}'); - $test->setDependencyInput(unserialize('{dependencyInput}')); - $test->setInIsolation(TRUE); - - ob_end_clean(); - $test->run($result); - $output = ''; - if (!$test->hasExpectationOnOutput()) { - $output = $test->getActualOutput(); + protected function formatThrowable(Throwable $t, ?int $status = null) : string + { + $message = trim(\PHPUnit\Framework\TestFailure::exceptionToString($t)); + if ($message) { + $message .= \PHP_EOL . \PHP_EOL . $this->formatStacktrace($t); + } else { + $message = $this->formatStacktrace($t); + } + return $message; } - - ini_set('xdebug.scream', '0'); - @rewind(STDOUT); /* @ as not every STDOUT target stream is rewindable */ - if ($stdout = @stream_get_contents(STDOUT)) { - $output = $stdout . $output; - $streamMetaData = stream_get_meta_data(STDOUT); - if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { - @ftruncate(STDOUT, 0); - @rewind(STDOUT); + protected function formatStacktrace(Throwable $t) : string + { + return \PHPUnit\Util\Filter::getFilteredStacktrace($t); + } + protected function formatTestResultMessage(Throwable $t, array $result, string $prefix = '│') : string + { + $message = $this->formatThrowable($t, $result['status']); + if ($message === '') { + return ''; + } + if (!($this->verbose || $result['verbose'])) { + return ''; } + return $this->prefixLines($prefix, $message); + } + protected function prefixLines(string $prefix, string $message) : string + { + $message = trim($message); + return implode(\PHP_EOL, array_map(static function (string $text) use($prefix) { + return ' ' . $prefix . ($text ? ' ' . $text : ''); + }, preg_split('/\\r\\n|\\r|\\n/', $message))); } - - print serialize( - [ - 'testResult' => $test->getResult(), - 'numAssertions' => $test->getNumAssertions(), - 'result' => $result, - 'output' => $output - ] - ); -} - -$configurationFilePath = '{configurationFilePath}'; - -if ('' !== $configurationFilePath) { - $configuration = (new Loader)->load($configurationFilePath); - - (new PhpHandler)->handle($configuration->php()); - - unset($configuration); -} - -function __phpunit_error_handler($errno, $errstr, $errfile, $errline) -{ - return true; -} - -set_error_handler('__phpunit_error_handler'); - -{constants} -{included_files} -{globals} - -restore_error_handler(); - -if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { - require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; - unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); } - -__phpunit_run_isolated_test(); + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\TestDox; -if ($composerAutoload) { - require_once $composerAutoload; +use function sprintf; +use PHPUnit\Framework\TestResult; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class HtmlResultPrinter extends \PHPUnit\Util\TestDox\ResultPrinter +{ + /** + * @var string + */ + private const PAGE_HEADER = <<<'EOT' + + + + + Test Documentation + + + +EOT; + /** + * @var string + */ + private const CLASS_HEADER = <<<'EOT' -if (class_exists('PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage')) { - $filter = new Filter; +

          %s

          +
            - $coverage = new CodeCoverage( - (new Selector)->{driverMethod}($filter), - $filter - ); +EOT; + /** + * @var string + */ + private const CLASS_FOOTER = <<<'EOT' +
          +EOT; + /** + * @var string + */ + private const PAGE_FOOTER = <<<'EOT' - if ({codeCoverageCacheDirectory}) { - $coverage->cacheStaticAnalysis({codeCoverageCacheDirectory}); + + +EOT; + public function printResult(TestResult $result) : void + { + } + /** + * Handler for 'start run' event. + */ + protected function startRun() : void + { + $this->write(self::PAGE_HEADER); + } + /** + * Handler for 'start class' event. + */ + protected function startClass(string $name) : void + { + $this->write(sprintf(self::CLASS_HEADER, $name, $this->currentTestClassPrettified)); + } + /** + * Handler for 'on test' event. + */ + protected function onTest(string $name, bool $success = \true) : void + { + $this->write(sprintf("
        • %s %s
        • \n", $success ? '#555753' : '#ef2929', $success ? '✓' : '❌', $name)); + } + /** + * Handler for 'end class' event. + */ + protected function endClass(string $name) : void + { + $this->write(self::CLASS_FOOTER); + } + /** + * Handler for 'end run' event. + */ + protected function endRun() : void + { + $this->write(self::PAGE_FOOTER); } - - $coverage->start(__FILE__); } + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; - if ($coverage) { - $output = $coverage->stop(); +use function array_diff; +use function array_values; +use function basename; +use function class_exists; +use function get_declared_classes; +use function sprintf; +use function stripos; +use function strlen; +use function substr; +use PHPUnit\Framework\TestCase; +use PHPUnit\Util\FileLoader; +use ReflectionClass; +use ReflectionException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 + */ +final class StandardTestSuiteLoader implements \PHPUnit\Runner\TestSuiteLoader +{ + /** + * @throws Exception + */ + public function load(string $suiteClassFile) : ReflectionClass + { + $suiteClassName = basename($suiteClassFile, '.php'); + $loadedClasses = get_declared_classes(); + if (!class_exists($suiteClassName, \false)) { + /* @noinspection UnusedFunctionResultInspection */ + FileLoader::checkAndLoad($suiteClassFile); + $loadedClasses = array_values(array_diff(get_declared_classes(), $loadedClasses)); + if (empty($loadedClasses)) { + throw $this->exceptionFor($suiteClassName, $suiteClassFile); + } } - - file_put_contents('{coverageFile}', serialize($output)); + if (!class_exists($suiteClassName, \false)) { + // this block will handle namespaced classes + $offset = 0 - strlen($suiteClassName); + foreach ($loadedClasses as $loadedClass) { + if (stripos(substr($loadedClass, $offset - 1), '\\' . $suiteClassName) === 0) { + $suiteClassName = $loadedClass; + break; + } + } + } + if (!class_exists($suiteClassName, \false)) { + throw $this->exceptionFor($suiteClassName, $suiteClassFile); + } + try { + $class = new ReflectionClass($suiteClassName); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Runner\Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if ($class->isSubclassOf(TestCase::class) && !$class->isAbstract()) { + return $class; + } + if ($class->hasMethod('suite')) { + try { + $method = $class->getMethod('suite'); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Runner\Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if (!$method->isAbstract() && $method->isPublic() && $method->isStatic()) { + return $class; + } + } + throw $this->exceptionFor($suiteClassName, $suiteClassFile); } -); - -ob_end_clean(); - -require '{job}'; + public function reload(ReflectionClass $aClass) : ReflectionClass + { + return $aClass; + } + private function exceptionFor(string $className, string $filename) : \PHPUnit\Runner\Exception + { + return new \PHPUnit\Runner\Exception(sprintf("Class '%s' could not be found in '%s'.", $className, $filename)); + } +} 6, \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE => 5, \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING => 4, \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE => 3, \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY => 2, \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED => 1, \PHPUnit\Runner\BaseTestRunner::STATUS_UNKNOWN => 0]; + private const SIZE_SORT_WEIGHT = [TestUtil::SMALL => 1, TestUtil::MEDIUM => 2, TestUtil::LARGE => 3, TestUtil::UNKNOWN => 4]; + /** + * @var array Associative array of (string => DEFECT_SORT_WEIGHT) elements + */ + private $defectSortOrder = []; + /** + * @var TestResultCache + */ + private $cache; + /** + * @var array A list of normalized names of tests before reordering + */ + private $originalExecutionOrder = []; + /** + * @var array A list of normalized names of tests affected by reordering + */ + private $executionOrder = []; + public function __construct(?\PHPUnit\Runner\TestResultCache $cache = null) + { + $this->cache = $cache ?? new \PHPUnit\Runner\NullTestResultCache(); + } + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws Exception */ - public function runJob(string $job, array $settings = []) : array + public function reorderTestsInSuite(Test $suite, int $order, bool $resolveDependencies, int $orderDefects, bool $isRootTestSuite = \true) : void + { + $allowedOrders = [self::ORDER_DEFAULT, self::ORDER_REVERSED, self::ORDER_RANDOMIZED, self::ORDER_DURATION, self::ORDER_SIZE]; + if (!in_array($order, $allowedOrders, \true)) { + throw new \PHPUnit\Runner\Exception('$order must be one of TestSuiteSorter::ORDER_[DEFAULT|REVERSED|RANDOMIZED|DURATION|SIZE]'); + } + $allowedOrderDefects = [self::ORDER_DEFAULT, self::ORDER_DEFECTS_FIRST]; + if (!in_array($orderDefects, $allowedOrderDefects, \true)) { + throw new \PHPUnit\Runner\Exception('$orderDefects must be one of TestSuiteSorter::ORDER_DEFAULT, TestSuiteSorter::ORDER_DEFECTS_FIRST'); + } + if ($isRootTestSuite) { + $this->originalExecutionOrder = $this->calculateTestExecutionOrder($suite); + } + if ($suite instanceof TestSuite) { + foreach ($suite as $_suite) { + $this->reorderTestsInSuite($_suite, $order, $resolveDependencies, $orderDefects, \false); + } + if ($orderDefects === self::ORDER_DEFECTS_FIRST) { + $this->addSuiteToDefectSortOrder($suite); + } + $this->sort($suite, $order, $resolveDependencies, $orderDefects); + } + if ($isRootTestSuite) { + $this->executionOrder = $this->calculateTestExecutionOrder($suite); + } + } + public function getOriginalExecutionOrder() : array + { + return $this->originalExecutionOrder; + } + public function getExecutionOrder() : array + { + return $this->executionOrder; + } + private function sort(TestSuite $suite, int $order, bool $resolveDependencies, int $orderDefects) : void + { + if (empty($suite->tests())) { + return; + } + if ($order === self::ORDER_REVERSED) { + $suite->setTests($this->reverse($suite->tests())); + } elseif ($order === self::ORDER_RANDOMIZED) { + $suite->setTests($this->randomize($suite->tests())); + } elseif ($order === self::ORDER_DURATION && $this->cache !== null) { + $suite->setTests($this->sortByDuration($suite->tests())); + } elseif ($order === self::ORDER_SIZE) { + $suite->setTests($this->sortBySize($suite->tests())); + } + if ($orderDefects === self::ORDER_DEFECTS_FIRST && $this->cache !== null) { + $suite->setTests($this->sortDefectsFirst($suite->tests())); + } + if ($resolveDependencies && !$suite instanceof DataProviderTestSuite) { + /** @var TestCase[] $tests */ + $tests = $suite->tests(); + $suite->setTests($this->resolveDependencies($tests)); + } + } + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + private function addSuiteToDefectSortOrder(TestSuite $suite) : void + { + $max = 0; + foreach ($suite->tests() as $test) { + if (!$test instanceof Reorderable) { + continue; + } + if (!isset($this->defectSortOrder[$test->sortId()])) { + $this->defectSortOrder[$test->sortId()] = self::DEFECT_SORT_WEIGHT[$this->cache->getState($test->sortId())]; + $max = max($max, $this->defectSortOrder[$test->sortId()]); + } + } + $this->defectSortOrder[$suite->sortId()] = $max; + } + private function reverse(array $tests) : array + { + return array_reverse($tests); + } + private function randomize(array $tests) : array + { + shuffle($tests); + return $tests; + } + private function sortDefectsFirst(array $tests) : array + { + usort( + $tests, + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + function ($left, $right) { + return $this->cmpDefectPriorityAndTime($left, $right); + } + ); + return $tests; + } + private function sortByDuration(array $tests) : array + { + usort( + $tests, + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + function ($left, $right) { + return $this->cmpDuration($left, $right); + } + ); + return $tests; + } + private function sortBySize(array $tests) : array + { + usort( + $tests, + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + function ($left, $right) { + return $this->cmpSize($left, $right); + } + ); + return $tests; + } + /** + * Comparator callback function to sort tests for "reach failure as fast as possible". + * + * 1. sort tests by defect weight defined in self::DEFECT_SORT_WEIGHT + * 2. when tests are equally defective, sort the fastest to the front + * 3. do not reorder successful tests + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + private function cmpDefectPriorityAndTime(Test $a, Test $b) : int + { + if (!($a instanceof Reorderable && $b instanceof Reorderable)) { + return 0; + } + $priorityA = $this->defectSortOrder[$a->sortId()] ?? 0; + $priorityB = $this->defectSortOrder[$b->sortId()] ?? 0; + if ($priorityB <=> $priorityA) { + // Sort defect weight descending + return $priorityB <=> $priorityA; + } + if ($priorityA || $priorityB) { + return $this->cmpDuration($a, $b); + } + // do not change execution order + return 0; + } + /** + * Compares test duration for sorting tests by duration ascending. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + private function cmpDuration(Test $a, Test $b) : int { - if ($this->stdin || $this->useTemporaryFile()) { - if (!($this->tempFile = \tempnam(\sys_get_temp_dir(), 'PHPUnit')) || \file_put_contents($this->tempFile, $job) === \false) { - throw new \PHPUnit\Framework\Exception('Unable to write temporary file'); - } - $job = $this->stdin; + if (!($a instanceof Reorderable && $b instanceof Reorderable)) { + return 0; } - return $this->runProcess($job, $settings); + return $this->cache->getTime($a->sortId()) <=> $this->cache->getTime($b->sortId()); } /** - * Returns an array of file handles to be used in place of pipes. + * Compares test size for sorting tests small->medium->large->unknown. */ - protected function getHandles() : array + private function cmpSize(Test $a, Test $b) : int { - return []; + $sizeA = $a instanceof TestCase || $a instanceof DataProviderTestSuite ? $a->getSize() : TestUtil::UNKNOWN; + $sizeB = $b instanceof TestCase || $b instanceof DataProviderTestSuite ? $b->getSize() : TestUtil::UNKNOWN; + return self::SIZE_SORT_WEIGHT[$sizeA] <=> self::SIZE_SORT_WEIGHT[$sizeB]; } /** - * Handles creating the child process and returning the STDOUT and STDERR. + * Reorder Tests within a TestCase in such a way as to resolve as many dependencies as possible. + * The algorithm will leave the tests in original running order when it can. + * For more details see the documentation for test dependencies. * - * @throws Exception + * Short description of algorithm: + * 1. Pick the next Test from remaining tests to be checked for dependencies. + * 2. If the test has no dependencies: mark done, start again from the top + * 3. If the test has dependencies but none left to do: mark done, start again from the top + * 4. When we reach the end add any leftover tests to the end. These will be marked 'skipped' during execution. + * + * @param array $tests + * + * @return array */ - protected function runProcess(string $job, array $settings) : array + private function resolveDependencies(array $tests) : array { - $handles = $this->getHandles(); - $env = null; - if ($this->env) { - $env = $_SERVER ?? []; - unset($env['argv'], $env['argc']); - $env = \array_merge($env, $this->env); - foreach ($env as $envKey => $envVar) { - if (\is_array($envVar)) { - unset($env[$envKey]); - } - } - } - $pipeSpec = [0 => $handles[0] ?? ['pipe', 'r'], 1 => $handles[1] ?? ['pipe', 'w'], 2 => $handles[2] ?? ['pipe', 'w']]; - $process = \proc_open($this->getCommand($settings, $this->tempFile), $pipeSpec, $pipes, null, $env); - if (!\is_resource($process)) { - throw new \PHPUnit\Framework\Exception('Unable to spawn worker process'); - } - if ($job) { - $this->process($pipes[0], $job); - } - \fclose($pipes[0]); - $stderr = $stdout = ''; - if ($this->timeout) { - unset($pipes[0]); - while (\true) { - $r = $pipes; - $w = null; - $e = null; - $n = @\stream_select($r, $w, $e, $this->timeout); - if ($n === \false) { - break; - } - if ($n === 0) { - \proc_terminate($process, 9); - throw new \PHPUnit\Framework\Exception(\sprintf('Job execution aborted after %d seconds', $this->timeout)); - } - if ($n > 0) { - foreach ($r as $pipe) { - $pipeOffset = 0; - foreach ($pipes as $i => $origPipe) { - if ($pipe === $origPipe) { - $pipeOffset = $i; - break; - } - } - if (!$pipeOffset) { - break; - } - $line = \fread($pipe, 8192); - if ($line === '' || $line === \false) { - \fclose($pipes[$pipeOffset]); - unset($pipes[$pipeOffset]); - } elseif ($pipeOffset === 1) { - $stdout .= $line; - } else { - $stderr .= $line; - } - } - if (empty($pipes)) { - break; - } - } - } - } else { - if (isset($pipes[1])) { - $stdout = \stream_get_contents($pipes[1]); - \fclose($pipes[1]); - } - if (isset($pipes[2])) { - $stderr = \stream_get_contents($pipes[2]); - \fclose($pipes[2]); + $newTestOrder = []; + $i = 0; + $provided = []; + do { + if ([] === array_diff($tests[$i]->requires(), $provided)) { + $provided = array_merge($provided, $tests[$i]->provides()); + $newTestOrder = array_merge($newTestOrder, array_splice($tests, $i, 1)); + $i = 0; + } else { + $i++; } - } - if (isset($handles[1])) { - \rewind($handles[1]); - $stdout = \stream_get_contents($handles[1]); - \fclose($handles[1]); - } - if (isset($handles[2])) { - \rewind($handles[2]); - $stderr = \stream_get_contents($handles[2]); - \fclose($handles[2]); - } - \proc_close($process); - $this->cleanup(); - return ['stdout' => $stdout, 'stderr' => $stderr]; + } while (!empty($tests) && $i < count($tests)); + return array_merge($newTestOrder, $tests); } /** - * @param resource $pipe + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - protected function process($pipe, string $job) : void - { - \fwrite($pipe, $job); - } - protected function cleanup() : void + private function calculateTestExecutionOrder(Test $suite) : array { - if ($this->tempFile) { - \unlink($this->tempFile); + $tests = []; + if ($suite instanceof TestSuite) { + foreach ($suite->tests() as $test) { + if (!$test instanceof TestSuite && $test instanceof Reorderable) { + $tests[] = $test->sortId(); + } else { + $tests = array_merge($tests, $this->calculateTestExecutionOrder($test)); + } + } } - } - protected function useTemporaryFile() : bool - { - return \false; + return $tests; } } - */ - private const EXCLUDED_CLASS_NAMES = [ - // composer - \PHPUnit\Composer\Autoload\ClassLoader::class => 1, - // doctrine/instantiator - \PHPUnit\Doctrine\Instantiator\Instantiator::class => 1, - // myclabs/deepcopy - \PHPUnit\DeepCopy\DeepCopy::class => 1, - // nikic/php-parser - \PHPUnit\PhpParser\Parser::class => 1, - // phar-io/manifest - \PHPUnit\PharIo\Manifest\Manifest::class => 1, - // phar-io/version - \PHPUnit\PharIo\Version\Version::class => 1, - // phpdocumentor/reflection-common - \PHPUnit\phpDocumentor\Reflection\Project::class => 1, - // phpdocumentor/reflection-docblock - \PHPUnit\phpDocumentor\Reflection\DocBlock::class => 1, - // phpdocumentor/type-resolver - \PHPUnit\phpDocumentor\Reflection\Type::class => 1, - // phpspec/prophecy - \Prophecy\Prophet::class => 1, - // phpunit/phpunit - \PHPUnit\Framework\TestCase::class => 2, - // phpunit/php-code-coverage - \PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage::class => 1, - // phpunit/php-file-iterator - \PHPUnit\SebastianBergmann\FileIterator\Facade::class => 1, - // phpunit/php-invoker - \PHPUnit\SebastianBergmann\Invoker\Invoker::class => 1, - // phpunit/php-text-template - \PHPUnit\SebastianBergmann\Template\Template::class => 1, - // phpunit/php-timer - \PHPUnit\SebastianBergmann\Timer\Timer::class => 1, - // sebastian/cli-parser - \PHPUnit\SebastianBergmann\CliParser\Parser::class => 1, - // sebastian/code-unit - \PHPUnit\SebastianBergmann\CodeUnit\CodeUnit::class => 1, - // sebastian/code-unit-reverse-lookup - \PHPUnit\SebastianBergmann\CodeUnitReverseLookup\Wizard::class => 1, - // sebastian/comparator - \PHPUnit\SebastianBergmann\Comparator\Comparator::class => 1, - // sebastian/complexity - \PHPUnit\SebastianBergmann\Complexity\Calculator::class => 1, - // sebastian/diff - \PHPUnit\SebastianBergmann\Diff\Diff::class => 1, - // sebastian/environment - \PHPUnit\SebastianBergmann\Environment\Runtime::class => 1, - // sebastian/exporter - \PHPUnit\SebastianBergmann\Exporter\Exporter::class => 1, - // sebastian/global-state - \PHPUnit\SebastianBergmann\GlobalState\Snapshot::class => 1, - // sebastian/lines-of-code - \PHPUnit\SebastianBergmann\LinesOfCode\Counter::class => 1, - // sebastian/object-enumerator - \PHPUnit\SebastianBergmann\ObjectEnumerator\Enumerator::class => 1, - // sebastian/recursion-context - \PHPUnit\SebastianBergmann\RecursionContext\Context::class => 1, - // sebastian/resource-operations - \PHPUnit\SebastianBergmann\ResourceOperations\ResourceOperations::class => 1, - // sebastian/type - \PHPUnit\SebastianBergmann\Type\TypeName::class => 1, - // sebastian/version - \PHPUnit\SebastianBergmann\Version::class => 1, - // symfony/polyfill-ctype - \PHPUnit\Symfony\Polyfill\Ctype\Ctype::class => 1, - // theseer/tokenizer - \PHPUnit\TheSeer\Tokenizer\Tokenizer::class => 1, - // webmozart/assert - \PHPUnit\Webmozart\Assert\Assert::class => 1, - ]; - /** - * @var string[] - */ - private static $directories; - public static function addDirectory(string $directory) : void - { - if (!\is_dir($directory)) { - throw new \PHPUnit\Util\Exception(\sprintf('"%s" is not a directory', $directory)); - } - self::$directories[] = \realpath($directory); - } - /** - * @throws Exception - * - * @return string[] - */ - public function getExcludedDirectories() : array - { - $this->initialize(); - return self::$directories; - } - /** - * @throws Exception - */ - public function isExcluded(string $file) : bool - { - if (\defined('PHPUNIT_TESTSUITE')) { - return \false; - } - $this->initialize(); - foreach (self::$directories as $directory) { - if (\strpos($file, $directory) === 0) { - return \true; - } - } - return \false; - } - /** - * @throws Exception - */ - private function initialize() : void - { - if (self::$directories === null) { - self::$directories = []; - foreach (self::EXCLUDED_CLASS_NAMES as $className => $parent) { - if (!\class_exists($className)) { - continue; - } - try { - $directory = (new \ReflectionClass($className))->getFileName(); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Util\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - for ($i = 0; $i < $parent; $i++) { - $directory = \dirname($directory); - } - self::$directories[] = $directory; - } - // Hide process isolation workaround on Windows. - if (\DIRECTORY_SEPARATOR === '\\') { - // tempnam() prefix is limited to first 3 chars. - // @see https://php.net/manual/en/function.tempnam.php - self::$directories[] = \sys_get_temp_dir() . '\\PHP'; - } - } - } + public function load(string $suiteClassFile) : ReflectionClass; + public function reload(ReflectionClass $aClass) : ReflectionClass; } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Extension; + +use PHPUnit\PharIo\Manifest\ApplicationName; +use PHPUnit\PharIo\Manifest\Exception as ManifestException; +use PHPUnit\PharIo\Manifest\ManifestLoader; +use PHPUnit\PharIo\Version\Version as PharIoVersion; +use PHPUnit\Runner\Version; +use PHPUnit\SebastianBergmann\FileIterator\Facade as FileIteratorFacade; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class PharLoader +{ + /** + * @psalm-return array{loadedExtensions: list, notLoadedExtensions: list} + */ + public function loadPharExtensionsInDirectory(string $directory) : array { - try { - $clone = clone $object; - } catch (\Throwable $t) { - return \false; + $loadedExtensions = []; + $notLoadedExtensions = []; + foreach ((new FileIteratorFacade())->getFilesAsArray($directory, '.phar') as $file) { + if (!\is_file('phar://' . $file . '/manifest.xml')) { + $notLoadedExtensions[] = $file . ' is not an extension for PHPUnit'; + continue; + } + try { + $applicationName = new ApplicationName('phpunit/phpunit'); + $version = new PharIoVersion(Version::series()); + $manifest = ManifestLoader::fromFile('phar://' . $file . '/manifest.xml'); + if (!$manifest->isExtensionFor($applicationName)) { + $notLoadedExtensions[] = $file . ' is not an extension for PHPUnit'; + continue; + } + if (!$manifest->isExtensionFor($applicationName, $version)) { + $notLoadedExtensions[] = $file . ' is not compatible with this version of PHPUnit'; + continue; + } + } catch (ManifestException $e) { + $notLoadedExtensions[] = $file . ': ' . $e->getMessage(); + continue; + } + /** + * @noinspection PhpIncludeInspection + * @psalm-suppress UnresolvableInclude + */ + require $file; + $loadedExtensions[] = $manifest->getName()->asString() . ' ' . $manifest->getVersion()->getVersionString(); } - return $clone instanceof $object; + return ['loadedExtensions' => $loadedExtensions, 'notLoadedExtensions' => $notLoadedExtensions]; } } createInstance($extensionConfiguration); + if (!$extension instanceof Hook) { + throw new Exception(sprintf('Class "%s" does not implement a PHPUnit\\Runner\\Hook interface', $extensionConfiguration->className())); + } + $runner->addExtension($extension); } - public function __construct(bool $convertDeprecationsToExceptions, bool $convertErrorsToExceptions, bool $convertNoticesToExceptions, bool $convertWarningsToExceptions) + /** + * @throws Exception + * + * @deprecated + */ + public function createTestListenerInstance(Extension $listenerConfiguration) : TestListener { - $this->convertDeprecationsToExceptions = $convertDeprecationsToExceptions; - $this->convertErrorsToExceptions = $convertErrorsToExceptions; - $this->convertNoticesToExceptions = $convertNoticesToExceptions; - $this->convertWarningsToExceptions = $convertWarningsToExceptions; + $listener = $this->createInstance($listenerConfiguration); + if (!$listener instanceof TestListener) { + throw new Exception(sprintf('Class "%s" does not implement the PHPUnit\\Framework\\TestListener interface', $listenerConfiguration->className())); + } + return $listener; } - public function __invoke(int $errorNumber, string $errorString, string $errorFile, int $errorLine) : bool + /** + * @throws Exception + */ + private function createInstance(Extension $extensionConfiguration) : object { - /* - * 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; + $this->ensureClassExists($extensionConfiguration); + try { + $reflector = new ReflectionClass($extensionConfiguration->className()); + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), (int) $e->getCode(), $e); } - switch ($errorNumber) { - case \E_NOTICE: - case \E_USER_NOTICE: - case \E_STRICT: - if (!$this->convertNoticesToExceptions) { - return \false; - } - throw new \PHPUnit\Framework\Error\Notice($errorString, $errorNumber, $errorFile, $errorLine); - case \E_WARNING: - case \E_USER_WARNING: - if (!$this->convertWarningsToExceptions) { - return \false; - } - throw new \PHPUnit\Framework\Error\Warning($errorString, $errorNumber, $errorFile, $errorLine); - case \E_DEPRECATED: - case \E_USER_DEPRECATED: - if (!$this->convertDeprecationsToExceptions) { - return \false; - } - throw new \PHPUnit\Framework\Error\Deprecated($errorString, $errorNumber, $errorFile, $errorLine); - default: - if (!$this->convertErrorsToExceptions) { - return \false; - } - throw new \PHPUnit\Framework\Error\Error($errorString, $errorNumber, $errorFile, $errorLine); + if (!$extensionConfiguration->hasArguments()) { + return $reflector->newInstance(); } + return $reflector->newInstanceArgs($extensionConfiguration->arguments()); } - public function register() : void + /** + * @throws Exception + */ + private function ensureClassExists(Extension $extensionConfiguration) : void { - if ($this->registered) { + if (class_exists($extensionConfiguration->className(), \false)) { return; } - $oldErrorHandler = \set_error_handler($this); - if ($oldErrorHandler !== null) { - \restore_error_handler(); - return; + if ($extensionConfiguration->hasSourceFile()) { + /** + * @noinspection PhpIncludeInspection + * @psalm-suppress UnresolvableInclude + */ + require_once $extensionConfiguration->sourceFile(); } - $this->registered = \true; - } - public function unregister() : void - { - if (!$this->registered) { - return; + if (!class_exists($extensionConfiguration->className())) { + throw new Exception(sprintf('Class "%s" does not exist', $extensionConfiguration->className())); } - \restore_error_handler(); } } openMemory(); - $writer->setIndent(\true); - $writer->startDocument(); - $writer->startElement('tests'); - $currentTestCase = null; - foreach (new \RecursiveIteratorIterator($suite->getIterator()) as $test) { - if ($test instanceof \PHPUnit\Framework\TestCase) { - if (\get_class($test) !== $currentTestCase) { - if ($currentTestCase !== null) { - $writer->endElement(); - } - $writer->startElement('testCaseClass'); - $writer->writeAttribute('name', \get_class($test)); - $currentTestCase = \get_class($test); - } - $writer->startElement('testCaseMethod'); - $writer->writeAttribute('name', $test->getName(\false)); - $writer->writeAttribute('groups', \implode(',', $test->getGroups())); - if (!empty($test->getDataSetAsString(\false))) { - $writer->writeAttribute('dataSet', \str_replace(' with data set ', '', $test->getDataSetAsString(\false))); - } - $writer->endElement(); - } elseif ($test instanceof \PHPUnit\Runner\PhptTestCase) { - if ($currentTestCase !== null) { - $writer->endElement(); - $currentTestCase = null; - } - $writer->startElement('phptFile'); - $writer->writeAttribute('path', $test->getName()); - $writer->endElement(); - } - } - if ($currentTestCase !== null) { - $writer->endElement(); - } - $writer->endElement(); - return $writer->outputMemory(); + return !in_array($hash, $this->groupTests, \true); } } getIterator()) as $test) { - if ($test instanceof \PHPUnit\Framework\TestCase) { - $name = \sprintf('%s::%s', \get_class($test), \str_replace(' with data set ', '', $test->getName())); - } elseif ($test instanceof \PHPUnit\Runner\PhptTestCase) { - $name = $test->getName(); - } else { - continue; - } - $buffer .= \sprintf(' - %s' . \PHP_EOL, $name); - } - return $buffer; + return in_array($hash, $this->groupTests, \true); } } */ - public function getBlacklistedDirectories() : array - { - return (new \PHPUnit\Util\ExcludeList())->getExcludedDirectories(); - } + private $filters = []; /** + * @param array|string $args + * * @throws Exception */ - public function isBlacklisted(string $file) : bool + public function addFilter(ReflectionClass $filter, $args) : void { - return (new \PHPUnit\Util\ExcludeList())->isExcluded($file); + if (!$filter->isSubclassOf(RecursiveFilterIterator::class)) { + throw new Exception(sprintf('Class "%s" does not extend RecursiveFilterIterator', $filter->name)); + } + $this->filters[] = [$filter, $args]; + } + public function factory(Iterator $iterator, TestSuite $suite) : FilterIterator + { + foreach ($this->filters as $filter) { + [$class, $args] = $filter; + $iterator = $class->newInstance($iterator, $args, $suite); + } + assert($iterator instanceof FilterIterator); + return $iterator; } } getGroupDetails() as $group => $tests) { + if (in_array((string) $group, $groups, \true)) { + $testHashes = array_map('spl_object_hash', $tests); + $this->groupTests = array_merge($this->groupTests, $testHashes); + } + } + } + public function accept() : bool + { + $test = $this->getInnerIterator()->current(); + if ($test instanceof TestSuite) { + return \true; + } + return $this->doAccept(spl_object_hash($test)); + } + protected abstract function doAccept(string $hash); } getSyntheticTrace(); - $eFile = $t->getSyntheticFile(); - $eLine = $t->getSyntheticLine(); - } elseif ($t instanceof \PHPUnit\Framework\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 \PHPUnit\Util\ExcludeList(); - foreach ($eTrace as $frame) { - if (self::shouldPrintFrame($frame, $prefix, $excludeList)) { - $filteredStacktrace .= \sprintf("%s:%s\n", $frame['file'], $frame['line'] ?? '?'); - } - } - return $filteredStacktrace; + parent::__construct($iterator); + $this->setFilter($filter); } /** - * @param false|string $prefix + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - private static function shouldPrintFrame(array $frame, $prefix, \PHPUnit\Util\ExcludeList $excludeList) : bool + public function accept() : bool { - if (!isset($frame['file'])) { - return \false; + $test = $this->getInnerIterator()->current(); + if ($test instanceof TestSuite) { + return \true; } - $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']); + $tmp = \PHPUnit\Util\Test::describe($test); + if ($test instanceof ErrorTestCase || $test instanceof WarningTestCase) { + $name = $test->getMessage(); + } elseif ($tmp[0] !== '') { + $name = implode('::', $tmp); } else { - $script = ''; + $name = $tmp[1]; } - return \is_file($file) && self::fileIsExcluded($file, $excludeList) && $fileIsNotPrefixed && $file !== $script; - } - private static function fileIsExcluded(string $file, \PHPUnit\Util\ExcludeList $excludeList) : bool - { - return (empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) || !\in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], \true)) && !$excludeList->isExcluded($file); + $accepted = @preg_match($this->filter, $name, $matches); + if ($accepted && isset($this->filterMax)) { + $set = end($matches); + $accepted = $set >= $this->filterMin && $set <= $this->filterMax; + } + return (bool) $accepted; } - private static function frameExists(array $trace, string $file, int $line) : bool + /** + * @throws Exception + */ + private function setFilter(string $filter) : void { - foreach ($trace as $frame) { - if (isset($frame['file'], $frame['line']) && $frame['file'] === $file && $frame['line'] === $line) { - return \true; + if (RegularExpression::safeMatch($filter, '') === \false) { + // Handles: + // * testAssertEqualsSucceeds#4 + // * testAssertEqualsSucceeds#4-8 + if (preg_match('/^(.*?)#(\\d+)(?:-(\\d+))?$/', $filter, $matches)) { + if (isset($matches[3]) && $matches[2] < $matches[3]) { + $filter = sprintf('%s.*with data set #(\\d+)$', $matches[1]); + $this->filterMin = (int) $matches[2]; + $this->filterMax = (int) $matches[3]; + } else { + $filter = sprintf('%s.*with data set #%s$', $matches[1], $matches[2]); + } + } elseif (preg_match('/^(.*?)@(.+)$/', $filter, $matches)) { + $filter = sprintf('%s.*with data set "%s"$', $matches[1], $matches[2]); } + // Escape delimiters in regular expression. Do NOT use preg_quote, + // to keep magic characters. + $filter = sprintf('/%s/i', str_replace('/', '\\/', $filter)); } - return \false; + $this->filter = $filter; } } getVersion(); + self::$version = (new VersionId('9.5.10', dirname(__DIR__, 2)))->getVersion(); } return self::$version; } public static function series() : string { - if (\strpos(self::id(), '-')) { - $version = \explode('-', self::id())[0]; + if (strpos(self::id(), '-')) { + $version = explode('-', self::id())[0]; } else { $version = self::id(); } - return \implode('.', \array_slice(\explode('.', $version), 0, 2)); + return implode('.', array_slice(explode('.', $version), 0, 2)); } public static function getVersionString() : string { @@ -53029,122 +57331,104 @@ declare (strict_types=1); */ namespace PHPUnit\Runner; +use const DIRECTORY_SEPARATOR; +use const LOCK_EX; +use function assert; +use function dirname; +use function file_get_contents; +use function file_put_contents; +use function in_array; +use function is_array; use function is_dir; use function is_file; -use function substr; -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\TestSuite; -use ReflectionClass; -use ReflectionException; -use PHPUnit\SebastianBergmann\FileIterator\Facade as FileIteratorFacade; +use function json_decode; +use function json_encode; +use PHPUnit\Util\Filesystem; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -abstract class BaseTestRunner +final class DefaultTestResultCache implements \PHPUnit\Runner\TestResultCache { /** * @var int */ - public const STATUS_UNKNOWN = -1; - /** - * @var int - */ - public const STATUS_PASSED = 0; - /** - * @var int - */ - public const STATUS_SKIPPED = 1; - /** - * @var int - */ - public const STATUS_INCOMPLETE = 2; - /** - * @var int - */ - public const STATUS_FAILURE = 3; + private const VERSION = 1; /** - * @var int + * @psalm-var list */ - public const STATUS_ERROR = 4; + private const ALLOWED_TEST_STATUSES = [\PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED, \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE, \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE, \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR, \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY, \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING]; /** - * @var int + * @var string */ - public const STATUS_RISKY = 5; + private const DEFAULT_RESULT_CACHE_FILENAME = '.phpunit.result.cache'; /** - * @var int + * @var string */ - public const STATUS_WARNING = 6; + private $cacheFilename; /** - * @var string + * @psalm-var array */ - public const SUITE_METHODNAME = 'suite'; + private $defects = []; /** - * Returns the loader to be used. + * @psalm-var array */ - public function getLoader() : \PHPUnit\Runner\TestSuiteLoader + private $times = []; + public function __construct(?string $filepath = null) { - return new \PHPUnit\Runner\StandardTestSuiteLoader(); + if ($filepath !== null && is_dir($filepath)) { + $filepath .= \DIRECTORY_SEPARATOR . self::DEFAULT_RESULT_CACHE_FILENAME; + } + $this->cacheFilename = $filepath ?? $_ENV['PHPUNIT_RESULT_CACHE'] ?? self::DEFAULT_RESULT_CACHE_FILENAME; } - /** - * Returns the Test corresponding to the given suite. - * This is a template method, subclasses override - * the runFailed() and clearStatus() methods. - * - * @param string|string[] $suffixes - * - * @throws Exception - */ - public function getTest(string $suiteClassFile, $suffixes = '') : ?\PHPUnit\Framework\TestSuite + public function setState(string $testName, int $state) : void { - if (\is_dir($suiteClassFile)) { - /** @var string[] $files */ - $files = (new \PHPUnit\SebastianBergmann\FileIterator\Facade())->getFilesAsArray($suiteClassFile, $suffixes); - $suite = new \PHPUnit\Framework\TestSuite($suiteClassFile); - $suite->addTestFiles($files); - return $suite; + if (!in_array($state, self::ALLOWED_TEST_STATUSES, \true)) { + return; } - if (\is_file($suiteClassFile) && \substr($suiteClassFile, -5, 5) === '.phpt') { - $suite = new \PHPUnit\Framework\TestSuite(); - $suite->addTestFile($suiteClassFile); - return $suite; + $this->defects[$testName] = $state; + } + public function getState(string $testName) : int + { + return $this->defects[$testName] ?? \PHPUnit\Runner\BaseTestRunner::STATUS_UNKNOWN; + } + public function setTime(string $testName, float $time) : void + { + $this->times[$testName] = $time; + } + public function getTime(string $testName) : float + { + return $this->times[$testName] ?? 0.0; + } + public function load() : void + { + if (!is_file($this->cacheFilename)) { + return; } - try { - $testClass = $this->loadSuiteClass($suiteClassFile); - } catch (\PHPUnit\Exception $e) { - $this->runFailed($e->getMessage()); - return null; + $data = json_decode(file_get_contents($this->cacheFilename), \true); + if ($data === null) { + return; } - try { - $suiteMethod = $testClass->getMethod(self::SUITE_METHODNAME); - if (!$suiteMethod->isStatic()) { - $this->runFailed('suite() method must be static.'); - return null; - } - $test = $suiteMethod->invoke(null, $testClass->getName()); - } catch (\ReflectionException $e) { - $test = new \PHPUnit\Framework\TestSuite($testClass); + if (!isset($data['version'])) { + return; } - $this->clearStatus(); - return $test; - } - /** - * Returns the loaded ReflectionClass for a suite name. - */ - protected function loadSuiteClass(string $suiteClassFile) : \ReflectionClass - { - return $this->getLoader()->load($suiteClassFile); + if ($data['version'] !== self::VERSION) { + return; + } + assert(isset($data['defects']) && is_array($data['defects'])); + assert(isset($data['times']) && is_array($data['times'])); + $this->defects = $data['defects']; + $this->times = $data['times']; } /** - * Clears the status message. + * @throws Exception */ - protected function clearStatus() : void + public function persist() : void { + if (!Filesystem::createDirectory(dirname($this->cacheFilename))) { + throw new \PHPUnit\Runner\Exception(\sprintf('Cannot create directory "%s" for result cache file', $this->cacheFilename)); + } + file_put_contents($this->cacheFilename, json_encode(['version' => self::VERSION, 'defects' => $this->defects, 'times' => $this->times]), \LOCK_EX); } - /** - * Override to define how to handle a failed loading of - * a test suite. - */ - protected abstract function runFailed(string $message) : void; } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface AfterTestErrorHook extends \PHPUnit\Runner\TestHook +{ + public function executeAfterTestError(string $test, string $message, float $time) : void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface AfterTestWarningHook extends \PHPUnit\Runner\TestHook +{ + public function executeAfterTestWarning(string $test, string $message, float $time) : void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface AfterTestFailureHook extends \PHPUnit\Runner\TestHook +{ + public function executeAfterTestFailure(string $test, string $message, float $time) : void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface AfterIncompleteTestHook extends \PHPUnit\Runner\TestHook +{ + public function executeAfterIncompleteTest(string $test, string $message, float $time) : void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface AfterSkippedTestHook extends \PHPUnit\Runner\TestHook +{ + public function executeAfterSkippedTest(string $test, string $message, float $time) : void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Framework\Warning; +use PHPUnit\Util\Test as TestUtil; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestListenerAdapter implements TestListener { /** - * @var string - */ - public const DEFAULT_RESULT_CACHE_FILENAME = '.phpunit.result.cache'; - /** - * Provide extra protection against incomplete or corrupt caches. - * - * @var int[] - */ - private const ALLOWED_CACHE_TEST_STATUSES = [\PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED, \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE, \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE, \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR, \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY, \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING]; - /** - * Path and filename for result cache file. - * - * @var string - */ - private $cacheFilename; - /** - * The list of defective tests. - * - * - * // Mark a test skipped - * $this->defects[$testName] = BaseTestRunner::TEST_SKIPPED; - * - * - * @var array - */ - private $defects = []; - /** - * The list of execution duration of suites and tests (in seconds). - * - * - * // Record running time for test - * $this->times[$testName] = 1.234; - * - * - * @var array - */ - private $times = []; - public function __construct(?string $filepath = null) - { - if ($filepath !== null && \is_dir($filepath)) { - // cache path provided, use default cache filename in that location - $filepath .= \DIRECTORY_SEPARATOR . self::DEFAULT_RESULT_CACHE_FILENAME; - } - $this->cacheFilename = $filepath ?? $_ENV['PHPUNIT_RESULT_CACHE'] ?? self::DEFAULT_RESULT_CACHE_FILENAME; - } - /** - * @throws Exception + * @var TestHook[] */ - public function persist() : void - { - $this->saveToFile(); - } + private $hooks = []; /** - * @throws Exception + * @var bool */ - public function saveToFile() : void + private $lastTestWasNotSuccessful; + public function add(\PHPUnit\Runner\TestHook $hook) : void { - if (\defined('PHPUNIT_TESTSUITE_RESULTCACHE')) { - return; - } - if (!\PHPUnit\Util\Filesystem::createDirectory(\dirname($this->cacheFilename))) { - throw new \PHPUnit\Runner\Exception(\sprintf('Cannot create directory "%s" for result cache file', $this->cacheFilename)); - } - \file_put_contents($this->cacheFilename, \serialize($this)); + $this->hooks[] = $hook; } - public function setState(string $testName, int $state) : void + public function startTest(Test $test) : void { - if ($state !== \PHPUnit\Runner\BaseTestRunner::STATUS_PASSED) { - $this->defects[$testName] = $state; + foreach ($this->hooks as $hook) { + if ($hook instanceof \PHPUnit\Runner\BeforeTestHook) { + $hook->executeBeforeTest(TestUtil::describeAsString($test)); + } } + $this->lastTestWasNotSuccessful = \false; } - public function getState(string $testName) : int - { - return $this->defects[$testName] ?? \PHPUnit\Runner\BaseTestRunner::STATUS_UNKNOWN; - } - public function setTime(string $testName, float $time) : void - { - $this->times[$testName] = $time; - } - public function getTime(string $testName) : float + public function addError(Test $test, Throwable $t, float $time) : void { - return $this->times[$testName] ?? 0.0; + foreach ($this->hooks as $hook) { + if ($hook instanceof \PHPUnit\Runner\AfterTestErrorHook) { + $hook->executeAfterTestError(TestUtil::describeAsString($test), $t->getMessage(), $time); + } + } + $this->lastTestWasNotSuccessful = \true; } - public function load() : void + public function addWarning(Test $test, Warning $e, float $time) : void { - $this->clear(); - if (!\is_file($this->cacheFilename)) { - return; - } - $cacheData = @\file_get_contents($this->cacheFilename); - // @codeCoverageIgnoreStart - if ($cacheData === \false) { - return; - } - // @codeCoverageIgnoreEnd - $cache = \PHPUnit\Util\ErrorHandler::invokeIgnoringWarnings(static function () use($cacheData) { - return @\unserialize($cacheData, ['allowed_classes' => [self::class]]); - }); - if ($cache === \false) { - return; - } - if ($cache instanceof self) { - /* @var DefaultTestResultCache $cache */ - $cache->copyStateToCache($this); + foreach ($this->hooks as $hook) { + if ($hook instanceof \PHPUnit\Runner\AfterTestWarningHook) { + $hook->executeAfterTestWarning(TestUtil::describeAsString($test), $e->getMessage(), $time); + } } + $this->lastTestWasNotSuccessful = \true; } - public function copyStateToCache(self $targetCache) : void + public function addFailure(Test $test, AssertionFailedError $e, float $time) : void { - foreach ($this->defects as $name => $state) { - $targetCache->setState($name, $state); - } - foreach ($this->times as $name => $time) { - $targetCache->setTime($name, $time); + foreach ($this->hooks as $hook) { + if ($hook instanceof \PHPUnit\Runner\AfterTestFailureHook) { + $hook->executeAfterTestFailure(TestUtil::describeAsString($test), $e->getMessage(), $time); + } } + $this->lastTestWasNotSuccessful = \true; } - public function clear() : void + public function addIncompleteTest(Test $test, Throwable $t, float $time) : void { - $this->defects = []; - $this->times = []; + foreach ($this->hooks as $hook) { + if ($hook instanceof \PHPUnit\Runner\AfterIncompleteTestHook) { + $hook->executeAfterIncompleteTest(TestUtil::describeAsString($test), $t->getMessage(), $time); + } + } + $this->lastTestWasNotSuccessful = \true; } - public function serialize() : string + public function addRiskyTest(Test $test, Throwable $t, float $time) : void { - return \serialize(['defects' => $this->defects, 'times' => $this->times]); + foreach ($this->hooks as $hook) { + if ($hook instanceof \PHPUnit\Runner\AfterRiskyTestHook) { + $hook->executeAfterRiskyTest(TestUtil::describeAsString($test), $t->getMessage(), $time); + } + } + $this->lastTestWasNotSuccessful = \true; } - /** - * @param string $serialized - */ - public function unserialize($serialized) : void + public function addSkippedTest(Test $test, Throwable $t, float $time) : void { - $data = \unserialize($serialized); - if (isset($data['times'])) { - foreach ($data['times'] as $testName => $testTime) { - \assert(\is_string($testName)); - \assert(\is_float($testTime)); - $this->times[$testName] = $testTime; + foreach ($this->hooks as $hook) { + if ($hook instanceof \PHPUnit\Runner\AfterSkippedTestHook) { + $hook->executeAfterSkippedTest(TestUtil::describeAsString($test), $t->getMessage(), $time); } } - if (isset($data['defects'])) { - foreach ($data['defects'] as $testName => $testResult) { - \assert(\is_string($testName)); - \assert(\is_int($testResult)); - if (\in_array($testResult, self::ALLOWED_CACHE_TEST_STATUSES, \true)) { - $this->defects[$testName] = $testResult; + $this->lastTestWasNotSuccessful = \true; + } + public function endTest(Test $test, float $time) : void + { + if (!$this->lastTestWasNotSuccessful) { + foreach ($this->hooks as $hook) { + if ($hook instanceof \PHPUnit\Runner\AfterSuccessfulTestHook) { + $hook->executeAfterSuccessfulTest(TestUtil::describeAsString($test), $time); } } } + foreach ($this->hooks as $hook) { + if ($hook instanceof \PHPUnit\Runner\AfterTestHook) { + $hook->executeAfterTest(TestUtil::describeAsString($test), $time); + } + } + } + public function startTestSuite(TestSuite $suite) : void + { + } + public function endTestSuite(TestSuite $suite) : void + { } } - */ - private $filters = []; - /** - * @param array|string $args - * - * @throws Exception - */ - public function addFilter(\ReflectionClass $filter, $args) : void - { - if (!$filter->isSubclassOf(\RecursiveFilterIterator::class)) { - throw new \PHPUnit\Runner\Exception(\sprintf('Class "%s" does not extend RecursiveFilterIterator', $filter->name)); - } - $this->filters[] = [$filter, $args]; - } - public function factory(\Iterator $iterator, \PHPUnit\Framework\TestSuite $suite) : \FilterIterator - { - foreach ($this->filters as $filter) { - [$class, $args] = $filter; - $iterator = $class->newInstance($iterator, $args, $suite); - } - \assert($iterator instanceof \FilterIterator); - return $iterator; - } + public function executeAfterRiskyTest(string $test, string $message, float $time) : void; } groupTests, \true); - } + public function executeBeforeFirstTest() : void; } setFilter($filter); - } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function accept() : bool - { - $test = $this->getInnerIterator()->current(); - if ($test instanceof \PHPUnit\Framework\TestSuite) { - return \true; - } - $tmp = \PHPUnit\Util\Test::describe($test); - if ($test instanceof \PHPUnit\Framework\WarningTestCase) { - $name = $test->getMessage(); - } elseif ($tmp[0] !== '') { - $name = \implode('::', $tmp); - } else { - $name = $tmp[1]; - } - $accepted = @\preg_match($this->filter, $name, $matches); - if ($accepted && isset($this->filterMax)) { - $set = \end($matches); - $accepted = $set >= $this->filterMin && $set <= $this->filterMax; - } - return (bool) $accepted; - } - /** - * @throws Exception - */ - private function setFilter(string $filter) : void - { - if (\PHPUnit\Util\RegularExpression::safeMatch($filter, '') === \false) { - // Handles: - // * testAssertEqualsSucceeds#4 - // * testAssertEqualsSucceeds#4-8 - if (\preg_match('/^(.*?)#(\\d+)(?:-(\\d+))?$/', $filter, $matches)) { - if (isset($matches[3]) && $matches[2] < $matches[3]) { - $filter = \sprintf('%s.*with data set #(\\d+)$', $matches[1]); - $this->filterMin = (int) $matches[2]; - $this->filterMax = (int) $matches[3]; - } else { - $filter = \sprintf('%s.*with data set #%s$', $matches[1], $matches[2]); - } - } elseif (\preg_match('/^(.*?)@(.+)$/', $filter, $matches)) { - $filter = \sprintf('%s.*with data set "%s"$', $matches[1], $matches[2]); - } - // Escape delimiters in regular expression. Do NOT use preg_quote, - // to keep magic characters. - $filter = \sprintf('/%s/i', \str_replace('/', '\\/', $filter)); - } - $this->filter = $filter; - } + public function executeAfterSuccessfulTest(string $test, float $time) : void; } getGroupDetails() as $group => $tests) { - if (\in_array((string) $group, $groups, \true)) { - $testHashes = \array_map('spl_object_hash', $tests); - $this->groupTests = \array_merge($this->groupTests, $testHashes); - } - } - } - public function accept() : bool - { - $test = $this->getInnerIterator()->current(); - if ($test instanceof \PHPUnit\Framework\TestSuite) { - return \true; - } - return $this->doAccept(\spl_object_hash($test)); - } - protected abstract function doAccept(string $hash); } groupTests, \true); - } + public function executeBeforeTest(string $test) : void; } cache = $cache; - } - public function flush() : void - { - $this->cache->persist(); - } - public function executeAfterSuccessfulTest(string $test, float $time) : void - { - $testName = $this->getTestName($test); - $this->cache->setTime($testName, \round($time, 3)); - } - public function executeAfterIncompleteTest(string $test, string $message, float $time) : void - { - $testName = $this->getTestName($test); - $this->cache->setTime($testName, \round($time, 3)); - $this->cache->setState($testName, \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE); - } - public function executeAfterRiskyTest(string $test, string $message, float $time) : void - { - $testName = $this->getTestName($test); - $this->cache->setTime($testName, \round($time, 3)); - $this->cache->setState($testName, \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY); - } - public function executeAfterSkippedTest(string $test, string $message, float $time) : void - { - $testName = $this->getTestName($test); - $this->cache->setTime($testName, \round($time, 3)); - $this->cache->setState($testName, \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED); - } - public function executeAfterTestError(string $test, string $message, float $time) : void - { - $testName = $this->getTestName($test); - $this->cache->setTime($testName, \round($time, 3)); - $this->cache->setState($testName, \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR); - } - public function executeAfterTestFailure(string $test, string $message, float $time) : void - { - $testName = $this->getTestName($test); - $this->cache->setTime($testName, \round($time, 3)); - $this->cache->setState($testName, \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE); - } - public function executeAfterTestWarning(string $test, string $message, float $time) : void - { - $testName = $this->getTestName($test); - $this->cache->setTime($testName, \round($time, 3)); - $this->cache->setState($testName, \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING); - } - public function executeAfterLastTest() : void - { - $this->flush(); - } - /** - * @param string $test A long description format of the current test - * - * @return string The test name without TestSuiteClassName:: and @dataprovider details - */ - private function getTestName(string $test) : string - { - $matches = []; - if (\preg_match('/^(?\\S+::\\S+)(?:(? with data set (?:#\\d+|"[^"]+"))\\s\\()?/', $test, $matches)) { - $test = $matches['name'] . ($matches['dataname'] ?? ''); - } - return $test; - } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface AfterLastTestHook extends \PHPUnit\Runner\Hook +{ + public function executeAfterLastTest() : void; } filename = $filename; + $this->phpUtil = $phpUtil ?: AbstractPhpProcess::factory(); + } /** - * @var int + * Counts the number of test cases executed by run(TestResult result). */ - public const ORDER_DURATION = 4; + public function count() : int + { + return 1; + } /** - * Order tests by @size annotation 'small', 'medium', 'large'. + * Runs a test and collects its result in a TestResult instance. * - * @var int - */ - public const ORDER_SIZE = 5; - /** - * List of sorting weights for all test result codes. A higher number gives higher priority. + * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException + * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception */ - private const DEFECT_SORT_WEIGHT = [\PHPUnit\Runner\BaseTestRunner::STATUS_ERROR => 6, \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE => 5, \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING => 4, \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE => 3, \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY => 2, \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED => 1, \PHPUnit\Runner\BaseTestRunner::STATUS_UNKNOWN => 0]; - private const SIZE_SORT_WEIGHT = [\PHPUnit\Util\Test::SMALL => 1, \PHPUnit\Util\Test::MEDIUM => 2, \PHPUnit\Util\Test::LARGE => 3, \PHPUnit\Util\Test::UNKNOWN => 4]; + public function run(TestResult $result = null) : TestResult + { + if ($result === null) { + $result = new TestResult(); + } + try { + $sections = $this->parse(); + } catch (\PHPUnit\Runner\Exception $e) { + $result->startTest($this); + $result->addFailure($this, new SkippedTestError($e->getMessage()), 0); + $result->endTest($this, 0); + return $result; + } + $code = $this->render($sections['FILE']); + $xfail = \false; + $settings = $this->parseIniSection($this->settings($result->getCollectCodeCoverageInformation())); + $result->startTest($this); + if (isset($sections['INI'])) { + $settings = $this->parseIniSection($sections['INI'], $settings); + } + if (isset($sections['ENV'])) { + $env = $this->parseEnvSection($sections['ENV']); + $this->phpUtil->setEnv($env); + } + $this->phpUtil->setUseStderrRedirection(\true); + if ($result->enforcesTimeLimit()) { + $this->phpUtil->setTimeout($result->getTimeoutForLargeTests()); + } + $skip = $this->runSkip($sections, $result, $settings); + if ($skip) { + return $result; + } + if (isset($sections['XFAIL'])) { + $xfail = trim($sections['XFAIL']); + } + if (isset($sections['STDIN'])) { + $this->phpUtil->setStdin($sections['STDIN']); + } + if (isset($sections['ARGS'])) { + $this->phpUtil->setArgs($sections['ARGS']); + } + if ($result->getCollectCodeCoverageInformation()) { + $codeCoverageCacheDirectory = null; + $pathCoverage = \false; + $codeCoverage = $result->getCodeCoverage(); + if ($codeCoverage) { + if ($codeCoverage->cachesStaticAnalysis()) { + $codeCoverageCacheDirectory = $codeCoverage->cacheDirectory(); + } + $pathCoverage = $codeCoverage->collectsBranchAndPathCoverage(); + } + $this->renderForCoverage($code, $pathCoverage, $codeCoverageCacheDirectory); + } + $timer = new Timer(); + $timer->start(); + $jobResult = $this->phpUtil->runJob($code, $this->stringifyIni($settings)); + $time = $timer->stop()->asSeconds(); + $this->output = $jobResult['stdout'] ?? ''; + if (isset($codeCoverage) && ($coverage = $this->cleanupForCoverage())) { + $codeCoverage->append($coverage, $this, \true, [], []); + } + try { + $this->assertPhptExpectation($sections, $this->output); + } catch (AssertionFailedError $e) { + $failure = $e; + if ($xfail !== \false) { + $failure = new IncompleteTestError($xfail, 0, $e); + } elseif ($e instanceof ExpectationFailedException) { + $comparisonFailure = $e->getComparisonFailure(); + if ($comparisonFailure) { + $diff = $comparisonFailure->getDiff(); + } else { + $diff = $e->getMessage(); + } + $hint = $this->getLocationHintFromDiff($diff, $sections); + $trace = array_merge($hint, debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS)); + $failure = new PHPTAssertionFailedError($e->getMessage(), 0, $trace[0]['file'], $trace[0]['line'], $trace, $comparisonFailure ? $diff : ''); + } + $result->addFailure($this, $failure, $time); + } catch (Throwable $t) { + $result->addError($this, $t, $time); + } + if ($xfail !== \false && $result->allCompletelyImplemented()) { + $result->addFailure($this, new IncompleteTestError('XFAIL section but test passes'), $time); + } + $this->runClean($sections, $result->getCollectCodeCoverageInformation()); + $result->endTest($this, $time); + return $result; + } /** - * @var array Associative array of (string => DEFECT_SORT_WEIGHT) elements + * Returns the name of the test case. */ - private $defectSortOrder = []; + public function getName() : string + { + return $this->toString(); + } /** - * @var TestResultCache + * Returns a string representation of the test case. */ - private $cache; + public function toString() : string + { + return $this->filename; + } + public function usesDataProvider() : bool + { + return \false; + } + public function getNumAssertions() : int + { + return 1; + } + public function getActualOutput() : string + { + return $this->output; + } + public function hasOutput() : bool + { + return !empty($this->output); + } + public function sortId() : string + { + return $this->filename; + } /** - * @var array A list of normalized names of tests before reordering + * @return list */ - private $originalExecutionOrder = []; + public function provides() : array + { + return []; + } /** - * @var array A list of normalized names of tests affected by reordering + * @return list */ - private $executionOrder = []; - public function __construct(?\PHPUnit\Runner\TestResultCache $cache = null) + public function requires() : array { - $this->cache = $cache ?? new \PHPUnit\Runner\NullTestResultCache(); + return []; } /** - * @throws Exception - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * Parse --INI-- section key value pairs and return as array. + * + * @param array|string $content */ - public function reorderTestsInSuite(\PHPUnit\Framework\Test $suite, int $order, bool $resolveDependencies, int $orderDefects, bool $isRootTestSuite = \true) : void + private function parseIniSection($content, array $ini = []) : array { - $allowedOrders = [self::ORDER_DEFAULT, self::ORDER_REVERSED, self::ORDER_RANDOMIZED, self::ORDER_DURATION, self::ORDER_SIZE]; - if (!\in_array($order, $allowedOrders, \true)) { - throw new \PHPUnit\Runner\Exception('$order must be one of TestSuiteSorter::ORDER_[DEFAULT|REVERSED|RANDOMIZED|DURATION|SIZE]'); - } - $allowedOrderDefects = [self::ORDER_DEFAULT, self::ORDER_DEFECTS_FIRST]; - if (!\in_array($orderDefects, $allowedOrderDefects, \true)) { - throw new \PHPUnit\Runner\Exception('$orderDefects must be one of TestSuiteSorter::ORDER_DEFAULT, TestSuiteSorter::ORDER_DEFECTS_FIRST'); - } - if ($isRootTestSuite) { - $this->originalExecutionOrder = $this->calculateTestExecutionOrder($suite); + if (is_string($content)) { + $content = explode("\n", trim($content)); } - if ($suite instanceof \PHPUnit\Framework\TestSuite) { - foreach ($suite as $_suite) { - $this->reorderTestsInSuite($_suite, $order, $resolveDependencies, $orderDefects, \false); + foreach ($content as $setting) { + if (strpos($setting, '=') === \false) { + continue; } - if ($orderDefects === self::ORDER_DEFECTS_FIRST) { - $this->addSuiteToDefectSortOrder($suite); + $setting = explode('=', $setting, 2); + $name = trim($setting[0]); + $value = trim($setting[1]); + if ($name === 'extension' || $name === 'zend_extension') { + if (!isset($ini[$name])) { + $ini[$name] = []; + } + $ini[$name][] = $value; + continue; } - $this->sort($suite, $order, $resolveDependencies, $orderDefects); + $ini[$name] = $value; } - if ($isRootTestSuite) { - $this->executionOrder = $this->calculateTestExecutionOrder($suite); + return $ini; + } + private function parseEnvSection(string $content) : array + { + $env = []; + foreach (explode("\n", trim($content)) as $e) { + $e = explode('=', trim($e), 2); + if (!empty($e[0]) && isset($e[1])) { + $env[$e[0]] = $e[1]; + } } + return $env; } - public function getOriginalExecutionOrder() : array + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * @throws ExpectationFailedException + */ + private function assertPhptExpectation(array $sections, string $output) : void { - return $this->originalExecutionOrder; + $assertions = ['EXPECT' => 'assertEquals', 'EXPECTF' => 'assertStringMatchesFormat', 'EXPECTREGEX' => 'assertMatchesRegularExpression']; + $actual = preg_replace('/\\r\\n/', "\n", trim($output)); + foreach ($assertions as $sectionName => $sectionAssertion) { + if (isset($sections[$sectionName])) { + $sectionContent = preg_replace('/\\r\\n/', "\n", trim($sections[$sectionName])); + $expected = $sectionName === 'EXPECTREGEX' ? "/{$sectionContent}/" : $sectionContent; + if ($expected === '') { + throw new \PHPUnit\Runner\Exception('No PHPT expectation found'); + } + Assert::$sectionAssertion($expected, $actual); + return; + } + } + throw new \PHPUnit\Runner\Exception('No PHPT assertion found'); } - public function getExecutionOrder() : array + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + private function runSkip(array &$sections, TestResult $result, array $settings) : bool { - return $this->executionOrder; + if (!isset($sections['SKIPIF'])) { + return \false; + } + $skipif = $this->render($sections['SKIPIF']); + $jobResult = $this->phpUtil->runJob($skipif, $this->stringifyIni($settings)); + if (!strncasecmp('skip', ltrim($jobResult['stdout']), 4)) { + $message = ''; + if (preg_match('/^\\s*skip\\s*(.+)\\s*/i', $jobResult['stdout'], $skipMatch)) { + $message = substr($skipMatch[1], 2); + } + $hint = $this->getLocationHint($message, $sections, 'SKIPIF'); + $trace = array_merge($hint, debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS)); + $result->addFailure($this, new SyntheticSkippedError($message, 0, $trace[0]['file'], $trace[0]['line'], $trace), 0); + $result->endTest($this, 0); + return \true; + } + return \false; } - private function sort(\PHPUnit\Framework\TestSuite $suite, int $order, bool $resolveDependencies, int $orderDefects) : void + private function runClean(array &$sections, bool $collectCoverage) : void { - if (empty($suite->tests())) { - return; + $this->phpUtil->setStdin(''); + $this->phpUtil->setArgs(''); + if (isset($sections['CLEAN'])) { + $cleanCode = $this->render($sections['CLEAN']); + $this->phpUtil->runJob($cleanCode, $this->settings($collectCoverage)); } - if ($order === self::ORDER_REVERSED) { - $suite->setTests($this->reverse($suite->tests())); - } elseif ($order === self::ORDER_RANDOMIZED) { - $suite->setTests($this->randomize($suite->tests())); - } elseif ($order === self::ORDER_DURATION && $this->cache !== null) { - $suite->setTests($this->sortByDuration($suite->tests())); - } elseif ($order === self::ORDER_SIZE) { - $suite->setTests($this->sortBySize($suite->tests())); + } + /** + * @throws Exception + */ + private function parse() : array + { + $sections = []; + $section = ''; + $unsupportedSections = ['CGI', 'COOKIE', 'DEFLATE_POST', 'EXPECTHEADERS', 'EXTENSIONS', 'GET', 'GZIP_POST', 'HEADERS', 'PHPDBG', 'POST', 'POST_RAW', 'PUT', 'REDIRECTTEST', 'REQUEST']; + $lineNr = 0; + foreach (file($this->filename) as $line) { + $lineNr++; + if (preg_match('/^--([_A-Z]+)--/', $line, $result)) { + $section = $result[1]; + $sections[$section] = ''; + $sections[$section . '_offset'] = $lineNr; + continue; + } + if (empty($section)) { + throw new \PHPUnit\Runner\Exception('Invalid PHPT file: empty section header'); + } + $sections[$section] .= $line; } - if ($orderDefects === self::ORDER_DEFECTS_FIRST && $this->cache !== null) { - $suite->setTests($this->sortDefectsFirst($suite->tests())); + if (isset($sections['FILEEOF'])) { + $sections['FILE'] = rtrim($sections['FILEEOF'], "\r\n"); + unset($sections['FILEEOF']); } - if ($resolveDependencies && !$suite instanceof \PHPUnit\Framework\DataProviderTestSuite) { - /** @var TestCase[] $tests */ - $tests = $suite->tests(); - $suite->setTests($this->resolveDependencies($tests)); + $this->parseExternal($sections); + if (!$this->validate($sections)) { + throw new \PHPUnit\Runner\Exception('Invalid PHPT file'); } + foreach ($unsupportedSections as $section) { + if (isset($sections[$section])) { + throw new \PHPUnit\Runner\Exception("PHPUnit does not support PHPT {$section} sections"); + } + } + return $sections; } /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception */ - private function addSuiteToDefectSortOrder(\PHPUnit\Framework\TestSuite $suite) : void + private function parseExternal(array &$sections) : void { - $max = 0; - foreach ($suite->tests() as $test) { - if (!$test instanceof \PHPUnit\Framework\Reorderable) { + $allowSections = ['FILE', 'EXPECT', 'EXPECTF', 'EXPECTREGEX']; + $testDirectory = dirname($this->filename) . \DIRECTORY_SEPARATOR; + foreach ($allowSections as $section) { + if (isset($sections[$section . '_EXTERNAL'])) { + $externalFilename = trim($sections[$section . '_EXTERNAL']); + if (!is_file($testDirectory . $externalFilename) || !is_readable($testDirectory . $externalFilename)) { + throw new \PHPUnit\Runner\Exception(sprintf('Could not load --%s-- %s for PHPT file', $section . '_EXTERNAL', $testDirectory . $externalFilename)); + } + $sections[$section] = file_get_contents($testDirectory . $externalFilename); + } + } + } + private function validate(array &$sections) : bool + { + $requiredSections = ['FILE', ['EXPECT', 'EXPECTF', 'EXPECTREGEX']]; + foreach ($requiredSections as $section) { + if (is_array($section)) { + $foundSection = \false; + foreach ($section as $anySection) { + if (isset($sections[$anySection])) { + $foundSection = \true; + break; + } + } + if (!$foundSection) { + return \false; + } continue; } - if (!isset($this->defectSortOrder[$test->sortId()])) { - $this->defectSortOrder[$test->sortId()] = self::DEFECT_SORT_WEIGHT[$this->cache->getState($test->sortId())]; - $max = \max($max, $this->defectSortOrder[$test->sortId()]); + if (!isset($sections[$section])) { + return \false; } } - $this->defectSortOrder[$suite->sortId()] = $max; + return \true; } - private function reverse(array $tests) : array + private function render(string $code) : string { - return \array_reverse($tests); + return str_replace(['__DIR__', '__FILE__'], ["'" . dirname($this->filename) . "'", "'" . $this->filename . "'"], $code); } - private function randomize(array $tests) : array + private function getCoverageFiles() : array { - \shuffle($tests); - return $tests; + $baseDir = dirname(realpath($this->filename)) . \DIRECTORY_SEPARATOR; + $basename = basename($this->filename, 'phpt'); + return ['coverage' => $baseDir . $basename . 'coverage', 'job' => $baseDir . $basename . 'php']; } - private function sortDefectsFirst(array $tests) : array + private function renderForCoverage(string &$job, bool $pathCoverage, ?string $codeCoverageCacheDirectory) : void { - \usort( - $tests, - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - function ($left, $right) { - return $this->cmpDefectPriorityAndTime($left, $right); - } - ); - return $tests; + $files = $this->getCoverageFiles(); + $template = new Template(__DIR__ . '/../Util/PHP/Template/PhptTestCase.tpl'); + $composerAutoload = '\'\''; + if (defined('PHPUNIT_COMPOSER_INSTALL')) { + $composerAutoload = var_export(PHPUNIT_COMPOSER_INSTALL, \true); + } + $phar = '\'\''; + if (defined('__PHPUNIT_PHAR__')) { + $phar = var_export(__PHPUNIT_PHAR__, \true); + } + $globals = ''; + if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + $globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . var_export($GLOBALS['__PHPUNIT_BOOTSTRAP'], \true) . ";\n"; + } + if ($codeCoverageCacheDirectory === null) { + $codeCoverageCacheDirectory = 'null'; + } else { + $codeCoverageCacheDirectory = "'" . $codeCoverageCacheDirectory . "'"; + } + $template->setVar(['composerAutoload' => $composerAutoload, 'phar' => $phar, 'globals' => $globals, 'job' => $files['job'], 'coverageFile' => $files['coverage'], 'driverMethod' => $pathCoverage ? 'forLineAndPathCoverage' : 'forLineCoverage', 'codeCoverageCacheDirectory' => $codeCoverageCacheDirectory]); + file_put_contents($files['job'], $job); + $job = $template->render(); } - private function sortByDuration(array $tests) : array + private function cleanupForCoverage() : RawCodeCoverageData { - \usort( - $tests, - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - function ($left, $right) { - return $this->cmpDuration($left, $right); + $coverage = RawCodeCoverageData::fromXdebugWithoutPathCoverage([]); + $files = $this->getCoverageFiles(); + if (is_file($files['coverage'])) { + $buffer = @file_get_contents($files['coverage']); + if ($buffer !== \false) { + $coverage = @unserialize($buffer); + if ($coverage === \false) { + $coverage = RawCodeCoverageData::fromXdebugWithoutPathCoverage([]); + } } - ); - return $tests; + } + foreach ($files as $file) { + @unlink($file); + } + return $coverage; } - private function sortBySize(array $tests) : array + private function stringifyIni(array $ini) : array { - \usort( - $tests, - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - function ($left, $right) { - return $this->cmpSize($left, $right); + $settings = []; + foreach ($ini as $key => $value) { + if (is_array($value)) { + foreach ($value as $val) { + $settings[] = $key . '=' . $val; + } + continue; } - ); - return $tests; + $settings[] = $key . '=' . $value; + } + return $settings; } - /** - * Comparator callback function to sort tests for "reach failure as fast as possible". - * - * 1. sort tests by defect weight defined in self::DEFECT_SORT_WEIGHT - * 2. when tests are equally defective, sort the fastest to the front - * 3. do not reorder successful tests - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - private function cmpDefectPriorityAndTime(\PHPUnit\Framework\Test $a, \PHPUnit\Framework\Test $b) : int + private function getLocationHintFromDiff(string $message, array $sections) : array { - if (!($a instanceof \PHPUnit\Framework\Reorderable && $b instanceof \PHPUnit\Framework\Reorderable)) { - return 0; - } - $priorityA = $this->defectSortOrder[$a->sortId()] ?? 0; - $priorityB = $this->defectSortOrder[$b->sortId()] ?? 0; - if ($priorityB <=> $priorityA) { - // Sort defect weight descending - return $priorityB <=> $priorityA; - } - if ($priorityA || $priorityB) { - return $this->cmpDuration($a, $b); + $needle = ''; + $previousLine = ''; + $block = 'message'; + foreach (preg_split('/\\r\\n|\\r|\\n/', $message) as $line) { + $line = trim($line); + if ($block === 'message' && $line === '--- Expected') { + $block = 'expected'; + } + if ($block === 'expected' && $line === '@@ @@') { + $block = 'diff'; + } + if ($block === 'diff') { + if (strpos($line, '+') === 0) { + $needle = $this->getCleanDiffLine($previousLine); + break; + } + if (strpos($line, '-') === 0) { + $needle = $this->getCleanDiffLine($line); + break; + } + } + if (!empty($line)) { + $previousLine = $line; + } } - // do not change execution order - return 0; + return $this->getLocationHint($needle, $sections); } - /** - * Compares test duration for sorting tests by duration ascending. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - private function cmpDuration(\PHPUnit\Framework\Test $a, \PHPUnit\Framework\Test $b) : int + private function getCleanDiffLine(string $line) : string { - if (!($a instanceof \PHPUnit\Framework\Reorderable && $b instanceof \PHPUnit\Framework\Reorderable)) { - return 0; + if (preg_match('/^[\\-+]([\'\\"]?)(.*)\\1$/', $line, $matches)) { + $line = $matches[2]; } - return $this->cache->getTime($a->sortId()) <=> $this->cache->getTime($b->sortId()); + return $line; } - /** - * Compares test size for sorting tests small->medium->large->unknown. - */ - private function cmpSize(\PHPUnit\Framework\Test $a, \PHPUnit\Framework\Test $b) : int + private function getLocationHint(string $needle, array $sections, ?string $sectionName = null) : array { - $sizeA = $a instanceof \PHPUnit\Framework\TestCase || $a instanceof \PHPUnit\Framework\DataProviderTestSuite ? $a->getSize() : \PHPUnit\Util\Test::UNKNOWN; - $sizeB = $b instanceof \PHPUnit\Framework\TestCase || $b instanceof \PHPUnit\Framework\DataProviderTestSuite ? $b->getSize() : \PHPUnit\Util\Test::UNKNOWN; - return self::SIZE_SORT_WEIGHT[$sizeA] <=> self::SIZE_SORT_WEIGHT[$sizeB]; + $needle = trim($needle); + if (empty($needle)) { + return [['file' => realpath($this->filename), 'line' => 1]]; + } + if ($sectionName) { + $search = [$sectionName]; + } else { + $search = [ + // 'FILE', + 'EXPECT', + 'EXPECTF', + 'EXPECTREGEX', + ]; + } + $sectionOffset = null; + foreach ($search as $section) { + if (!isset($sections[$section])) { + continue; + } + if (isset($sections[$section . '_EXTERNAL'])) { + $externalFile = trim($sections[$section . '_EXTERNAL']); + return [['file' => realpath(dirname($this->filename) . \DIRECTORY_SEPARATOR . $externalFile), 'line' => 1], ['file' => realpath($this->filename), 'line' => ($sections[$section . '_EXTERNAL_offset'] ?? 0) + 1]]; + } + $sectionOffset = $sections[$section . '_offset'] ?? 0; + $offset = $sectionOffset + 1; + foreach (preg_split('/\\r\\n|\\r|\\n/', $sections[$section]) as $line) { + if (strpos($line, $needle) !== \false) { + return [['file' => realpath($this->filename), 'line' => $offset]]; + } + $offset++; + } + } + if ($sectionName) { + // String not found in specified section, show user the start of the named section + return [['file' => realpath($this->filename), 'line' => $sectionOffset]]; + } + // No section specified, show user start of code + return [['file' => realpath($this->filename), 'line' => 1]]; } /** - * Reorder Tests within a TestCase in such a way as to resolve as many dependencies as possible. - * The algorithm will leave the tests in original running order when it can. - * For more details see the documentation for test dependencies. - * - * Short description of algorithm: - * 1. Pick the next Test from remaining tests to be checked for dependencies. - * 2. If the test has no dependencies: mark done, start again from the top - * 3. If the test has dependencies but none left to do: mark done, start again from the top - * 4. When we reach the end add any leftover tests to the end. These will be marked 'skipped' during execution. - * - * @param array $tests - * - * @return array + * @psalm-return list */ - private function resolveDependencies(array $tests) : array + private function settings(bool $collectCoverage) : array { - $newTestOrder = []; - $i = 0; - $provided = []; - do { - if ([] === \array_diff($tests[$i]->requires(), $provided)) { - $provided = \array_merge($provided, $tests[$i]->provides()); - $newTestOrder = \array_merge($newTestOrder, \array_splice($tests, $i, 1)); - $i = 0; + $settings = ['allow_url_fopen=1', 'auto_append_file=', 'auto_prepend_file=', 'disable_functions=', 'display_errors=1', 'docref_ext=.html', 'docref_root=', 'error_append_string=', 'error_prepend_string=', 'error_reporting=-1', 'html_errors=0', 'log_errors=0', 'open_basedir=', 'output_buffering=Off', 'output_handler=', 'report_memleaks=0', 'report_zend_debug=0']; + if (extension_loaded('pcov')) { + if ($collectCoverage) { + $settings[] = 'pcov.enabled=1'; } else { - $i++; + $settings[] = 'pcov.enabled=0'; } - } while (!empty($tests) && $i < \count($tests)); - return \array_merge($newTestOrder, $tests); - } - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - private function calculateTestExecutionOrder(\PHPUnit\Framework\Test $suite) : array - { - $tests = []; - if ($suite instanceof \PHPUnit\Framework\TestSuite) { - foreach ($suite->tests() as $test) { - if (!$test instanceof \PHPUnit\Framework\TestSuite && $test instanceof \PHPUnit\Framework\Reorderable) { - $tests[] = $test->sortId(); + } + if (extension_loaded('xdebug')) { + if (version_compare(phpversion('xdebug'), '3', '>=')) { + if ($collectCoverage) { + $settings[] = 'xdebug.mode=coverage'; } else { - $tests = \array_merge($tests, $this->calculateTestExecutionOrder($test)); + $settings[] = 'xdebug.mode=off'; + } + } else { + $settings[] = 'xdebug.default_enable=0'; + if ($collectCoverage) { + $settings[] = 'xdebug.coverage_enable=1'; } } } - return $tests; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface AfterTestWarningHook extends \PHPUnit\Runner\TestHook -{ - public function executeAfterTestWarning(string $test, string $message, float $time) : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface AfterSkippedTestHook extends \PHPUnit\Runner\TestHook -{ - public function executeAfterSkippedTest(string $test, string $message, float $time) : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface Hook -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface TestHook extends \PHPUnit\Runner\Hook -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface BeforeTestHook extends \PHPUnit\Runner\TestHook -{ - public function executeBeforeTest(string $test) : void; + return $settings; + } } getFilesAsArray($suiteClassFile, $suffixes); + $suite = new TestSuite($suiteClassFile); + $suite->addTestFiles($files); + return $suite; + } + if (is_file($suiteClassFile) && substr($suiteClassFile, -5, 5) === '.phpt') { + $suite = new TestSuite(); + $suite->addTestFile($suiteClassFile); + return $suite; + } + try { + $testClass = $this->loadSuiteClass($suiteClassFile); + } catch (\PHPUnit\Exception $e) { + $this->runFailed($e->getMessage()); + return null; + } + try { + $suiteMethod = $testClass->getMethod(self::SUITE_METHODNAME); + if (!$suiteMethod->isStatic()) { + $this->runFailed('suite() method must be static.'); + return null; + } + $test = $suiteMethod->invoke(null, $testClass->getName()); + } catch (ReflectionException $e) { + $test = new TestSuite($testClass); + } + $this->clearStatus(); + return $test; + } + /** + * Returns the loaded ReflectionClass for a suite name. + */ + protected function loadSuiteClass(string $suiteClassFile) : ReflectionClass + { + return $this->getLoader()->load($suiteClassFile); + } + /** + * Clears the status message. + */ + protected function clearStatus() : void + { + } + /** + * Override to define how to handle a failed loading of + * a test suite. + */ + protected abstract function runFailed(string $message) : void; } hooks[] = $hook; + $this->cache = $cache; } - public function startTest(\PHPUnit\Framework\Test $test) : void + public function flush() : void { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\BeforeTestHook) { - $hook->executeBeforeTest(\PHPUnit\Util\Test::describeAsString($test)); - } - } - $this->lastTestWasNotSuccessful = \false; + $this->cache->persist(); } - public function addError(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + public function executeAfterSuccessfulTest(string $test, float $time) : void { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterTestErrorHook) { - $hook->executeAfterTestError(\PHPUnit\Util\Test::describeAsString($test), $t->getMessage(), $time); - } - } - $this->lastTestWasNotSuccessful = \true; + $testName = $this->getTestName($test); + $this->cache->setTime($testName, round($time, 3)); } - public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time) : void + public function executeAfterIncompleteTest(string $test, string $message, float $time) : void { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterTestWarningHook) { - $hook->executeAfterTestWarning(\PHPUnit\Util\Test::describeAsString($test), $e->getMessage(), $time); - } - } - $this->lastTestWasNotSuccessful = \true; + $testName = $this->getTestName($test); + $this->cache->setTime($testName, round($time, 3)); + $this->cache->setState($testName, \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE); } - public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time) : void + public function executeAfterRiskyTest(string $test, string $message, float $time) : void { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterTestFailureHook) { - $hook->executeAfterTestFailure(\PHPUnit\Util\Test::describeAsString($test), $e->getMessage(), $time); - } - } - $this->lastTestWasNotSuccessful = \true; + $testName = $this->getTestName($test); + $this->cache->setTime($testName, round($time, 3)); + $this->cache->setState($testName, \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY); } - public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + public function executeAfterSkippedTest(string $test, string $message, float $time) : void { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterIncompleteTestHook) { - $hook->executeAfterIncompleteTest(\PHPUnit\Util\Test::describeAsString($test), $t->getMessage(), $time); - } - } - $this->lastTestWasNotSuccessful = \true; + $testName = $this->getTestName($test); + $this->cache->setTime($testName, round($time, 3)); + $this->cache->setState($testName, \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED); } - public function addRiskyTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + public function executeAfterTestError(string $test, string $message, float $time) : void { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterRiskyTestHook) { - $hook->executeAfterRiskyTest(\PHPUnit\Util\Test::describeAsString($test), $t->getMessage(), $time); - } - } - $this->lastTestWasNotSuccessful = \true; + $testName = $this->getTestName($test); + $this->cache->setTime($testName, round($time, 3)); + $this->cache->setState($testName, \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR); } - public function addSkippedTest(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void + public function executeAfterTestFailure(string $test, string $message, float $time) : void { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterSkippedTestHook) { - $hook->executeAfterSkippedTest(\PHPUnit\Util\Test::describeAsString($test), $t->getMessage(), $time); - } - } - $this->lastTestWasNotSuccessful = \true; + $testName = $this->getTestName($test); + $this->cache->setTime($testName, round($time, 3)); + $this->cache->setState($testName, \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE); } - public function endTest(\PHPUnit\Framework\Test $test, float $time) : void + public function executeAfterTestWarning(string $test, string $message, float $time) : void { - if (!$this->lastTestWasNotSuccessful) { - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterSuccessfulTestHook) { - $hook->executeAfterSuccessfulTest(\PHPUnit\Util\Test::describeAsString($test), $time); - } - } - } - foreach ($this->hooks as $hook) { - if ($hook instanceof \PHPUnit\Runner\AfterTestHook) { - $hook->executeAfterTest(\PHPUnit\Util\Test::describeAsString($test), $time); - } - } + $testName = $this->getTestName($test); + $this->cache->setTime($testName, round($time, 3)); + $this->cache->setState($testName, \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING); } - public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + public function executeAfterLastTest() : void { + $this->flush(); } - public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + /** + * @param string $test A long description format of the current test + * + * @return string The test name without TestSuiteClassName:: and @dataprovider details + */ + private function getTestName(string $test) : string { + $matches = []; + if (preg_match('/^(?\\S+::\\S+)(?:(? with data set (?:#\\d+|"[^"]+"))\\s\\()?/', $test, $matches)) { + $test = $matches['name'] . ($matches['dataname'] ?? ''); + } + return $test; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; +namespace PHPUnit\TextUI\XmlConfiguration; +use function count; +use function iterator_count; +use Countable; +use Iterator; /** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -interface AfterRiskyTestHook extends \PHPUnit\Runner\TestHook +final class GroupCollectionIterator implements Countable, Iterator { - public function executeAfterRiskyTest(string $test, string $message, float $time) : void; + /** + * @var Group[] + */ + private $groups; + /** + * @var int + */ + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\GroupCollection $groups) + { + $this->groups = $groups->asArray(); + } + public function count() : int + { + return iterator_count($this); + } + public function rewind() : void + { + $this->position = 0; + } + public function valid() : bool + { + return $this->position < count($this->groups); + } + public function key() : int + { + return $this->position; + } + public function current() : \PHPUnit\TextUI\XmlConfiguration\Group + { + return $this->groups[$this->position]; + } + public function next() : void + { + $this->position++; + } } groups = $groups; + } + /** + * @return Group[] + */ + public function asArray() : array + { + return $this->groups; + } + /** + * @return string[] + */ + public function asArrayOfStrings() : array + { + $result = []; + foreach ($this->groups as $group) { + $result[] = $group->name(); + } + return $result; + } + public function isEmpty() : bool + { + return empty($this->groups); + } + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\GroupCollectionIterator + { + return new \PHPUnit\TextUI\XmlConfiguration\GroupCollectionIterator($this); + } } include = $include; + $this->exclude = $exclude; + } + public function hasInclude() : bool + { + return !$this->include->isEmpty(); + } + public function include() : \PHPUnit\TextUI\XmlConfiguration\GroupCollection + { + return $this->include; + } + public function hasExclude() : bool + { + return !$this->exclude->isEmpty(); + } + public function exclude() : \PHPUnit\TextUI\XmlConfiguration\GroupCollection + { + return $this->exclude; + } } name = $name; + } + public function name() : string + { + return $this->name; + } } + + + + {tests_directory} + + -declare (strict_types=1); -/* - * This file is part of PHPUnit. - * - * (c) Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; + + + {src_directory} + + + -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface AfterIncompleteTestHook extends \PHPUnit\Runner\TestHook -{ - public function executeAfterIncompleteTest(string $test, string $message, float $time) : void; +EOT; + public function generateDefaultConfiguration(string $phpunitVersion, string $bootstrapScript, string $testsDirectory, string $srcDirectory, string $cacheDirectory) : string + { + return str_replace(['{phpunit_version}', '{bootstrap_script}', '{tests_directory}', '{src_directory}', '{cache_directory}'], [$phpunitVersion, $bootstrapScript, $testsDirectory, $srcDirectory, $cacheDirectory], self::TEMPLATE); + } } target = $target; } - public function persist() : void + public function target() : File { + return $this->target; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use function array_diff; -use function array_values; -use function basename; -use function class_exists; -use function get_declared_classes; -use function sprintf; -use function stripos; -use function strlen; -use function substr; -use PHPUnit\Framework\TestCase; -use PHPUnit\Util\FileLoader; -use ReflectionClass; -use ReflectionException; + */ +namespace PHPUnit\TextUI\XmlConfiguration\Logging; + +use PHPUnit\TextUI\XmlConfiguration\File; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 + * @psalm-immutable */ -final class StandardTestSuiteLoader implements \PHPUnit\Runner\TestSuiteLoader +final class Junit { /** - * @throws Exception + * @var File */ - public function load(string $suiteClassFile) : \ReflectionClass - { - $suiteClassName = \basename($suiteClassFile, '.php'); - $loadedClasses = \get_declared_classes(); - if (!\class_exists($suiteClassName, \false)) { - /* @noinspection UnusedFunctionResultInspection */ - \PHPUnit\Util\FileLoader::checkAndLoad($suiteClassFile); - $loadedClasses = \array_values(\array_diff(\get_declared_classes(), $loadedClasses)); - if (empty($loadedClasses)) { - throw $this->exceptionFor($suiteClassName, $suiteClassFile); - } - } - if (!\class_exists($suiteClassName, \false)) { - // this block will handle namespaced classes - $offset = 0 - \strlen($suiteClassName); - foreach ($loadedClasses as $loadedClass) { - if (\stripos(\substr($loadedClass, $offset - 1), '\\' . $suiteClassName) === 0) { - $suiteClassName = $loadedClass; - break; - } - } - } - if (!\class_exists($suiteClassName, \false)) { - throw $this->exceptionFor($suiteClassName, $suiteClassFile); - } - try { - $class = new \ReflectionClass($suiteClassName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Runner\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($class->isSubclassOf(\PHPUnit\Framework\TestCase::class) && !$class->isAbstract()) { - return $class; - } - if ($class->hasMethod('suite')) { - try { - $method = $class->getMethod('suite'); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Runner\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if (!$method->isAbstract() && $method->isPublic() && $method->isStatic()) { - return $class; - } - } - throw $this->exceptionFor($suiteClassName, $suiteClassFile); - } - public function reload(\ReflectionClass $aClass) : \ReflectionClass + private $target; + public function __construct(File $target) { - return $aClass; + $this->target = $target; } - private function exceptionFor(string $className, string $filename) : \PHPUnit\Runner\Exception + public function target() : File { - return new \PHPUnit\Runner\Exception(\sprintf("Class '%s' could not be found in '%s'.", $className, $filename)); + return $this->target; } } filename = $filename; - $this->phpUtil = $phpUtil ?: \PHPUnit\Util\PHP\AbstractPhpProcess::factory(); - } + private $testDoxHtml; /** - * Counts the number of test cases executed by run(TestResult result). + * @var ?TestDoxText */ - public function count() : int - { - return 1; - } + private $testDoxText; /** - * Runs a test and collects its result in a TestResult instance. - * - * @throws Exception - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var ?TestDoxXml */ - public function run(\PHPUnit\Framework\TestResult $result = null) : \PHPUnit\Framework\TestResult + private $testDoxXml; + public function __construct(?\PHPUnit\TextUI\XmlConfiguration\Logging\Junit $junit, ?\PHPUnit\TextUI\XmlConfiguration\Logging\Text $text, ?\PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity $teamCity, ?TestDoxHtml $testDoxHtml, ?TestDoxText $testDoxText, ?TestDoxXml $testDoxXml) { - if ($result === null) { - $result = new \PHPUnit\Framework\TestResult(); - } - try { - $sections = $this->parse(); - } catch (\PHPUnit\Runner\Exception $e) { - $result->startTest($this); - $result->addFailure($this, new \PHPUnit\Framework\SkippedTestError($e->getMessage()), 0); - $result->endTest($this, 0); - return $result; - } - $code = $this->render($sections['FILE']); - $xfail = \false; - $settings = $this->parseIniSection($this->settings($result->getCollectCodeCoverageInformation())); - $result->startTest($this); - if (isset($sections['INI'])) { - $settings = $this->parseIniSection($sections['INI'], $settings); - } - if (isset($sections['ENV'])) { - $env = $this->parseEnvSection($sections['ENV']); - $this->phpUtil->setEnv($env); - } - $this->phpUtil->setUseStderrRedirection(\true); - if ($result->enforcesTimeLimit()) { - $this->phpUtil->setTimeout($result->getTimeoutForLargeTests()); - } - $skip = $this->runSkip($sections, $result, $settings); - if ($skip) { - return $result; - } - if (isset($sections['XFAIL'])) { - $xfail = \trim($sections['XFAIL']); - } - if (isset($sections['STDIN'])) { - $this->phpUtil->setStdin($sections['STDIN']); - } - if (isset($sections['ARGS'])) { - $this->phpUtil->setArgs($sections['ARGS']); - } - if ($result->getCollectCodeCoverageInformation()) { - $codeCoverageCacheDirectory = null; - $pathCoverage = \false; - $codeCoverage = $result->getCodeCoverage(); - if ($codeCoverage) { - if ($codeCoverage->cachesStaticAnalysis()) { - $codeCoverageCacheDirectory = $codeCoverage->cacheDirectory(); - } - $pathCoverage = $codeCoverage->collectsBranchAndPathCoverage(); - } - $this->renderForCoverage($code, $pathCoverage, $codeCoverageCacheDirectory); - } - $timer = new \PHPUnit\SebastianBergmann\Timer\Timer(); - $timer->start(); - $jobResult = $this->phpUtil->runJob($code, $this->stringifyIni($settings)); - $time = $timer->stop()->asSeconds(); - $this->output = $jobResult['stdout'] ?? ''; - if (isset($codeCoverage) && ($coverage = $this->cleanupForCoverage())) { - $codeCoverage->append($coverage, $this, \true, [], []); - } - try { - $this->assertPhptExpectation($sections, $this->output); - } catch (\PHPUnit\Framework\AssertionFailedError $e) { - $failure = $e; - if ($xfail !== \false) { - $failure = new \PHPUnit\Framework\IncompleteTestError($xfail, 0, $e); - } elseif ($e instanceof \PHPUnit\Framework\ExpectationFailedException) { - $comparisonFailure = $e->getComparisonFailure(); - if ($comparisonFailure) { - $diff = $comparisonFailure->getDiff(); - } else { - $diff = $e->getMessage(); - } - $hint = $this->getLocationHintFromDiff($diff, $sections); - $trace = \array_merge($hint, \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS)); - $failure = new \PHPUnit\Framework\PHPTAssertionFailedError($e->getMessage(), 0, $trace[0]['file'], $trace[0]['line'], $trace, $comparisonFailure ? $diff : ''); - } - $result->addFailure($this, $failure, $time); - } catch (\Throwable $t) { - $result->addError($this, $t, $time); - } - if ($xfail !== \false && $result->allCompletelyImplemented()) { - $result->addFailure($this, new \PHPUnit\Framework\IncompleteTestError('XFAIL section but test passes'), $time); - } - $this->runClean($sections, $result->getCollectCodeCoverageInformation()); - $result->endTest($this, $time); - return $result; + $this->junit = $junit; + $this->text = $text; + $this->teamCity = $teamCity; + $this->testDoxHtml = $testDoxHtml; + $this->testDoxText = $testDoxText; + $this->testDoxXml = $testDoxXml; } - /** - * Returns the name of the test case. - */ - public function getName() : string + public function hasJunit() : bool { - return $this->toString(); + return $this->junit !== null; } - /** - * Returns a string representation of the test case. - */ - public function toString() : string + public function junit() : \PHPUnit\TextUI\XmlConfiguration\Logging\Junit { - return $this->filename; + if ($this->junit === null) { + throw new Exception('Logger "JUnit XML" is not configured'); + } + return $this->junit; } - public function usesDataProvider() : bool + public function hasText() : bool { - return \false; + return $this->text !== null; } - public function getNumAssertions() : int + public function text() : \PHPUnit\TextUI\XmlConfiguration\Logging\Text { - return 1; + if ($this->text === null) { + throw new Exception('Logger "Text" is not configured'); + } + return $this->text; } - public function getActualOutput() : string + public function hasTeamCity() : bool { - return $this->output; + return $this->teamCity !== null; } - public function hasOutput() : bool + public function teamCity() : \PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity { - return !empty($this->output); + if ($this->teamCity === null) { + throw new Exception('Logger "Team City" is not configured'); + } + return $this->teamCity; } - public function sortId() : string + public function hasTestDoxHtml() : bool { - return $this->filename; + return $this->testDoxHtml !== null; } - /** - * @return list - */ - public function provides() : array + public function testDoxHtml() : TestDoxHtml { - return []; + if ($this->testDoxHtml === null) { + throw new Exception('Logger "TestDox HTML" is not configured'); + } + return $this->testDoxHtml; } - /** - * @return list - */ - public function requires() : array + public function hasTestDoxText() : bool { - return []; + return $this->testDoxText !== null; } - /** - * Parse --INI-- section key value pairs and return as array. - * - * @param array|string $content - */ - private function parseIniSection($content, array $ini = []) : array + public function testDoxText() : TestDoxText { - if (\is_string($content)) { - $content = \explode("\n", \trim($content)); - } - foreach ($content as $setting) { - if (\strpos($setting, '=') === \false) { - continue; - } - $setting = \explode('=', $setting, 2); - $name = \trim($setting[0]); - $value = \trim($setting[1]); - if ($name === 'extension' || $name === 'zend_extension') { - if (!isset($ini[$name])) { - $ini[$name] = []; - } - $ini[$name][] = $value; - continue; - } - $ini[$name] = $value; + if ($this->testDoxText === null) { + throw new Exception('Logger "TestDox Text" is not configured'); } - return $ini; + return $this->testDoxText; } - private function parseEnvSection(string $content) : array + public function hasTestDoxXml() : bool { - $env = []; - foreach (\explode("\n", \trim($content)) as $e) { - $e = \explode('=', \trim($e), 2); - if (!empty($e[0]) && isset($e[1])) { - $env[$e[0]] = $e[1]; - } - } - return $env; + return $this->testDoxXml !== null; } - /** - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - */ - private function assertPhptExpectation(array $sections, string $output) : void + public function testDoxXml() : TestDoxXml { - $assertions = ['EXPECT' => 'assertEquals', 'EXPECTF' => 'assertStringMatchesFormat', 'EXPECTREGEX' => 'assertMatchesRegularExpression']; - $actual = \preg_replace('/\\r\\n/', "\n", \trim($output)); - foreach ($assertions as $sectionName => $sectionAssertion) { - if (isset($sections[$sectionName])) { - $sectionContent = \preg_replace('/\\r\\n/', "\n", \trim($sections[$sectionName])); - $expected = $sectionName === 'EXPECTREGEX' ? "/{$sectionContent}/" : $sectionContent; - if ($expected === '') { - throw new \PHPUnit\Runner\Exception('No PHPT expectation found'); - } - \PHPUnit\Framework\Assert::$sectionAssertion($expected, $actual); - return; - } + if ($this->testDoxXml === null) { + throw new Exception('Logger "TestDox XML" is not configured'); } - throw new \PHPUnit\Runner\Exception('No PHPT assertion found'); + return $this->testDoxXml; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\Logging; + +use PHPUnit\TextUI\XmlConfiguration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class TeamCity +{ /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var File */ - private function runSkip(array &$sections, \PHPUnit\Framework\TestResult $result, array $settings) : bool + private $target; + public function __construct(File $target) { - if (!isset($sections['SKIPIF'])) { - return \false; - } - $skipif = $this->render($sections['SKIPIF']); - $jobResult = $this->phpUtil->runJob($skipif, $this->stringifyIni($settings)); - if (!\strncasecmp('skip', \ltrim($jobResult['stdout']), 4)) { - $message = ''; - if (\preg_match('/^\\s*skip\\s*(.+)\\s*/i', $jobResult['stdout'], $skipMatch)) { - $message = \substr($skipMatch[1], 2); - } - $hint = $this->getLocationHint($message, $sections, 'SKIPIF'); - $trace = \array_merge($hint, \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS)); - $result->addFailure($this, new \PHPUnit\Framework\SyntheticSkippedError($message, 0, $trace[0]['file'], $trace[0]['line'], $trace), 0); - $result->endTest($this, 0); - return \true; - } - return \false; + $this->target = $target; } - private function runClean(array &$sections, bool $collectCoverage) : void + public function target() : File { - $this->phpUtil->setStdin(''); - $this->phpUtil->setArgs(''); - if (isset($sections['CLEAN'])) { - $cleanCode = $this->render($sections['CLEAN']); - $this->phpUtil->runJob($cleanCode, $this->settings($collectCoverage)); - } + return $this->target; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\Logging\TestDox; + +use PHPUnit\TextUI\XmlConfiguration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class Xml +{ /** - * @throws Exception + * @var File */ - private function parse() : array + private $target; + public function __construct(File $target) { - $sections = []; - $section = ''; - $unsupportedSections = ['CGI', 'COOKIE', 'DEFLATE_POST', 'EXPECTHEADERS', 'EXTENSIONS', 'GET', 'GZIP_POST', 'HEADERS', 'PHPDBG', 'POST', 'POST_RAW', 'PUT', 'REDIRECTTEST', 'REQUEST']; - $lineNr = 0; - foreach (\file($this->filename) as $line) { - $lineNr++; - if (\preg_match('/^--([_A-Z]+)--/', $line, $result)) { - $section = $result[1]; - $sections[$section] = ''; - $sections[$section . '_offset'] = $lineNr; - continue; - } - if (empty($section)) { - throw new \PHPUnit\Runner\Exception('Invalid PHPT file: empty section header'); - } - $sections[$section] .= $line; - } - if (isset($sections['FILEEOF'])) { - $sections['FILE'] = \rtrim($sections['FILEEOF'], "\r\n"); - unset($sections['FILEEOF']); - } - $this->parseExternal($sections); - if (!$this->validate($sections)) { - throw new \PHPUnit\Runner\Exception('Invalid PHPT file'); - } - foreach ($unsupportedSections as $section) { - if (isset($sections[$section])) { - throw new \PHPUnit\Runner\Exception("PHPUnit does not support PHPT {$section} sections"); - } - } - return $sections; + $this->target = $target; } + public function target() : File + { + return $this->target; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\Logging\TestDox; + +use PHPUnit\TextUI\XmlConfiguration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class Html +{ /** - * @throws Exception + * @var File */ - private function parseExternal(array &$sections) : void + private $target; + public function __construct(File $target) { - $allowSections = ['FILE', 'EXPECT', 'EXPECTF', 'EXPECTREGEX']; - $testDirectory = \dirname($this->filename) . \DIRECTORY_SEPARATOR; - foreach ($allowSections as $section) { - if (isset($sections[$section . '_EXTERNAL'])) { - $externalFilename = \trim($sections[$section . '_EXTERNAL']); - if (!\is_file($testDirectory . $externalFilename) || !\is_readable($testDirectory . $externalFilename)) { - throw new \PHPUnit\Runner\Exception(\sprintf('Could not load --%s-- %s for PHPT file', $section . '_EXTERNAL', $testDirectory . $externalFilename)); - } - $sections[$section] = \file_get_contents($testDirectory . $externalFilename); - } - } + $this->target = $target; } - private function validate(array &$sections) : bool + public function target() : File { - $requiredSections = ['FILE', ['EXPECT', 'EXPECTF', 'EXPECTREGEX']]; - foreach ($requiredSections as $section) { - if (\is_array($section)) { - $foundSection = \false; - foreach ($section as $anySection) { - if (isset($sections[$anySection])) { - $foundSection = \true; - break; - } - } - if (!$foundSection) { - return \false; - } - continue; - } - if (!isset($sections[$section])) { - return \false; - } - } - return \true; + return $this->target; } - private function render(string $code) : string +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\Logging\TestDox; + +use PHPUnit\TextUI\XmlConfiguration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class Text +{ + /** + * @var File + */ + private $target; + public function __construct(File $target) { - return \str_replace(['__DIR__', '__FILE__'], ["'" . \dirname($this->filename) . "'", "'" . $this->filename . "'"], $code); + $this->target = $target; } - private function getCoverageFiles() : array + public function target() : File { - $baseDir = \dirname(\realpath($this->filename)) . \DIRECTORY_SEPARATOR; - $basename = \basename($this->filename, 'phpt'); - return ['coverage' => $baseDir . $basename . 'coverage', 'job' => $baseDir . $basename . 'php']; + return $this->target; } - private function renderForCoverage(string &$job, bool $pathCoverage, ?string $codeCoverageCacheDirectory) : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class DirectoryCollectionIterator implements Countable, Iterator +{ + /** + * @var Directory[] + */ + private $directories; + /** + * @var int + */ + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection $directories) { - $files = $this->getCoverageFiles(); - $template = new \PHPUnit\SebastianBergmann\Template\Template(__DIR__ . '/../Util/PHP/Template/PhptTestCase.tpl'); - $composerAutoload = '\'\''; - if (\defined('PHPUNIT_COMPOSER_INSTALL') && !\defined('PHPUNIT_TESTSUITE')) { - $composerAutoload = \var_export(PHPUNIT_COMPOSER_INSTALL, \true); - } - $phar = '\'\''; - if (\defined('__PHPUNIT_PHAR__')) { - $phar = \var_export(__PHPUNIT_PHAR__, \true); - } - $globals = ''; - if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { - $globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . \var_export($GLOBALS['__PHPUNIT_BOOTSTRAP'], \true) . ";\n"; - } - if ($codeCoverageCacheDirectory === null) { - $codeCoverageCacheDirectory = 'null'; - } else { - $codeCoverageCacheDirectory = "'" . $codeCoverageCacheDirectory . "'"; - } - $template->setVar(['composerAutoload' => $composerAutoload, 'phar' => $phar, 'globals' => $globals, 'job' => $files['job'], 'coverageFile' => $files['coverage'], 'driverMethod' => $pathCoverage ? 'forLineAndPathCoverage' : 'forLineCoverage', 'codeCoverageCacheDirectory' => $codeCoverageCacheDirectory]); - \file_put_contents($files['job'], $job); - $job = $template->render(); + $this->directories = $directories->asArray(); } - private function cleanupForCoverage() : \PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData + public function count() : int { - $coverage = \PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData::fromXdebugWithoutPathCoverage([]); - $files = $this->getCoverageFiles(); - if (\is_file($files['coverage'])) { - $buffer = @\file_get_contents($files['coverage']); - if ($buffer !== \false) { - $coverage = @\unserialize($buffer); - if ($coverage === \false) { - $coverage = \PHPUnit\SebastianBergmann\CodeCoverage\RawCodeCoverageData::fromXdebugWithoutPathCoverage([]); - } - } - } - foreach ($files as $file) { - @\unlink($file); - } - return $coverage; + return iterator_count($this); } - private function stringifyIni(array $ini) : array + public function rewind() : void { - $settings = []; - foreach ($ini as $key => $value) { - if (\is_array($value)) { - foreach ($value as $val) { - $settings[] = $key . '=' . $val; - } - continue; - } - $settings[] = $key . '=' . $value; - } - return $settings; + $this->position = 0; } - private function getLocationHintFromDiff(string $message, array $sections) : array + public function valid() : bool { - $needle = ''; - $previousLine = ''; - $block = 'message'; - foreach (\preg_split('/\\r\\n|\\r|\\n/', $message) as $line) { - $line = \trim($line); - if ($block === 'message' && $line === '--- Expected') { - $block = 'expected'; - } - if ($block === 'expected' && $line === '@@ @@') { - $block = 'diff'; - } - if ($block === 'diff') { - if (\strpos($line, '+') === 0) { - $needle = $this->getCleanDiffLine($previousLine); - break; - } - if (\strpos($line, '-') === 0) { - $needle = $this->getCleanDiffLine($line); - break; - } - } - if (!empty($line)) { - $previousLine = $line; - } - } - return $this->getLocationHint($needle, $sections); + return $this->position < count($this->directories); } - private function getCleanDiffLine(string $line) : string + public function key() : int { - if (\preg_match('/^[\\-+]([\'\\"]?)(.*)\\1$/', $line, $matches)) { - $line = $matches[2]; - } - return $line; + return $this->position; } - private function getLocationHint(string $needle, array $sections, ?string $sectionName = null) : array + public function current() : \PHPUnit\TextUI\XmlConfiguration\Directory { - $needle = \trim($needle); - if (empty($needle)) { - return [['file' => \realpath($this->filename), 'line' => 1]]; - } - if ($sectionName) { - $search = [$sectionName]; - } else { - $search = [ - // 'FILE', - 'EXPECT', - 'EXPECTF', - 'EXPECTREGEX', - ]; - } - $sectionOffset = null; - foreach ($search as $section) { - if (!isset($sections[$section])) { - continue; - } - if (isset($sections[$section . '_EXTERNAL'])) { - $externalFile = \trim($sections[$section . '_EXTERNAL']); - return [['file' => \realpath(\dirname($this->filename) . \DIRECTORY_SEPARATOR . $externalFile), 'line' => 1], ['file' => \realpath($this->filename), 'line' => ($sections[$section . '_EXTERNAL_offset'] ?? 0) + 1]]; - } - $sectionOffset = $sections[$section . '_offset'] ?? 0; - $offset = $sectionOffset + 1; - foreach (\preg_split('/\\r\\n|\\r|\\n/', $sections[$section]) as $line) { - if (\strpos($line, $needle) !== \false) { - return [['file' => \realpath($this->filename), 'line' => $offset]]; - } - $offset++; - } - } - if ($sectionName) { - // String not found in specified section, show user the start of the named section - return [['file' => \realpath($this->filename), 'line' => $sectionOffset]]; - } - // No section specified, show user start of code - return [['file' => \realpath($this->filename), 'line' => 1]]; + return $this->directories[$this->position]; } - /** - * @psalm-return list - */ - private function settings(bool $collectCoverage) : array + public function next() : void { - $settings = ['allow_url_fopen=1', 'auto_append_file=', 'auto_prepend_file=', 'disable_functions=', 'display_errors=1', 'docref_ext=.html', 'docref_root=', 'error_append_string=', 'error_prepend_string=', 'error_reporting=-1', 'html_errors=0', 'log_errors=0', 'open_basedir=', 'output_buffering=Off', 'output_handler=', 'report_memleaks=0', 'report_zend_debug=0']; - if (\extension_loaded('pcov')) { - if ($collectCoverage) { - $settings[] = 'pcov.enabled=1'; - } else { - $settings[] = 'pcov.enabled=0'; - } - } - if (\extension_loaded('xdebug')) { - if (\version_compare(\phpversion('xdebug'), '3', '>=')) { - if ($collectCoverage) { - $settings[] = 'xdebug.mode=coverage'; - } else { - $settings[] = 'xdebug.mode=off'; - } - } else { - $settings[] = 'xdebug.default_enable=0'; - if ($collectCoverage) { - $settings[] = 'xdebug.coverage_enable=1'; - } - } - } - return $settings; + $this->position++; } } directories = $directories; + } /** - * @var bool + * @return Directory[] */ - protected $runTestInSeparateProcess = \false; + public function asArray() : array + { + return $this->directories; + } + public function count() : int + { + return count($this->directories); + } + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\DirectoryCollectionIterator + { + return new \PHPUnit\TextUI\XmlConfiguration\DirectoryCollectionIterator($this); + } + public function isEmpty() : bool + { + return $this->count() === 0; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class File +{ /** - * The name of the test suite. - * * @var string */ - protected $name = ''; - /** - * The test groups of the test suite. - * - * @psalm-var array> - */ - protected $groups = []; + private $path; + public function __construct(string $path) + { + $this->path = $path; + } + public function path() : string + { + return $this->path; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class FileCollectionIterator implements Countable, Iterator +{ /** - * The tests in the test suite. - * - * @var Test[] + * @var File[] */ - protected $tests = []; + private $files; /** - * The number of tests in the test suite. - * * @var int */ - protected $numTests = -1; - /** - * @var bool - */ - protected $testCase = \false; - /** - * @var string[] - */ - protected $foundClasses = []; - /** - * @var null|list - */ - protected $providedTests; - /** - * @var null|list - */ - protected $requiredTests; + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\FileCollection $files) + { + $this->files = $files->asArray(); + } + public function count() : int + { + return iterator_count($this); + } + public function rewind() : void + { + $this->position = 0; + } + public function valid() : bool + { + return $this->position < count($this->files); + } + public function key() : int + { + return $this->position; + } + public function current() : \PHPUnit\TextUI\XmlConfiguration\File + { + return $this->files[$this->position]; + } + public function next() : void + { + $this->position++; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class FileCollection implements Countable, IteratorAggregate +{ /** - * @var bool + * @var File[] */ - private $beStrictAboutChangesToGlobalState; + private $files; /** - * @var Factory + * @param File[] $files */ - private $iteratorFilter; + public static function fromArray(array $files) : self + { + return new self(...$files); + } + private function __construct(\PHPUnit\TextUI\XmlConfiguration\File ...$files) + { + $this->files = $files; + } /** - * @var string[] + * @return File[] */ - private $declaredClasses; + public function asArray() : array + { + return $this->files; + } + public function count() : int + { + return count($this->files); + } + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\FileCollectionIterator + { + return new \PHPUnit\TextUI\XmlConfiguration\FileCollectionIterator($this); + } + public function isEmpty() : bool + { + return $this->count() === 0; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class Directory +{ /** - * @psalm-var array + * @var string */ - private $warnings = []; - /** - * Constructs a new TestSuite. - * - * - PHPUnit\Framework\TestSuite() constructs an empty TestSuite. - * - * - PHPUnit\Framework\TestSuite(ReflectionClass) constructs a - * TestSuite from the given class. - * - * - PHPUnit\Framework\TestSuite(ReflectionClass, String) - * constructs a TestSuite from the given class with the given - * name. - * - * - PHPUnit\Framework\TestSuite(String) either constructs a - * TestSuite from the given class (if the passed string is the - * name of an existing class) or constructs an empty TestSuite - * with the given name. - * - * @param ReflectionClass|string $theClass - * + private $path; + public function __construct(string $path) + { + $this->path = $path; + } + public function path() : string + { + return $this->path; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use const DIRECTORY_SEPARATOR; +use const PHP_VERSION; +use function assert; +use function defined; +use function dirname; +use function explode; +use function is_file; +use function is_numeric; +use function preg_match; +use function stream_resolve_include_path; +use function strlen; +use function strpos; +use function strtolower; +use function substr; +use function trim; +use DOMDocument; +use DOMElement; +use DOMNodeList; +use DOMXPath; +use PHPUnit\Runner\TestSuiteSorter; +use PHPUnit\Runner\Version; +use PHPUnit\TextUI\DefaultResultPrinter; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\Directory as FilterDirectory; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection as FilterDirectoryCollection; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Clover; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Cobertura; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Crap4j; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Html as CodeCoverageHtml; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Php as CodeCoveragePhp; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Text as CodeCoverageText; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Xml as CodeCoverageXml; +use PHPUnit\TextUI\XmlConfiguration\Logging\Junit; +use PHPUnit\TextUI\XmlConfiguration\Logging\Logging; +use PHPUnit\TextUI\XmlConfiguration\Logging\TeamCity; +use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Html as TestDoxHtml; +use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Text as TestDoxText; +use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Xml as TestDoxXml; +use PHPUnit\TextUI\XmlConfiguration\Logging\Text; +use PHPUnit\TextUI\XmlConfiguration\TestSuite as TestSuiteConfiguration; +use PHPUnit\Util\TestDox\CliTestDoxPrinter; +use PHPUnit\Util\VersionComparisonOperator; +use PHPUnit\Util\Xml; +use PHPUnit\Util\Xml\Exception as XmlException; +use PHPUnit\Util\Xml\Loader as XmlLoader; +use PHPUnit\Util\Xml\SchemaFinder; +use PHPUnit\Util\Xml\Validator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Loader +{ + /** * @throws Exception */ - public function __construct($theClass = '', string $name = '') + public function load(string $filename) : \PHPUnit\TextUI\XmlConfiguration\Configuration { - if (!\is_string($theClass) && !$theClass instanceof \ReflectionClass) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'ReflectionClass object or string'); + try { + $document = (new XmlLoader())->loadFile($filename, \false, \true, \true); + } catch (XmlException $e) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception($e->getMessage(), (int) $e->getCode(), $e); } - $this->declaredClasses = \get_declared_classes(); - if (!$theClass instanceof \ReflectionClass) { - if (\class_exists($theClass, \true)) { - if ($name === '') { - $name = $theClass; - } - try { - $theClass = new \ReflectionClass($theClass); - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } else { - $this->setName($theClass); - return; - } + $xpath = new DOMXPath($document); + try { + $xsdFilename = (new SchemaFinder())->find(Version::series()); + } catch (XmlException $e) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception($e->getMessage(), (int) $e->getCode(), $e); } - if (!$theClass->isSubclassOf(\PHPUnit\Framework\TestCase::class)) { - $this->setName((string) $theClass); - return; + return new \PHPUnit\TextUI\XmlConfiguration\Configuration($filename, (new Validator())->validate($document, $xsdFilename), $this->extensions($filename, $xpath), $this->codeCoverage($filename, $xpath, $document), $this->groups($xpath), $this->testdoxGroups($xpath), $this->listeners($filename, $xpath), $this->logging($filename, $xpath), $this->php($filename, $xpath), $this->phpunit($filename, $document), $this->testSuite($filename, $xpath)); + } + public function logging(string $filename, DOMXPath $xpath) : Logging + { + if ($xpath->query('logging/log')->length !== 0) { + return $this->legacyLogging($filename, $xpath); } - if ($name !== '') { - $this->setName($name); - } else { - $this->setName($theClass->getName()); + $junit = null; + $element = $this->element($xpath, 'logging/junit'); + if ($element) { + $junit = new Junit(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); } - $constructor = $theClass->getConstructor(); - if ($constructor !== null && !$constructor->isPublic()) { - $this->addTest(new \PHPUnit\Framework\WarningTestCase(\sprintf('Class "%s" has no public constructor.', $theClass->getName()))); - return; + $text = null; + $element = $this->element($xpath, 'logging/text'); + if ($element) { + $text = new Text(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); } - foreach ($theClass->getMethods() as $method) { - if ($method->getDeclaringClass()->getName() === \PHPUnit\Framework\Assert::class) { - continue; - } - if ($method->getDeclaringClass()->getName() === \PHPUnit\Framework\TestCase::class) { + $teamCity = null; + $element = $this->element($xpath, 'logging/teamcity'); + if ($element) { + $teamCity = new TeamCity(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $testDoxHtml = null; + $element = $this->element($xpath, 'logging/testdoxHtml'); + if ($element) { + $testDoxHtml = new TestDoxHtml(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $testDoxText = null; + $element = $this->element($xpath, 'logging/testdoxText'); + if ($element) { + $testDoxText = new TestDoxText(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $testDoxXml = null; + $element = $this->element($xpath, 'logging/testdoxXml'); + if ($element) { + $testDoxXml = new TestDoxXml(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + return new Logging($junit, $text, $teamCity, $testDoxHtml, $testDoxText, $testDoxXml); + } + public function legacyLogging(string $filename, DOMXPath $xpath) : Logging + { + $junit = null; + $teamCity = null; + $testDoxHtml = null; + $testDoxText = null; + $testDoxXml = null; + $text = null; + foreach ($xpath->query('logging/log') as $log) { + assert($log instanceof DOMElement); + $type = (string) $log->getAttribute('type'); + $target = (string) $log->getAttribute('target'); + if (!$target) { continue; } - if (!\PHPUnit\Util\Test::isTestMethod($method)) { - continue; + $target = $this->toAbsolutePath($filename, $target); + switch ($type) { + case 'plain': + $text = new Text(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; + case 'junit': + $junit = new Junit(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; + case 'teamcity': + $teamCity = new TeamCity(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; + case 'testdox-html': + $testDoxHtml = new TestDoxHtml(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; + case 'testdox-text': + $testDoxText = new TestDoxText(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; + case 'testdox-xml': + $testDoxXml = new TestDoxXml(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; } - $this->addTestMethod($theClass, $method); } - if (empty($this->tests)) { - $this->addTest(new \PHPUnit\Framework\WarningTestCase(\sprintf('No tests found in class "%s".', $theClass->getName()))); + return new Logging($junit, $text, $teamCity, $testDoxHtml, $testDoxText, $testDoxXml); + } + private function extensions(string $filename, DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection + { + $extensions = []; + foreach ($xpath->query('extensions/extension') as $extension) { + assert($extension instanceof DOMElement); + $extensions[] = $this->getElementConfigurationParameters($filename, $extension); } - $this->testCase = \true; + return \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection::fromArray($extensions); } - /** - * Returns a string representation of the test suite. - */ - public function toString() : string + private function getElementConfigurationParameters(string $filename, DOMElement $element) : \PHPUnit\TextUI\XmlConfiguration\Extension { - return $this->getName(); + /** @psalm-var class-string $class */ + $class = (string) $element->getAttribute('class'); + $file = ''; + $arguments = $this->getConfigurationArguments($filename, $element->childNodes); + if ($element->getAttribute('file')) { + $file = $this->toAbsolutePath($filename, (string) $element->getAttribute('file'), \true); + } + return new \PHPUnit\TextUI\XmlConfiguration\Extension($class, $file, $arguments); } - /** - * Adds a test to the suite. - * - * @param array $groups - */ - public function addTest(\PHPUnit\Framework\Test $test, $groups = []) : void + private function toAbsolutePath(string $filename, string $path, bool $useIncludePath = \false) : string { - try { - $class = new \ReflectionClass($test); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); + $path = trim($path); + if (strpos($path, '/') === 0) { + return $path; } - // @codeCoverageIgnoreEnd - if (!$class->isAbstract()) { - $this->tests[] = $test; - $this->clearCaches(); - if ($test instanceof self && empty($groups)) { - $groups = $test->getGroups(); - } - if ($this->containsOnlyVirtualGroups($groups)) { - $groups[] = 'default'; - } - foreach ($groups as $group) { - if (!isset($this->groups[$group])) { - $this->groups[$group] = [$test]; - } else { - $this->groups[$group][] = $test; - } - } - if ($test instanceof \PHPUnit\Framework\TestCase) { - $test->setGroups($groups); + // Matches the following on Windows: + // - \\NetworkComputer\Path + // - \\.\D: + // - \\.\c: + // - C:\Windows + // - C:\windows + // - C:/windows + // - c:/windows + if (defined('PHP_WINDOWS_VERSION_BUILD') && ($path[0] === '\\' || strlen($path) >= 3 && preg_match('#^[A-Z]\\:[/\\\\]#i', substr($path, 0, 3)))) { + return $path; + } + if (strpos($path, '://') !== \false) { + return $path; + } + $file = dirname($filename) . \DIRECTORY_SEPARATOR . $path; + if ($useIncludePath && !is_file($file)) { + $includePathFile = stream_resolve_include_path($path); + if ($includePathFile) { + $file = $includePathFile; } } + return $file; } - /** - * Adds the tests from the given class to the suite. - * - * @psalm-param object|class-string $testClass - * - * @throws Exception - */ - public function addTestSuite($testClass) : void + private function getConfigurationArguments(string $filename, DOMNodeList $nodes) : array { - if (!(\is_object($testClass) || \is_string($testClass) && \class_exists($testClass))) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'class name or object'); + $arguments = []; + if ($nodes->length === 0) { + return $arguments; } - if (!\is_object($testClass)) { - try { - $testClass = new \ReflectionClass($testClass); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); + foreach ($nodes as $node) { + if (!$node instanceof DOMElement) { + continue; } - // @codeCoverageIgnoreEnd - } - if ($testClass instanceof self) { - $this->addTest($testClass); - } elseif ($testClass instanceof \ReflectionClass) { - $suiteMethod = \false; - if (!$testClass->isAbstract() && $testClass->hasMethod(\PHPUnit\Runner\BaseTestRunner::SUITE_METHODNAME)) { - try { - $method = $testClass->getMethod(\PHPUnit\Runner\BaseTestRunner::SUITE_METHODNAME); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); + if ($node->tagName !== 'arguments') { + continue; + } + foreach ($node->childNodes as $argument) { + if (!$argument instanceof DOMElement) { + continue; } - // @codeCoverageIgnoreEnd - if ($method->isStatic()) { - $this->addTest($method->invoke(null, $testClass->getName())); - $suiteMethod = \true; + if ($argument->tagName === 'file' || $argument->tagName === 'directory') { + $arguments[] = $this->toAbsolutePath($filename, (string) $argument->textContent); + } else { + $arguments[] = Xml::xmlToVariable($argument); } } - if (!$suiteMethod && !$testClass->isAbstract() && $testClass->isSubclassOf(\PHPUnit\Framework\TestCase::class)) { - $this->addTest(new self($testClass)); - } - } else { - throw new \PHPUnit\Framework\Exception(); } + return $arguments; } - public function addWarning(string $warning) : void - { - $this->warnings[] = $warning; - } - /** - * Wraps both addTest() and addTestSuite - * as well as the separate import statements for the user's convenience. - * - * If the named file cannot be read or there are no new tests that can be - * added, a PHPUnit\Framework\WarningTestCase will be created instead, - * leaving the current test run untouched. - * - * @throws Exception - */ - public function addTestFile(string $filename) : void + private function codeCoverage(string $filename, DOMXPath $xpath, DOMDocument $document) : CodeCoverage { - if (\is_file($filename) && \substr($filename, -5) === '.phpt') { - $this->addTest(new \PHPUnit\Runner\PhptTestCase($filename)); - $this->declaredClasses = \get_declared_classes(); - return; - } - $numTests = \count($this->tests); - // The given file may contain further stub classes in addition to the - // test class itself. Figure out the actual test class. - $filename = \PHPUnit\Util\FileLoader::checkAndLoad($filename); - $newClasses = \array_diff(\get_declared_classes(), $this->declaredClasses); - // The diff is empty in case a parent class (with test methods) is added - // AFTER a child class that inherited from it. To account for that case, - // accumulate all discovered classes, so the parent class may be found in - // a later invocation. - if (!empty($newClasses)) { - // On the assumption that test classes are defined first in files, - // process discovered classes in approximate LIFO order, so as to - // avoid unnecessary reflection. - $this->foundClasses = \array_merge($newClasses, $this->foundClasses); - $this->declaredClasses = \get_declared_classes(); + if ($xpath->query('filter/whitelist')->length !== 0) { + return $this->legacyCodeCoverage($filename, $xpath, $document); } - // The test class's name must match the filename, either in full, or as - // a PEAR/PSR-0 prefixed short name ('NameSpace_ShortName'), or as a - // PSR-1 local short name ('NameSpace\ShortName'). The comparison must be - // anchored to prevent false-positive matches (e.g., 'OtherShortName'). - $shortName = \basename($filename, '.php'); - $shortNameRegEx = '/(?:^|_|\\\\)' . \preg_quote($shortName, '/') . '$/'; - foreach ($this->foundClasses as $i => $className) { - if (\preg_match($shortNameRegEx, $className)) { - try { - $class = new \ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($class->getFileName() == $filename) { - $newClasses = [$className]; - unset($this->foundClasses[$i]); - break; - } + $cacheDirectory = null; + $pathCoverage = \false; + $includeUncoveredFiles = \true; + $processUncoveredFiles = \false; + $ignoreDeprecatedCodeUnits = \false; + $disableCodeCoverageIgnore = \false; + $element = $this->element($xpath, 'coverage'); + if ($element) { + $cacheDirectory = $this->getStringAttribute($element, 'cacheDirectory'); + if ($cacheDirectory !== null) { + $cacheDirectory = new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, $cacheDirectory)); } + $pathCoverage = $this->getBooleanAttribute($element, 'pathCoverage', \false); + $includeUncoveredFiles = $this->getBooleanAttribute($element, 'includeUncoveredFiles', \true); + $processUncoveredFiles = $this->getBooleanAttribute($element, 'processUncoveredFiles', \false); + $ignoreDeprecatedCodeUnits = $this->getBooleanAttribute($element, 'ignoreDeprecatedCodeUnits', \false); + $disableCodeCoverageIgnore = $this->getBooleanAttribute($element, 'disableCodeCoverageIgnore', \false); } - foreach ($newClasses as $className) { - try { - $class = new \ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if (\dirname($class->getFileName()) === __DIR__) { - continue; - } - if (!$class->isAbstract()) { - if ($class->hasMethod(\PHPUnit\Runner\BaseTestRunner::SUITE_METHODNAME)) { - try { - $method = $class->getMethod(\PHPUnit\Runner\BaseTestRunner::SUITE_METHODNAME); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($method->isStatic()) { - $this->addTest($method->invoke(null, $className)); - } - } elseif ($class->implementsInterface(\PHPUnit\Framework\Test::class)) { - $expectedClassName = $shortName; - if (($pos = \strpos($expectedClassName, '.')) !== \false) { - $expectedClassName = \substr($expectedClassName, 0, $pos); - } - if ($class->getShortName() !== $expectedClassName) { - $this->addWarning(\sprintf("Test case class not matching filename is deprecated\n in %s\n Class name was '%s', expected '%s'", $filename, $class->getShortName(), $expectedClassName)); - } - $this->addTestSuite($class); - } - } + $clover = null; + $element = $this->element($xpath, 'coverage/report/clover'); + if ($element) { + $clover = new Clover(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); } - if (\count($this->tests) > ++$numTests) { - $this->addWarning(\sprintf("Multiple test case classes per file is deprecated\n in %s", $filename)); + $cobertura = null; + $element = $this->element($xpath, 'coverage/report/cobertura'); + if ($element) { + $cobertura = new Cobertura(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); } - $this->numTests = -1; - } - /** - * Wrapper for addTestFile() that adds multiple test files. - * - * @throws Exception - */ - public function addTestFiles(iterable $fileNames) : void - { - foreach ($fileNames as $filename) { - $this->addTestFile((string) $filename); + $crap4j = null; + $element = $this->element($xpath, 'coverage/report/crap4j'); + if ($element) { + $crap4j = new Crap4j(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile'))), $this->getIntegerAttribute($element, 'threshold', 30)); } - } - /** - * Counts the number of test cases that will be run by this test. - * - * @todo refactor usage of numTests in DefaultResultPrinter - */ - public function count() : int - { - $this->numTests = 0; - foreach ($this as $test) { - $this->numTests += \count($test); + $html = null; + $element = $this->element($xpath, 'coverage/report/html'); + if ($element) { + $html = new CodeCoverageHtml(new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputDirectory'))), $this->getIntegerAttribute($element, 'lowUpperBound', 50), $this->getIntegerAttribute($element, 'highLowerBound', 90)); } - return $this->numTests; - } - /** - * Returns the name of the suite. - */ - public function getName() : string - { - return $this->name; - } - /** - * Returns the test groups of the suite. - * - * @psalm-return list - */ - public function getGroups() : array - { - return \array_map(static function ($key) : string { - return (string) $key; - }, \array_keys($this->groups)); - } - public function getGroupDetails() : array - { - return $this->groups; - } - /** - * Set tests groups of the test case. - */ - public function setGroupDetails(array $groups) : void - { - $this->groups = $groups; + $php = null; + $element = $this->element($xpath, 'coverage/report/php'); + if ($element) { + $php = new CodeCoveragePhp(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile')))); + } + $text = null; + $element = $this->element($xpath, 'coverage/report/text'); + if ($element) { + $text = new CodeCoverageText(new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputFile'))), $this->getBooleanAttribute($element, 'showUncoveredFiles', \false), $this->getBooleanAttribute($element, 'showOnlySummary', \false)); + } + $xml = null; + $element = $this->element($xpath, 'coverage/report/xml'); + if ($element) { + $xml = new CodeCoverageXml(new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, (string) $this->getStringAttribute($element, 'outputDirectory')))); + } + return new CodeCoverage($cacheDirectory, $this->readFilterDirectories($filename, $xpath, 'coverage/include/directory'), $this->readFilterFiles($filename, $xpath, 'coverage/include/file'), $this->readFilterDirectories($filename, $xpath, 'coverage/exclude/directory'), $this->readFilterFiles($filename, $xpath, 'coverage/exclude/file'), $pathCoverage, $includeUncoveredFiles, $processUncoveredFiles, $ignoreDeprecatedCodeUnits, $disableCodeCoverageIgnore, $clover, $cobertura, $crap4j, $html, $php, $text, $xml); } /** - * Runs the tests and collects their result in a TestResult. - * - * @throws \PHPUnit\Framework\CodeCoverageException - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Warning + * @deprecated */ - public function run(\PHPUnit\Framework\TestResult $result = null) : \PHPUnit\Framework\TestResult + private function legacyCodeCoverage(string $filename, DOMXPath $xpath, DOMDocument $document) : CodeCoverage { - if ($result === null) { - $result = $this->createResult(); - } - if (\count($this) === 0) { - return $result; - } - /** @psalm-var class-string $className */ - $className = $this->name; - $hookMethods = \PHPUnit\Util\Test::getHookMethods($className); - $result->startTestSuite($this); - $test = null; - try { - foreach ($hookMethods['beforeClass'] as $beforeClassMethod) { - if ($this->testCase && \class_exists($this->name, \false) && \method_exists($this->name, $beforeClassMethod)) { - if ($missingRequirements = \PHPUnit\Util\Test::getMissingRequirements($this->name, $beforeClassMethod)) { - $this->markTestSuiteSkipped(\implode(\PHP_EOL, $missingRequirements)); - } - \call_user_func([$this->name, $beforeClassMethod]); - } - } - } catch (\PHPUnit\Framework\SkippedTestSuiteError $error) { - foreach ($this->tests() as $test) { - $result->startTest($test); - $result->addFailure($test, $error, 0); - $result->endTest($test, 0); + $ignoreDeprecatedCodeUnits = $this->getBooleanAttribute($document->documentElement, 'ignoreDeprecatedCodeUnitsFromCodeCoverage', \false); + $disableCodeCoverageIgnore = $this->getBooleanAttribute($document->documentElement, 'disableCodeCoverageIgnore', \false); + $includeUncoveredFiles = \true; + $processUncoveredFiles = \false; + $element = $this->element($xpath, 'filter/whitelist'); + if ($element) { + if ($element->hasAttribute('addUncoveredFilesFromWhitelist')) { + $includeUncoveredFiles = (bool) $this->getBoolean((string) $element->getAttribute('addUncoveredFilesFromWhitelist'), \true); } - $result->endTestSuite($this); - return $result; - } catch (\Throwable $t) { - $errorAdded = \false; - foreach ($this->tests() as $test) { - if ($result->shouldStop()) { - break; - } - $result->startTest($test); - if (!$errorAdded) { - $result->addError($test, $t, 0); - $errorAdded = \true; - } else { - $result->addFailure($test, new \PHPUnit\Framework\SkippedTestError('Test skipped because of an error in hook method'), 0); - } - $result->endTest($test, 0); + if ($element->hasAttribute('processUncoveredFilesFromWhitelist')) { + $processUncoveredFiles = (bool) $this->getBoolean((string) $element->getAttribute('processUncoveredFilesFromWhitelist'), \false); } - $result->endTestSuite($this); - return $result; } - foreach ($this as $test) { - if ($result->shouldStop()) { - break; - } - if ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof self) { - $test->setBeStrictAboutChangesToGlobalState($this->beStrictAboutChangesToGlobalState); - $test->setBackupGlobals($this->backupGlobals); - $test->setBackupStaticAttributes($this->backupStaticAttributes); - $test->setRunTestInSeparateProcess($this->runTestInSeparateProcess); + $clover = null; + $cobertura = null; + $crap4j = null; + $html = null; + $php = null; + $text = null; + $xml = null; + foreach ($xpath->query('logging/log') as $log) { + assert($log instanceof DOMElement); + $type = (string) $log->getAttribute('type'); + $target = (string) $log->getAttribute('target'); + if (!$target) { + continue; } - $test->run($result); - } - foreach ($hookMethods['afterClass'] as $afterClassMethod) { - if ($this->testCase && \class_exists($this->name, \false) && \method_exists($this->name, $afterClassMethod)) { - try { - \call_user_func([$this->name, $afterClassMethod]); - } catch (\Throwable $t) { - $message = "Exception in {$this->name}::{$afterClassMethod}" . \PHP_EOL . $t->getMessage(); - $error = new \PHPUnit\Framework\SyntheticError($message, 0, $t->getFile(), $t->getLine(), $t->getTrace()); - $placeholderTest = clone $test; - $placeholderTest->setName($afterClassMethod); - $result->startTest($placeholderTest); - $result->addFailure($placeholderTest, $error, 0); - $result->endTest($placeholderTest, 0); - } + $target = $this->toAbsolutePath($filename, $target); + switch ($type) { + case 'coverage-clover': + $clover = new Clover(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; + case 'coverage-cobertura': + $cobertura = new Cobertura(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; + case 'coverage-crap4j': + $crap4j = new Crap4j(new \PHPUnit\TextUI\XmlConfiguration\File($target), $this->getIntegerAttribute($log, 'threshold', 30)); + break; + case 'coverage-html': + $html = new CodeCoverageHtml(new \PHPUnit\TextUI\XmlConfiguration\Directory($target), $this->getIntegerAttribute($log, 'lowUpperBound', 50), $this->getIntegerAttribute($log, 'highLowerBound', 90)); + break; + case 'coverage-php': + $php = new CodeCoveragePhp(new \PHPUnit\TextUI\XmlConfiguration\File($target)); + break; + case 'coverage-text': + $text = new CodeCoverageText(new \PHPUnit\TextUI\XmlConfiguration\File($target), $this->getBooleanAttribute($log, 'showUncoveredFiles', \false), $this->getBooleanAttribute($log, 'showOnlySummary', \false)); + break; + case 'coverage-xml': + $xml = new CodeCoverageXml(new \PHPUnit\TextUI\XmlConfiguration\Directory($target)); + break; } } - $result->endTestSuite($this); - return $result; - } - public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess) : void - { - $this->runTestInSeparateProcess = $runTestInSeparateProcess; - } - public function setName(string $name) : void - { - $this->name = $name; + return new CodeCoverage(null, $this->readFilterDirectories($filename, $xpath, 'filter/whitelist/directory'), $this->readFilterFiles($filename, $xpath, 'filter/whitelist/file'), $this->readFilterDirectories($filename, $xpath, 'filter/whitelist/exclude/directory'), $this->readFilterFiles($filename, $xpath, 'filter/whitelist/exclude/file'), \false, $includeUncoveredFiles, $processUncoveredFiles, $ignoreDeprecatedCodeUnits, $disableCodeCoverageIgnore, $clover, $cobertura, $crap4j, $html, $php, $text, $xml); } /** - * Returns the tests as an enumeration. + * If $value is 'false' or 'true', this returns the value that $value represents. + * Otherwise, returns $default, which may be a string in rare cases. * - * @return Test[] + * @see \PHPUnit\TextUI\XmlConfigurationTest::testPHPConfigurationIsReadCorrectly + * + * @param bool|string $default + * + * @return bool|string */ - public function tests() : array + private function getBoolean(string $value, $default) { - return $this->tests; + if (strtolower($value) === 'false') { + return \false; + } + if (strtolower($value) === 'true') { + return \true; + } + return $default; } - /** - * Set tests of the test suite. - * - * @param Test[] $tests - */ - public function setTests(array $tests) : void + private function readFilterDirectories(string $filename, DOMXPath $xpath, string $query) : FilterDirectoryCollection { - $this->tests = $tests; + $directories = []; + foreach ($xpath->query($query) as $directoryNode) { + assert($directoryNode instanceof DOMElement); + $directoryPath = (string) $directoryNode->textContent; + if (!$directoryPath) { + continue; + } + $directories[] = new FilterDirectory($this->toAbsolutePath($filename, $directoryPath), $directoryNode->hasAttribute('prefix') ? (string) $directoryNode->getAttribute('prefix') : '', $directoryNode->hasAttribute('suffix') ? (string) $directoryNode->getAttribute('suffix') : '.php', $directoryNode->hasAttribute('group') ? (string) $directoryNode->getAttribute('group') : 'DEFAULT'); + } + return FilterDirectoryCollection::fromArray($directories); } - /** - * Mark the test suite as skipped. - * - * @param string $message - * - * @throws SkippedTestSuiteError - * - * @psalm-return never-return - */ - public function markTestSuiteSkipped($message = '') : void + private function readFilterFiles(string $filename, DOMXPath $xpath, string $query) : \PHPUnit\TextUI\XmlConfiguration\FileCollection + { + $files = []; + foreach ($xpath->query($query) as $file) { + $filePath = (string) $file->textContent; + if ($filePath) { + $files[] = new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, $filePath)); + } + } + return \PHPUnit\TextUI\XmlConfiguration\FileCollection::fromArray($files); + } + private function groups(DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\Groups { - throw new \PHPUnit\Framework\SkippedTestSuiteError($message); + return $this->parseGroupConfiguration($xpath, 'groups'); } - /** - * @param bool $beStrictAboutChangesToGlobalState - */ - public function setBeStrictAboutChangesToGlobalState($beStrictAboutChangesToGlobalState) : void + private function testdoxGroups(DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\Groups { - if (null === $this->beStrictAboutChangesToGlobalState && \is_bool($beStrictAboutChangesToGlobalState)) { - $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; + return $this->parseGroupConfiguration($xpath, 'testdoxGroups'); + } + private function parseGroupConfiguration(DOMXPath $xpath, string $root) : \PHPUnit\TextUI\XmlConfiguration\Groups + { + $include = []; + $exclude = []; + foreach ($xpath->query($root . '/include/group') as $group) { + $include[] = new \PHPUnit\TextUI\XmlConfiguration\Group((string) $group->textContent); + } + foreach ($xpath->query($root . '/exclude/group') as $group) { + $exclude[] = new \PHPUnit\TextUI\XmlConfiguration\Group((string) $group->textContent); } + return new \PHPUnit\TextUI\XmlConfiguration\Groups(\PHPUnit\TextUI\XmlConfiguration\GroupCollection::fromArray($include), \PHPUnit\TextUI\XmlConfiguration\GroupCollection::fromArray($exclude)); } - /** - * @param bool $backupGlobals - */ - public function setBackupGlobals($backupGlobals) : void + private function listeners(string $filename, DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection { - if (null === $this->backupGlobals && \is_bool($backupGlobals)) { - $this->backupGlobals = $backupGlobals; + $listeners = []; + foreach ($xpath->query('listeners/listener') as $listener) { + assert($listener instanceof DOMElement); + $listeners[] = $this->getElementConfigurationParameters($filename, $listener); } + return \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection::fromArray($listeners); } - /** - * @param bool $backupStaticAttributes - */ - public function setBackupStaticAttributes($backupStaticAttributes) : void + private function getBooleanAttribute(DOMElement $element, string $attribute, bool $default) : bool { - if (null === $this->backupStaticAttributes && \is_bool($backupStaticAttributes)) { - $this->backupStaticAttributes = $backupStaticAttributes; + if (!$element->hasAttribute($attribute)) { + return $default; } + return (bool) $this->getBoolean((string) $element->getAttribute($attribute), \false); } - /** - * Returns an iterator for this test suite. - */ - public function getIterator() : \Iterator + private function getIntegerAttribute(DOMElement $element, string $attribute, int $default) : int { - $iterator = new \PHPUnit\Framework\TestSuiteIterator($this); - if ($this->iteratorFilter !== null) { - $iterator = $this->iteratorFilter->factory($iterator, $this); + if (!$element->hasAttribute($attribute)) { + return $default; } - return $iterator; + return $this->getInteger((string) $element->getAttribute($attribute), $default); } - public function injectFilter(\PHPUnit\Runner\Filter\Factory $filter) : void + private function getStringAttribute(DOMElement $element, string $attribute) : ?string { - $this->iteratorFilter = $filter; - foreach ($this as $test) { - if ($test instanceof self) { - $test->injectFilter($filter); - } + if (!$element->hasAttribute($attribute)) { + return null; } + return (string) $element->getAttribute($attribute); } - /** - * @psalm-return array - */ - public function warnings() : array + private function getInteger(string $value, int $default) : int { - return \array_unique($this->warnings); + if (is_numeric($value)) { + return (int) $value; + } + return $default; } - /** - * @return list - */ - public function provides() : array + private function php(string $filename, DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\Php { - if ($this->providedTests === null) { - $this->providedTests = []; - if (\is_callable($this->sortId(), \true)) { - $this->providedTests[] = new \PHPUnit\Framework\ExecutionOrderDependency($this->sortId()); + $includePaths = []; + foreach ($xpath->query('php/includePath') as $includePath) { + $path = (string) $includePath->textContent; + if ($path) { + $includePaths[] = new \PHPUnit\TextUI\XmlConfiguration\Directory($this->toAbsolutePath($filename, $path)); } - foreach ($this->tests as $test) { - if (!$test instanceof \PHPUnit\Framework\Reorderable) { - // @codeCoverageIgnoreStart - continue; - // @codeCoverageIgnoreEnd + } + $iniSettings = []; + foreach ($xpath->query('php/ini') as $ini) { + assert($ini instanceof DOMElement); + $iniSettings[] = new \PHPUnit\TextUI\XmlConfiguration\IniSetting((string) $ini->getAttribute('name'), (string) $ini->getAttribute('value')); + } + $constants = []; + foreach ($xpath->query('php/const') as $const) { + assert($const instanceof DOMElement); + $value = (string) $const->getAttribute('value'); + $constants[] = new \PHPUnit\TextUI\XmlConfiguration\Constant((string) $const->getAttribute('name'), $this->getBoolean($value, $value)); + } + $variables = ['var' => [], 'env' => [], 'post' => [], 'get' => [], 'cookie' => [], 'server' => [], 'files' => [], 'request' => []]; + foreach (['var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) { + foreach ($xpath->query('php/' . $array) as $var) { + assert($var instanceof DOMElement); + $name = (string) $var->getAttribute('name'); + $value = (string) $var->getAttribute('value'); + $force = \false; + $verbatim = \false; + if ($var->hasAttribute('force')) { + $force = (bool) $this->getBoolean($var->getAttribute('force'), \false); } - $this->providedTests = \PHPUnit\Framework\ExecutionOrderDependency::mergeUnique($this->providedTests, $test->provides()); + if ($var->hasAttribute('verbatim')) { + $verbatim = $this->getBoolean($var->getAttribute('verbatim'), \false); + } + if (!$verbatim) { + $value = $this->getBoolean($value, $value); + } + $variables[$array][] = new \PHPUnit\TextUI\XmlConfiguration\Variable($name, $value, $force); } } - return $this->providedTests; + return new \PHPUnit\TextUI\XmlConfiguration\Php(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection::fromArray($includePaths), \PHPUnit\TextUI\XmlConfiguration\IniSettingCollection::fromArray($iniSettings), \PHPUnit\TextUI\XmlConfiguration\ConstantCollection::fromArray($constants), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['var']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['env']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['post']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['get']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['cookie']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['server']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['files']), \PHPUnit\TextUI\XmlConfiguration\VariableCollection::fromArray($variables['request'])); } - /** - * @return list - */ - public function requires() : array + private function phpunit(string $filename, DOMDocument $document) : \PHPUnit\TextUI\XmlConfiguration\PHPUnit { - if ($this->requiredTests === null) { - $this->requiredTests = []; - foreach ($this->tests as $test) { - if (!$test instanceof \PHPUnit\Framework\Reorderable) { - // @codeCoverageIgnoreStart - continue; - // @codeCoverageIgnoreEnd + $executionOrder = TestSuiteSorter::ORDER_DEFAULT; + $defectsFirst = \false; + $resolveDependencies = $this->getBooleanAttribute($document->documentElement, 'resolveDependencies', \true); + if ($document->documentElement->hasAttribute('executionOrder')) { + foreach (explode(',', $document->documentElement->getAttribute('executionOrder')) as $order) { + switch ($order) { + case 'default': + $executionOrder = TestSuiteSorter::ORDER_DEFAULT; + $defectsFirst = \false; + $resolveDependencies = \true; + break; + case 'depends': + $resolveDependencies = \true; + break; + case 'no-depends': + $resolveDependencies = \false; + break; + case 'defects': + $defectsFirst = \true; + break; + case 'duration': + $executionOrder = TestSuiteSorter::ORDER_DURATION; + break; + case 'random': + $executionOrder = TestSuiteSorter::ORDER_RANDOMIZED; + break; + case 'reverse': + $executionOrder = TestSuiteSorter::ORDER_REVERSED; + break; + case 'size': + $executionOrder = TestSuiteSorter::ORDER_SIZE; + break; } - $this->requiredTests = \PHPUnit\Framework\ExecutionOrderDependency::mergeUnique(\PHPUnit\Framework\ExecutionOrderDependency::filterInvalid($this->requiredTests), $test->requires()); } - $this->requiredTests = \PHPUnit\Framework\ExecutionOrderDependency::diff($this->requiredTests, $this->provides()); } - return $this->requiredTests; + $printerClass = $this->getStringAttribute($document->documentElement, 'printerClass'); + $testdox = $this->getBooleanAttribute($document->documentElement, 'testdox', \false); + $conflictBetweenPrinterClassAndTestdox = \false; + if ($testdox) { + if ($printerClass !== null) { + $conflictBetweenPrinterClassAndTestdox = \true; + } + $printerClass = CliTestDoxPrinter::class; + } + $cacheResultFile = $this->getStringAttribute($document->documentElement, 'cacheResultFile'); + if ($cacheResultFile !== null) { + $cacheResultFile = $this->toAbsolutePath($filename, $cacheResultFile); + } + $bootstrap = $this->getStringAttribute($document->documentElement, 'bootstrap'); + if ($bootstrap !== null) { + $bootstrap = $this->toAbsolutePath($filename, $bootstrap); + } + $extensionsDirectory = $this->getStringAttribute($document->documentElement, 'extensionsDirectory'); + if ($extensionsDirectory !== null) { + $extensionsDirectory = $this->toAbsolutePath($filename, $extensionsDirectory); + } + $testSuiteLoaderFile = $this->getStringAttribute($document->documentElement, 'testSuiteLoaderFile'); + if ($testSuiteLoaderFile !== null) { + $testSuiteLoaderFile = $this->toAbsolutePath($filename, $testSuiteLoaderFile); + } + $printerFile = $this->getStringAttribute($document->documentElement, 'printerFile'); + if ($printerFile !== null) { + $printerFile = $this->toAbsolutePath($filename, $printerFile); + } + return new \PHPUnit\TextUI\XmlConfiguration\PHPUnit($this->getBooleanAttribute($document->documentElement, 'cacheResult', \true), $cacheResultFile, $this->getColumns($document), $this->getColors($document), $this->getBooleanAttribute($document->documentElement, 'stderr', \false), $this->getBooleanAttribute($document->documentElement, 'noInteraction', \false), $this->getBooleanAttribute($document->documentElement, 'verbose', \false), $this->getBooleanAttribute($document->documentElement, 'reverseDefectList', \false), $this->getBooleanAttribute($document->documentElement, 'convertDeprecationsToExceptions', \false), $this->getBooleanAttribute($document->documentElement, 'convertErrorsToExceptions', \true), $this->getBooleanAttribute($document->documentElement, 'convertNoticesToExceptions', \true), $this->getBooleanAttribute($document->documentElement, 'convertWarningsToExceptions', \true), $this->getBooleanAttribute($document->documentElement, 'forceCoversAnnotation', \false), $bootstrap, $this->getBooleanAttribute($document->documentElement, 'processIsolation', \false), $this->getBooleanAttribute($document->documentElement, 'failOnEmptyTestSuite', \false), $this->getBooleanAttribute($document->documentElement, 'failOnIncomplete', \false), $this->getBooleanAttribute($document->documentElement, 'failOnRisky', \false), $this->getBooleanAttribute($document->documentElement, 'failOnSkipped', \false), $this->getBooleanAttribute($document->documentElement, 'failOnWarning', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnDefect', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnError', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnFailure', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnWarning', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnIncomplete', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnRisky', \false), $this->getBooleanAttribute($document->documentElement, 'stopOnSkipped', \false), $extensionsDirectory, $this->getStringAttribute($document->documentElement, 'testSuiteLoaderClass'), $testSuiteLoaderFile, $printerClass, $printerFile, $this->getBooleanAttribute($document->documentElement, 'beStrictAboutChangesToGlobalState', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutOutputDuringTests', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutResourceUsageDuringSmallTests', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutTestsThatDoNotTestAnything', \true), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutTodoAnnotatedTests', \false), $this->getBooleanAttribute($document->documentElement, 'beStrictAboutCoversAnnotation', \false), $this->getBooleanAttribute($document->documentElement, 'enforceTimeLimit', \false), $this->getIntegerAttribute($document->documentElement, 'defaultTimeLimit', 1), $this->getIntegerAttribute($document->documentElement, 'timeoutForSmallTests', 1), $this->getIntegerAttribute($document->documentElement, 'timeoutForMediumTests', 10), $this->getIntegerAttribute($document->documentElement, 'timeoutForLargeTests', 60), $this->getStringAttribute($document->documentElement, 'defaultTestSuite'), $executionOrder, $resolveDependencies, $defectsFirst, $this->getBooleanAttribute($document->documentElement, 'backupGlobals', \false), $this->getBooleanAttribute($document->documentElement, 'backupStaticAttributes', \false), $this->getBooleanAttribute($document->documentElement, 'registerMockObjectsFromTestArgumentsRecursively', \false), $conflictBetweenPrinterClassAndTestdox); } - public function sortId() : string + private function getColors(DOMDocument $document) : string { - return $this->getName() . '::class'; + $colors = DefaultResultPrinter::COLOR_DEFAULT; + if ($document->documentElement->hasAttribute('colors')) { + /* only allow boolean for compatibility with previous versions + 'always' only allowed from command line */ + if ($this->getBoolean($document->documentElement->getAttribute('colors'), \false)) { + $colors = DefaultResultPrinter::COLOR_AUTO; + } else { + $colors = DefaultResultPrinter::COLOR_NEVER; + } + } + return $colors; } /** - * Creates a default TestResult object. + * @return int|string */ - protected function createResult() : \PHPUnit\Framework\TestResult + private function getColumns(DOMDocument $document) { - return new \PHPUnit\Framework\TestResult(); + $columns = 80; + if ($document->documentElement->hasAttribute('columns')) { + $columns = (string) $document->documentElement->getAttribute('columns'); + if ($columns !== 'max') { + $columns = $this->getInteger($columns, 80); + } + } + return $columns; } - /** - * @throws Exception - */ - protected function addTestMethod(\ReflectionClass $class, \ReflectionMethod $method) : void + private function testSuite(string $filename, DOMXPath $xpath) : \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection { - $methodName = $method->getName(); - $test = (new \PHPUnit\Framework\TestBuilder())->build($class, $methodName); - if ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof \PHPUnit\Framework\DataProviderTestSuite) { - $test->setDependencies(\PHPUnit\Util\Test::getDependencies($class->getName(), $methodName)); + $testSuites = []; + foreach ($this->getTestSuiteElements($xpath) as $element) { + $exclude = []; + foreach ($element->getElementsByTagName('exclude') as $excludeNode) { + $excludeFile = (string) $excludeNode->textContent; + if ($excludeFile) { + $exclude[] = new \PHPUnit\TextUI\XmlConfiguration\File($this->toAbsolutePath($filename, $excludeFile)); + } + } + $directories = []; + foreach ($element->getElementsByTagName('directory') as $directoryNode) { + assert($directoryNode instanceof DOMElement); + $directory = (string) $directoryNode->textContent; + if (empty($directory)) { + continue; + } + $prefix = ''; + if ($directoryNode->hasAttribute('prefix')) { + $prefix = (string) $directoryNode->getAttribute('prefix'); + } + $suffix = 'Test.php'; + if ($directoryNode->hasAttribute('suffix')) { + $suffix = (string) $directoryNode->getAttribute('suffix'); + } + $phpVersion = \PHP_VERSION; + if ($directoryNode->hasAttribute('phpVersion')) { + $phpVersion = (string) $directoryNode->getAttribute('phpVersion'); + } + $phpVersionOperator = new VersionComparisonOperator('>='); + if ($directoryNode->hasAttribute('phpVersionOperator')) { + $phpVersionOperator = new VersionComparisonOperator((string) $directoryNode->getAttribute('phpVersionOperator')); + } + $directories[] = new \PHPUnit\TextUI\XmlConfiguration\TestDirectory($this->toAbsolutePath($filename, $directory), $prefix, $suffix, $phpVersion, $phpVersionOperator); + } + $files = []; + foreach ($element->getElementsByTagName('file') as $fileNode) { + assert($fileNode instanceof DOMElement); + $file = (string) $fileNode->textContent; + if (empty($file)) { + continue; + } + $phpVersion = \PHP_VERSION; + if ($fileNode->hasAttribute('phpVersion')) { + $phpVersion = (string) $fileNode->getAttribute('phpVersion'); + } + $phpVersionOperator = new VersionComparisonOperator('>='); + if ($fileNode->hasAttribute('phpVersionOperator')) { + $phpVersionOperator = new VersionComparisonOperator((string) $fileNode->getAttribute('phpVersionOperator')); + } + $files[] = new \PHPUnit\TextUI\XmlConfiguration\TestFile($this->toAbsolutePath($filename, $file), $phpVersion, $phpVersionOperator); + } + $testSuites[] = new TestSuiteConfiguration((string) $element->getAttribute('name'), \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection::fromArray($directories), \PHPUnit\TextUI\XmlConfiguration\TestFileCollection::fromArray($files), \PHPUnit\TextUI\XmlConfiguration\FileCollection::fromArray($exclude)); } - $this->addTest($test, \PHPUnit\Util\Test::getGroups($class->getName(), $methodName)); + return \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection::fromArray($testSuites); } - private function clearCaches() : void + /** + * @return DOMElement[] + */ + private function getTestSuiteElements(DOMXPath $xpath) : array { - $this->numTests = -1; - $this->providedTests = null; - $this->requiredTests = null; + /** @var DOMElement[] $elements */ + $elements = []; + $testSuiteNodes = $xpath->query('testsuites/testsuite'); + if ($testSuiteNodes->length === 0) { + $testSuiteNodes = $xpath->query('testsuite'); + } + if ($testSuiteNodes->length === 1) { + $element = $testSuiteNodes->item(0); + assert($element instanceof DOMElement); + $elements[] = $element; + } else { + foreach ($testSuiteNodes as $testSuiteNode) { + assert($testSuiteNode instanceof DOMElement); + $elements[] = $testSuiteNode; + } + } + return $elements; } - private function containsOnlyVirtualGroups(array $groups) : bool + private function element(DOMXPath $xpath, string $element) : ?DOMElement { - foreach ($groups as $group) { - if (\strpos($group, '__phpunit_') !== 0) { - return \false; - } + $nodes = $xpath->query($element); + if ($nodes->length === 1) { + $node = $nodes->item(0); + assert($node instanceof DOMElement); + return $node; } - return \true; + return null; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; +namespace PHPUnit\TextUI\XmlConfiguration; -use const PHP_EOL; -use function count; -use function function_exists; -use function get_class; -use function sprintf; -use AssertionError; -use Countable; -use Error; -use PHPUnit\Framework\MockObject\Exception as MockObjectException; -use PHPUnit\Util\ErrorHandler; -use PHPUnit\Util\ExcludeList; -use PHPUnit\Util\Printer; -use PHPUnit\Util\Test as TestUtil; -use ReflectionClass; -use ReflectionException; -use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; -use PHPUnit\SebastianBergmann\CodeCoverage\Exception as OriginalCodeCoverageException; -use PHPUnit\SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException; -use PHPUnit\SebastianBergmann\Invoker\Invoker; -use PHPUnit\SebastianBergmann\Invoker\TimeoutException; -use PHPUnit\SebastianBergmann\ResourceOperations\ResourceOperations; -use PHPUnit\SebastianBergmann\Timer\Timer; -use Throwable; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage; +use PHPUnit\TextUI\XmlConfiguration\Logging\Logging; +use PHPUnit\Util\Xml\ValidationResult; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable */ -final class TestResult implements \Countable +final class Configuration { /** - * @var array - */ - private $passed = []; - /** - * @var array - */ - private $passedTestClasses = []; - /** - * @var bool - */ - private $currentTestSuiteFailed = \false; - /** - * @var TestFailure[] - */ - private $errors = []; - /** - * @var TestFailure[] + * @var string */ - private $failures = []; + private $filename; /** - * @var TestFailure[] + * @var ValidationResult */ - private $warnings = []; + private $validationResult; /** - * @var TestFailure[] + * @var ExtensionCollection */ - private $notImplemented = []; + private $extensions; /** - * @var TestFailure[] + * @var CodeCoverage */ - private $risky = []; + private $codeCoverage; /** - * @var TestFailure[] + * @var Groups */ - private $skipped = []; + private $groups; /** - * @deprecated Use the `TestHook` interfaces instead - * - * @var TestListener[] + * @var Groups */ - private $listeners = []; + private $testdoxGroups; /** - * @var int + * @var ExtensionCollection */ - private $runTests = 0; + private $listeners; /** - * @var float + * @var Logging */ - private $time = 0; + private $logging; /** - * @var TestSuite + * @var Php */ - private $topTestSuite; + private $php; /** - * Code Coverage information. - * - * @var CodeCoverage + * @var PHPUnit */ - private $codeCoverage; + private $phpunit; /** - * @var bool + * @var TestSuiteCollection */ - private $convertDeprecationsToExceptions = \true; + private $testSuite; + public function __construct(string $filename, ValidationResult $validationResult, \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection $extensions, CodeCoverage $codeCoverage, \PHPUnit\TextUI\XmlConfiguration\Groups $groups, \PHPUnit\TextUI\XmlConfiguration\Groups $testdoxGroups, \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection $listeners, Logging $logging, \PHPUnit\TextUI\XmlConfiguration\Php $php, \PHPUnit\TextUI\XmlConfiguration\PHPUnit $phpunit, \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection $testSuite) + { + $this->filename = $filename; + $this->validationResult = $validationResult; + $this->extensions = $extensions; + $this->codeCoverage = $codeCoverage; + $this->groups = $groups; + $this->testdoxGroups = $testdoxGroups; + $this->listeners = $listeners; + $this->logging = $logging; + $this->php = $php; + $this->phpunit = $phpunit; + $this->testSuite = $testSuite; + } + public function filename() : string + { + return $this->filename; + } + public function hasValidationErrors() : bool + { + return $this->validationResult->hasValidationErrors(); + } + public function validationErrors() : string + { + return $this->validationResult->asString(); + } + public function extensions() : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection + { + return $this->extensions; + } + public function codeCoverage() : CodeCoverage + { + return $this->codeCoverage; + } + public function groups() : \PHPUnit\TextUI\XmlConfiguration\Groups + { + return $this->groups; + } + public function testdoxGroups() : \PHPUnit\TextUI\XmlConfiguration\Groups + { + return $this->testdoxGroups; + } + public function listeners() : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollection + { + return $this->listeners; + } + public function logging() : Logging + { + return $this->logging; + } + public function php() : \PHPUnit\TextUI\XmlConfiguration\Php + { + return $this->php; + } + public function phpunit() : \PHPUnit\TextUI\XmlConfiguration\PHPUnit + { + return $this->phpunit; + } + public function testSuite() : \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection + { + return $this->testSuite; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class PHPUnit +{ /** * @var bool */ - private $convertErrorsToExceptions = \true; + private $cacheResult; /** - * @var bool + * @var ?string */ - private $convertNoticesToExceptions = \true; + private $cacheResultFile; /** - * @var bool + * @var int|string */ - private $convertWarningsToExceptions = \true; + private $columns; /** - * @var bool + * @var string */ - private $stop = \false; + private $colors; /** * @var bool */ - private $stopOnError = \false; + private $stderr; /** * @var bool */ - private $stopOnFailure = \false; + private $noInteraction; /** * @var bool */ - private $stopOnWarning = \false; + private $verbose; /** * @var bool */ - private $beStrictAboutTestsThatDoNotTestAnything = \true; + private $reverseDefectList; /** * @var bool */ - private $beStrictAboutOutputDuringTests = \false; + private $convertDeprecationsToExceptions; /** * @var bool */ - private $beStrictAboutTodoAnnotatedTests = \false; + private $convertErrorsToExceptions; /** * @var bool */ - private $beStrictAboutResourceUsageDuringSmallTests = \false; + private $convertNoticesToExceptions; /** * @var bool */ - private $enforceTimeLimit = \false; + private $convertWarningsToExceptions; /** * @var bool */ - private $forceCoversAnnotation = \false; - /** - * @var int - */ - private $timeoutForSmallTests = 1; - /** - * @var int - */ - private $timeoutForMediumTests = 10; + private $forceCoversAnnotation; /** - * @var int + * @var ?string */ - private $timeoutForLargeTests = 60; + private $bootstrap; /** * @var bool */ - private $stopOnRisky = \false; + private $processIsolation; /** * @var bool */ - private $stopOnIncomplete = \false; + private $failOnEmptyTestSuite; /** * @var bool */ - private $stopOnSkipped = \false; + private $failOnIncomplete; /** * @var bool */ - private $lastTestFailed = \false; + private $failOnRisky; /** - * @var int + * @var bool */ - private $defaultTimeLimit = 0; + private $failOnSkipped; /** * @var bool */ - private $stopOnDefect = \false; + private $failOnWarning; /** * @var bool */ - private $registerMockObjectsFromTestArgumentsRecursively = \false; + private $stopOnDefect; /** - * @deprecated Use the `TestHook` interfaces instead - * - * @codeCoverageIgnore - * - * Registers a TestListener. + * @var bool */ - public function addListener(\PHPUnit\Framework\TestListener $listener) : void - { - $this->listeners[] = $listener; - } + private $stopOnError; /** - * @deprecated Use the `TestHook` interfaces instead - * - * @codeCoverageIgnore - * - * Unregisters a TestListener. + * @var bool */ - public function removeListener(\PHPUnit\Framework\TestListener $listener) : void - { - foreach ($this->listeners as $key => $_listener) { - if ($listener === $_listener) { - unset($this->listeners[$key]); - } - } - } + private $stopOnFailure; /** - * @deprecated Use the `TestHook` interfaces instead - * - * @codeCoverageIgnore - * - * Flushes all flushable TestListeners. + * @var bool */ - public function flushListeners() : void - { - foreach ($this->listeners as $listener) { - if ($listener instanceof \PHPUnit\Util\Printer) { - $listener->flush(); - } - } - } + private $stopOnWarning; /** - * Adds an error to the list of errors. + * @var bool */ - public function addError(\PHPUnit\Framework\Test $test, \Throwable $t, float $time) : void - { - if ($t instanceof \PHPUnit\Framework\RiskyTestError) { - $this->risky[] = new \PHPUnit\Framework\TestFailure($test, $t); - $notifyMethod = 'addRiskyTest'; - if ($test instanceof \PHPUnit\Framework\TestCase) { - $test->markAsRisky(); - } - if ($this->stopOnRisky || $this->stopOnDefect) { - $this->stop(); - } - } elseif ($t instanceof \PHPUnit\Framework\IncompleteTest) { - $this->notImplemented[] = new \PHPUnit\Framework\TestFailure($test, $t); - $notifyMethod = 'addIncompleteTest'; - if ($this->stopOnIncomplete) { - $this->stop(); - } - } elseif ($t instanceof \PHPUnit\Framework\SkippedTest) { - $this->skipped[] = new \PHPUnit\Framework\TestFailure($test, $t); - $notifyMethod = 'addSkippedTest'; - if ($this->stopOnSkipped) { - $this->stop(); - } - } else { - $this->errors[] = new \PHPUnit\Framework\TestFailure($test, $t); - $notifyMethod = 'addError'; - if ($this->stopOnError || $this->stopOnFailure) { - $this->stop(); - } - } - // @see https://github.com/sebastianbergmann/phpunit/issues/1953 - if ($t instanceof \Error) { - $t = new \PHPUnit\Framework\ExceptionWrapper($t); - } - foreach ($this->listeners as $listener) { - $listener->{$notifyMethod}($test, $t, $time); - } - $this->lastTestFailed = \true; - $this->time += $time; - } + private $stopOnIncomplete; /** - * Adds a warning to the list of warnings. - * The passed in exception caused the warning. + * @var bool */ - public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time) : void - { - if ($this->stopOnWarning || $this->stopOnDefect) { - $this->stop(); - } - $this->warnings[] = new \PHPUnit\Framework\TestFailure($test, $e); - foreach ($this->listeners as $listener) { - $listener->addWarning($test, $e, $time); - } - $this->time += $time; - } + private $stopOnRisky; /** - * Adds a failure to the list of failures. - * The passed in exception caused the failure. + * @var bool */ - public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time) : void - { - if ($e instanceof \PHPUnit\Framework\RiskyTestError || $e instanceof \PHPUnit\Framework\OutputError) { - $this->risky[] = new \PHPUnit\Framework\TestFailure($test, $e); - $notifyMethod = 'addRiskyTest'; - if ($test instanceof \PHPUnit\Framework\TestCase) { - $test->markAsRisky(); - } - if ($this->stopOnRisky || $this->stopOnDefect) { - $this->stop(); - } - } elseif ($e instanceof \PHPUnit\Framework\IncompleteTest) { - $this->notImplemented[] = new \PHPUnit\Framework\TestFailure($test, $e); - $notifyMethod = 'addIncompleteTest'; - if ($this->stopOnIncomplete) { - $this->stop(); - } - } elseif ($e instanceof \PHPUnit\Framework\SkippedTest) { - $this->skipped[] = new \PHPUnit\Framework\TestFailure($test, $e); - $notifyMethod = 'addSkippedTest'; - if ($this->stopOnSkipped) { - $this->stop(); - } - } else { - $this->failures[] = new \PHPUnit\Framework\TestFailure($test, $e); - $notifyMethod = 'addFailure'; - if ($this->stopOnFailure || $this->stopOnDefect) { - $this->stop(); - } - } - foreach ($this->listeners as $listener) { - $listener->{$notifyMethod}($test, $e, $time); - } - $this->lastTestFailed = \true; - $this->time += $time; - } + private $stopOnSkipped; /** - * Informs the result that a test suite will be started. + * @var ?string */ - public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) : void - { - $this->currentTestSuiteFailed = \false; - if ($this->topTestSuite === null) { - $this->topTestSuite = $suite; - } - foreach ($this->listeners as $listener) { - $listener->startTestSuite($suite); - } - } + private $extensionsDirectory; /** - * Informs the result that a test suite was completed. + * @var ?string + * + * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 */ - public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) : void - { - if (!$this->currentTestSuiteFailed) { - $this->passedTestClasses[] = $suite->getName(); - } - foreach ($this->listeners as $listener) { - $listener->endTestSuite($suite); - } - } + private $testSuiteLoaderClass; /** - * Informs the result that a test will be started. + * @var ?string + * + * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 */ - public function startTest(\PHPUnit\Framework\Test $test) : void - { - $this->lastTestFailed = \false; - $this->runTests += \count($test); - foreach ($this->listeners as $listener) { - $listener->startTest($test); - } - } + private $testSuiteLoaderFile; /** - * Informs the result that a test was completed. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var ?string */ - public function endTest(\PHPUnit\Framework\Test $test, float $time) : void - { - foreach ($this->listeners as $listener) { - $listener->endTest($test, $time); - } - if (!$this->lastTestFailed && $test instanceof \PHPUnit\Framework\TestCase) { - $class = \get_class($test); - $key = $class . '::' . $test->getName(); - $this->passed[$key] = ['result' => $test->getResult(), 'size' => \PHPUnit\Util\Test::getSize($class, $test->getName(\false))]; - $this->time += $time; - } - if ($this->lastTestFailed && $test instanceof \PHPUnit\Framework\TestCase) { - $this->currentTestSuiteFailed = \true; - } - } + private $printerClass; /** - * Returns true if no risky test occurred. + * @var ?string */ - public function allHarmless() : bool - { - return $this->riskyCount() === 0; - } + private $printerFile; /** - * Gets the number of risky tests. + * @var bool */ - public function riskyCount() : int - { - return \count($this->risky); - } + private $beStrictAboutChangesToGlobalState; /** - * Returns true if no incomplete test occurred. + * @var bool */ - public function allCompletelyImplemented() : bool - { - return $this->notImplementedCount() === 0; - } + private $beStrictAboutOutputDuringTests; /** - * Gets the number of incomplete tests. + * @var bool */ - public function notImplementedCount() : int - { - return \count($this->notImplemented); - } + private $beStrictAboutResourceUsageDuringSmallTests; /** - * Returns an array of TestFailure objects for the risky tests. - * - * @return TestFailure[] + * @var bool */ - public function risky() : array - { - return $this->risky; - } + private $beStrictAboutTestsThatDoNotTestAnything; /** - * Returns an array of TestFailure objects for the incomplete tests. - * - * @return TestFailure[] + * @var bool */ - public function notImplemented() : array - { - return $this->notImplemented; - } + private $beStrictAboutTodoAnnotatedTests; /** - * Returns true if no test has been skipped. + * @var bool */ - public function noneSkipped() : bool - { - return $this->skippedCount() === 0; - } + private $beStrictAboutCoversAnnotation; /** - * Gets the number of skipped tests. + * @var bool */ - public function skippedCount() : int - { - return \count($this->skipped); - } + private $enforceTimeLimit; /** - * Returns an array of TestFailure objects for the skipped tests. - * - * @return TestFailure[] + * @var int */ - public function skipped() : array - { - return $this->skipped; - } + private $defaultTimeLimit; /** - * Gets the number of detected errors. + * @var int */ - public function errorCount() : int - { - return \count($this->errors); - } + private $timeoutForSmallTests; /** - * Returns an array of TestFailure objects for the errors. - * - * @return TestFailure[] + * @var int */ - public function errors() : array - { - return $this->errors; - } + private $timeoutForMediumTests; /** - * Gets the number of detected failures. + * @var int */ - public function failureCount() : int - { - return \count($this->failures); - } + private $timeoutForLargeTests; /** - * Returns an array of TestFailure objects for the failures. - * - * @return TestFailure[] + * @var ?string */ - public function failures() : array - { - return $this->failures; - } + private $defaultTestSuite; /** - * Gets the number of detected warnings. + * @var int */ - public function warningCount() : int - { - return \count($this->warnings); - } + private $executionOrder; /** - * Returns an array of TestFailure objects for the warnings. - * - * @return TestFailure[] + * @var bool */ - public function warnings() : array - { - return $this->warnings; - } + private $resolveDependencies; /** - * Returns the names of the tests that have passed. + * @var bool */ - public function passed() : array - { - return $this->passed; - } + private $defectsFirst; /** - * Returns the names of the TestSuites that have passed. - * - * This enables @depends-annotations for TestClassName::class + * @var bool */ - public function passedClasses() : array - { - return $this->passedTestClasses; - } + private $backupGlobals; /** - * Returns the (top) test suite. + * @var bool */ - public function topTestSuite() : \PHPUnit\Framework\TestSuite - { - return $this->topTestSuite; - } + private $backupStaticAttributes; /** - * Returns whether code coverage information should be collected. + * @var bool */ - public function getCollectCodeCoverageInformation() : bool - { - return $this->codeCoverage !== null; - } + private $registerMockObjectsFromTestArgumentsRecursively; /** - * Runs a TestCase. - * - * @throws CodeCoverageException - * @throws UnintentionallyCoveredCodeException - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var bool */ - public function run(\PHPUnit\Framework\Test $test) : void + private $conflictBetweenPrinterClassAndTestdox; + public function __construct(bool $cacheResult, ?string $cacheResultFile, $columns, string $colors, bool $stderr, bool $noInteraction, bool $verbose, bool $reverseDefectList, bool $convertDeprecationsToExceptions, bool $convertErrorsToExceptions, bool $convertNoticesToExceptions, bool $convertWarningsToExceptions, bool $forceCoversAnnotation, ?string $bootstrap, bool $processIsolation, bool $failOnEmptyTestSuite, bool $failOnIncomplete, bool $failOnRisky, bool $failOnSkipped, bool $failOnWarning, bool $stopOnDefect, bool $stopOnError, bool $stopOnFailure, bool $stopOnWarning, bool $stopOnIncomplete, bool $stopOnRisky, bool $stopOnSkipped, ?string $extensionsDirectory, ?string $testSuiteLoaderClass, ?string $testSuiteLoaderFile, ?string $printerClass, ?string $printerFile, bool $beStrictAboutChangesToGlobalState, bool $beStrictAboutOutputDuringTests, bool $beStrictAboutResourceUsageDuringSmallTests, bool $beStrictAboutTestsThatDoNotTestAnything, bool $beStrictAboutTodoAnnotatedTests, bool $beStrictAboutCoversAnnotation, bool $enforceTimeLimit, int $defaultTimeLimit, int $timeoutForSmallTests, int $timeoutForMediumTests, int $timeoutForLargeTests, ?string $defaultTestSuite, int $executionOrder, bool $resolveDependencies, bool $defectsFirst, bool $backupGlobals, bool $backupStaticAttributes, bool $registerMockObjectsFromTestArgumentsRecursively, bool $conflictBetweenPrinterClassAndTestdox) { - \PHPUnit\Framework\Assert::resetCount(); - if ($test instanceof \PHPUnit\Framework\TestCase) { - $test->setRegisterMockObjectsFromTestArgumentsRecursively($this->registerMockObjectsFromTestArgumentsRecursively); - $isAnyCoverageRequired = \PHPUnit\Util\Test::requiresCodeCoverageDataCollection($test); - } - $error = \false; - $failure = \false; - $warning = \false; - $incomplete = \false; - $risky = \false; - $skipped = \false; - $this->startTest($test); - if ($this->convertDeprecationsToExceptions || $this->convertErrorsToExceptions || $this->convertNoticesToExceptions || $this->convertWarningsToExceptions) { - $errorHandler = new \PHPUnit\Util\ErrorHandler($this->convertDeprecationsToExceptions, $this->convertErrorsToExceptions, $this->convertNoticesToExceptions, $this->convertWarningsToExceptions); - $errorHandler->register(); - } - $collectCodeCoverage = $this->codeCoverage !== null && !$test instanceof \PHPUnit\Framework\WarningTestCase && $isAnyCoverageRequired; - if ($collectCodeCoverage) { - $this->codeCoverage->start($test); - } - $monitorFunctions = $this->beStrictAboutResourceUsageDuringSmallTests && !$test instanceof \PHPUnit\Framework\WarningTestCase && $test->getSize() === \PHPUnit\Util\Test::SMALL && \function_exists('xdebug_start_function_monitor'); - if ($monitorFunctions) { - /* @noinspection ForgottenDebugOutputInspection */ - \xdebug_start_function_monitor(\PHPUnit\SebastianBergmann\ResourceOperations\ResourceOperations::getFunctions()); - } - $timer = new \PHPUnit\SebastianBergmann\Timer\Timer(); - $timer->start(); - try { - $invoker = new \PHPUnit\SebastianBergmann\Invoker\Invoker(); - if (!$test instanceof \PHPUnit\Framework\WarningTestCase && $this->enforceTimeLimit && ($this->defaultTimeLimit || $test->getSize() != \PHPUnit\Util\Test::UNKNOWN) && $invoker->canInvokeWithTimeout()) { - switch ($test->getSize()) { - case \PHPUnit\Util\Test::SMALL: - $_timeout = $this->timeoutForSmallTests; - break; - case \PHPUnit\Util\Test::MEDIUM: - $_timeout = $this->timeoutForMediumTests; - break; - case \PHPUnit\Util\Test::LARGE: - $_timeout = $this->timeoutForLargeTests; - break; - case \PHPUnit\Util\Test::UNKNOWN: - $_timeout = $this->defaultTimeLimit; - break; - } - $invoker->invoke([$test, 'runBare'], [], $_timeout); - } else { - $test->runBare(); - } - } catch (\PHPUnit\SebastianBergmann\Invoker\TimeoutException $e) { - $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError($e->getMessage()), $_timeout); - $risky = \true; - } catch (\PHPUnit\Framework\MockObject\Exception $e) { - $e = new \PHPUnit\Framework\Warning($e->getMessage()); - $warning = \true; - } catch (\PHPUnit\Framework\AssertionFailedError $e) { - $failure = \true; - if ($e instanceof \PHPUnit\Framework\RiskyTestError) { - $risky = \true; - } elseif ($e instanceof \PHPUnit\Framework\IncompleteTestError) { - $incomplete = \true; - } elseif ($e instanceof \PHPUnit\Framework\SkippedTestError) { - $skipped = \true; - } - } catch (\AssertionError $e) { - $test->addToAssertionCount(1); - $failure = \true; - $frame = $e->getTrace()[0]; - $e = new \PHPUnit\Framework\AssertionFailedError(\sprintf('%s in %s:%s', $e->getMessage(), $frame['file'], $frame['line'])); - } catch (\PHPUnit\Framework\Warning $e) { - $warning = \true; - } catch (\PHPUnit\Framework\Exception $e) { - $error = \true; - } catch (\Throwable $e) { - $e = new \PHPUnit\Framework\ExceptionWrapper($e); - $error = \true; - } - $time = $timer->stop()->asSeconds(); - $test->addToAssertionCount(\PHPUnit\Framework\Assert::getCount()); - if ($monitorFunctions) { - $excludeList = new \PHPUnit\Util\ExcludeList(); - /** @noinspection ForgottenDebugOutputInspection */ - $functions = \xdebug_get_monitored_functions(); - /* @noinspection ForgottenDebugOutputInspection */ - \xdebug_stop_function_monitor(); - foreach ($functions as $function) { - if (!$excludeList->isExcluded($function['filename'])) { - $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError(\sprintf('%s() used in %s:%s', $function['function'], $function['filename'], $function['lineno'])), $time); - } - } - } - if ($this->beStrictAboutTestsThatDoNotTestAnything && $test->getNumAssertions() === 0) { - $risky = \true; - } - if ($this->forceCoversAnnotation && !$error && !$failure && !$warning && !$incomplete && !$skipped && !$risky) { - $annotations = $test->getAnnotations(); - if (!isset($annotations['class']['covers']) && !isset($annotations['method']['covers']) && !isset($annotations['class']['coversNothing']) && !isset($annotations['method']['coversNothing'])) { - $this->addFailure($test, new \PHPUnit\Framework\MissingCoversAnnotationException('This test does not have a @covers annotation but is expected to have one'), $time); - $risky = \true; - } - } - if ($collectCodeCoverage) { - $append = !$risky && !$incomplete && !$skipped; - $linesToBeCovered = []; - $linesToBeUsed = []; - if ($append && $test instanceof \PHPUnit\Framework\TestCase) { - try { - $linesToBeCovered = \PHPUnit\Util\Test::getLinesToBeCovered(\get_class($test), $test->getName(\false)); - $linesToBeUsed = \PHPUnit\Util\Test::getLinesToBeUsed(\get_class($test), $test->getName(\false)); - } catch (\PHPUnit\Framework\InvalidCoversTargetException $cce) { - $this->addWarning($test, new \PHPUnit\Framework\Warning($cce->getMessage()), $time); - } - } - try { - $this->codeCoverage->stop($append, $linesToBeCovered, $linesToBeUsed); - } catch (\PHPUnit\SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException $cce) { - $unintentionallyCoveredCodeError = new \PHPUnit\Framework\UnintentionallyCoveredCodeError('This test executed code that is not listed as code to be covered or used:' . \PHP_EOL . $cce->getMessage()); - } catch (\PHPUnit\SebastianBergmann\CodeCoverage\Exception $cce) { - $error = \true; - $e = $e ?? $cce; - } - } - if (isset($errorHandler)) { - $errorHandler->unregister(); - unset($errorHandler); - } - if ($error) { - $this->addError($test, $e, $time); - } elseif ($failure) { - $this->addFailure($test, $e, $time); - } elseif ($warning) { - $this->addWarning($test, $e, $time); - } elseif (isset($unintentionallyCoveredCodeError)) { - $this->addFailure($test, $unintentionallyCoveredCodeError, $time); - } elseif ($this->beStrictAboutTestsThatDoNotTestAnything && !$test->doesNotPerformAssertions() && $test->getNumAssertions() === 0) { - try { - $reflected = new \ReflectionClass($test); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $name = $test->getName(\false); - if ($name && $reflected->hasMethod($name)) { - try { - $reflected = $reflected->getMethod($name); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } - $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError(\sprintf("This test did not perform any assertions\n\n%s:%d", $reflected->getFileName(), $reflected->getStartLine())), $time); - } elseif ($this->beStrictAboutTestsThatDoNotTestAnything && $test->doesNotPerformAssertions() && $test->getNumAssertions() > 0) { - $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError(\sprintf('This test is annotated with "@doesNotPerformAssertions" but performed %d assertions', $test->getNumAssertions())), $time); - } elseif ($this->beStrictAboutOutputDuringTests && $test->hasOutput()) { - $this->addFailure($test, new \PHPUnit\Framework\OutputError(\sprintf('This test printed output: %s', $test->getActualOutput())), $time); - } elseif ($this->beStrictAboutTodoAnnotatedTests && $test instanceof \PHPUnit\Framework\TestCase) { - $annotations = $test->getAnnotations(); - if (isset($annotations['method']['todo'])) { - $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError('Test method is annotated with @todo'), $time); - } - } - $this->endTest($test, $time); + $this->cacheResult = $cacheResult; + $this->cacheResultFile = $cacheResultFile; + $this->columns = $columns; + $this->colors = $colors; + $this->stderr = $stderr; + $this->noInteraction = $noInteraction; + $this->verbose = $verbose; + $this->reverseDefectList = $reverseDefectList; + $this->convertDeprecationsToExceptions = $convertDeprecationsToExceptions; + $this->convertErrorsToExceptions = $convertErrorsToExceptions; + $this->convertNoticesToExceptions = $convertNoticesToExceptions; + $this->convertWarningsToExceptions = $convertWarningsToExceptions; + $this->forceCoversAnnotation = $forceCoversAnnotation; + $this->bootstrap = $bootstrap; + $this->processIsolation = $processIsolation; + $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; + $this->failOnIncomplete = $failOnIncomplete; + $this->failOnRisky = $failOnRisky; + $this->failOnSkipped = $failOnSkipped; + $this->failOnWarning = $failOnWarning; + $this->stopOnDefect = $stopOnDefect; + $this->stopOnError = $stopOnError; + $this->stopOnFailure = $stopOnFailure; + $this->stopOnWarning = $stopOnWarning; + $this->stopOnIncomplete = $stopOnIncomplete; + $this->stopOnRisky = $stopOnRisky; + $this->stopOnSkipped = $stopOnSkipped; + $this->extensionsDirectory = $extensionsDirectory; + $this->testSuiteLoaderClass = $testSuiteLoaderClass; + $this->testSuiteLoaderFile = $testSuiteLoaderFile; + $this->printerClass = $printerClass; + $this->printerFile = $printerFile; + $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; + $this->beStrictAboutOutputDuringTests = $beStrictAboutOutputDuringTests; + $this->beStrictAboutResourceUsageDuringSmallTests = $beStrictAboutResourceUsageDuringSmallTests; + $this->beStrictAboutTestsThatDoNotTestAnything = $beStrictAboutTestsThatDoNotTestAnything; + $this->beStrictAboutTodoAnnotatedTests = $beStrictAboutTodoAnnotatedTests; + $this->beStrictAboutCoversAnnotation = $beStrictAboutCoversAnnotation; + $this->enforceTimeLimit = $enforceTimeLimit; + $this->defaultTimeLimit = $defaultTimeLimit; + $this->timeoutForSmallTests = $timeoutForSmallTests; + $this->timeoutForMediumTests = $timeoutForMediumTests; + $this->timeoutForLargeTests = $timeoutForLargeTests; + $this->defaultTestSuite = $defaultTestSuite; + $this->executionOrder = $executionOrder; + $this->resolveDependencies = $resolveDependencies; + $this->defectsFirst = $defectsFirst; + $this->backupGlobals = $backupGlobals; + $this->backupStaticAttributes = $backupStaticAttributes; + $this->registerMockObjectsFromTestArgumentsRecursively = $registerMockObjectsFromTestArgumentsRecursively; + $this->conflictBetweenPrinterClassAndTestdox = $conflictBetweenPrinterClassAndTestdox; } - /** - * Gets the number of run tests. - */ - public function count() : int + public function cacheResult() : bool { - return $this->runTests; + return $this->cacheResult; } /** - * Checks whether the test run should stop. + * @psalm-assert-if-true !null $this->cacheResultFile */ - public function shouldStop() : bool + public function hasCacheResultFile() : bool { - return $this->stop; + return $this->cacheResultFile !== null; } /** - * Marks that the test run should stop. + * @throws Exception */ - public function stop() : void + public function cacheResultFile() : string { - $this->stop = \true; + if (!$this->hasCacheResultFile()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Cache result file is not configured'); + } + return (string) $this->cacheResultFile; } - /** - * Returns the code coverage object. - */ - public function getCodeCoverage() : ?\PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage + public function columns() { - return $this->codeCoverage; + return $this->columns; } - /** - * Sets the code coverage object. - */ - public function setCodeCoverage(\PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage $codeCoverage) : void + public function colors() : string { - $this->codeCoverage = $codeCoverage; + return $this->colors; } - /** - * Enables or disables the deprecation-to-exception conversion. - */ - public function convertDeprecationsToExceptions(bool $flag) : void + public function stderr() : bool { - $this->convertDeprecationsToExceptions = $flag; + return $this->stderr; } - /** - * Returns the deprecation-to-exception conversion setting. - */ - public function getConvertDeprecationsToExceptions() : bool + public function noInteraction() : bool { - return $this->convertDeprecationsToExceptions; + return $this->noInteraction; } - /** - * Enables or disables the error-to-exception conversion. - */ - public function convertErrorsToExceptions(bool $flag) : void + public function verbose() : bool { - $this->convertErrorsToExceptions = $flag; + return $this->verbose; } - /** - * Returns the error-to-exception conversion setting. - */ - public function getConvertErrorsToExceptions() : bool + public function reverseDefectList() : bool { - return $this->convertErrorsToExceptions; + return $this->reverseDefectList; } - /** - * Enables or disables the notice-to-exception conversion. - */ - public function convertNoticesToExceptions(bool $flag) : void + public function convertDeprecationsToExceptions() : bool { - $this->convertNoticesToExceptions = $flag; + return $this->convertDeprecationsToExceptions; } - /** - * Returns the notice-to-exception conversion setting. - */ - public function getConvertNoticesToExceptions() : bool + public function convertErrorsToExceptions() : bool { - return $this->convertNoticesToExceptions; + return $this->convertErrorsToExceptions; } - /** - * Enables or disables the warning-to-exception conversion. - */ - public function convertWarningsToExceptions(bool $flag) : void + public function convertNoticesToExceptions() : bool { - $this->convertWarningsToExceptions = $flag; + return $this->convertNoticesToExceptions; } - /** - * Returns the warning-to-exception conversion setting. - */ - public function getConvertWarningsToExceptions() : bool + public function convertWarningsToExceptions() : bool { return $this->convertWarningsToExceptions; } - /** - * Enables or disables the stopping when an error occurs. - */ - public function stopOnError(bool $flag) : void + public function forceCoversAnnotation() : bool { - $this->stopOnError = $flag; + return $this->forceCoversAnnotation; } /** - * Enables or disables the stopping when a failure occurs. + * @psalm-assert-if-true !null $this->bootstrap */ - public function stopOnFailure(bool $flag) : void + public function hasBootstrap() : bool { - $this->stopOnFailure = $flag; + return $this->bootstrap !== null; } /** - * Enables or disables the stopping when a warning occurs. + * @throws Exception */ - public function stopOnWarning(bool $flag) : void + public function bootstrap() : string { - $this->stopOnWarning = $flag; + if (!$this->hasBootstrap()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Bootstrap script is not configured'); + } + return (string) $this->bootstrap; } - public function beStrictAboutTestsThatDoNotTestAnything(bool $flag) : void + public function processIsolation() : bool { - $this->beStrictAboutTestsThatDoNotTestAnything = $flag; + return $this->processIsolation; } - public function isStrictAboutTestsThatDoNotTestAnything() : bool + public function failOnEmptyTestSuite() : bool { - return $this->beStrictAboutTestsThatDoNotTestAnything; + return $this->failOnEmptyTestSuite; } - public function beStrictAboutOutputDuringTests(bool $flag) : void + public function failOnIncomplete() : bool { - $this->beStrictAboutOutputDuringTests = $flag; + return $this->failOnIncomplete; } - public function isStrictAboutOutputDuringTests() : bool + public function failOnRisky() : bool { - return $this->beStrictAboutOutputDuringTests; + return $this->failOnRisky; } - public function beStrictAboutResourceUsageDuringSmallTests(bool $flag) : void + public function failOnSkipped() : bool { - $this->beStrictAboutResourceUsageDuringSmallTests = $flag; + return $this->failOnSkipped; } - public function isStrictAboutResourceUsageDuringSmallTests() : bool + public function failOnWarning() : bool { - return $this->beStrictAboutResourceUsageDuringSmallTests; + return $this->failOnWarning; } - public function enforceTimeLimit(bool $flag) : void + public function stopOnDefect() : bool { - $this->enforceTimeLimit = $flag; + return $this->stopOnDefect; } - public function enforcesTimeLimit() : bool + public function stopOnError() : bool { - return $this->enforceTimeLimit; + return $this->stopOnError; } - public function beStrictAboutTodoAnnotatedTests(bool $flag) : void + public function stopOnFailure() : bool { - $this->beStrictAboutTodoAnnotatedTests = $flag; + return $this->stopOnFailure; } - public function isStrictAboutTodoAnnotatedTests() : bool + public function stopOnWarning() : bool { - return $this->beStrictAboutTodoAnnotatedTests; + return $this->stopOnWarning; } - public function forceCoversAnnotation() : void + public function stopOnIncomplete() : bool { - $this->forceCoversAnnotation = \true; + return $this->stopOnIncomplete; } - public function forcesCoversAnnotation() : bool + public function stopOnRisky() : bool { - return $this->forceCoversAnnotation; + return $this->stopOnRisky; } - /** - * Enables or disables the stopping for risky tests. - */ - public function stopOnRisky(bool $flag) : void + public function stopOnSkipped() : bool { - $this->stopOnRisky = $flag; + return $this->stopOnSkipped; } /** - * Enables or disables the stopping for incomplete tests. + * @psalm-assert-if-true !null $this->extensionsDirectory */ - public function stopOnIncomplete(bool $flag) : void + public function hasExtensionsDirectory() : bool { - $this->stopOnIncomplete = $flag; + return $this->extensionsDirectory !== null; } /** - * Enables or disables the stopping for skipped tests. + * @throws Exception */ - public function stopOnSkipped(bool $flag) : void + public function extensionsDirectory() : string { - $this->stopOnSkipped = $flag; + if (!$this->hasExtensionsDirectory()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Extensions directory is not configured'); + } + return (string) $this->extensionsDirectory; } /** - * Enables or disables the stopping for defects: error, failure, warning. + * @psalm-assert-if-true !null $this->testSuiteLoaderClass + * + * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 */ - public function stopOnDefect(bool $flag) : void + public function hasTestSuiteLoaderClass() : bool { - $this->stopOnDefect = $flag; + return $this->testSuiteLoaderClass !== null; } /** - * Returns the time spent running the tests. + * @throws Exception + * + * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 */ - public function time() : float + public function testSuiteLoaderClass() : string { - return $this->time; + if (!$this->hasTestSuiteLoaderClass()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('TestSuiteLoader class is not configured'); + } + return (string) $this->testSuiteLoaderClass; } /** - * Returns whether the entire test was successful or not. + * @psalm-assert-if-true !null $this->testSuiteLoaderFile + * + * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 */ - public function wasSuccessful() : bool - { - return $this->wasSuccessfulIgnoringWarnings() && empty($this->warnings); - } - public function wasSuccessfulIgnoringWarnings() : bool - { - return empty($this->errors) && empty($this->failures); - } - public function wasSuccessfulAndNoTestIsRiskyOrSkippedOrIncomplete() : bool + public function hasTestSuiteLoaderFile() : bool { - return $this->wasSuccessful() && $this->allHarmless() && $this->allCompletelyImplemented() && $this->noneSkipped(); + return $this->testSuiteLoaderFile !== null; } /** - * Sets the default timeout for tests. + * @throws Exception + * + * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 */ - public function setDefaultTimeLimit(int $timeout) : void + public function testSuiteLoaderFile() : string { - $this->defaultTimeLimit = $timeout; + if (!$this->hasTestSuiteLoaderFile()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('TestSuiteLoader sourcecode file is not configured'); + } + return (string) $this->testSuiteLoaderFile; } /** - * Sets the timeout for small tests. + * @psalm-assert-if-true !null $this->printerClass */ - public function setTimeoutForSmallTests(int $timeout) : void + public function hasPrinterClass() : bool { - $this->timeoutForSmallTests = $timeout; + return $this->printerClass !== null; } /** - * Sets the timeout for medium tests. + * @throws Exception */ - public function setTimeoutForMediumTests(int $timeout) : void + public function printerClass() : string { - $this->timeoutForMediumTests = $timeout; + if (!$this->hasPrinterClass()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('ResultPrinter class is not configured'); + } + return (string) $this->printerClass; } /** - * Sets the timeout for large tests. + * @psalm-assert-if-true !null $this->printerFile */ - public function setTimeoutForLargeTests(int $timeout) : void + public function hasPrinterFile() : bool { - $this->timeoutForLargeTests = $timeout; + return $this->printerFile !== null; } /** - * Returns the set timeout for large tests. + * @throws Exception */ - public function getTimeoutForLargeTests() : int + public function printerFile() : string { - return $this->timeoutForLargeTests; + if (!$this->hasPrinterFile()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('ResultPrinter sourcecode file is not configured'); + } + return (string) $this->printerFile; } - public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag) : void + public function beStrictAboutChangesToGlobalState() : bool { - $this->registerMockObjectsFromTestArgumentsRecursively = $flag; + return $this->beStrictAboutChangesToGlobalState; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class SkippedTestCase extends \PHPUnit\Framework\TestCase -{ - /** - * @var bool - */ - protected $backupGlobals = \false; - /** - * @var bool - */ - protected $backupStaticAttributes = \false; - /** - * @var bool - */ - protected $runTestInSeparateProcess = \false; - /** - * @var string - */ - private $message; - public function __construct(string $className, string $methodName, string $message = '') + public function beStrictAboutOutputDuringTests() : bool { - parent::__construct($className . '::' . $methodName); - $this->message = $message; + return $this->beStrictAboutOutputDuringTests; } - public function getMessage() : string + public function beStrictAboutResourceUsageDuringSmallTests() : bool { - return $this->message; + return $this->beStrictAboutResourceUsageDuringSmallTests; + } + public function beStrictAboutTestsThatDoNotTestAnything() : bool + { + return $this->beStrictAboutTestsThatDoNotTestAnything; + } + public function beStrictAboutTodoAnnotatedTests() : bool + { + return $this->beStrictAboutTodoAnnotatedTests; + } + public function beStrictAboutCoversAnnotation() : bool + { + return $this->beStrictAboutCoversAnnotation; + } + public function enforceTimeLimit() : bool + { + return $this->enforceTimeLimit; + } + public function defaultTimeLimit() : int + { + return $this->defaultTimeLimit; + } + public function timeoutForSmallTests() : int + { + return $this->timeoutForSmallTests; + } + public function timeoutForMediumTests() : int + { + return $this->timeoutForMediumTests; + } + public function timeoutForLargeTests() : int + { + return $this->timeoutForLargeTests; } /** - * Returns a string representation of the test case. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @psalm-assert-if-true !null $this->defaultTestSuite */ - public function toString() : string + public function hasDefaultTestSuite() : bool { - return $this->getName(); + return $this->defaultTestSuite !== null; } /** * @throws Exception */ - protected function runTest() : void + public function defaultTestSuite() : string { - $this->markTestSkipped($this->message); + if (!$this->hasDefaultTestSuite()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception('Default test suite is not configured'); + } + return (string) $this->defaultTestSuite; + } + public function executionOrder() : int + { + return $this->executionOrder; + } + public function resolveDependencies() : bool + { + return $this->resolveDependencies; + } + public function defectsFirst() : bool + { + return $this->defectsFirst; + } + public function backupGlobals() : bool + { + return $this->backupGlobals; + } + public function backupStaticAttributes() : bool + { + return $this->backupStaticAttributes; + } + public function registerMockObjectsFromTestArgumentsRecursively() : bool + { + return $this->registerMockObjectsFromTestArgumentsRecursively; + } + public function conflictBetweenPrinterClassAndTestdox() : bool + { + return $this->conflictBetweenPrinterClassAndTestdox; } } getName(); - if (!$theClass->isInstantiable()) { - return new \PHPUnit\Framework\WarningTestCase(\sprintf('Cannot instantiate class "%s".', $className)); - } - $backupSettings = \PHPUnit\Util\Test::getBackupSettings($className, $methodName); - $preserveGlobalState = \PHPUnit\Util\Test::getPreserveGlobalStateSettings($className, $methodName); - $runTestInSeparateProcess = \PHPUnit\Util\Test::getProcessIsolationSettings($className, $methodName); - $runClassInSeparateProcess = \PHPUnit\Util\Test::getClassProcessIsolationSettings($className, $methodName); - $constructor = $theClass->getConstructor(); - if ($constructor === null) { - throw new \PHPUnit\Framework\Exception('No valid test provided.'); - } - $parameters = $constructor->getParameters(); - // TestCase() or TestCase($name) - if (\count($parameters) < 2) { - $test = $this->buildTestWithoutData($className); - } else { - try { - $data = \PHPUnit\Util\Test::getProvidedData($className, $methodName); - } catch (\PHPUnit\Framework\IncompleteTestError $e) { - $message = \sprintf("Test for %s::%s marked incomplete by data provider\n%s", $className, $methodName, $this->throwableToString($e)); - $data = new \PHPUnit\Framework\IncompleteTestCase($className, $methodName, $message); - } catch (\PHPUnit\Framework\SkippedTestError $e) { - $message = \sprintf("Test for %s::%s skipped by data provider\n%s", $className, $methodName, $this->throwableToString($e)); - $data = new \PHPUnit\Framework\SkippedTestCase($className, $methodName, $message); - } catch (\Throwable $t) { - $message = \sprintf("The data provider specified for %s::%s is invalid.\n%s", $className, $methodName, $this->throwableToString($t)); - $data = new \PHPUnit\Framework\WarningTestCase($message); - } - // Test method with @dataProvider. - if (isset($data)) { - $test = $this->buildDataProviderTestSuite($methodName, $className, $data, $runTestInSeparateProcess, $preserveGlobalState, $runClassInSeparateProcess, $backupSettings); - } else { - $test = $this->buildTestWithoutData($className); - } - } - if ($test instanceof \PHPUnit\Framework\TestCase) { - $test->setName($methodName); - $this->configureTestCase($test, $runTestInSeparateProcess, $preserveGlobalState, $runClassInSeparateProcess, $backupSettings); - } - return $test; - } - /** @psalm-param class-string $className */ - private function buildTestWithoutData(string $className) - { - return new $className(); - } - /** @psalm-param class-string $className */ - private function buildDataProviderTestSuite(string $methodName, string $className, $data, bool $runTestInSeparateProcess, ?bool $preserveGlobalState, bool $runClassInSeparateProcess, array $backupSettings) : \PHPUnit\Framework\DataProviderTestSuite + /** + * @var string + * @psalm-var class-string + */ + private $className; + /** + * @var string + */ + private $sourceFile; + /** + * @var array + */ + private $arguments; + /** + * @psalm-param class-string $className + */ + public function __construct(string $className, string $sourceFile, array $arguments) { - $dataProviderTestSuite = new \PHPUnit\Framework\DataProviderTestSuite($className . '::' . $methodName); - $groups = \PHPUnit\Util\Test::getGroups($className, $methodName); - if ($data instanceof \PHPUnit\Framework\WarningTestCase || $data instanceof \PHPUnit\Framework\SkippedTestCase || $data instanceof \PHPUnit\Framework\IncompleteTestCase) { - $dataProviderTestSuite->addTest($data, $groups); - } else { - foreach ($data as $_dataName => $_data) { - $_test = new $className($methodName, $_data, $_dataName); - \assert($_test instanceof \PHPUnit\Framework\TestCase); - $this->configureTestCase($_test, $runTestInSeparateProcess, $preserveGlobalState, $runClassInSeparateProcess, $backupSettings); - $dataProviderTestSuite->addTest($_test, $groups); - } - } - return $dataProviderTestSuite; + $this->className = $className; + $this->sourceFile = $sourceFile; + $this->arguments = $arguments; } - private function configureTestCase(\PHPUnit\Framework\TestCase $test, bool $runTestInSeparateProcess, ?bool $preserveGlobalState, bool $runClassInSeparateProcess, array $backupSettings) : void - { - if ($runTestInSeparateProcess) { - $test->setRunTestInSeparateProcess(\true); - if ($preserveGlobalState !== null) { - $test->setPreserveGlobalState($preserveGlobalState); - } - } - if ($runClassInSeparateProcess) { - $test->setRunClassInSeparateProcess(\true); - if ($preserveGlobalState !== null) { - $test->setPreserveGlobalState($preserveGlobalState); - } - } - if ($backupSettings['backupGlobals'] !== null) { - $test->setBackupGlobals($backupSettings['backupGlobals']); - } - if ($backupSettings['backupStaticAttributes'] !== null) { - $test->setBackupStaticAttributes($backupSettings['backupStaticAttributes']); - } + /** + * @psalm-return class-string + */ + public function className() : string + { + return $this->className; } - private function throwableToString(\Throwable $t) : string + public function hasSourceFile() : bool { - $message = $t->getMessage(); - if (empty(\trim($message))) { - $message = ''; - } - if ($t instanceof \PHPUnit\Util\InvalidDataSetException) { - return \sprintf("%s\n%s", $message, \PHPUnit\Util\Filter::getFilteredStacktrace($t)); - } - return \sprintf("%s: %s\n%s", \get_class($t), $message, \PHPUnit\Util\Filter::getFilteredStacktrace($t)); + return $this->sourceFile !== ''; + } + public function sourceFile() : string + { + return $this->sourceFile; + } + public function hasArguments() : bool + { + return !empty($this->arguments); + } + public function arguments() : array + { + return $this->arguments; } } extensions = $extensions; } /** - * Asserts that a haystack contains a needle. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception + * @return Extension[] */ - public static function assertContains($needle, iterable $haystack, string $message = '') : void + public function asArray() : array { - $constraint = new \PHPUnit\Framework\Constraint\TraversableContainsIdentical($needle); - static::assertThat($haystack, $constraint, $message); + return $this->extensions; } - public static function assertContainsEquals($needle, iterable $haystack, string $message = '') : void + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\ExtensionCollectionIterator { - $constraint = new \PHPUnit\Framework\Constraint\TraversableContainsEqual($needle); - static::assertThat($haystack, $constraint, $message); + return new \PHPUnit\TextUI\XmlConfiguration\ExtensionCollectionIterator($this); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ExtensionCollectionIterator implements Countable, Iterator +{ /** - * Asserts that a haystack does not contain a needle. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception + * @var Extension[] */ - public static function assertNotContains($needle, iterable $haystack, string $message = '') : void + private $extensions; + /** + * @var int + */ + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\ExtensionCollection $extensions) { - $constraint = new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\TraversableContainsIdentical($needle)); - static::assertThat($haystack, $constraint, $message); + $this->extensions = $extensions->asArray(); } - public static function assertNotContainsEquals($needle, iterable $haystack, string $message = '') : void + public function count() : int { - $constraint = new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\TraversableContainsEqual($needle)); - static::assertThat($haystack, $constraint, $message); + return iterator_count($this); } - /** - * Asserts that a haystack contains only values of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '') : void + public function rewind() : void { - if ($isNativeType === null) { - $isNativeType = \PHPUnit\Util\Type::isType($type); - } - static::assertThat($haystack, new \PHPUnit\Framework\Constraint\TraversableContainsOnly($type, $isNativeType), $message); + $this->position = 0; } - /** - * Asserts that a haystack contains only instances of a given class name. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = '') : void + public function valid() : bool { - static::assertThat($haystack, new \PHPUnit\Framework\Constraint\TraversableContainsOnly($className, \false), $message); + return $this->position < count($this->extensions); } - /** - * Asserts that a haystack does not contain only values of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '') : void + public function key() : int { - if ($isNativeType === null) { - $isNativeType = \PHPUnit\Util\Type::isType($type); - } - static::assertThat($haystack, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\TraversableContainsOnly($type, $isNativeType)), $message); + return $this->position; } - /** - * Asserts the number of elements of an array, Countable or Traversable. - * - * @param Countable|iterable $haystack - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - */ - public static function assertCount(int $expectedCount, $haystack, string $message = '') : void + public function current() : \PHPUnit\TextUI\XmlConfiguration\Extension { - if (!$haystack instanceof \Countable && !\is_iterable($haystack)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'countable or iterable'); - } - static::assertThat($haystack, new \PHPUnit\Framework\Constraint\Count($expectedCount), $message); + return $this->extensions[$this->position]; } - /** - * Asserts the number of elements of an array, Countable or Traversable. - * - * @param Countable|iterable $haystack - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - */ - public static function assertNotCount(int $expectedCount, $haystack, string $message = '') : void + public function next() : void { - if (!$haystack instanceof \Countable && !\is_iterable($haystack)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'countable or iterable'); - } - $constraint = new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\Count($expectedCount)); - static::assertThat($haystack, $constraint, $message); + $this->position++; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class TestSuite +{ /** - * Asserts that two variables are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var string */ - public static function assertEquals($expected, $actual, string $message = '') : void - { - $constraint = new \PHPUnit\Framework\Constraint\IsEqual($expected); - static::assertThat($actual, $constraint, $message); - } + private $name; /** - * Asserts that two variables are equal (canonicalizing). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var TestDirectoryCollection */ - public static function assertEqualsCanonicalizing($expected, $actual, string $message = '') : void - { - $constraint = new \PHPUnit\Framework\Constraint\IsEqualCanonicalizing($expected); - static::assertThat($actual, $constraint, $message); - } + private $directories; /** - * Asserts that two variables are equal (ignoring case). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var TestFileCollection */ - public static function assertEqualsIgnoringCase($expected, $actual, string $message = '') : void - { - $constraint = new \PHPUnit\Framework\Constraint\IsEqualIgnoringCase($expected); - static::assertThat($actual, $constraint, $message); - } + private $files; /** - * Asserts that two variables are equal (with delta). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var FileCollection */ - public static function assertEqualsWithDelta($expected, $actual, float $delta, string $message = '') : void + private $exclude; + public function __construct(string $name, \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection $directories, \PHPUnit\TextUI\XmlConfiguration\TestFileCollection $files, \PHPUnit\TextUI\XmlConfiguration\FileCollection $exclude) { - $constraint = new \PHPUnit\Framework\Constraint\IsEqualWithDelta($expected, $delta); - static::assertThat($actual, $constraint, $message); + $this->name = $name; + $this->directories = $directories; + $this->files = $files; + $this->exclude = $exclude; } - /** - * Asserts that two variables are not equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotEquals($expected, $actual, string $message = '') : void + public function name() : string { - $constraint = new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsEqual($expected)); - static::assertThat($actual, $constraint, $message); + return $this->name; } - /** - * Asserts that two variables are not equal (canonicalizing). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotEqualsCanonicalizing($expected, $actual, string $message = '') : void + public function directories() : \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection { - $constraint = new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsEqualCanonicalizing($expected)); - static::assertThat($actual, $constraint, $message); + return $this->directories; } - /** - * Asserts that two variables are not equal (ignoring case). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotEqualsIgnoringCase($expected, $actual, string $message = '') : void + public function files() : \PHPUnit\TextUI\XmlConfiguration\TestFileCollection { - $constraint = new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsEqualIgnoringCase($expected)); - static::assertThat($actual, $constraint, $message); + return $this->files; } - /** - * Asserts that two variables are not equal (with delta). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotEqualsWithDelta($expected, $actual, float $delta, string $message = '') : void + public function exclude() : \PHPUnit\TextUI\XmlConfiguration\FileCollection { - $constraint = new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsEqualWithDelta($expected, $delta)); - static::assertThat($actual, $constraint, $message); + return $this->exclude; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use PHPUnit\Util\VersionComparisonOperator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class TestDirectory +{ /** - * @throws ExpectationFailedException + * @var string */ - public static function assertObjectEquals(object $expected, object $actual, string $method = 'equals', string $message = '') : void - { - static::assertThat($actual, static::objectEquals($expected, $method), $message); - } + private $path; /** - * Asserts that a variable is empty. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert empty $actual + * @var string */ - public static function assertEmpty($actual, string $message = '') : void - { - static::assertThat($actual, static::isEmpty(), $message); - } + private $prefix; /** - * Asserts that a variable is not empty. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !empty $actual + * @var string */ - public static function assertNotEmpty($actual, string $message = '') : void - { - static::assertThat($actual, static::logicalNot(static::isEmpty()), $message); - } + private $suffix; /** - * Asserts that a value is greater than another value. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var string */ - public static function assertGreaterThan($expected, $actual, string $message = '') : void - { - static::assertThat($actual, static::greaterThan($expected), $message); - } + private $phpVersion; /** - * Asserts that a value is greater than or equal to another value. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var VersionComparisonOperator */ - public static function assertGreaterThanOrEqual($expected, $actual, string $message = '') : void + private $phpVersionOperator; + public function __construct(string $path, string $prefix, string $suffix, string $phpVersion, VersionComparisonOperator $phpVersionOperator) { - static::assertThat($actual, static::greaterThanOrEqual($expected), $message); + $this->path = $path; + $this->prefix = $prefix; + $this->suffix = $suffix; + $this->phpVersion = $phpVersion; + $this->phpVersionOperator = $phpVersionOperator; } - /** - * Asserts that a value is smaller than another value. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertLessThan($expected, $actual, string $message = '') : void + public function path() : string { - static::assertThat($actual, static::lessThan($expected), $message); + return $this->path; } - /** - * Asserts that a value is smaller than or equal to another value. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertLessThanOrEqual($expected, $actual, string $message = '') : void + public function prefix() : string { - static::assertThat($actual, static::lessThanOrEqual($expected), $message); + return $this->prefix; } - /** - * Asserts that the contents of one file is equal to the contents of another - * file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileEquals(string $expected, string $actual, string $message = '') : void + public function suffix() : string { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); - $constraint = new \PHPUnit\Framework\Constraint\IsEqual(\file_get_contents($expected)); - static::assertThat(\file_get_contents($actual), $constraint, $message); + return $this->suffix; } - /** - * Asserts that the contents of one file is equal to the contents of another - * file (canonicalizing). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileEqualsCanonicalizing(string $expected, string $actual, string $message = '') : void + public function phpVersion() : string { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); - $constraint = new \PHPUnit\Framework\Constraint\IsEqualCanonicalizing(\file_get_contents($expected)); - static::assertThat(\file_get_contents($actual), $constraint, $message); + return $this->phpVersion; } + public function phpVersionOperator() : VersionComparisonOperator + { + return $this->phpVersionOperator; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestSuiteCollectionIterator implements Countable, Iterator +{ /** - * Asserts that the contents of one file is equal to the contents of another - * file (ignoring case). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var TestSuite[] + */ + private $testSuites; + /** + * @var int */ - public static function assertFileEqualsIgnoringCase(string $expected, string $actual, string $message = '') : void + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection $testSuites) { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); - $constraint = new \PHPUnit\Framework\Constraint\IsEqualIgnoringCase(\file_get_contents($expected)); - static::assertThat(\file_get_contents($actual), $constraint, $message); + $this->testSuites = $testSuites->asArray(); } - /** - * Asserts that the contents of one file is not equal to the contents of - * another file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileNotEquals(string $expected, string $actual, string $message = '') : void + public function count() : int { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); - $constraint = new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsEqual(\file_get_contents($expected))); - static::assertThat(\file_get_contents($actual), $constraint, $message); + return iterator_count($this); } - /** - * Asserts that the contents of one file is not equal to the contents of another - * file (canonicalizing). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileNotEqualsCanonicalizing(string $expected, string $actual, string $message = '') : void + public function rewind() : void { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); - $constraint = new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsEqualCanonicalizing(\file_get_contents($expected))); - static::assertThat(\file_get_contents($actual), $constraint, $message); + $this->position = 0; } - /** - * Asserts that the contents of one file is not equal to the contents of another - * file (ignoring case). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileNotEqualsIgnoringCase(string $expected, string $actual, string $message = '') : void + public function valid() : bool { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); - $constraint = new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsEqualIgnoringCase(\file_get_contents($expected))); - static::assertThat(\file_get_contents($actual), $constraint, $message); + return $this->position < count($this->testSuites); } - /** - * Asserts that the contents of a string is equal - * to the contents of a file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = '') : void + public function key() : int { - static::assertFileExists($expectedFile, $message); - $constraint = new \PHPUnit\Framework\Constraint\IsEqual(\file_get_contents($expectedFile)); - static::assertThat($actualString, $constraint, $message); + return $this->position; } - /** - * Asserts that the contents of a string is equal - * to the contents of a file (canonicalizing). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '') : void + public function current() : \PHPUnit\TextUI\XmlConfiguration\TestSuite { - static::assertFileExists($expectedFile, $message); - $constraint = new \PHPUnit\Framework\Constraint\IsEqualCanonicalizing(\file_get_contents($expectedFile)); - static::assertThat($actualString, $constraint, $message); + return $this->testSuites[$this->position]; } - /** - * Asserts that the contents of a string is equal - * to the contents of a file (ignoring case). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = '') : void + public function next() : void { - static::assertFileExists($expectedFile, $message); - $constraint = new \PHPUnit\Framework\Constraint\IsEqualIgnoringCase(\file_get_contents($expectedFile)); - static::assertThat($actualString, $constraint, $message); + $this->position++; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use PHPUnit\Util\VersionComparisonOperator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class TestFile +{ /** - * Asserts that the contents of a string is not equal - * to the contents of a file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var string */ - public static function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = '') : void - { - static::assertFileExists($expectedFile, $message); - $constraint = new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsEqual(\file_get_contents($expectedFile))); - static::assertThat($actualString, $constraint, $message); - } + private $path; /** - * Asserts that the contents of a string is not equal - * to the contents of a file (canonicalizing). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var string */ - public static function assertStringNotEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '') : void - { - static::assertFileExists($expectedFile, $message); - $constraint = new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsEqualCanonicalizing(\file_get_contents($expectedFile))); - static::assertThat($actualString, $constraint, $message); - } + private $phpVersion; /** - * Asserts that the contents of a string is not equal - * to the contents of a file (ignoring case). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var VersionComparisonOperator */ - public static function assertStringNotEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = '') : void + private $phpVersionOperator; + public function __construct(string $path, string $phpVersion, VersionComparisonOperator $phpVersionOperator) { - static::assertFileExists($expectedFile, $message); - $constraint = new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsEqualIgnoringCase(\file_get_contents($expectedFile))); - static::assertThat($actualString, $constraint, $message); + $this->path = $path; + $this->phpVersion = $phpVersion; + $this->phpVersionOperator = $phpVersionOperator; } - /** - * Asserts that a file/dir is readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertIsReadable(string $filename, string $message = '') : void + public function path() : string { - static::assertThat($filename, new \PHPUnit\Framework\Constraint\IsReadable(), $message); + return $this->path; } - /** - * Asserts that a file/dir exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertIsNotReadable(string $filename, string $message = '') : void + public function phpVersion() : string { - static::assertThat($filename, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsReadable()), $message); + return $this->phpVersion; } - /** - * Asserts that a file/dir exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4062 - */ - public static function assertNotIsReadable(string $filename, string $message = '') : void + public function phpVersionOperator() : VersionComparisonOperator { - self::createWarning('assertNotIsReadable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertIsNotReadable() instead.'); - static::assertThat($filename, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsReadable()), $message); + return $this->phpVersionOperator; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class TestDirectoryCollection implements Countable, IteratorAggregate +{ /** - * Asserts that a file/dir exists and is writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var TestDirectory[] */ - public static function assertIsWritable(string $filename, string $message = '') : void - { - static::assertThat($filename, new \PHPUnit\Framework\Constraint\IsWritable(), $message); - } + private $directories; /** - * Asserts that a file/dir exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @param TestDirectory[] $directories */ - public static function assertIsNotWritable(string $filename, string $message = '') : void + public static function fromArray(array $directories) : self { - static::assertThat($filename, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsWritable()), $message); + return new self(...$directories); } - /** - * Asserts that a file/dir exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4065 - */ - public static function assertNotIsWritable(string $filename, string $message = '') : void + private function __construct(\PHPUnit\TextUI\XmlConfiguration\TestDirectory ...$directories) { - self::createWarning('assertNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertIsNotWritable() instead.'); - static::assertThat($filename, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsWritable()), $message); + $this->directories = $directories; } /** - * Asserts that a directory exists. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @return TestDirectory[] */ - public static function assertDirectoryExists(string $directory, string $message = '') : void + public function asArray() : array { - static::assertThat($directory, new \PHPUnit\Framework\Constraint\DirectoryExists(), $message); + return $this->directories; } - /** - * Asserts that a directory does not exist. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertDirectoryDoesNotExist(string $directory, string $message = '') : void + public function count() : int { - static::assertThat($directory, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\DirectoryExists()), $message); + return count($this->directories); } - /** - * Asserts that a directory does not exist. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4068 - */ - public static function assertDirectoryNotExists(string $directory, string $message = '') : void + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollectionIterator { - self::createWarning('assertDirectoryNotExists() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryDoesNotExist() instead.'); - static::assertThat($directory, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\DirectoryExists()), $message); + return new \PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollectionIterator($this); } - /** - * Asserts that a directory exists and is readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertDirectoryIsReadable(string $directory, string $message = '') : void + public function isEmpty() : bool { - self::assertDirectoryExists($directory, $message); - self::assertIsReadable($directory, $message); + return $this->count() === 0; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class TestSuiteCollection implements Countable, IteratorAggregate +{ /** - * Asserts that a directory exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var TestSuite[] */ - public static function assertDirectoryIsNotReadable(string $directory, string $message = '') : void - { - self::assertDirectoryExists($directory, $message); - self::assertIsNotReadable($directory, $message); - } + private $testSuites; /** - * Asserts that a directory exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4071 + * @param TestSuite[] $testSuites */ - public static function assertDirectoryNotIsReadable(string $directory, string $message = '') : void + public static function fromArray(array $testSuites) : self { - self::createWarning('assertDirectoryNotIsReadable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryIsNotReadable() instead.'); - self::assertDirectoryExists($directory, $message); - self::assertIsNotReadable($directory, $message); + return new self(...$testSuites); } - /** - * Asserts that a directory exists and is writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertDirectoryIsWritable(string $directory, string $message = '') : void + private function __construct(\PHPUnit\TextUI\XmlConfiguration\TestSuite ...$testSuites) { - self::assertDirectoryExists($directory, $message); - self::assertIsWritable($directory, $message); + $this->testSuites = $testSuites; } /** - * Asserts that a directory exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @return TestSuite[] */ - public static function assertDirectoryIsNotWritable(string $directory, string $message = '') : void + public function asArray() : array { - self::assertDirectoryExists($directory, $message); - self::assertIsNotWritable($directory, $message); + return $this->testSuites; } - /** - * Asserts that a directory exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4074 - */ - public static function assertDirectoryNotIsWritable(string $directory, string $message = '') : void + public function count() : int { - self::createWarning('assertDirectoryNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryIsNotWritable() instead.'); - self::assertDirectoryExists($directory, $message); - self::assertIsNotWritable($directory, $message); + return count($this->testSuites); } - /** - * Asserts that a file exists. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileExists(string $filename, string $message = '') : void + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollectionIterator { - static::assertThat($filename, new \PHPUnit\Framework\Constraint\FileExists(), $message); + return new \PHPUnit\TextUI\XmlConfiguration\TestSuiteCollectionIterator($this); } - /** - * Asserts that a file does not exist. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileDoesNotExist(string $filename, string $message = '') : void + public function isEmpty() : bool { - static::assertThat($filename, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\FileExists()), $message); + return $this->count() === 0; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestFileCollectionIterator implements Countable, Iterator +{ /** - * Asserts that a file does not exist. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4077 + * @var TestFile[] */ - public static function assertFileNotExists(string $filename, string $message = '') : void - { - self::createWarning('assertFileNotExists() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileDoesNotExist() instead.'); - static::assertThat($filename, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\FileExists()), $message); - } + private $files; /** - * Asserts that a file exists and is readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var int */ - public static function assertFileIsReadable(string $file, string $message = '') : void + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\TestFileCollection $files) { - self::assertFileExists($file, $message); - self::assertIsReadable($file, $message); + $this->files = $files->asArray(); } - /** - * Asserts that a file exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileIsNotReadable(string $file, string $message = '') : void + public function count() : int { - self::assertFileExists($file, $message); - self::assertIsNotReadable($file, $message); + return iterator_count($this); } - /** - * Asserts that a file exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4080 - */ - public static function assertFileNotIsReadable(string $file, string $message = '') : void + public function rewind() : void { - self::createWarning('assertFileNotIsReadable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileIsNotReadable() instead.'); - self::assertFileExists($file, $message); - self::assertIsNotReadable($file, $message); + $this->position = 0; } - /** - * Asserts that a file exists and is writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileIsWritable(string $file, string $message = '') : void + public function valid() : bool { - self::assertFileExists($file, $message); - self::assertIsWritable($file, $message); + return $this->position < count($this->files); } - /** - * Asserts that a file exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileIsNotWritable(string $file, string $message = '') : void + public function key() : int { - self::assertFileExists($file, $message); - self::assertIsNotWritable($file, $message); + return $this->position; } - /** - * Asserts that a file exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4083 - */ - public static function assertFileNotIsWritable(string $file, string $message = '') : void + public function current() : \PHPUnit\TextUI\XmlConfiguration\TestFile { - self::createWarning('assertFileNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileIsNotWritable() instead.'); - self::assertFileExists($file, $message); - self::assertIsNotWritable($file, $message); + return $this->files[$this->position]; } - /** - * Asserts that a condition is true. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert true $condition - */ - public static function assertTrue($condition, string $message = '') : void + public function next() : void { - static::assertThat($condition, static::isTrue(), $message); + $this->position++; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class TestFileCollection implements Countable, IteratorAggregate +{ + /** + * @var TestFile[] + */ + private $files; /** - * Asserts that a condition is not true. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !true $condition + * @param TestFile[] $files */ - public static function assertNotTrue($condition, string $message = '') : void + public static function fromArray(array $files) : self { - static::assertThat($condition, static::logicalNot(static::isTrue()), $message); + return new self(...$files); } - /** - * Asserts that a condition is false. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert false $condition - */ - public static function assertFalse($condition, string $message = '') : void + private function __construct(\PHPUnit\TextUI\XmlConfiguration\TestFile ...$files) { - static::assertThat($condition, static::isFalse(), $message); + $this->files = $files; } /** - * Asserts that a condition is not false. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !false $condition + * @return TestFile[] */ - public static function assertNotFalse($condition, string $message = '') : void + public function asArray() : array { - static::assertThat($condition, static::logicalNot(static::isFalse()), $message); + return $this->files; } - /** - * Asserts that a variable is null. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert null $actual - */ - public static function assertNull($actual, string $message = '') : void + public function count() : int { - static::assertThat($actual, static::isNull(), $message); + return count($this->files); } - /** - * Asserts that a variable is not null. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !null $actual - */ - public static function assertNotNull($actual, string $message = '') : void + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\TestFileCollectionIterator { - static::assertThat($actual, static::logicalNot(static::isNull()), $message); + return new \PHPUnit\TextUI\XmlConfiguration\TestFileCollectionIterator($this); } - /** - * Asserts that a variable is finite. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFinite($actual, string $message = '') : void + public function isEmpty() : bool { - static::assertThat($actual, static::isFinite(), $message); + return $this->count() === 0; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestDirectoryCollectionIterator implements Countable, Iterator +{ /** - * Asserts that a variable is infinite. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var TestDirectory[] */ - public static function assertInfinite($actual, string $message = '') : void - { - static::assertThat($actual, static::isInfinite(), $message); - } + private $directories; /** - * Asserts that a variable is nan. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var int */ - public static function assertNan($actual, string $message = '') : void + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\TestDirectoryCollection $directories) { - static::assertThat($actual, static::isNan(), $message); + $this->directories = $directories->asArray(); } - /** - * Asserts that a class has a specified attribute. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - */ - public static function assertClassHasAttribute(string $attributeName, string $className, string $message = '') : void + public function count() : int { - if (!self::isValidClassAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!\class_exists($className)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'class name'); - } - static::assertThat($className, new \PHPUnit\Framework\Constraint\ClassHasAttribute($attributeName), $message); + return iterator_count($this); } - /** - * Asserts that a class does not have a specified attribute. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - */ - public static function assertClassNotHasAttribute(string $attributeName, string $className, string $message = '') : void + public function rewind() : void { - if (!self::isValidClassAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!\class_exists($className)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'class name'); - } - static::assertThat($className, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\ClassHasAttribute($attributeName)), $message); + $this->position = 0; } - /** - * Asserts that a class has a specified static attribute. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - */ - public static function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = '') : void + public function valid() : bool { - if (!self::isValidClassAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!\class_exists($className)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'class name'); - } - static::assertThat($className, new \PHPUnit\Framework\Constraint\ClassHasStaticAttribute($attributeName), $message); + return $this->position < count($this->directories); } - /** - * Asserts that a class does not have a specified static attribute. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - */ - public static function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = '') : void + public function key() : int { - if (!self::isValidClassAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!\class_exists($className)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'class name'); - } - static::assertThat($className, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\ClassHasStaticAttribute($attributeName)), $message); + return $this->position; } - /** - * Asserts that an object has a specified attribute. - * - * @param object $object - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - */ - public static function assertObjectHasAttribute(string $attributeName, $object, string $message = '') : void + public function current() : \PHPUnit\TextUI\XmlConfiguration\TestDirectory { - if (!self::isValidObjectAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!\is_object($object)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'object'); - } - static::assertThat($object, new \PHPUnit\Framework\Constraint\ObjectHasAttribute($attributeName), $message); + return $this->directories[$this->position]; } - /** - * Asserts that an object does not have a specified attribute. - * - * @param object $object - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - */ - public static function assertObjectNotHasAttribute(string $attributeName, $object, string $message = '') : void + public function next() : void { - if (!self::isValidObjectAttributeName($attributeName)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'valid attribute name'); - } - if (!\is_object($object)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'object'); - } - static::assertThat($object, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\ObjectHasAttribute($attributeName)), $message); + $this->position++; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class DirectoryCollectionIterator implements Countable, Iterator +{ /** - * Asserts that two variables have the same type and value. - * Used on objects, it asserts that two variables reference - * the same object. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-template ExpectedType - * @psalm-param ExpectedType $expected - * @psalm-assert =ExpectedType $actual + * @var Directory[] */ - public static function assertSame($expected, $actual, string $message = '') : void - { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\IsIdentical($expected), $message); - } + private $directories; /** - * Asserts that two variables do not have the same type and value. - * Used on objects, it asserts that two variables do not reference - * the same object. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var int */ - public static function assertNotSame($expected, $actual, string $message = '') : void + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection $directories) { - if (\is_bool($expected) && \is_bool($actual)) { - static::assertNotEquals($expected, $actual, $message); - } - static::assertThat($actual, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsIdentical($expected)), $message); + $this->directories = $directories->asArray(); } - /** - * Asserts that a variable is of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @psalm-template ExpectedType of object - * @psalm-param class-string $expected - * @psalm-assert =ExpectedType $actual - */ - public static function assertInstanceOf(string $expected, $actual, string $message = '') : void + public function count() : int { - if (!\class_exists($expected) && !\interface_exists($expected)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'class or interface name'); - } - static::assertThat($actual, new \PHPUnit\Framework\Constraint\IsInstanceOf($expected), $message); + return iterator_count($this); } - /** - * Asserts that a variable is not of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - * - * @psalm-template ExpectedType of object - * @psalm-param class-string $expected - * @psalm-assert !ExpectedType $actual - */ - public static function assertNotInstanceOf(string $expected, $actual, string $message = '') : void + public function rewind() : void { - if (!\class_exists($expected) && !\interface_exists($expected)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'class or interface name'); - } - static::assertThat($actual, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsInstanceOf($expected)), $message); + $this->position = 0; } - /** - * Asserts that a variable is of type array. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert array $actual - */ - public static function assertIsArray($actual, string $message = '') : void + public function valid() : bool { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_ARRAY), $message); + return $this->position < count($this->directories); } - /** - * Asserts that a variable is of type bool. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert bool $actual - */ - public static function assertIsBool($actual, string $message = '') : void + public function key() : int { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_BOOL), $message); + return $this->position; } - /** - * Asserts that a variable is of type float. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert float $actual - */ - public static function assertIsFloat($actual, string $message = '') : void + public function current() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\Directory { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_FLOAT), $message); + return $this->directories[$this->position]; } - /** - * Asserts that a variable is of type int. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert int $actual - */ - public static function assertIsInt($actual, string $message = '') : void + public function next() : void { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_INT), $message); + $this->position++; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class DirectoryCollection implements Countable, IteratorAggregate +{ /** - * Asserts that a variable is of type numeric. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert numeric $actual + * @var Directory[] */ - public static function assertIsNumeric($actual, string $message = '') : void - { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_NUMERIC), $message); - } + private $directories; /** - * Asserts that a variable is of type object. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert object $actual + * @param Directory[] $directories */ - public static function assertIsObject($actual, string $message = '') : void + public static function fromArray(array $directories) : self { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_OBJECT), $message); + return new self(...$directories); } - /** - * Asserts that a variable is of type resource. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert resource $actual - */ - public static function assertIsResource($actual, string $message = '') : void + private function __construct(\PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\Directory ...$directories) { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_RESOURCE), $message); + $this->directories = $directories; } /** - * Asserts that a variable is of type resource and is closed. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert resource $actual + * @return Directory[] */ - public static function assertIsClosedResource($actual, string $message = '') : void + public function asArray() : array { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_CLOSED_RESOURCE), $message); + return $this->directories; } - /** - * Asserts that a variable is of type string. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert string $actual - */ - public static function assertIsString($actual, string $message = '') : void + public function count() : int { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_STRING), $message); + return count($this->directories); } - /** - * Asserts that a variable is of type scalar. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert scalar $actual - */ - public static function assertIsScalar($actual, string $message = '') : void + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollectionIterator { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_SCALAR), $message); + return new \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollectionIterator($this); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class Directory +{ /** - * Asserts that a variable is of type callable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert callable $actual + * @var string */ - public static function assertIsCallable($actual, string $message = '') : void + private $path; + /** + * @var string + */ + private $prefix; + /** + * @var string + */ + private $suffix; + /** + * @var string + */ + private $group; + public function __construct(string $path, string $prefix, string $suffix, string $group) + { + $this->path = $path; + $this->prefix = $prefix; + $this->suffix = $suffix; + $this->group = $group; + } + public function path() : string + { + return $this->path; + } + public function prefix() : string + { + return $this->prefix; + } + public function suffix() : string + { + return $this->suffix; + } + public function group() : string { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_CALLABLE), $message); + return $this->group; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage; + +use function count; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Clover; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Cobertura; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Crap4j; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Html; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Php; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Text; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Xml; +use PHPUnit\TextUI\XmlConfiguration\Directory; +use PHPUnit\TextUI\XmlConfiguration\Exception; +use PHPUnit\TextUI\XmlConfiguration\FileCollection; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class CodeCoverage +{ /** - * Asserts that a variable is of type iterable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert iterable $actual + * @var ?Directory + */ + private $cacheDirectory; + /** + * @var DirectoryCollection */ - public static function assertIsIterable($actual, string $message = '') : void - { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_ITERABLE), $message); - } + private $directories; /** - * Asserts that a variable is not of type array. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !array $actual + * @var FileCollection */ - public static function assertIsNotArray($actual, string $message = '') : void - { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_ARRAY)), $message); - } + private $files; /** - * Asserts that a variable is not of type bool. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !bool $actual + * @var DirectoryCollection */ - public static function assertIsNotBool($actual, string $message = '') : void - { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_BOOL)), $message); - } + private $excludeDirectories; /** - * Asserts that a variable is not of type float. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !float $actual + * @var FileCollection */ - public static function assertIsNotFloat($actual, string $message = '') : void - { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_FLOAT)), $message); - } + private $excludeFiles; /** - * Asserts that a variable is not of type int. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !int $actual + * @var bool */ - public static function assertIsNotInt($actual, string $message = '') : void - { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_INT)), $message); - } + private $pathCoverage; /** - * Asserts that a variable is not of type numeric. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !numeric $actual + * @var bool */ - public static function assertIsNotNumeric($actual, string $message = '') : void - { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_NUMERIC)), $message); - } + private $includeUncoveredFiles; /** - * Asserts that a variable is not of type object. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !object $actual + * @var bool */ - public static function assertIsNotObject($actual, string $message = '') : void - { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_OBJECT)), $message); - } + private $processUncoveredFiles; /** - * Asserts that a variable is not of type resource. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !resource $actual + * @var bool */ - public static function assertIsNotResource($actual, string $message = '') : void - { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_RESOURCE)), $message); - } + private $ignoreDeprecatedCodeUnits; /** - * Asserts that a variable is not of type resource. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !resource $actual + * @var bool */ - public static function assertIsNotClosedResource($actual, string $message = '') : void - { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_CLOSED_RESOURCE)), $message); - } + private $disableCodeCoverageIgnore; /** - * Asserts that a variable is not of type string. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !string $actual + * @var ?Clover */ - public static function assertIsNotString($actual, string $message = '') : void - { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_STRING)), $message); - } + private $clover; /** - * Asserts that a variable is not of type scalar. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !scalar $actual + * @var ?Cobertura */ - public static function assertIsNotScalar($actual, string $message = '') : void - { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_SCALAR)), $message); - } + private $cobertura; /** - * Asserts that a variable is not of type callable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !callable $actual + * @var ?Crap4j */ - public static function assertIsNotCallable($actual, string $message = '') : void - { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_CALLABLE)), $message); - } + private $crap4j; /** - * Asserts that a variable is not of type iterable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-assert !iterable $actual + * @var ?Html */ - public static function assertIsNotIterable($actual, string $message = '') : void - { - static::assertThat($actual, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\IsType(\PHPUnit\Framework\Constraint\IsType::TYPE_ITERABLE)), $message); - } + private $html; /** - * Asserts that a string matches a given regular expression. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var ?Php */ - public static function assertMatchesRegularExpression(string $pattern, string $string, string $message = '') : void - { - static::assertThat($string, new \PHPUnit\Framework\Constraint\RegularExpression($pattern), $message); - } + private $php; /** - * Asserts that a string matches a given regular expression. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4086 + * @var ?Text */ - public static function assertRegExp(string $pattern, string $string, string $message = '') : void - { - self::createWarning('assertRegExp() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertMatchesRegularExpression() instead.'); - static::assertThat($string, new \PHPUnit\Framework\Constraint\RegularExpression($pattern), $message); - } + private $text; /** - * Asserts that a string does not match a given regular expression. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var ?Xml */ - public static function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = '') : void + private $xml; + public function __construct(?Directory $cacheDirectory, DirectoryCollection $directories, FileCollection $files, DirectoryCollection $excludeDirectories, FileCollection $excludeFiles, bool $pathCoverage, bool $includeUncoveredFiles, bool $processUncoveredFiles, bool $ignoreDeprecatedCodeUnits, bool $disableCodeCoverageIgnore, ?Clover $clover, ?Cobertura $cobertura, ?Crap4j $crap4j, ?Html $html, ?Php $php, ?Text $text, ?Xml $xml) { - static::assertThat($string, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\RegularExpression($pattern)), $message); + $this->cacheDirectory = $cacheDirectory; + $this->directories = $directories; + $this->files = $files; + $this->excludeDirectories = $excludeDirectories; + $this->excludeFiles = $excludeFiles; + $this->pathCoverage = $pathCoverage; + $this->includeUncoveredFiles = $includeUncoveredFiles; + $this->processUncoveredFiles = $processUncoveredFiles; + $this->ignoreDeprecatedCodeUnits = $ignoreDeprecatedCodeUnits; + $this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore; + $this->clover = $clover; + $this->cobertura = $cobertura; + $this->crap4j = $crap4j; + $this->html = $html; + $this->php = $php; + $this->text = $text; + $this->xml = $xml; } /** - * Asserts that a string does not match a given regular expression. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4089 + * @psalm-assert-if-true !null $this->cacheDirectory */ - public static function assertNotRegExp(string $pattern, string $string, string $message = '') : void + public function hasCacheDirectory() : bool { - self::createWarning('assertNotRegExp() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDoesNotMatchRegularExpression() instead.'); - static::assertThat($string, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\RegularExpression($pattern)), $message); + return $this->cacheDirectory !== null; } /** - * Assert that the size of two arrays (or `Countable` or `Traversable` objects) - * is the same. - * - * @param Countable|iterable $expected - * @param Countable|iterable $actual - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws Exception */ - public static function assertSameSize($expected, $actual, string $message = '') : void + public function cacheDirectory() : Directory { - if (!$expected instanceof \Countable && !\is_iterable($expected)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'countable or iterable'); - } - if (!$actual instanceof \Countable && !\is_iterable($actual)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'countable or iterable'); + if (!$this->hasCacheDirectory()) { + throw new Exception('No cache directory has been configured'); } - static::assertThat($actual, new \PHPUnit\Framework\Constraint\SameSize($expected), $message); + return $this->cacheDirectory; } - /** - * Assert that the size of two arrays (or `Countable` or `Traversable` objects) - * is not the same. - * - * @param Countable|iterable $expected - * @param Countable|iterable $actual - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - */ - public static function assertNotSameSize($expected, $actual, string $message = '') : void + public function hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport() : bool { - if (!$expected instanceof \Countable && !\is_iterable($expected)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'countable or iterable'); - } - if (!$actual instanceof \Countable && !\is_iterable($actual)) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'countable or iterable'); - } - static::assertThat($actual, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\SameSize($expected)), $message); + return count($this->directories) > 0 || count($this->files) > 0; } - /** - * Asserts that a string matches a given format string. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringMatchesFormat(string $format, string $string, string $message = '') : void + public function directories() : DirectoryCollection { - static::assertThat($string, new \PHPUnit\Framework\Constraint\StringMatchesFormatDescription($format), $message); + return $this->directories; } - /** - * Asserts that a string does not match a given format string. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringNotMatchesFormat(string $format, string $string, string $message = '') : void + public function files() : FileCollection { - static::assertThat($string, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\StringMatchesFormatDescription($format)), $message); + return $this->files; } - /** - * Asserts that a string matches a given format file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = '') : void + public function excludeDirectories() : DirectoryCollection { - static::assertFileExists($formatFile, $message); - static::assertThat($string, new \PHPUnit\Framework\Constraint\StringMatchesFormatDescription(\file_get_contents($formatFile)), $message); + return $this->excludeDirectories; } - /** - * Asserts that a string does not match a given format string. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = '') : void + public function excludeFiles() : FileCollection { - static::assertFileExists($formatFile, $message); - static::assertThat($string, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\StringMatchesFormatDescription(\file_get_contents($formatFile))), $message); + return $this->excludeFiles; } - /** - * Asserts that a string starts with a given prefix. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringStartsWith(string $prefix, string $string, string $message = '') : void + public function pathCoverage() : bool { - static::assertThat($string, new \PHPUnit\Framework\Constraint\StringStartsWith($prefix), $message); + return $this->pathCoverage; } - /** - * Asserts that a string starts not with a given prefix. - * - * @param string $prefix - * @param string $string - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringStartsNotWith($prefix, $string, string $message = '') : void + public function includeUncoveredFiles() : bool { - static::assertThat($string, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\StringStartsWith($prefix)), $message); + return $this->includeUncoveredFiles; } - /** - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringContainsString(string $needle, string $haystack, string $message = '') : void + public function ignoreDeprecatedCodeUnits() : bool { - $constraint = new \PHPUnit\Framework\Constraint\StringContains($needle, \false); - static::assertThat($haystack, $constraint, $message); + return $this->ignoreDeprecatedCodeUnits; + } + public function disableCodeCoverageIgnore() : bool + { + return $this->disableCodeCoverageIgnore; + } + public function processUncoveredFiles() : bool + { + return $this->processUncoveredFiles; } /** - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @psalm-assert-if-true !null $this->clover */ - public static function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void + public function hasClover() : bool { - $constraint = new \PHPUnit\Framework\Constraint\StringContains($needle, \true); - static::assertThat($haystack, $constraint, $message); + return $this->clover !== null; } /** - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception */ - public static function assertStringNotContainsString(string $needle, string $haystack, string $message = '') : void + public function clover() : Clover { - $constraint = new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\StringContains($needle)); - static::assertThat($haystack, $constraint, $message); + if (!$this->hasClover()) { + throw new Exception('Code Coverage report "Clover XML" has not been configured'); + } + return $this->clover; } /** - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @psalm-assert-if-true !null $this->cobertura */ - public static function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void + public function hasCobertura() : bool { - $constraint = new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\StringContains($needle, \true)); - static::assertThat($haystack, $constraint, $message); + return $this->cobertura !== null; } /** - * Asserts that a string ends with a given suffix. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception */ - public static function assertStringEndsWith(string $suffix, string $string, string $message = '') : void + public function cobertura() : Cobertura { - static::assertThat($string, new \PHPUnit\Framework\Constraint\StringEndsWith($suffix), $message); + if (!$this->hasCobertura()) { + throw new Exception('Code Coverage report "Cobertura XML" has not been configured'); + } + return $this->cobertura; } /** - * Asserts that a string ends not with a given suffix. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @psalm-assert-if-true !null $this->crap4j */ - public static function assertStringEndsNotWith(string $suffix, string $string, string $message = '') : void + public function hasCrap4j() : bool { - static::assertThat($string, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\StringEndsWith($suffix)), $message); + return $this->crap4j !== null; } /** - * Asserts that two XML files are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws Exception */ - public static function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void + public function crap4j() : Crap4j { - $expected = (new \PHPUnit\Util\Xml\Loader())->loadFile($expectedFile); - $actual = (new \PHPUnit\Util\Xml\Loader())->loadFile($actualFile); - static::assertEquals($expected, $actual, $message); + if (!$this->hasCrap4j()) { + throw new Exception('Code Coverage report "Crap4J" has not been configured'); + } + return $this->crap4j; } /** - * Asserts that two XML files are not equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws \PHPUnit\Util\Exception + * @psalm-assert-if-true !null $this->html */ - public static function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void + public function hasHtml() : bool { - $expected = (new \PHPUnit\Util\Xml\Loader())->loadFile($expectedFile); - $actual = (new \PHPUnit\Util\Xml\Loader())->loadFile($actualFile); - static::assertNotEquals($expected, $actual, $message); + return $this->html !== null; } /** - * Asserts that two XML documents are equal. - * - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws \PHPUnit\Util\Xml\Exception + * @throws Exception */ - public static function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void + public function html() : Html { - if (!\is_string($actualXml)) { - self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - $actual = $actualXml; - } else { - $actual = (new \PHPUnit\Util\Xml\Loader())->load($actualXml); + if (!$this->hasHtml()) { + throw new Exception('Code Coverage report "HTML" has not been configured'); } - $expected = (new \PHPUnit\Util\Xml\Loader())->loadFile($expectedFile); - static::assertEquals($expected, $actual, $message); + return $this->html; } /** - * Asserts that two XML documents are not equal. - * - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws \PHPUnit\Util\Xml\Exception + * @psalm-assert-if-true !null $this->php */ - public static function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void + public function hasPhp() : bool { - if (!\is_string($actualXml)) { - self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - $actual = $actualXml; - } else { - $actual = (new \PHPUnit\Util\Xml\Loader())->load($actualXml); - } - $expected = (new \PHPUnit\Util\Xml\Loader())->loadFile($expectedFile); - static::assertNotEquals($expected, $actual, $message); + return $this->php !== null; } /** - * Asserts that two XML documents are equal. - * - * @param DOMDocument|string $expectedXml - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws \PHPUnit\Util\Xml\Exception + * @throws Exception */ - public static function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = '') : void + public function php() : Php { - if (!\is_string($expectedXml)) { - self::createWarning('Passing an argument of type DOMDocument for the $expectedXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - $expected = $expectedXml; - } else { - $expected = (new \PHPUnit\Util\Xml\Loader())->load($expectedXml); - } - if (!\is_string($actualXml)) { - self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - $actual = $actualXml; - } else { - $actual = (new \PHPUnit\Util\Xml\Loader())->load($actualXml); + if (!$this->hasPhp()) { + throw new Exception('Code Coverage report "PHP" has not been configured'); } - static::assertEquals($expected, $actual, $message); + return $this->php; } /** - * Asserts that two XML documents are not equal. - * - * @param DOMDocument|string $expectedXml - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws \PHPUnit\Util\Xml\Exception + * @psalm-assert-if-true !null $this->text */ - public static function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = '') : void + public function hasText() : bool { - if (!\is_string($expectedXml)) { - self::createWarning('Passing an argument of type DOMDocument for the $expectedXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - $expected = $expectedXml; - } else { - $expected = (new \PHPUnit\Util\Xml\Loader())->load($expectedXml); - } - if (!\is_string($actualXml)) { - self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); - $actual = $actualXml; - } else { - $actual = (new \PHPUnit\Util\Xml\Loader())->load($actualXml); - } - static::assertNotEquals($expected, $actual, $message); + return $this->text !== null; } /** - * Asserts that a hierarchy of DOMElements matches. - * - * @throws AssertionFailedError - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4091 + * @throws Exception */ - public static function assertEqualXMLStructure(\DOMElement $expectedElement, \DOMElement $actualElement, bool $checkAttributes = \false, string $message = '') : void + public function text() : Text { - self::createWarning('assertEqualXMLStructure() is deprecated and will be removed in PHPUnit 10.'); - $expectedElement = \PHPUnit\Util\Xml::import($expectedElement); - $actualElement = \PHPUnit\Util\Xml::import($actualElement); - static::assertSame($expectedElement->tagName, $actualElement->tagName, $message); - if ($checkAttributes) { - static::assertSame($expectedElement->attributes->length, $actualElement->attributes->length, \sprintf('%s%sNumber of attributes on node "%s" does not match', $message, !empty($message) ? "\n" : '', $expectedElement->tagName)); - for ($i = 0; $i < $expectedElement->attributes->length; $i++) { - $expectedAttribute = $expectedElement->attributes->item($i); - $actualAttribute = $actualElement->attributes->getNamedItem($expectedAttribute->name); - \assert($expectedAttribute instanceof \DOMAttr); - if (!$actualAttribute) { - static::fail(\sprintf('%s%sCould not find attribute "%s" on node "%s"', $message, !empty($message) ? "\n" : '', $expectedAttribute->name, $expectedElement->tagName)); - } - } - } - \PHPUnit\Util\Xml::removeCharacterDataNodes($expectedElement); - \PHPUnit\Util\Xml::removeCharacterDataNodes($actualElement); - static::assertSame($expectedElement->childNodes->length, $actualElement->childNodes->length, \sprintf('%s%sNumber of child nodes of "%s" differs', $message, !empty($message) ? "\n" : '', $expectedElement->tagName)); - for ($i = 0; $i < $expectedElement->childNodes->length; $i++) { - static::assertEqualXMLStructure($expectedElement->childNodes->item($i), $actualElement->childNodes->item($i), $checkAttributes, $message); + if (!$this->hasText()) { + throw new Exception('Code Coverage report "Text" has not been configured'); } + return $this->text; } /** - * Evaluates a PHPUnit\Framework\Constraint matcher object. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @psalm-assert-if-true !null $this->xml */ - public static function assertThat($value, \PHPUnit\Framework\Constraint\Constraint $constraint, string $message = '') : void + public function hasXml() : bool { - self::$count += \count($constraint); - $constraint->evaluate($value, $message); + return $this->xml !== null; } /** - * Asserts that a string is a valid JSON string. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception */ - public static function assertJson(string $actualJson, string $message = '') : void + public function xml() : Xml { - static::assertThat($actualJson, static::isJson(), $message); + if (!$this->hasXml()) { + throw new Exception('Code Coverage report "XML" has not been configured'); + } + return $this->xml; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage; + +use PHPUnit\SebastianBergmann\CodeCoverage\Filter; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class FilterMapper +{ + public function map(Filter $filter, \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage $configuration) : void + { + foreach ($configuration->directories() as $directory) { + $filter->includeDirectory($directory->path(), $directory->suffix(), $directory->prefix()); + } + foreach ($configuration->files() as $file) { + $filter->includeFile($file->path()); + } + foreach ($configuration->excludeDirectories() as $directory) { + $filter->excludeDirectory($directory->path(), $directory->suffix(), $directory->prefix()); + } + foreach ($configuration->excludeFiles() as $file) { + $filter->excludeFile($file->path()); + } } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; + +use PHPUnit\TextUI\XmlConfiguration\Directory; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class Xml +{ /** - * Asserts that two given JSON encoded objects or arrays are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var Directory */ - public static function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = '') : void + private $target; + public function __construct(Directory $target) { - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); - static::assertThat($actualJson, new \PHPUnit\Framework\Constraint\JsonMatches($expectedJson), $message); + $this->target = $target; } - /** - * Asserts that two given JSON encoded objects or arrays are not equal. - * - * @param string $expectedJson - * @param string $actualJson - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = '') : void + public function target() : Directory { - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); - static::assertThat($actualJson, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\JsonMatches($expectedJson)), $message); + return $this->target; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; + +use PHPUnit\TextUI\XmlConfiguration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class Php +{ /** - * Asserts that the generated JSON encoded object and the content of the given file are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var File */ - public static function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void + private $target; + public function __construct(File $target) { - static::assertFileExists($expectedFile, $message); - $expectedJson = \file_get_contents($expectedFile); - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); - static::assertThat($actualJson, new \PHPUnit\Framework\Constraint\JsonMatches($expectedJson), $message); + $this->target = $target; } - /** - * Asserts that the generated JSON encoded object and the content of the given file are not equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void + public function target() : File { - static::assertFileExists($expectedFile, $message); - $expectedJson = \file_get_contents($expectedFile); - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); - static::assertThat($actualJson, new \PHPUnit\Framework\Constraint\LogicalNot(new \PHPUnit\Framework\Constraint\JsonMatches($expectedJson)), $message); + return $this->target; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; + +use PHPUnit\TextUI\XmlConfiguration\Directory; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class Html +{ /** - * Asserts that two JSON files are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var Directory */ - public static function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void - { - static::assertFileExists($expectedFile, $message); - static::assertFileExists($actualFile, $message); - $actualJson = \file_get_contents($actualFile); - $expectedJson = \file_get_contents($expectedFile); - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); - $constraintExpected = new \PHPUnit\Framework\Constraint\JsonMatches($expectedJson); - $constraintActual = new \PHPUnit\Framework\Constraint\JsonMatches($actualJson); - static::assertThat($expectedJson, $constraintActual, $message); - static::assertThat($actualJson, $constraintExpected, $message); - } + private $target; /** - * Asserts that two JSON files are not equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var int */ - public static function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void - { - static::assertFileExists($expectedFile, $message); - static::assertFileExists($actualFile, $message); - $actualJson = \file_get_contents($actualFile); - $expectedJson = \file_get_contents($expectedFile); - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); - $constraintExpected = new \PHPUnit\Framework\Constraint\JsonMatches($expectedJson); - $constraintActual = new \PHPUnit\Framework\Constraint\JsonMatches($actualJson); - static::assertThat($expectedJson, new \PHPUnit\Framework\Constraint\LogicalNot($constraintActual), $message); - static::assertThat($actualJson, new \PHPUnit\Framework\Constraint\LogicalNot($constraintExpected), $message); - } + private $lowUpperBound; /** - * @throws Exception + * @var int */ - public static function logicalAnd() : \PHPUnit\Framework\Constraint\LogicalAnd - { - $constraints = \func_get_args(); - $constraint = new \PHPUnit\Framework\Constraint\LogicalAnd(); - $constraint->setConstraints($constraints); - return $constraint; - } - public static function logicalOr() : \PHPUnit\Framework\Constraint\LogicalOr - { - $constraints = \func_get_args(); - $constraint = new \PHPUnit\Framework\Constraint\LogicalOr(); - $constraint->setConstraints($constraints); - return $constraint; - } - public static function logicalNot(\PHPUnit\Framework\Constraint\Constraint $constraint) : \PHPUnit\Framework\Constraint\LogicalNot + private $highLowerBound; + public function __construct(Directory $target, int $lowUpperBound, int $highLowerBound) { - return new \PHPUnit\Framework\Constraint\LogicalNot($constraint); + $this->target = $target; + $this->lowUpperBound = $lowUpperBound; + $this->highLowerBound = $highLowerBound; } - public static function logicalXor() : \PHPUnit\Framework\Constraint\LogicalXor + public function target() : Directory { - $constraints = \func_get_args(); - $constraint = new \PHPUnit\Framework\Constraint\LogicalXor(); - $constraint->setConstraints($constraints); - return $constraint; + return $this->target; } - public static function anything() : \PHPUnit\Framework\Constraint\IsAnything + public function lowUpperBound() : int { - return new \PHPUnit\Framework\Constraint\IsAnything(); + return $this->lowUpperBound; } - public static function isTrue() : \PHPUnit\Framework\Constraint\IsTrue + public function highLowerBound() : int { - return new \PHPUnit\Framework\Constraint\IsTrue(); + return $this->highLowerBound; } - public static function callback(callable $callback) : \PHPUnit\Framework\Constraint\Callback +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; + +use PHPUnit\TextUI\XmlConfiguration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class Crap4j +{ + /** + * @var File + */ + private $target; + /** + * @var int + */ + private $threshold; + public function __construct(File $target, int $threshold) { - return new \PHPUnit\Framework\Constraint\Callback($callback); + $this->target = $target; + $this->threshold = $threshold; } - public static function isFalse() : \PHPUnit\Framework\Constraint\IsFalse + public function target() : File { - return new \PHPUnit\Framework\Constraint\IsFalse(); + return $this->target; } - public static function isJson() : \PHPUnit\Framework\Constraint\IsJson + public function threshold() : int { - return new \PHPUnit\Framework\Constraint\IsJson(); + return $this->threshold; } - public static function isNull() : \PHPUnit\Framework\Constraint\IsNull +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; + +use PHPUnit\TextUI\XmlConfiguration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class Text +{ + /** + * @var File + */ + private $target; + /** + * @var bool + */ + private $showUncoveredFiles; + /** + * @var bool + */ + private $showOnlySummary; + public function __construct(File $target, bool $showUncoveredFiles, bool $showOnlySummary) { - return new \PHPUnit\Framework\Constraint\IsNull(); + $this->target = $target; + $this->showUncoveredFiles = $showUncoveredFiles; + $this->showOnlySummary = $showOnlySummary; } - public static function isFinite() : \PHPUnit\Framework\Constraint\IsFinite + public function target() : File { - return new \PHPUnit\Framework\Constraint\IsFinite(); + return $this->target; } - public static function isInfinite() : \PHPUnit\Framework\Constraint\IsInfinite + public function showUncoveredFiles() : bool { - return new \PHPUnit\Framework\Constraint\IsInfinite(); + return $this->showUncoveredFiles; } - public static function isNan() : \PHPUnit\Framework\Constraint\IsNan + public function showOnlySummary() : bool { - return new \PHPUnit\Framework\Constraint\IsNan(); + return $this->showOnlySummary; } - public static function containsEqual($value) : \PHPUnit\Framework\Constraint\TraversableContainsEqual +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; + +use PHPUnit\TextUI\XmlConfiguration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class Clover +{ + /** + * @var File + */ + private $target; + public function __construct(File $target) { - return new \PHPUnit\Framework\Constraint\TraversableContainsEqual($value); + $this->target = $target; } - public static function containsIdentical($value) : \PHPUnit\Framework\Constraint\TraversableContainsIdentical + public function target() : File { - return new \PHPUnit\Framework\Constraint\TraversableContainsIdentical($value); + return $this->target; } - public static function containsOnly(string $type) : \PHPUnit\Framework\Constraint\TraversableContainsOnly +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report; + +use PHPUnit\TextUI\XmlConfiguration\File; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class Cobertura +{ + /** + * @var File + */ + private $target; + public function __construct(File $target) { - return new \PHPUnit\Framework\Constraint\TraversableContainsOnly($type); + $this->target = $target; } - public static function containsOnlyInstancesOf(string $className) : \PHPUnit\Framework\Constraint\TraversableContainsOnly + public function target() : File { - return new \PHPUnit\Framework\Constraint\TraversableContainsOnly($className, \false); + return $this->target; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Exception extends RuntimeException implements \PHPUnit\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class Php +{ /** - * @param int|string $key + * @var DirectoryCollection + */ + private $includePaths; + /** + * @var IniSettingCollection + */ + private $iniSettings; + /** + * @var ConstantCollection + */ + private $constants; + /** + * @var VariableCollection + */ + private $globalVariables; + /** + * @var VariableCollection + */ + private $envVariables; + /** + * @var VariableCollection + */ + private $postVariables; + /** + * @var VariableCollection + */ + private $getVariables; + /** + * @var VariableCollection + */ + private $cookieVariables; + /** + * @var VariableCollection + */ + private $serverVariables; + /** + * @var VariableCollection + */ + private $filesVariables; + /** + * @var VariableCollection */ - public static function arrayHasKey($key) : \PHPUnit\Framework\Constraint\ArrayHasKey + private $requestVariables; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection $includePaths, \PHPUnit\TextUI\XmlConfiguration\IniSettingCollection $iniSettings, \PHPUnit\TextUI\XmlConfiguration\ConstantCollection $constants, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $globalVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $envVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $postVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $getVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $cookieVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $serverVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $filesVariables, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $requestVariables) { - return new \PHPUnit\Framework\Constraint\ArrayHasKey($key); + $this->includePaths = $includePaths; + $this->iniSettings = $iniSettings; + $this->constants = $constants; + $this->globalVariables = $globalVariables; + $this->envVariables = $envVariables; + $this->postVariables = $postVariables; + $this->getVariables = $getVariables; + $this->cookieVariables = $cookieVariables; + $this->serverVariables = $serverVariables; + $this->filesVariables = $filesVariables; + $this->requestVariables = $requestVariables; } - public static function equalTo($value) : \PHPUnit\Framework\Constraint\IsEqual + public function includePaths() : \PHPUnit\TextUI\XmlConfiguration\DirectoryCollection { - return new \PHPUnit\Framework\Constraint\IsEqual($value, 0.0, \false, \false); + return $this->includePaths; } - public static function equalToCanonicalizing($value) : \PHPUnit\Framework\Constraint\IsEqualCanonicalizing + public function iniSettings() : \PHPUnit\TextUI\XmlConfiguration\IniSettingCollection { - return new \PHPUnit\Framework\Constraint\IsEqualCanonicalizing($value); + return $this->iniSettings; } - public static function equalToIgnoringCase($value) : \PHPUnit\Framework\Constraint\IsEqualIgnoringCase + public function constants() : \PHPUnit\TextUI\XmlConfiguration\ConstantCollection { - return new \PHPUnit\Framework\Constraint\IsEqualIgnoringCase($value); + return $this->constants; } - public static function equalToWithDelta($value, float $delta) : \PHPUnit\Framework\Constraint\IsEqualWithDelta + public function globalVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection { - return new \PHPUnit\Framework\Constraint\IsEqualWithDelta($value, $delta); + return $this->globalVariables; } - public static function isEmpty() : \PHPUnit\Framework\Constraint\IsEmpty + public function envVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection { - return new \PHPUnit\Framework\Constraint\IsEmpty(); + return $this->envVariables; } - public static function isWritable() : \PHPUnit\Framework\Constraint\IsWritable + public function postVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection { - return new \PHPUnit\Framework\Constraint\IsWritable(); + return $this->postVariables; } - public static function isReadable() : \PHPUnit\Framework\Constraint\IsReadable + public function getVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection { - return new \PHPUnit\Framework\Constraint\IsReadable(); + return $this->getVariables; } - public static function directoryExists() : \PHPUnit\Framework\Constraint\DirectoryExists + public function cookieVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection { - return new \PHPUnit\Framework\Constraint\DirectoryExists(); + return $this->cookieVariables; } - public static function fileExists() : \PHPUnit\Framework\Constraint\FileExists + public function serverVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection { - return new \PHPUnit\Framework\Constraint\FileExists(); + return $this->serverVariables; } - public static function greaterThan($value) : \PHPUnit\Framework\Constraint\GreaterThan + public function filesVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection { - return new \PHPUnit\Framework\Constraint\GreaterThan($value); + return $this->filesVariables; } - public static function greaterThanOrEqual($value) : \PHPUnit\Framework\Constraint\LogicalOr + public function requestVariables() : \PHPUnit\TextUI\XmlConfiguration\VariableCollection { - return static::logicalOr(new \PHPUnit\Framework\Constraint\IsEqual($value), new \PHPUnit\Framework\Constraint\GreaterThan($value)); + return $this->requestVariables; } - public static function classHasAttribute(string $attributeName) : \PHPUnit\Framework\Constraint\ClassHasAttribute +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use Countable; +use IteratorAggregate; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class VariableCollection implements Countable, IteratorAggregate +{ + /** + * @var Variable[] + */ + private $variables; + /** + * @param Variable[] $variables + */ + public static function fromArray(array $variables) : self { - return new \PHPUnit\Framework\Constraint\ClassHasAttribute($attributeName); + return new self(...$variables); } - public static function classHasStaticAttribute(string $attributeName) : \PHPUnit\Framework\Constraint\ClassHasStaticAttribute + private function __construct(\PHPUnit\TextUI\XmlConfiguration\Variable ...$variables) { - return new \PHPUnit\Framework\Constraint\ClassHasStaticAttribute($attributeName); + $this->variables = $variables; } - public static function objectHasAttribute($attributeName) : \PHPUnit\Framework\Constraint\ObjectHasAttribute + /** + * @return Variable[] + */ + public function asArray() : array { - return new \PHPUnit\Framework\Constraint\ObjectHasAttribute($attributeName); + return $this->variables; } - public static function identicalTo($value) : \PHPUnit\Framework\Constraint\IsIdentical + public function count() : int { - return new \PHPUnit\Framework\Constraint\IsIdentical($value); + return count($this->variables); } - public static function isInstanceOf(string $className) : \PHPUnit\Framework\Constraint\IsInstanceOf + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\VariableCollectionIterator { - return new \PHPUnit\Framework\Constraint\IsInstanceOf($className); + return new \PHPUnit\TextUI\XmlConfiguration\VariableCollectionIterator($this); } - public static function isType(string $type) : \PHPUnit\Framework\Constraint\IsType +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @psalm-immutable + */ +final class Variable +{ + /** + * @var string + */ + private $name; + /** + * @var mixed + */ + private $value; + /** + * @var bool + */ + private $force; + public function __construct(string $name, $value, bool $force) { - return new \PHPUnit\Framework\Constraint\IsType($type); + $this->name = $name; + $this->value = $value; + $this->force = $force; } - public static function lessThan($value) : \PHPUnit\Framework\Constraint\LessThan + public function name() : string { - return new \PHPUnit\Framework\Constraint\LessThan($value); + return $this->name; } - public static function lessThanOrEqual($value) : \PHPUnit\Framework\Constraint\LogicalOr + public function value() { - return static::logicalOr(new \PHPUnit\Framework\Constraint\IsEqual($value), new \PHPUnit\Framework\Constraint\LessThan($value)); + return $this->value; } - public static function matchesRegularExpression(string $pattern) : \PHPUnit\Framework\Constraint\RegularExpression + public function force() : bool { - return new \PHPUnit\Framework\Constraint\RegularExpression($pattern); + return $this->force; } - public static function matches(string $string) : \PHPUnit\Framework\Constraint\StringMatchesFormatDescription +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ConstantCollectionIterator implements Countable, Iterator +{ + /** + * @var Constant[] + */ + private $constants; + /** + * @var int + */ + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\ConstantCollection $constants) { - return new \PHPUnit\Framework\Constraint\StringMatchesFormatDescription($string); + $this->constants = $constants->asArray(); } - public static function stringStartsWith($prefix) : \PHPUnit\Framework\Constraint\StringStartsWith + public function count() : int { - return new \PHPUnit\Framework\Constraint\StringStartsWith($prefix); + return iterator_count($this); } - public static function stringContains(string $string, bool $case = \true) : \PHPUnit\Framework\Constraint\StringContains + public function rewind() : void { - return new \PHPUnit\Framework\Constraint\StringContains($string, $case); + $this->position = 0; } - public static function stringEndsWith(string $suffix) : \PHPUnit\Framework\Constraint\StringEndsWith + public function valid() : bool { - return new \PHPUnit\Framework\Constraint\StringEndsWith($suffix); + return $this->position < count($this->constants); } - public static function countOf(int $count) : \PHPUnit\Framework\Constraint\Count + public function key() : int { - return new \PHPUnit\Framework\Constraint\Count($count); + return $this->position; } - public static function objectEquals(object $object, string $method = 'equals') : \PHPUnit\Framework\Constraint\ObjectEquals + public function current() : \PHPUnit\TextUI\XmlConfiguration\Constant { - return new \PHPUnit\Framework\Constraint\ObjectEquals($object, $method); + return $this->constants[$this->position]; } - /** - * Fails a test with the given message. - * - * @throws AssertionFailedError - * - * @psalm-return never-return - */ - public static function fail(string $message = '') : void + public function next() : void { - self::$count++; - throw new \PHPUnit\Framework\AssertionFailedError($message); + $this->position++; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function count; +use function iterator_count; +use Countable; +use Iterator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class IniSettingCollectionIterator implements Countable, Iterator +{ /** - * Mark the test as incomplete. - * - * @throws IncompleteTestError - * - * @psalm-return never-return + * @var IniSetting[] */ - public static function markTestIncomplete(string $message = '') : void - { - throw new \PHPUnit\Framework\IncompleteTestError($message); - } + private $iniSettings; /** - * Mark the test as skipped. - * - * @throws SkippedTestError - * @throws SyntheticSkippedError - * - * @psalm-return never-return + * @var int */ - public static function markTestSkipped(string $message = '') : void + private $position; + public function __construct(\PHPUnit\TextUI\XmlConfiguration\IniSettingCollection $iniSettings) { - if ($hint = self::detectLocationHint($message)) { - $trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS); - \array_unshift($trace, $hint); - throw new \PHPUnit\Framework\SyntheticSkippedError($hint['message'], 0, $hint['file'], (int) $hint['line'], $trace); - } - throw new \PHPUnit\Framework\SkippedTestError($message); + $this->iniSettings = $iniSettings->asArray(); } - /** - * Return the current assertion count. - */ - public static function getCount() : int + public function count() : int { - return self::$count; + return iterator_count($this); } - /** - * Reset the assertion counter. - */ - public static function resetCount() : void + public function rewind() : void { - self::$count = 0; + $this->position = 0; } - private static function detectLocationHint(string $message) : ?array + public function valid() : bool { - $hint = null; - $lines = \preg_split('/\\r\\n|\\r|\\n/', $message); - while (\strpos($lines[0], '__OFFSET') !== \false) { - $offset = \explode('=', \array_shift($lines)); - if ($offset[0] === '__OFFSET_FILE') { - $hint['file'] = $offset[1]; - } - if ($offset[0] === '__OFFSET_LINE') { - $hint['line'] = $offset[1]; - } - } - if ($hint) { - $hint['message'] = \implode(\PHP_EOL, $lines); - } - return $hint; + return $this->position < count($this->iniSettings); } - private static function isValidObjectAttributeName(string $attributeName) : bool + public function key() : int { - return (bool) \preg_match('/[^\\x00-\\x1f\\x7f-\\x9f]+/', $attributeName); + return $this->position; } - private static function isValidClassAttributeName(string $attributeName) : bool + public function current() : \PHPUnit\TextUI\XmlConfiguration\IniSetting { - return (bool) \preg_match('/[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*/', $attributeName); + return $this->iniSettings[$this->position]; } - /** - * @codeCoverageIgnore - */ - private static function createWarning(string $warning) : void + public function next() : void { - foreach (\debug_backtrace() as $step) { - if (isset($step['object']) && $step['object'] instanceof \PHPUnit\Framework\TestCase) { - \assert($step['object'] instanceof \PHPUnit\Framework\TestCase); - $step['object']->addWarning($warning); - break; - } - } + $this->position++; } } message = $message; + return new self(...$iniSettings); } - public function getMessage() : string + private function __construct(\PHPUnit\TextUI\XmlConfiguration\IniSetting ...$iniSettings) { - return $this->message; + $this->iniSettings = $iniSettings; } /** - * Returns a string representation of the test case. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @return IniSetting[] */ - public function toString() : string + public function asArray() : array { - return $this->getName(); + return $this->iniSettings; } - /** - * @throws Exception - */ - protected function runTest() : void + public function count() : int { - $this->markTestIncomplete($this->message); + return count($this->iniSettings); + } + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\IniSettingCollectionIterator + { + return new \PHPUnit\TextUI\XmlConfiguration\IniSettingCollectionIterator($this); } } - */ - private $dependencies = []; - /** - * @param list $dependencies - */ - public function setDependencies(array $dependencies) : void + public function handle(\PHPUnit\TextUI\XmlConfiguration\Php $configuration) : void { - $this->dependencies = $dependencies; - foreach ($this->tests as $test) { - if (!$test instanceof \PHPUnit\Framework\TestCase) { - // @codeCoverageIgnoreStart - continue; - // @codeCoverageIgnoreStart + $this->handleIncludePaths($configuration->includePaths()); + $this->handleIniSettings($configuration->iniSettings()); + $this->handleConstants($configuration->constants()); + $this->handleGlobalVariables($configuration->globalVariables()); + $this->handleServerVariables($configuration->serverVariables()); + $this->handleEnvVariables($configuration->envVariables()); + $this->handleVariables('_POST', $configuration->postVariables()); + $this->handleVariables('_GET', $configuration->getVariables()); + $this->handleVariables('_COOKIE', $configuration->cookieVariables()); + $this->handleVariables('_FILES', $configuration->filesVariables()); + $this->handleVariables('_REQUEST', $configuration->requestVariables()); + } + private function handleIncludePaths(\PHPUnit\TextUI\XmlConfiguration\DirectoryCollection $includePaths) : void + { + if (!$includePaths->isEmpty()) { + $includePathsAsStrings = []; + foreach ($includePaths as $includePath) { + $includePathsAsStrings[] = $includePath->path(); } - $test->setDependencies($dependencies); + ini_set('include_path', implode(\PATH_SEPARATOR, $includePathsAsStrings) . \PATH_SEPARATOR . ini_get('include_path')); } } - /** - * @return list - */ - public function provides() : array + private function handleIniSettings(\PHPUnit\TextUI\XmlConfiguration\IniSettingCollection $iniSettings) : void { - if ($this->providedTests === null) { - $this->providedTests = [new \PHPUnit\Framework\ExecutionOrderDependency($this->getName())]; + foreach ($iniSettings as $iniSetting) { + $value = $iniSetting->value(); + if (defined($value)) { + $value = (string) constant($value); + } + ini_set($iniSetting->name(), $value); } - return $this->providedTests; } - /** - * @return list - */ - public function requires() : array + private function handleConstants(\PHPUnit\TextUI\XmlConfiguration\ConstantCollection $constants) : void { - // A DataProviderTestSuite does not have to traverse its child tests - // as these are inherited and cannot reference dataProvider rows directly - return $this->dependencies; + foreach ($constants as $constant) { + if (!defined($constant->name())) { + define($constant->name(), $constant->value()); + } + } } - /** - * Returns the size of the each test created using the data provider(s). - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function getSize() : int + private function handleGlobalVariables(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void + { + foreach ($variables as $variable) { + $GLOBALS[$variable->name()] = $variable->value(); + } + } + private function handleServerVariables(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void + { + foreach ($variables as $variable) { + $_SERVER[$variable->name()] = $variable->value(); + } + } + private function handleVariables(string $target, \PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void + { + foreach ($variables as $variable) { + $GLOBALS[$target][$variable->name()] = $variable->value(); + } + } + private function handleEnvVariables(\PHPUnit\TextUI\XmlConfiguration\VariableCollection $variables) : void { - [$className, $methodName] = \explode('::', $this->getName()); - return \PHPUnit\Util\Test::getSize($className, $methodName); + foreach ($variables as $variable) { + $name = $variable->name(); + $value = $variable->value(); + $force = $variable->force(); + if ($force || getenv($name) === \false) { + putenv("{$name}={$value}"); + } + $value = getenv($name); + if ($force || !isset($_ENV[$name])) { + $_ENV[$name] = $value; + } + } } } name = $name; + $this->value = $value; + } + public function name() : string + { + return $this->name; + } + public function value() + { + return $this->value; + } } variables = $variables->asArray(); + } + public function count() : int + { + return iterator_count($this); + } + public function rewind() : void + { + $this->position = 0; + } + public function valid() : bool + { + return $this->position < count($this->variables); + } + public function key() : int + { + return $this->position; + } + public function current() : \PHPUnit\TextUI\XmlConfiguration\Variable + { + return $this->variables[$this->position]; + } + public function next() : void + { + $this->position++; + } } getMessage(); + return new self(...$constants); + } + private function __construct(\PHPUnit\TextUI\XmlConfiguration\Constant ...$constants) + { + $this->constants = $constants; + } + /** + * @return Constant[] + */ + public function asArray() : array + { + return $this->constants; + } + public function count() : int + { + return count($this->constants); + } + public function getIterator() : \PHPUnit\TextUI\XmlConfiguration\ConstantCollectionIterator + { + return new \PHPUnit\TextUI\XmlConfiguration\ConstantCollectionIterator($this); } } name = $name; + $this->value = $value; + } + public function name() : string + { + return $this->name; + } + public function value() : string + { + return $this->value; + } } detect($filename); + if (!$origin->detected()) { + throw new \PHPUnit\TextUI\XmlConfiguration\Exception(sprintf('"%s" is not a valid PHPUnit XML configuration file that can be migrated', $filename)); + } + $configurationDocument = (new XmlLoader())->loadFile($filename, \false, \true, \true); + foreach ((new \PHPUnit\TextUI\XmlConfiguration\MigrationBuilder())->build($origin->version()) as $migration) { + $migration->migrate($configurationDocument); + } + $configurationDocument->formatOutput = \true; + $configurationDocument->preserveWhiteSpace = \false; + return $configurationDocument->saveXML(); + } } syntheticFile = $file; - $this->syntheticLine = $line; - $this->syntheticTrace = $trace; - } - public function getSyntheticFile() : string - { - return $this->syntheticFile; - } - public function getSyntheticLine() : int - { - return $this->syntheticLine; - } - public function getSyntheticTrace() : array - { - return $this->syntheticTrace; - } } getMessage() . \PHP_EOL; + $whitelist = $document->getElementsByTagName('whitelist')->item(0); + if (!$whitelist) { + return; + } + $coverage = $document->getElementsByTagName('coverage')->item(0); + if (!$coverage instanceof DOMElement) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); + } + $map = ['addUncoveredFilesFromWhitelist' => 'includeUncoveredFiles', 'processUncoveredFilesFromWhitelist' => 'processUncoveredFiles']; + foreach ($map as $old => $new) { + if (!$whitelist->hasAttribute($old)) { + continue; + } + $coverage->setAttribute($new, $whitelist->getAttribute($old)); + $whitelist->removeAttribute($old); + } } } 'disableCodeCoverageIgnore', 'ignoreDeprecatedCodeUnitsFromCodeCoverage' => 'ignoreDeprecatedCodeUnits']; + $root = $document->documentElement; + $coverage = $document->getElementsByTagName('coverage')->item(0); + if (!$coverage instanceof DOMElement) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); + } + foreach ($map as $old => $new) { + if (!$root->hasAttribute($old)) { + continue; + } + $coverage->setAttribute($new, $root->getAttribute($old)); + $root->removeAttribute($old); + } + } } documentElement; + if ($root->hasAttribute('cacheTokens')) { + $root->removeAttribute('cacheTokens'); + } + } } getMessage() . \PHP_EOL; + $logging = $document->getElementsByTagName('logging')->item(0); + if (!$logging instanceof DOMElement) { + return; + } + $types = ['junit' => 'junit', 'teamcity' => 'teamcity', 'testdox-html' => 'testdoxHtml', 'testdox-text' => 'testdoxText', 'testdox-xml' => 'testdoxXml', 'plain' => 'text']; + $logNodes = []; + foreach ($logging->getElementsByTagName('log') as $logNode) { + if (!isset($types[$logNode->getAttribute('type')])) { + continue; + } + $logNodes[] = $logNode; + } + foreach ($logNodes as $oldNode) { + $newLogNode = $document->createElement($types[$oldNode->getAttribute('type')]); + $newLogNode->setAttribute('outputFile', $oldNode->getAttribute('target')); + $logging->replaceChild($newLogNode, $oldNode); + } } } serializableTrace = $this->getTrace(); - foreach (\array_keys($this->serializableTrace) as $key) { - unset($this->serializableTrace[$key]['args']); - } - } - public function __toString() : string - { - $string = \PHPUnit\Framework\TestFailure::exceptionToString($this); - if ($trace = \PHPUnit\Util\Filter::getFilteredStacktrace($this)) { - $string .= "\n" . $trace; - } - return $string; - } - public function __sleep() : array + protected function forType() : string { - return \array_keys(\get_object_vars($this)); + return 'coverage-clover'; } - /** - * Returns the serializable trace (without 'args'). - */ - public function getSerializableTrace() : array + protected function toReportFormat(DOMElement $logNode) : DOMElement { - return $this->serializableTrace; + $clover = $logNode->ownerDocument->createElement('clover'); + $clover->setAttribute('outputFile', $logNode->getAttribute('target')); + return $clover; } } getMessage() . \PHP_EOL; + $php = $logNode->ownerDocument->createElement('php'); + $php->setAttribute('outputFile', $logNode->getAttribute('target')); + return $php; } } getMessage() . \PHP_EOL; + $document->documentElement->setAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation', 'https://schema.phpunit.de/9.3/phpunit.xsd'); } } getMessage() . \PHP_EOL; + $crap4j = $logNode->ownerDocument->createElement('crap4j'); + $crap4j->setAttribute('outputFile', $logNode->getAttribute('target')); + $this->migrateAttributes($logNode, $crap4j, ['threshold']); + return $crap4j; } } diff = $diff; - } - public function getDiff() : string + public function migrate(DOMDocument $document) : void { - return $this->diff; + $coverage = $document->createElement('coverage'); + $document->documentElement->insertBefore($coverage, $document->documentElement->firstChild); } } getElementsByTagName('whitelist')->item(0); + if ($whitelist instanceof DOMElement) { + $this->ensureEmpty($whitelist); + $whitelist->parentNode->removeChild($whitelist); + } + $filter = $document->getElementsByTagName('filter')->item(0); + if ($filter instanceof DOMElement) { + $this->ensureEmpty($filter); + $filter->parentNode->removeChild($filter); + } } - private function __construct(string $message = '', int $code = 0, \Exception $previous = null) + /** + * @throws MigrationException + */ + private function ensureEmpty(DOMElement $element) : void { - parent::__construct($message, $code, $previous); + if ($element->attributes->length > 0) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException(sprintf('%s element has unexpected attributes', $element->nodeName)); + } + if ($element->getElementsByTagName('*')->length > 0) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException(sprintf('%s element has unexpected children', $element->nodeName)); + } } } getElementsByTagName('whitelist')->item(0); + if ($whitelist === null) { + return; + } + $coverage = $document->getElementsByTagName('coverage')->item(0); + if (!$coverage instanceof DOMElement) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); + } + $include = $document->createElement('include'); + $coverage->appendChild($include); + foreach (SnapshotNodeList::fromNodeList($whitelist->childNodes) as $child) { + if (!$child instanceof DOMElement || $child->nodeName !== 'directory') { + continue; + } + $include->appendChild($child); + } + } } getElementsByTagName('logging')->item(0); + if (!$logging instanceof DOMElement) { + return; + } + foreach (SnapshotNodeList::fromNodeList($logging->getElementsByTagName('log')) as $logNode) { + switch ($logNode->getAttribute('type')) { + case 'json': + case 'tap': + $logging->removeChild($logNode); + } + } + } } ownerDocument->createElement('html'); + $html->setAttribute('outputDirectory', $logNode->getAttribute('target')); + $this->migrateAttributes($logNode, $html, ['lowUpperBound', 'highLowerBound']); + return $html; + } } getElementsByTagName('coverage')->item(0); + if (!$coverage instanceof DOMElement) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); + } + $logNode = $this->findLogNode($document); + if ($logNode === null) { + return; + } + $reportChild = $this->toReportFormat($logNode); + $report = $coverage->getElementsByTagName('report')->item(0); + if ($report === null) { + $report = $coverage->appendChild($document->createElement('report')); + } + $report->appendChild($reportChild); + $logNode->parentNode->removeChild($logNode); + } + protected function migrateAttributes(DOMElement $src, DOMElement $dest, array $attributes) : void + { + foreach ($attributes as $attr) { + if (!$src->hasAttribute($attr)) { + continue; + } + $dest->setAttribute($attr, $src->getAttribute($attr)); + $src->removeAttribute($attr); + } + } + protected abstract function forType() : string; + protected abstract function toReportFormat(DOMElement $logNode) : DOMElement; + private function findLogNode(DOMDocument $document) : ?DOMElement + { + $logNode = (new DOMXPath($document))->query(sprintf('//logging/log[@type="%s"]', $this->forType()))->item(0); + if (!$logNode instanceof DOMElement) { + return null; + } + return $logNode; + } } ownerDocument->createElement('text'); + $text->setAttribute('outputFile', $logNode->getAttribute('target')); + $this->migrateAttributes($logNode, $text, ['showUncoveredFiles', 'showOnlySummary']); + return $text; + } } getElementsByTagName('whitelist')->item(0); + if ($whitelist === null) { + return; + } + $excludeNodes = SnapshotNodeList::fromNodeList($whitelist->getElementsByTagName('exclude')); + if ($excludeNodes->count() === 0) { + return; + } + $coverage = $document->getElementsByTagName('coverage')->item(0); + if (!$coverage instanceof DOMElement) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Unexpected state - No coverage element'); + } + $targetExclude = $coverage->getElementsByTagName('exclude')->item(0); + if ($targetExclude === null) { + $targetExclude = $coverage->appendChild($document->createElement('exclude')); + } + foreach ($excludeNodes as $excludeNode) { + \assert($excludeNode instanceof DOMElement); + foreach (SnapshotNodeList::fromNodeList($excludeNode->childNodes) as $child) { + if (!$child instanceof DOMElement || !\in_array($child->nodeName, ['directory', 'file'], \true)) { + continue; + } + $targetExclude->appendChild($child); + } + if ($excludeNode->getElementsByTagName('*')->count() !== 0) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationException('Dangling child elements in exclude found.'); + } + $whitelist->removeChild($excludeNode); + } + } } comparisonFailure = $comparisonFailure; - parent::__construct($message, 0, $previous); + return 'coverage-xml'; } - public function getComparisonFailure() : ?\PHPUnit\SebastianBergmann\Comparator\ComparisonFailure + protected function toReportFormat(DOMElement $logNode) : DOMElement { - return $this->comparisonFailure; + $xml = $logNode->ownerDocument->createElement('xml'); + $xml->setAttribute('outputDirectory', $logNode->getAttribute('target')); + return $xml; } } [\PHPUnit\TextUI\XmlConfiguration\RemoveLogTypes::class], '9.2' => [\PHPUnit\TextUI\XmlConfiguration\RemoveCacheTokensAttribute::class, \PHPUnit\TextUI\XmlConfiguration\IntroduceCoverageElement::class, \PHPUnit\TextUI\XmlConfiguration\MoveAttributesFromRootToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveAttributesFromFilterWhitelistToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveWhitelistDirectoriesToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\MoveWhitelistExcludesToCoverage::class, \PHPUnit\TextUI\XmlConfiguration\RemoveEmptyFilter::class, \PHPUnit\TextUI\XmlConfiguration\CoverageCloverToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageCrap4jToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageHtmlToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoveragePhpToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageTextToReport::class, \PHPUnit\TextUI\XmlConfiguration\CoverageXmlToReport::class, \PHPUnit\TextUI\XmlConfiguration\ConvertLogTypes::class, \PHPUnit\TextUI\XmlConfiguration\UpdateSchemaLocationTo93::class]]; /** - * Wrapper for getMessage() which is declared as final. + * @throws MigrationBuilderException */ - public function toString() : string + public function build(string $fromVersion) : array { - return $this->getMessage(); + if (!array_key_exists($fromVersion, self::AVAILABLE_MIGRATIONS)) { + throw new \PHPUnit\TextUI\XmlConfiguration\MigrationBuilderException(sprintf('Migration from schema version %s is not supported', $fromVersion)); + } + $stack = []; + foreach (self::AVAILABLE_MIGRATIONS as $version => $migrations) { + if (version_compare($version, $fromVersion, '<')) { + continue; + } + foreach ($migrations as $migration) { + $stack[] = new $migration(); + } + } + return $stack; } } [['text' => 'phpunit [options] UnitTest.php'], ['text' => 'phpunit [options] ']], 'Code Coverage Options' => [['arg' => '--coverage-clover ', 'desc' => 'Generate code coverage report in Clover XML format'], ['arg' => '--coverage-cobertura ', 'desc' => 'Generate code coverage report in Cobertura XML format'], ['arg' => '--coverage-crap4j ', 'desc' => 'Generate code coverage report in Crap4J XML format'], ['arg' => '--coverage-html ', 'desc' => 'Generate code coverage report in HTML format'], ['arg' => '--coverage-php ', 'desc' => 'Export PHP_CodeCoverage object to file'], ['arg' => '--coverage-text=', 'desc' => 'Generate code coverage report in text format [default: standard output]'], ['arg' => '--coverage-xml ', 'desc' => 'Generate code coverage report in PHPUnit XML format'], ['arg' => '--coverage-cache ', 'desc' => 'Cache static analysis results'], ['arg' => '--warm-coverage-cache', 'desc' => 'Warm static analysis cache'], ['arg' => '--coverage-filter ', 'desc' => 'Include in code coverage analysis'], ['arg' => '--path-coverage', 'desc' => 'Perform path coverage analysis'], ['arg' => '--disable-coverage-ignore', 'desc' => 'Disable annotations for ignoring code coverage'], ['arg' => '--no-coverage', 'desc' => 'Ignore code coverage configuration']], 'Logging Options' => [['arg' => '--log-junit ', 'desc' => 'Log test execution in JUnit XML format to file'], ['arg' => '--log-teamcity ', 'desc' => 'Log test execution in TeamCity format to file'], ['arg' => '--testdox-html ', 'desc' => 'Write agile documentation in HTML format to file'], ['arg' => '--testdox-text ', 'desc' => 'Write agile documentation in Text format to file'], ['arg' => '--testdox-xml ', 'desc' => 'Write agile documentation in XML format to file'], ['arg' => '--reverse-list', 'desc' => 'Print defects in reverse order'], ['arg' => '--no-logging', 'desc' => 'Ignore logging configuration']], 'Test Selection Options' => [['arg' => '--list-suites', 'desc' => 'List available test suites'], ['arg' => '--testsuite ', 'desc' => 'Filter which testsuite to run'], ['arg' => '--list-groups', 'desc' => 'List available test groups'], ['arg' => '--group ', 'desc' => 'Only runs tests from the specified group(s)'], ['arg' => '--exclude-group ', 'desc' => 'Exclude tests from the specified group(s)'], ['arg' => '--covers ', 'desc' => 'Only runs tests annotated with "@covers "'], ['arg' => '--uses ', 'desc' => 'Only runs tests annotated with "@uses "'], ['arg' => '--list-tests', 'desc' => 'List available tests'], ['arg' => '--list-tests-xml ', 'desc' => 'List available tests in XML format'], ['arg' => '--filter ', 'desc' => 'Filter which tests to run'], ['arg' => '--test-suffix ', 'desc' => 'Only search for test in files with specified suffix(es). Default: Test.php,.phpt']], 'Test Execution Options' => [['arg' => '--dont-report-useless-tests', 'desc' => 'Do not report tests that do not test anything'], ['arg' => '--strict-coverage', 'desc' => 'Be strict about @covers annotation usage'], ['arg' => '--strict-global-state', 'desc' => 'Be strict about changes to global state'], ['arg' => '--disallow-test-output', 'desc' => 'Be strict about output during tests'], ['arg' => '--disallow-resource-usage', 'desc' => 'Be strict about resource usage during small tests'], ['arg' => '--enforce-time-limit', 'desc' => 'Enforce time limit based on test size'], ['arg' => '--default-time-limit ', 'desc' => 'Timeout in seconds for tests without @small, @medium or @large'], ['arg' => '--disallow-todo-tests', 'desc' => 'Disallow @todo-annotated tests'], ['spacer' => ''], ['arg' => '--process-isolation', 'desc' => 'Run each test in a separate PHP process'], ['arg' => '--globals-backup', 'desc' => 'Backup and restore $GLOBALS for each test'], ['arg' => '--static-backup', 'desc' => 'Backup and restore static attributes for each test'], ['spacer' => ''], ['arg' => '--colors ', 'desc' => 'Use colors in output ("never", "auto" or "always")'], ['arg' => '--columns ', 'desc' => 'Number of columns to use for progress output'], ['arg' => '--columns max', 'desc' => 'Use maximum number of columns for progress output'], ['arg' => '--stderr', 'desc' => 'Write to STDERR instead of STDOUT'], ['arg' => '--stop-on-defect', 'desc' => 'Stop execution upon first not-passed test'], ['arg' => '--stop-on-error', 'desc' => 'Stop execution upon first error'], ['arg' => '--stop-on-failure', 'desc' => 'Stop execution upon first error or failure'], ['arg' => '--stop-on-warning', 'desc' => 'Stop execution upon first warning'], ['arg' => '--stop-on-risky', 'desc' => 'Stop execution upon first risky test'], ['arg' => '--stop-on-skipped', 'desc' => 'Stop execution upon first skipped test'], ['arg' => '--stop-on-incomplete', 'desc' => 'Stop execution upon first incomplete test'], ['arg' => '--fail-on-incomplete', 'desc' => 'Treat incomplete tests as failures'], ['arg' => '--fail-on-risky', 'desc' => 'Treat risky tests as failures'], ['arg' => '--fail-on-skipped', 'desc' => 'Treat skipped tests as failures'], ['arg' => '--fail-on-warning', 'desc' => 'Treat tests with warnings as failures'], ['arg' => '-v|--verbose', 'desc' => 'Output more verbose information'], ['arg' => '--debug', 'desc' => 'Display debugging information'], ['spacer' => ''], ['arg' => '--repeat ', 'desc' => 'Runs the test(s) repeatedly'], ['arg' => '--teamcity', 'desc' => 'Report test execution progress in TeamCity format'], ['arg' => '--testdox', 'desc' => 'Report test execution progress in TestDox format'], ['arg' => '--testdox-group', 'desc' => 'Only include tests from the specified group(s)'], ['arg' => '--testdox-exclude-group', 'desc' => 'Exclude tests from the specified group(s)'], ['arg' => '--no-interaction', 'desc' => 'Disable TestDox progress animation'], ['arg' => '--printer ', 'desc' => 'TestListener implementation to use'], ['spacer' => ''], ['arg' => '--order-by ', 'desc' => 'Run tests in order: default|defects|duration|no-depends|random|reverse|size'], ['arg' => '--random-order-seed ', 'desc' => 'Use a specific random seed for random order'], ['arg' => '--cache-result', 'desc' => 'Write test results to cache file'], ['arg' => '--do-not-cache-result', 'desc' => 'Do not write test results to cache file']], 'Configuration Options' => [['arg' => '--prepend ', 'desc' => 'A PHP script that is included as early as possible'], ['arg' => '--bootstrap ', 'desc' => 'A PHP script that is included before the tests run'], ['arg' => '-c|--configuration ', 'desc' => 'Read configuration from XML file'], ['arg' => '--no-configuration', 'desc' => 'Ignore default configuration file (phpunit.xml)'], ['arg' => '--extensions ', 'desc' => 'A comma separated list of PHPUnit extensions to load'], ['arg' => '--no-extensions', 'desc' => 'Do not load PHPUnit extensions'], ['arg' => '--include-path ', 'desc' => 'Prepend PHP\'s include_path with given path(s)'], ['arg' => '-d ', 'desc' => 'Sets a php.ini value'], ['arg' => '--cache-result-file ', 'desc' => 'Specify result cache path and filename'], ['arg' => '--generate-configuration', 'desc' => 'Generate configuration file with suggested settings'], ['arg' => '--migrate-configuration', 'desc' => 'Migrate configuration file to current format']], 'Miscellaneous Options' => [['arg' => '-h|--help', 'desc' => 'Prints this usage information'], ['arg' => '--version', 'desc' => 'Prints the version and exits'], ['arg' => '--atleast-version ', 'desc' => 'Checks that version is greater than min and exits'], ['arg' => '--check-version', 'desc' => 'Check whether PHPUnit is the latest version']]]; + /** + * @var int Number of columns required to write the longest option name to the console + */ + private $maxArgLength = 0; + /** + * @var int Number of columns left for the description field after padding and option + */ + private $maxDescLength; + /** + * @var bool Use color highlights for sections, options and parameters + */ + private $hasColor = \false; + public function __construct(?int $width = null, ?bool $withColor = null) + { + if ($width === null) { + $width = (new Console())->getNumberOfColumns(); + } + if ($withColor === null) { + $this->hasColor = (new Console())->hasColorSupport(); + } else { + $this->hasColor = $withColor; + } + foreach (self::HELP_TEXT as $options) { + foreach ($options as $option) { + if (isset($option['arg'])) { + $this->maxArgLength = max($this->maxArgLength, isset($option['arg']) ? strlen($option['arg']) : 0); + } + } + } + $this->maxDescLength = $width - $this->maxArgLength - 4; + } + /** + * Write the help file to the CLI, adapting width and colors to the console. + */ + public function writeToConsole() : void + { + if ($this->hasColor) { + $this->writeWithColor(); + } else { + $this->writePlaintext(); + } + } + private function writePlaintext() : void { - parent::__construct(\sprintf('Comparison method %s::%s() does not exist.', $className, $methodName), 0, null); + foreach (self::HELP_TEXT as $section => $options) { + print "{$section}:" . \PHP_EOL; + if ($section !== 'Usage') { + print \PHP_EOL; + } + foreach ($options as $option) { + if (isset($option['spacer'])) { + print \PHP_EOL; + } + if (isset($option['text'])) { + print self::LEFT_MARGIN . $option['text'] . \PHP_EOL; + } + if (isset($option['arg'])) { + $arg = str_pad($option['arg'], $this->maxArgLength); + print self::LEFT_MARGIN . $arg . ' ' . $option['desc'] . \PHP_EOL; + } + } + print \PHP_EOL; + } } - public function __toString() : string + private function writeWithColor() : void { - return $this->getMessage() . \PHP_EOL; + foreach (self::HELP_TEXT as $section => $options) { + print Color::colorize('fg-yellow', "{$section}:") . \PHP_EOL; + foreach ($options as $option) { + if (isset($option['spacer'])) { + print \PHP_EOL; + } + if (isset($option['text'])) { + print self::LEFT_MARGIN . $option['text'] . \PHP_EOL; + } + if (isset($option['arg'])) { + $arg = Color::colorize('fg-green', str_pad($option['arg'], $this->maxArgLength)); + $arg = preg_replace_callback('/(<[^>]+>)/', static function ($matches) { + return Color::colorize('fg-cyan', $matches[0]); + }, $arg); + $desc = explode(\PHP_EOL, wordwrap($option['desc'], $this->maxDescLength, \PHP_EOL)); + print self::LEFT_MARGIN . $arg . ' ' . $desc[0] . \PHP_EOL; + for ($i = 1; $i < count($desc); $i++) { + print str_repeat(' ', $this->maxArgLength + 3) . $desc[$i] . \PHP_EOL; + } + } + } + print \PHP_EOL; + } } } - */ - public function provides() : array; - /** - * @return list - */ - public function requires() : array; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; +namespace PHPUnit\TextUI; -use const DIRECTORY_SEPARATOR; use const PHP_EOL; -use const PHP_MAJOR_VERSION; -use const PREG_OFFSET_CAPTURE; -use const WSDL_CACHE_NONE; -use function array_diff_assoc; +use const PHP_SAPI; +use const PHP_VERSION; +use function array_diff; +use function array_map; use function array_merge; -use function array_pop; -use function array_unique; +use function assert; use function class_exists; use function count; -use function explode; -use function extension_loaded; -use function implode; -use function in_array; -use function interface_exists; +use function dirname; +use function file_put_contents; +use function htmlspecialchars; use function is_array; -use function is_object; -use function md5; -use function mt_rand; -use function preg_match; -use function preg_match_all; +use function is_int; +use function is_string; +use function mt_srand; use function range; -use function serialize; -use function sort; +use function realpath; use function sprintf; -use function str_replace; -use function strlen; -use function strpos; -use function strtolower; -use function substr; -use function trait_exists; -use PHPUnit\Doctrine\Instantiator\Exception\ExceptionInterface as InstantiatorException; -use PHPUnit\Doctrine\Instantiator\Instantiator; -use Exception; -use Iterator; -use IteratorAggregate; -use PHPUnit\Framework\InvalidArgumentException; +use function time; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\TestResult; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Runner\AfterLastTestHook; +use PHPUnit\Runner\BaseTestRunner; +use PHPUnit\Runner\BeforeFirstTestHook; +use PHPUnit\Runner\DefaultTestResultCache; +use PHPUnit\Runner\Extension\ExtensionHandler; +use PHPUnit\Runner\Filter\ExcludeGroupFilterIterator; +use PHPUnit\Runner\Filter\Factory; +use PHPUnit\Runner\Filter\IncludeGroupFilterIterator; +use PHPUnit\Runner\Filter\NameFilterIterator; +use PHPUnit\Runner\Hook; +use PHPUnit\Runner\NullTestResultCache; +use PHPUnit\Runner\ResultCacheExtension; +use PHPUnit\Runner\StandardTestSuiteLoader; +use PHPUnit\Runner\TestHook; +use PHPUnit\Runner\TestListenerAdapter; +use PHPUnit\Runner\TestSuiteLoader; +use PHPUnit\Runner\TestSuiteSorter; +use PHPUnit\Runner\Version; +use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\FilterMapper; +use PHPUnit\TextUI\XmlConfiguration\Configuration; +use PHPUnit\TextUI\XmlConfiguration\Loader; +use PHPUnit\TextUI\XmlConfiguration\PhpHandler; +use PHPUnit\Util\Filesystem; +use PHPUnit\Util\Log\JUnit; +use PHPUnit\Util\Log\TeamCity; +use PHPUnit\Util\Printer; +use PHPUnit\Util\TestDox\CliTestDoxPrinter; +use PHPUnit\Util\TestDox\HtmlResultPrinter; +use PHPUnit\Util\TestDox\TextResultPrinter; +use PHPUnit\Util\TestDox\XmlResultPrinter; +use PHPUnit\Util\XdebugFilterScriptGenerator; +use PHPUnit\Util\Xml\SchemaDetector; use ReflectionClass; use ReflectionException; -use ReflectionMethod; -use PHPUnit\SebastianBergmann\Template\Template; -use SoapClient; -use SoapFault; -use Throwable; -use Traversable; +use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; +use PHPUnit\SebastianBergmann\CodeCoverage\Driver\Selector; +use PHPUnit\SebastianBergmann\CodeCoverage\Exception as CodeCoverageException; +use PHPUnit\SebastianBergmann\CodeCoverage\Filter as CodeCoverageFilter; +use PHPUnit\SebastianBergmann\CodeCoverage\Report\Clover as CloverReport; +use PHPUnit\SebastianBergmann\CodeCoverage\Report\Cobertura as CoberturaReport; +use PHPUnit\SebastianBergmann\CodeCoverage\Report\Crap4j as Crap4jReport; +use PHPUnit\SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlReport; +use PHPUnit\SebastianBergmann\CodeCoverage\Report\PHP as PhpReport; +use PHPUnit\SebastianBergmann\CodeCoverage\Report\Text as TextReport; +use PHPUnit\SebastianBergmann\CodeCoverage\Report\Xml\Facade as XmlReport; +use PHPUnit\SebastianBergmann\Comparator\Comparator; +use PHPUnit\SebastianBergmann\Environment\Runtime; +use PHPUnit\SebastianBergmann\Invoker\Invoker; +use PHPUnit\SebastianBergmann\Timer\Timer; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Generator +final class TestRunner extends BaseTestRunner { + public const SUCCESS_EXIT = 0; + public const FAILURE_EXIT = 1; + public const EXCEPTION_EXIT = 2; /** - * @var array + * @var bool */ - private const EXCLUDED_METHOD_NAMES = ['__CLASS__' => \true, '__DIR__' => \true, '__FILE__' => \true, '__FUNCTION__' => \true, '__LINE__' => \true, '__METHOD__' => \true, '__NAMESPACE__' => \true, '__TRAIT__' => \true, '__clone' => \true, '__halt_compiler' => \true]; + private static $versionStringPrinted = \false; /** - * @var array + * @var CodeCoverageFilter */ - private static $cache = []; + private $codeCoverageFilter; /** - * @var Template[] + * @var TestSuiteLoader */ - private static $templates = []; + private $loader; /** - * Returns a mock object for the specified class. - * - * @param null|array $methods - * - * @throws RuntimeException + * @var ResultPrinter */ - public function getMock(string $type, $methods = [], array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, bool $cloneArguments = \true, bool $callOriginalMethods = \false, object $proxyTarget = null, bool $allowMockingUnknownTypes = \true, bool $returnValueGeneration = \true) : \PHPUnit\Framework\MockObject\MockObject + private $printer; + /** + * @var bool + */ + private $messagePrinted = \false; + /** + * @var Hook[] + */ + private $extensions = []; + /** + * @var Timer + */ + private $timer; + public function __construct(TestSuiteLoader $loader = null, CodeCoverageFilter $filter = null) + { + if ($filter === null) { + $filter = new CodeCoverageFilter(); + } + $this->codeCoverageFilter = $filter; + $this->loader = $loader; + $this->timer = new Timer(); + } + /** + * @throws \PHPUnit\Runner\Exception + * @throws \PHPUnit\TextUI\XmlConfiguration\Exception + * @throws Exception + */ + public function run(TestSuite $suite, array $arguments = [], array $warnings = [], bool $exit = \true) : TestResult { - if (!\is_array($methods) && null !== $methods) { - throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'array'); + if (isset($arguments['configuration'])) { + $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] = $arguments['configuration']; } - if ($type === 'Traversable' || $type === '\\Traversable') { - $type = 'Iterator'; + $this->handleConfiguration($arguments); + $warnings = array_merge($warnings, $arguments['warnings']); + if (is_int($arguments['columns']) && $arguments['columns'] < 16) { + $arguments['columns'] = 16; + $tooFewColumnsRequested = \true; } - if (!$allowMockingUnknownTypes && !\class_exists($type, $callAutoload) && !\interface_exists($type, $callAutoload)) { - throw new \PHPUnit\Framework\MockObject\RuntimeException(\sprintf('Cannot stub or mock class or interface "%s" which does not exist', $type)); + if (isset($arguments['bootstrap'])) { + $GLOBALS['__PHPUNIT_BOOTSTRAP'] = $arguments['bootstrap']; } - if (null !== $methods) { - foreach ($methods as $method) { - if (!\preg_match('~[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*~', (string) $method)) { - throw new \PHPUnit\Framework\MockObject\RuntimeException(\sprintf('Cannot stub or mock method with invalid name "%s"', $method)); + if ($arguments['backupGlobals'] === \true) { + $suite->setBackupGlobals(\true); + } + if ($arguments['backupStaticAttributes'] === \true) { + $suite->setBackupStaticAttributes(\true); + } + if ($arguments['beStrictAboutChangesToGlobalState'] === \true) { + $suite->setBeStrictAboutChangesToGlobalState(\true); + } + if ($arguments['executionOrder'] === TestSuiteSorter::ORDER_RANDOMIZED) { + mt_srand($arguments['randomOrderSeed']); + } + if ($arguments['cacheResult']) { + if (!isset($arguments['cacheResultFile'])) { + if (isset($arguments['configurationObject'])) { + assert($arguments['configurationObject'] instanceof Configuration); + $cacheLocation = $arguments['configurationObject']->filename(); + } else { + $cacheLocation = $_SERVER['PHP_SELF']; + } + $arguments['cacheResultFile'] = null; + $cacheResultFile = realpath($cacheLocation); + if ($cacheResultFile !== \false) { + $arguments['cacheResultFile'] = dirname($cacheResultFile); + } + } + $cache = new DefaultTestResultCache($arguments['cacheResultFile']); + $this->addExtension(new ResultCacheExtension($cache)); + } + if ($arguments['executionOrder'] !== TestSuiteSorter::ORDER_DEFAULT || $arguments['executionOrderDefects'] !== TestSuiteSorter::ORDER_DEFAULT || $arguments['resolveDependencies']) { + $cache = $cache ?? new NullTestResultCache(); + $cache->load(); + $sorter = new TestSuiteSorter($cache); + $sorter->reorderTestsInSuite($suite, $arguments['executionOrder'], $arguments['resolveDependencies'], $arguments['executionOrderDefects']); + $originalExecutionOrder = $sorter->getOriginalExecutionOrder(); + unset($sorter); + } + if (is_int($arguments['repeat']) && $arguments['repeat'] > 0) { + $_suite = new TestSuite(); + /* @noinspection PhpUnusedLocalVariableInspection */ + foreach (range(1, $arguments['repeat']) as $step) { + $_suite->addTest($suite); + } + $suite = $_suite; + unset($_suite); + } + $result = $this->createTestResult(); + $listener = new TestListenerAdapter(); + $listenerNeeded = \false; + foreach ($this->extensions as $extension) { + if ($extension instanceof TestHook) { + $listener->add($extension); + $listenerNeeded = \true; + } + } + if ($listenerNeeded) { + $result->addListener($listener); + } + unset($listener, $listenerNeeded); + if ($arguments['convertDeprecationsToExceptions']) { + $result->convertDeprecationsToExceptions(\true); + } + if (!$arguments['convertErrorsToExceptions']) { + $result->convertErrorsToExceptions(\false); + } + if (!$arguments['convertNoticesToExceptions']) { + $result->convertNoticesToExceptions(\false); + } + if (!$arguments['convertWarningsToExceptions']) { + $result->convertWarningsToExceptions(\false); + } + if ($arguments['stopOnError']) { + $result->stopOnError(\true); + } + if ($arguments['stopOnFailure']) { + $result->stopOnFailure(\true); + } + if ($arguments['stopOnWarning']) { + $result->stopOnWarning(\true); + } + if ($arguments['stopOnIncomplete']) { + $result->stopOnIncomplete(\true); + } + if ($arguments['stopOnRisky']) { + $result->stopOnRisky(\true); + } + if ($arguments['stopOnSkipped']) { + $result->stopOnSkipped(\true); + } + if ($arguments['stopOnDefect']) { + $result->stopOnDefect(\true); + } + if ($arguments['registerMockObjectsFromTestArgumentsRecursively']) { + $result->setRegisterMockObjectsFromTestArgumentsRecursively(\true); + } + if ($this->printer === null) { + if (isset($arguments['printer'])) { + if ($arguments['printer'] instanceof \PHPUnit\TextUI\ResultPrinter) { + $this->printer = $arguments['printer']; + } elseif (is_string($arguments['printer']) && class_exists($arguments['printer'], \false)) { + try { + $reflector = new ReflectionClass($arguments['printer']); + if ($reflector->implementsInterface(\PHPUnit\TextUI\ResultPrinter::class)) { + $this->printer = $this->createPrinter($arguments['printer'], $arguments); + } + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + } else { + $this->printer = $this->createPrinter(\PHPUnit\TextUI\DefaultResultPrinter::class, $arguments); + } + } + if (isset($originalExecutionOrder) && $this->printer instanceof CliTestDoxPrinter) { + assert($this->printer instanceof CliTestDoxPrinter); + $this->printer->setOriginalExecutionOrder($originalExecutionOrder); + $this->printer->setShowProgressAnimation(!$arguments['noInteraction']); + } + $this->printer->write(Version::getVersionString() . "\n"); + self::$versionStringPrinted = \true; + foreach ($arguments['listeners'] as $listener) { + $result->addListener($listener); + } + $result->addListener($this->printer); + $coverageFilterFromConfigurationFile = \false; + $coverageFilterFromOption = \false; + $codeCoverageReports = 0; + if (isset($arguments['testdoxHTMLFile'])) { + $result->addListener(new HtmlResultPrinter($arguments['testdoxHTMLFile'], $arguments['testdoxGroups'], $arguments['testdoxExcludeGroups'])); + } + if (isset($arguments['testdoxTextFile'])) { + $result->addListener(new TextResultPrinter($arguments['testdoxTextFile'], $arguments['testdoxGroups'], $arguments['testdoxExcludeGroups'])); + } + if (isset($arguments['testdoxXMLFile'])) { + $result->addListener(new XmlResultPrinter($arguments['testdoxXMLFile'])); + } + if (isset($arguments['teamcityLogfile'])) { + $result->addListener(new TeamCity($arguments['teamcityLogfile'])); + } + if (isset($arguments['junitLogfile'])) { + $result->addListener(new JUnit($arguments['junitLogfile'], $arguments['reportUselessTests'])); + } + if (isset($arguments['coverageClover'])) { + $codeCoverageReports++; + } + if (isset($arguments['coverageCobertura'])) { + $codeCoverageReports++; + } + if (isset($arguments['coverageCrap4J'])) { + $codeCoverageReports++; + } + if (isset($arguments['coverageHtml'])) { + $codeCoverageReports++; + } + if (isset($arguments['coveragePHP'])) { + $codeCoverageReports++; + } + if (isset($arguments['coverageText'])) { + $codeCoverageReports++; + } + if (isset($arguments['coverageXml'])) { + $codeCoverageReports++; + } + if ($codeCoverageReports > 0 || isset($arguments['xdebugFilterFile'])) { + if (isset($arguments['coverageFilter'])) { + if (!is_array($arguments['coverageFilter'])) { + $coverageFilterDirectories = [$arguments['coverageFilter']]; + } else { + $coverageFilterDirectories = $arguments['coverageFilter']; + } + foreach ($coverageFilterDirectories as $coverageFilterDirectory) { + $this->codeCoverageFilter->includeDirectory($coverageFilterDirectory); + } + $coverageFilterFromOption = \true; + } + if (isset($arguments['configurationObject'])) { + assert($arguments['configurationObject'] instanceof Configuration); + $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage(); + if ($codeCoverageConfiguration->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) { + $coverageFilterFromConfigurationFile = \true; + (new FilterMapper())->map($this->codeCoverageFilter, $codeCoverageConfiguration); + } + } + } + if ($codeCoverageReports > 0) { + try { + if (isset($codeCoverageConfiguration) && ($codeCoverageConfiguration->pathCoverage() || isset($arguments['pathCoverage']) && $arguments['pathCoverage'] === \true)) { + $codeCoverageDriver = (new Selector())->forLineAndPathCoverage($this->codeCoverageFilter); + } else { + $codeCoverageDriver = (new Selector())->forLineCoverage($this->codeCoverageFilter); + } + $codeCoverage = new CodeCoverage($codeCoverageDriver, $this->codeCoverageFilter); + if (isset($codeCoverageConfiguration) && $codeCoverageConfiguration->hasCacheDirectory()) { + $codeCoverage->cacheStaticAnalysis($codeCoverageConfiguration->cacheDirectory()->path()); + } + if (isset($arguments['coverageCacheDirectory'])) { + $codeCoverage->cacheStaticAnalysis($arguments['coverageCacheDirectory']); + } + $codeCoverage->excludeSubclassesOfThisClassFromUnintentionallyCoveredCodeCheck(Comparator::class); + if ($arguments['strictCoverage']) { + $codeCoverage->enableCheckForUnintentionallyCoveredCode(); + } + if (isset($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'])) { + if ($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage']) { + $codeCoverage->ignoreDeprecatedCode(); + } else { + $codeCoverage->doNotIgnoreDeprecatedCode(); + } + } + if (isset($arguments['disableCodeCoverageIgnore'])) { + if ($arguments['disableCodeCoverageIgnore']) { + $codeCoverage->disableAnnotationsForIgnoringCode(); + } else { + $codeCoverage->enableAnnotationsForIgnoringCode(); + } + } + if (isset($arguments['configurationObject'])) { + $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage(); + if ($codeCoverageConfiguration->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) { + if ($codeCoverageConfiguration->includeUncoveredFiles()) { + $codeCoverage->includeUncoveredFiles(); + } else { + $codeCoverage->excludeUncoveredFiles(); + } + if ($codeCoverageConfiguration->processUncoveredFiles()) { + $codeCoverage->processUncoveredFiles(); + } else { + $codeCoverage->doNotProcessUncoveredFiles(); + } + } + } + if ($this->codeCoverageFilter->isEmpty()) { + if (!$coverageFilterFromConfigurationFile && !$coverageFilterFromOption) { + $warnings[] = 'No filter is configured, code coverage will not be processed'; + } else { + $warnings[] = 'Incorrect filter configuration, code coverage will not be processed'; + } + unset($codeCoverage); + } + } catch (CodeCoverageException $e) { + $warnings[] = $e->getMessage(); + } + } + if ($arguments['verbose']) { + if (\PHP_SAPI === 'phpdbg') { + $this->writeMessage('Runtime', 'PHPDBG ' . \PHP_VERSION); + } else { + $runtime = 'PHP ' . \PHP_VERSION; + if (isset($codeCoverageDriver)) { + $runtime .= ' with ' . $codeCoverageDriver->nameAndVersion(); + } + $this->writeMessage('Runtime', $runtime); + } + if (isset($arguments['configurationObject'])) { + assert($arguments['configurationObject'] instanceof Configuration); + $this->writeMessage('Configuration', $arguments['configurationObject']->filename()); + } + foreach ($arguments['loadedExtensions'] as $extension) { + $this->writeMessage('Extension', $extension); + } + foreach ($arguments['notLoadedExtensions'] as $extension) { + $this->writeMessage('Extension', $extension); + } + } + if ($arguments['executionOrder'] === TestSuiteSorter::ORDER_RANDOMIZED) { + $this->writeMessage('Random Seed', (string) $arguments['randomOrderSeed']); + } + if (isset($tooFewColumnsRequested)) { + $warnings[] = 'Less than 16 columns requested, number of columns set to 16'; + } + if ((new Runtime())->discardsComments()) { + $warnings[] = 'opcache.save_comments=0 set; annotations will not work'; + } + if (isset($arguments['conflictBetweenPrinterClassAndTestdox'])) { + $warnings[] = 'Directives printerClass and testdox are mutually exclusive'; + } + foreach ($warnings as $warning) { + $this->writeMessage('Warning', $warning); + } + if (isset($arguments['configurationObject'])) { + assert($arguments['configurationObject'] instanceof Configuration); + if ($arguments['configurationObject']->hasValidationErrors()) { + if ((new SchemaDetector())->detect($arguments['configurationObject']->filename())->detected()) { + $this->writeMessage('Warning', 'Your XML configuration validates against a deprecated schema.'); + $this->writeMessage('Suggestion', 'Migrate your XML configuration using "--migrate-configuration"!'); + } else { + $this->write("\n Warning - The configuration file did not pass validation!\n The following problems have been detected:\n"); + $this->write($arguments['configurationObject']->validationErrors()); + $this->write("\n Test results may not be as expected.\n\n"); + } + } + } + if (isset($arguments['xdebugFilterFile'], $codeCoverageConfiguration)) { + $this->write(\PHP_EOL . 'Please note that --dump-xdebug-filter and --prepend are deprecated and will be removed in PHPUnit 10.' . \PHP_EOL); + $script = (new XdebugFilterScriptGenerator())->generate($codeCoverageConfiguration); + if ($arguments['xdebugFilterFile'] !== 'php://stdout' && $arguments['xdebugFilterFile'] !== 'php://stderr' && !Filesystem::createDirectory(dirname($arguments['xdebugFilterFile']))) { + $this->write(sprintf('Cannot write Xdebug filter script to %s ' . \PHP_EOL, $arguments['xdebugFilterFile'])); + exit(self::EXCEPTION_EXIT); + } + file_put_contents($arguments['xdebugFilterFile'], $script); + $this->write(sprintf('Wrote Xdebug filter script to %s ' . \PHP_EOL . \PHP_EOL, $arguments['xdebugFilterFile'])); + exit(self::SUCCESS_EXIT); + } + $this->printer->write("\n"); + if (isset($codeCoverage)) { + $result->setCodeCoverage($codeCoverage); + } + $result->beStrictAboutTestsThatDoNotTestAnything($arguments['reportUselessTests']); + $result->beStrictAboutOutputDuringTests($arguments['disallowTestOutput']); + $result->beStrictAboutTodoAnnotatedTests($arguments['disallowTodoAnnotatedTests']); + $result->beStrictAboutResourceUsageDuringSmallTests($arguments['beStrictAboutResourceUsageDuringSmallTests']); + if ($arguments['enforceTimeLimit'] === \true && !(new Invoker())->canInvokeWithTimeout()) { + $this->writeMessage('Error', 'PHP extension pcntl is required for enforcing time limits'); + } + $result->enforceTimeLimit($arguments['enforceTimeLimit']); + $result->setDefaultTimeLimit($arguments['defaultTimeLimit']); + $result->setTimeoutForSmallTests($arguments['timeoutForSmallTests']); + $result->setTimeoutForMediumTests($arguments['timeoutForMediumTests']); + $result->setTimeoutForLargeTests($arguments['timeoutForLargeTests']); + if (isset($arguments['forceCoversAnnotation']) && $arguments['forceCoversAnnotation'] === \true) { + $result->forceCoversAnnotation(); + } + $this->processSuiteFilters($suite, $arguments); + $suite->setRunTestInSeparateProcess($arguments['processIsolation']); + foreach ($this->extensions as $extension) { + if ($extension instanceof BeforeFirstTestHook) { + $extension->executeBeforeFirstTest(); + } + } + $testSuiteWarningsPrinted = \false; + foreach ($suite->warnings() as $warning) { + $this->writeMessage('Warning', $warning); + $testSuiteWarningsPrinted = \true; + } + if ($testSuiteWarningsPrinted) { + $this->write(\PHP_EOL); + } + $suite->run($result); + foreach ($this->extensions as $extension) { + if ($extension instanceof AfterLastTestHook) { + $extension->executeAfterLastTest(); + } + } + $result->flushListeners(); + $this->printer->printResult($result); + if (isset($codeCoverage)) { + if (isset($arguments['coverageClover'])) { + $this->codeCoverageGenerationStart('Clover XML'); + try { + $writer = new CloverReport(); + $writer->process($codeCoverage, $arguments['coverageClover']); + $this->codeCoverageGenerationSucceeded(); + unset($writer); + } catch (CodeCoverageException $e) { + $this->codeCoverageGenerationFailed($e); } } - if ($methods !== \array_unique($methods)) { - throw new \PHPUnit\Framework\MockObject\RuntimeException(\sprintf('Cannot stub or mock using a method list that contains duplicates: "%s" (duplicate: "%s")', \implode(', ', $methods), \implode(', ', \array_unique(\array_diff_assoc($methods, \array_unique($methods)))))); + if (isset($arguments['coverageCobertura'])) { + $this->codeCoverageGenerationStart('Cobertura XML'); + try { + $writer = new CoberturaReport(); + $writer->process($codeCoverage, $arguments['coverageCobertura']); + $this->codeCoverageGenerationSucceeded(); + unset($writer); + } catch (CodeCoverageException $e) { + $this->codeCoverageGenerationFailed($e); + } } - } - if ($mockClassName !== '' && \class_exists($mockClassName, \false)) { - try { - $reflector = new \ReflectionClass($mockClassName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); + if (isset($arguments['coverageCrap4J'])) { + $this->codeCoverageGenerationStart('Crap4J XML'); + try { + $writer = new Crap4jReport($arguments['crap4jThreshold']); + $writer->process($codeCoverage, $arguments['coverageCrap4J']); + $this->codeCoverageGenerationSucceeded(); + unset($writer); + } catch (CodeCoverageException $e) { + $this->codeCoverageGenerationFailed($e); + } } - // @codeCoverageIgnoreEnd - if (!$reflector->implementsInterface(\PHPUnit\Framework\MockObject\MockObject::class)) { - throw new \PHPUnit\Framework\MockObject\RuntimeException(\sprintf('Class "%s" already exists.', $mockClassName)); + if (isset($arguments['coverageHtml'])) { + $this->codeCoverageGenerationStart('HTML'); + try { + $writer = new HtmlReport($arguments['reportLowUpperBound'], $arguments['reportHighLowerBound'], sprintf(' and PHPUnit %s', Version::id())); + $writer->process($codeCoverage, $arguments['coverageHtml']); + $this->codeCoverageGenerationSucceeded(); + unset($writer); + } catch (CodeCoverageException $e) { + $this->codeCoverageGenerationFailed($e); + } } - } - if (!$callOriginalConstructor && $callOriginalMethods) { - throw new \PHPUnit\Framework\MockObject\RuntimeException('Proxying to original methods requires invoking the original constructor'); - } - $mock = $this->generate($type, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods); - return $this->getObject($mock, $type, $callOriginalConstructor, $callAutoload, $arguments, $callOriginalMethods, $proxyTarget, $returnValueGeneration); - } - /** - * Returns a mock object for the specified abstract class with all abstract - * methods of the class mocked. - * - * Concrete methods to mock can be specified with the $mockedMethods parameter. - * - * @psalm-template RealInstanceType of object - * @psalm-param class-string $originalClassName - * @psalm-return MockObject&RealInstanceType - * - * @throws RuntimeException - */ - public function getMockForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = null, bool $cloneArguments = \true) : \PHPUnit\Framework\MockObject\MockObject - { - if (\class_exists($originalClassName, $callAutoload) || \interface_exists($originalClassName, $callAutoload)) { - try { - $reflector = new \ReflectionClass($originalClassName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); + if (isset($arguments['coveragePHP'])) { + $this->codeCoverageGenerationStart('PHP'); + try { + $writer = new PhpReport(); + $writer->process($codeCoverage, $arguments['coveragePHP']); + $this->codeCoverageGenerationSucceeded(); + unset($writer); + } catch (CodeCoverageException $e) { + $this->codeCoverageGenerationFailed($e); + } } - // @codeCoverageIgnoreEnd - $methods = $mockedMethods; - foreach ($reflector->getMethods() as $method) { - if ($method->isAbstract() && !\in_array($method->getName(), $methods ?? [], \true)) { - $methods[] = $method->getName(); + if (isset($arguments['coverageText'])) { + if ($arguments['coverageText'] === 'php://stdout') { + $outputStream = $this->printer; + $colors = $arguments['colors'] && $arguments['colors'] !== \PHPUnit\TextUI\DefaultResultPrinter::COLOR_NEVER; + } else { + $outputStream = new Printer($arguments['coverageText']); + $colors = \false; } + $processor = new TextReport($arguments['reportLowUpperBound'], $arguments['reportHighLowerBound'], $arguments['coverageTextShowUncoveredFiles'], $arguments['coverageTextShowOnlySummary']); + $outputStream->write($processor->process($codeCoverage, $colors)); } - if (empty($methods)) { - $methods = null; + if (isset($arguments['coverageXml'])) { + $this->codeCoverageGenerationStart('PHPUnit XML'); + try { + $writer = new XmlReport(Version::id()); + $writer->process($codeCoverage, $arguments['coverageXml']); + $this->codeCoverageGenerationSucceeded(); + unset($writer); + } catch (CodeCoverageException $e) { + $this->codeCoverageGenerationFailed($e); + } } - return $this->getMock($originalClassName, $methods, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $cloneArguments); } - throw new \PHPUnit\Framework\MockObject\RuntimeException(\sprintf('Class "%s" does not exist.', $originalClassName)); - } - /** - * Returns a mock object for the specified trait with all abstract methods - * of the trait mocked. Concrete methods to mock can be specified with the - * `$mockedMethods` parameter. - * - * @psalm-param trait-string $traitName - * - * @throws RuntimeException - */ - public function getMockForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = null, bool $cloneArguments = \true) : \PHPUnit\Framework\MockObject\MockObject - { - if (!\trait_exists($traitName, $callAutoload)) { - throw new \PHPUnit\Framework\MockObject\RuntimeException(\sprintf('Trait "%s" does not exist.', $traitName)); + if ($exit) { + if (isset($arguments['failOnEmptyTestSuite']) && $arguments['failOnEmptyTestSuite'] === \true && count($result) === 0) { + exit(self::FAILURE_EXIT); + } + if ($result->wasSuccessfulIgnoringWarnings()) { + if ($arguments['failOnRisky'] && !$result->allHarmless()) { + exit(self::FAILURE_EXIT); + } + if ($arguments['failOnWarning'] && $result->warningCount() > 0) { + exit(self::FAILURE_EXIT); + } + if ($arguments['failOnIncomplete'] && $result->notImplementedCount() > 0) { + exit(self::FAILURE_EXIT); + } + if ($arguments['failOnSkipped'] && $result->skippedCount() > 0) { + exit(self::FAILURE_EXIT); + } + exit(self::SUCCESS_EXIT); + } + if ($result->errorCount() > 0) { + exit(self::EXCEPTION_EXIT); + } + if ($result->failureCount() > 0) { + exit(self::FAILURE_EXIT); + } } - $className = $this->generateClassName($traitName, '', 'Trait_'); - $classTemplate = $this->getTemplate('trait_class.tpl'); - $classTemplate->setVar(['prologue' => 'abstract ', 'class_name' => $className['className'], 'trait_name' => $traitName]); - $mockTrait = new \PHPUnit\Framework\MockObject\MockTrait($classTemplate->render(), $className['className']); - $mockTrait->generate(); - return $this->getMockForAbstractClass($className['className'], $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); + return $result; } /** - * Returns an object for the specified trait. - * - * @psalm-param trait-string $traitName - * - * @throws RuntimeException + * Returns the loader to be used. */ - public function getObjectForTrait(string $traitName, string $traitClassName = '', bool $callAutoload = \true, bool $callOriginalConstructor = \false, array $arguments = []) : object - { - if (!\trait_exists($traitName, $callAutoload)) { - throw new \PHPUnit\Framework\MockObject\RuntimeException(\sprintf('Trait "%s" does not exist.', $traitName)); - } - $className = $this->generateClassName($traitName, $traitClassName, 'Trait_'); - $classTemplate = $this->getTemplate('trait_class.tpl'); - $classTemplate->setVar(['prologue' => '', 'class_name' => $className['className'], 'trait_name' => $traitName]); - return $this->getObject(new \PHPUnit\Framework\MockObject\MockTrait($classTemplate->render(), $className['className']), '', $callOriginalConstructor, $callAutoload, $arguments); - } - public function generate(string $type, array $methods = null, string $mockClassName = '', bool $callOriginalClone = \true, bool $callAutoload = \true, bool $cloneArguments = \true, bool $callOriginalMethods = \false) : \PHPUnit\Framework\MockObject\MockClass + public function getLoader() : TestSuiteLoader { - if ($mockClassName !== '') { - return $this->generateMock($type, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods); - } - $key = \md5($type . \serialize($methods) . \serialize($callOriginalClone) . \serialize($cloneArguments) . \serialize($callOriginalMethods)); - if (!isset(self::$cache[$key])) { - self::$cache[$key] = $this->generateMock($type, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods); + if ($this->loader === null) { + $this->loader = new StandardTestSuiteLoader(); } - return self::$cache[$key]; + return $this->loader; } - /** - * @throws RuntimeException - */ - public function generateClassFromWsdl(string $wsdlFile, string $className, array $methods = [], array $options = []) : string + public function addExtension(Hook $extension) : void { - if (!\extension_loaded('soap')) { - throw new \PHPUnit\Framework\MockObject\RuntimeException('The SOAP extension is required to generate a mock object from WSDL.'); - } - $options = \array_merge($options, ['cache_wsdl' => \WSDL_CACHE_NONE]); - try { - $client = new \SoapClient($wsdlFile, $options); - $_methods = \array_unique($client->__getFunctions()); - unset($client); - } catch (\SoapFault $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); - } - \sort($_methods); - $methodTemplate = $this->getTemplate('wsdl_method.tpl'); - $methodsBuffer = ''; - foreach ($_methods as $method) { - \preg_match_all('/[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*\\(/', $method, $matches, \PREG_OFFSET_CAPTURE); - $lastFunction = \array_pop($matches[0]); - $nameStart = $lastFunction[1]; - $nameEnd = $nameStart + \strlen($lastFunction[0]) - 1; - $name = \str_replace('(', '', $lastFunction[0]); - if (empty($methods) || \in_array($name, $methods, \true)) { - $args = \explode(',', \str_replace(')', '', \substr($method, $nameEnd + 1))); - foreach (\range(0, \count($args) - 1) as $i) { - $args[$i] = \substr($args[$i], \strpos($args[$i], '$')); - } - $methodTemplate->setVar(['method_name' => $name, 'arguments' => \implode(', ', $args)]); - $methodsBuffer .= $methodTemplate->render(); - } - } - $optionsBuffer = '['; - foreach ($options as $key => $value) { - $optionsBuffer .= $key . ' => ' . $value; - } - $optionsBuffer .= ']'; - $classTemplate = $this->getTemplate('wsdl_class.tpl'); - $namespace = ''; - if (\strpos($className, '\\') !== \false) { - $parts = \explode('\\', $className); - $className = \array_pop($parts); - $namespace = 'namespace ' . \implode('\\', $parts) . ';' . "\n\n"; - } - $classTemplate->setVar(['namespace' => $namespace, 'class_name' => $className, 'wsdl' => $wsdlFile, 'options' => $optionsBuffer, 'methods' => $methodsBuffer]); - return $classTemplate->render(); + $this->extensions[] = $extension; } /** - * @throws RuntimeException - * - * @return string[] + * Override to define how to handle a failed loading of + * a test suite. */ - public function getClassMethods(string $className) : array + protected function runFailed(string $message) : void { - try { - $class = new \ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $methods = []; - foreach ($class->getMethods() as $method) { - if ($method->isPublic() || $method->isAbstract()) { - $methods[] = $method->getName(); - } - } - return $methods; + $this->write($message . \PHP_EOL); + exit(self::FAILURE_EXIT); } - /** - * @throws RuntimeException - * - * @return MockMethod[] - */ - public function mockClassMethods(string $className, bool $callOriginalMethods, bool $cloneArguments) : array + private function createTestResult() : TestResult { - try { - $class = new \ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $methods = []; - foreach ($class->getMethods() as $method) { - if (($method->isPublic() || $method->isAbstract()) && $this->canMockMethod($method)) { - $methods[] = \PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments); - } - } - return $methods; + return new TestResult(); } - /** - * @throws RuntimeException - * - * @return MockMethod[] - */ - public function mockInterfaceMethods(string $interfaceName, bool $cloneArguments) : array + private function write(string $buffer) : void { - try { - $class = new \ReflectionClass($interfaceName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); + if (\PHP_SAPI !== 'cli' && \PHP_SAPI !== 'phpdbg') { + $buffer = htmlspecialchars($buffer); } - // @codeCoverageIgnoreEnd - $methods = []; - foreach ($class->getMethods() as $method) { - $methods[] = \PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, \false, $cloneArguments); + if ($this->printer !== null) { + $this->printer->write($buffer); + } else { + print $buffer; } - return $methods; } /** - * @psalm-param class-string $interfaceName - * - * @return ReflectionMethod[] + * @throws \PHPUnit\TextUI\XmlConfiguration\Exception + * @throws Exception */ - private function userDefinedInterfaceMethods(string $interfaceName) : array + private function handleConfiguration(array &$arguments) : void { - try { - // @codeCoverageIgnoreStart - $interface = new \ReflectionClass($interfaceName); - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); + if (!isset($arguments['configurationObject']) && isset($arguments['configuration'])) { + $arguments['configurationObject'] = (new Loader())->load($arguments['configuration']); } - // @codeCoverageIgnoreEnd - $methods = []; - foreach ($interface->getMethods() as $method) { - if (!$method->isUserDefined()) { - continue; - } - $methods[] = $method; + if (!isset($arguments['warnings'])) { + $arguments['warnings'] = []; } - return $methods; - } - private function getObject(\PHPUnit\Framework\MockObject\MockType $mockClass, $type = '', bool $callOriginalConstructor = \false, bool $callAutoload = \false, array $arguments = [], bool $callOriginalMethods = \false, object $proxyTarget = null, bool $returnValueGeneration = \true) - { - $className = $mockClass->generate(); - if ($callOriginalConstructor) { - if (\count($arguments) === 0) { - $object = new $className(); - } else { - try { - $class = new \ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); + $arguments['debug'] = $arguments['debug'] ?? \false; + $arguments['filter'] = $arguments['filter'] ?? \false; + $arguments['listeners'] = $arguments['listeners'] ?? []; + if (isset($arguments['configurationObject'])) { + (new PhpHandler())->handle($arguments['configurationObject']->php()); + $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage(); + if (!isset($arguments['noCoverage'])) { + if (!isset($arguments['coverageClover']) && $codeCoverageConfiguration->hasClover()) { + $arguments['coverageClover'] = $codeCoverageConfiguration->clover()->target()->path(); } - // @codeCoverageIgnoreEnd - $object = $class->newInstanceArgs($arguments); - } - } else { - try { - $object = (new \PHPUnit\Doctrine\Instantiator\Instantiator())->instantiate($className); - } catch (\PHPUnit\Doctrine\Instantiator\Exception\ExceptionInterface $exception) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($exception->getMessage()); - } - } - if ($callOriginalMethods) { - if (!\is_object($proxyTarget)) { - if (\count($arguments) === 0) { - $proxyTarget = new $type(); - } else { - try { - $class = new \ReflectionClass($type); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); + if (!isset($arguments['coverageCobertura']) && $codeCoverageConfiguration->hasCobertura()) { + $arguments['coverageCobertura'] = $codeCoverageConfiguration->cobertura()->target()->path(); + } + if (!isset($arguments['coverageCrap4J']) && $codeCoverageConfiguration->hasCrap4j()) { + $arguments['coverageCrap4J'] = $codeCoverageConfiguration->crap4j()->target()->path(); + if (!isset($arguments['crap4jThreshold'])) { + $arguments['crap4jThreshold'] = $codeCoverageConfiguration->crap4j()->threshold(); } - // @codeCoverageIgnoreEnd - $proxyTarget = $class->newInstanceArgs($arguments); + } + if (!isset($arguments['coverageHtml']) && $codeCoverageConfiguration->hasHtml()) { + $arguments['coverageHtml'] = $codeCoverageConfiguration->html()->target()->path(); + if (!isset($arguments['reportLowUpperBound'])) { + $arguments['reportLowUpperBound'] = $codeCoverageConfiguration->html()->lowUpperBound(); + } + if (!isset($arguments['reportHighLowerBound'])) { + $arguments['reportHighLowerBound'] = $codeCoverageConfiguration->html()->highLowerBound(); + } + } + if (!isset($arguments['coveragePHP']) && $codeCoverageConfiguration->hasPhp()) { + $arguments['coveragePHP'] = $codeCoverageConfiguration->php()->target()->path(); + } + if (!isset($arguments['coverageText']) && $codeCoverageConfiguration->hasText()) { + $arguments['coverageText'] = $codeCoverageConfiguration->text()->target()->path(); + $arguments['coverageTextShowUncoveredFiles'] = $codeCoverageConfiguration->text()->showUncoveredFiles(); + $arguments['coverageTextShowOnlySummary'] = $codeCoverageConfiguration->text()->showOnlySummary(); + } + if (!isset($arguments['coverageXml']) && $codeCoverageConfiguration->hasXml()) { + $arguments['coverageXml'] = $codeCoverageConfiguration->xml()->target()->path(); } } - $object->__phpunit_setOriginalObject($proxyTarget); - } - if ($object instanceof \PHPUnit\Framework\MockObject\MockObject) { - $object->__phpunit_setReturnValueGeneration($returnValueGeneration); - } - return $object; - } - /** - * @throws RuntimeException - */ - private function generateMock(string $type, ?array $explicitMethods, string $mockClassName, bool $callOriginalClone, bool $callAutoload, bool $cloneArguments, bool $callOriginalMethods) : \PHPUnit\Framework\MockObject\MockClass - { - $classTemplate = $this->getTemplate('mocked_class.tpl'); - $additionalInterfaces = []; - $mockedCloneMethod = \false; - $unmockedCloneMethod = \false; - $isClass = \false; - $isInterface = \false; - $class = null; - $mockMethods = new \PHPUnit\Framework\MockObject\MockMethodSet(); - $_mockClassName = $this->generateClassName($type, $mockClassName, 'Mock_'); - if (\class_exists($_mockClassName['fullClassName'], $callAutoload)) { - $isClass = \true; - } elseif (\interface_exists($_mockClassName['fullClassName'], $callAutoload)) { - $isInterface = \true; - } - if (!$isClass && !$isInterface) { - $prologue = 'class ' . $_mockClassName['originalClassName'] . "\n{\n}\n\n"; - if (!empty($_mockClassName['namespaceName'])) { - $prologue = 'namespace ' . $_mockClassName['namespaceName'] . " {\n\n" . $prologue . "}\n\n" . "namespace {\n\n"; - $epilogue = "\n\n}"; + $phpunitConfiguration = $arguments['configurationObject']->phpunit(); + $arguments['backupGlobals'] = $arguments['backupGlobals'] ?? $phpunitConfiguration->backupGlobals(); + $arguments['backupStaticAttributes'] = $arguments['backupStaticAttributes'] ?? $phpunitConfiguration->backupStaticAttributes(); + $arguments['beStrictAboutChangesToGlobalState'] = $arguments['beStrictAboutChangesToGlobalState'] ?? $phpunitConfiguration->beStrictAboutChangesToGlobalState(); + $arguments['cacheResult'] = $arguments['cacheResult'] ?? $phpunitConfiguration->cacheResult(); + $arguments['colors'] = $arguments['colors'] ?? $phpunitConfiguration->colors(); + $arguments['convertDeprecationsToExceptions'] = $arguments['convertDeprecationsToExceptions'] ?? $phpunitConfiguration->convertDeprecationsToExceptions(); + $arguments['convertErrorsToExceptions'] = $arguments['convertErrorsToExceptions'] ?? $phpunitConfiguration->convertErrorsToExceptions(); + $arguments['convertNoticesToExceptions'] = $arguments['convertNoticesToExceptions'] ?? $phpunitConfiguration->convertNoticesToExceptions(); + $arguments['convertWarningsToExceptions'] = $arguments['convertWarningsToExceptions'] ?? $phpunitConfiguration->convertWarningsToExceptions(); + $arguments['processIsolation'] = $arguments['processIsolation'] ?? $phpunitConfiguration->processIsolation(); + $arguments['stopOnDefect'] = $arguments['stopOnDefect'] ?? $phpunitConfiguration->stopOnDefect(); + $arguments['stopOnError'] = $arguments['stopOnError'] ?? $phpunitConfiguration->stopOnError(); + $arguments['stopOnFailure'] = $arguments['stopOnFailure'] ?? $phpunitConfiguration->stopOnFailure(); + $arguments['stopOnWarning'] = $arguments['stopOnWarning'] ?? $phpunitConfiguration->stopOnWarning(); + $arguments['stopOnIncomplete'] = $arguments['stopOnIncomplete'] ?? $phpunitConfiguration->stopOnIncomplete(); + $arguments['stopOnRisky'] = $arguments['stopOnRisky'] ?? $phpunitConfiguration->stopOnRisky(); + $arguments['stopOnSkipped'] = $arguments['stopOnSkipped'] ?? $phpunitConfiguration->stopOnSkipped(); + $arguments['failOnEmptyTestSuite'] = $arguments['failOnEmptyTestSuite'] ?? $phpunitConfiguration->failOnEmptyTestSuite(); + $arguments['failOnIncomplete'] = $arguments['failOnIncomplete'] ?? $phpunitConfiguration->failOnIncomplete(); + $arguments['failOnRisky'] = $arguments['failOnRisky'] ?? $phpunitConfiguration->failOnRisky(); + $arguments['failOnSkipped'] = $arguments['failOnSkipped'] ?? $phpunitConfiguration->failOnSkipped(); + $arguments['failOnWarning'] = $arguments['failOnWarning'] ?? $phpunitConfiguration->failOnWarning(); + $arguments['enforceTimeLimit'] = $arguments['enforceTimeLimit'] ?? $phpunitConfiguration->enforceTimeLimit(); + $arguments['defaultTimeLimit'] = $arguments['defaultTimeLimit'] ?? $phpunitConfiguration->defaultTimeLimit(); + $arguments['timeoutForSmallTests'] = $arguments['timeoutForSmallTests'] ?? $phpunitConfiguration->timeoutForSmallTests(); + $arguments['timeoutForMediumTests'] = $arguments['timeoutForMediumTests'] ?? $phpunitConfiguration->timeoutForMediumTests(); + $arguments['timeoutForLargeTests'] = $arguments['timeoutForLargeTests'] ?? $phpunitConfiguration->timeoutForLargeTests(); + $arguments['reportUselessTests'] = $arguments['reportUselessTests'] ?? $phpunitConfiguration->beStrictAboutTestsThatDoNotTestAnything(); + $arguments['strictCoverage'] = $arguments['strictCoverage'] ?? $phpunitConfiguration->beStrictAboutCoversAnnotation(); + $arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] = $arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] ?? $codeCoverageConfiguration->ignoreDeprecatedCodeUnits(); + $arguments['disallowTestOutput'] = $arguments['disallowTestOutput'] ?? $phpunitConfiguration->beStrictAboutOutputDuringTests(); + $arguments['disallowTodoAnnotatedTests'] = $arguments['disallowTodoAnnotatedTests'] ?? $phpunitConfiguration->beStrictAboutTodoAnnotatedTests(); + $arguments['beStrictAboutResourceUsageDuringSmallTests'] = $arguments['beStrictAboutResourceUsageDuringSmallTests'] ?? $phpunitConfiguration->beStrictAboutResourceUsageDuringSmallTests(); + $arguments['verbose'] = $arguments['verbose'] ?? $phpunitConfiguration->verbose(); + $arguments['reverseDefectList'] = $arguments['reverseDefectList'] ?? $phpunitConfiguration->reverseDefectList(); + $arguments['forceCoversAnnotation'] = $arguments['forceCoversAnnotation'] ?? $phpunitConfiguration->forceCoversAnnotation(); + $arguments['disableCodeCoverageIgnore'] = $arguments['disableCodeCoverageIgnore'] ?? $codeCoverageConfiguration->disableCodeCoverageIgnore(); + $arguments['registerMockObjectsFromTestArgumentsRecursively'] = $arguments['registerMockObjectsFromTestArgumentsRecursively'] ?? $phpunitConfiguration->registerMockObjectsFromTestArgumentsRecursively(); + $arguments['noInteraction'] = $arguments['noInteraction'] ?? $phpunitConfiguration->noInteraction(); + $arguments['executionOrder'] = $arguments['executionOrder'] ?? $phpunitConfiguration->executionOrder(); + $arguments['resolveDependencies'] = $arguments['resolveDependencies'] ?? $phpunitConfiguration->resolveDependencies(); + if (!isset($arguments['bootstrap']) && $phpunitConfiguration->hasBootstrap()) { + $arguments['bootstrap'] = $phpunitConfiguration->bootstrap(); } - $mockedCloneMethod = \true; - } else { - try { - $class = new \ReflectionClass($_mockClassName['fullClassName']); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); + if (!isset($arguments['cacheResultFile']) && $phpunitConfiguration->hasCacheResultFile()) { + $arguments['cacheResultFile'] = $phpunitConfiguration->cacheResultFile(); } - // @codeCoverageIgnoreEnd - if ($class->isFinal()) { - throw new \PHPUnit\Framework\MockObject\RuntimeException(\sprintf('Class "%s" is declared "final" and cannot be mocked.', $_mockClassName['fullClassName'])); + if (!isset($arguments['executionOrderDefects'])) { + $arguments['executionOrderDefects'] = $phpunitConfiguration->defectsFirst() ? TestSuiteSorter::ORDER_DEFECTS_FIRST : TestSuiteSorter::ORDER_DEFAULT; } - // @see https://github.com/sebastianbergmann/phpunit/issues/2995 - if ($isInterface && $class->implementsInterface(\Throwable::class)) { - $actualClassName = \Exception::class; - $additionalInterfaces[] = $class->getName(); - $isInterface = \false; - try { - $class = new \ReflectionClass($actualClassName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - foreach ($this->userDefinedInterfaceMethods($_mockClassName['fullClassName']) as $method) { - $methodName = $method->getName(); - if ($class->hasMethod($methodName)) { - try { - $classMethod = $class->getMethod($methodName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if (!$this->canMockMethod($classMethod)) { - continue; - } - } - $mockMethods->addMethods(\PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments)); - } - $_mockClassName = $this->generateClassName($actualClassName, $_mockClassName['className'], 'Mock_'); + if ($phpunitConfiguration->conflictBetweenPrinterClassAndTestdox()) { + $arguments['conflictBetweenPrinterClassAndTestdox'] = \true; } - // @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/103 - if ($isInterface && $class->implementsInterface(\Traversable::class) && !$class->implementsInterface(\Iterator::class) && !$class->implementsInterface(\IteratorAggregate::class)) { - $additionalInterfaces[] = \Iterator::class; - $mockMethods->addMethods(...$this->mockClassMethods(\Iterator::class, $callOriginalMethods, $cloneArguments)); + $groupCliArgs = []; + if (!empty($arguments['groups'])) { + $groupCliArgs = $arguments['groups']; } - if ($class->hasMethod('__clone')) { - try { - $cloneMethod = $class->getMethod('__clone'); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); + $groupConfiguration = $arguments['configurationObject']->groups(); + if (!isset($arguments['groups']) && $groupConfiguration->hasInclude()) { + $arguments['groups'] = $groupConfiguration->include()->asArrayOfStrings(); + } + if (!isset($arguments['excludeGroups']) && $groupConfiguration->hasExclude()) { + $arguments['excludeGroups'] = array_diff($groupConfiguration->exclude()->asArrayOfStrings(), $groupCliArgs); + } + $extensionHandler = new ExtensionHandler(); + foreach ($arguments['configurationObject']->extensions() as $extension) { + $extensionHandler->registerExtension($extension, $this); + } + foreach ($arguments['configurationObject']->listeners() as $listener) { + $arguments['listeners'][] = $extensionHandler->createTestListenerInstance($listener); + } + unset($extensionHandler); + foreach ($arguments['unavailableExtensions'] as $extension) { + $arguments['warnings'][] = sprintf('Extension "%s" is not available', $extension); + } + $loggingConfiguration = $arguments['configurationObject']->logging(); + if (!isset($arguments['noLogging'])) { + if ($loggingConfiguration->hasText()) { + $arguments['listeners'][] = new \PHPUnit\TextUI\DefaultResultPrinter($loggingConfiguration->text()->target()->path(), \true); } - // @codeCoverageIgnoreEnd - if (!$cloneMethod->isFinal()) { - if ($callOriginalClone && !$isInterface) { - $unmockedCloneMethod = \true; - } else { - $mockedCloneMethod = \true; - } + if (!isset($arguments['teamcityLogfile']) && $loggingConfiguration->hasTeamCity()) { + $arguments['teamcityLogfile'] = $loggingConfiguration->teamCity()->target()->path(); } - } else { - $mockedCloneMethod = \true; - } - } - if ($isClass && $explicitMethods === []) { - $mockMethods->addMethods(...$this->mockClassMethods($_mockClassName['fullClassName'], $callOriginalMethods, $cloneArguments)); - } - if ($isInterface && ($explicitMethods === [] || $explicitMethods === null)) { - $mockMethods->addMethods(...$this->mockInterfaceMethods($_mockClassName['fullClassName'], $cloneArguments)); - } - if (\is_array($explicitMethods)) { - foreach ($explicitMethods as $methodName) { - if ($class !== null && $class->hasMethod($methodName)) { - try { - $method = $class->getMethod($methodName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($this->canMockMethod($method)) { - $mockMethods->addMethods(\PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments)); - } - } else { - $mockMethods->addMethods(\PHPUnit\Framework\MockObject\MockMethod::fromName($_mockClassName['fullClassName'], $methodName, $cloneArguments)); + if (!isset($arguments['junitLogfile']) && $loggingConfiguration->hasJunit()) { + $arguments['junitLogfile'] = $loggingConfiguration->junit()->target()->path(); + } + if (!isset($arguments['testdoxHTMLFile']) && $loggingConfiguration->hasTestDoxHtml()) { + $arguments['testdoxHTMLFile'] = $loggingConfiguration->testDoxHtml()->target()->path(); + } + if (!isset($arguments['testdoxTextFile']) && $loggingConfiguration->hasTestDoxText()) { + $arguments['testdoxTextFile'] = $loggingConfiguration->testDoxText()->target()->path(); } + if (!isset($arguments['testdoxXMLFile']) && $loggingConfiguration->hasTestDoxXml()) { + $arguments['testdoxXMLFile'] = $loggingConfiguration->testDoxXml()->target()->path(); + } + } + $testdoxGroupConfiguration = $arguments['configurationObject']->testdoxGroups(); + if (!isset($arguments['testdoxGroups']) && $testdoxGroupConfiguration->hasInclude()) { + $arguments['testdoxGroups'] = $testdoxGroupConfiguration->include()->asArrayOfStrings(); + } + if (!isset($arguments['testdoxExcludeGroups']) && $testdoxGroupConfiguration->hasExclude()) { + $arguments['testdoxExcludeGroups'] = $testdoxGroupConfiguration->exclude()->asArrayOfStrings(); } } - $mockedMethods = ''; - $configurable = []; - foreach ($mockMethods->asArray() as $mockMethod) { - $mockedMethods .= $mockMethod->generateCode(); - $configurable[] = new \PHPUnit\Framework\MockObject\ConfigurableMethod($mockMethod->getName(), $mockMethod->getReturnType()); + $extensionHandler = new ExtensionHandler(); + foreach ($arguments['extensions'] as $extension) { + $extensionHandler->registerExtension($extension, $this); } - $method = ''; - if (!$mockMethods->hasMethod('method') && (!isset($class) || !$class->hasMethod('method'))) { - $method = \PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\Method;'; + unset($extensionHandler); + $arguments['backupGlobals'] = $arguments['backupGlobals'] ?? null; + $arguments['backupStaticAttributes'] = $arguments['backupStaticAttributes'] ?? null; + $arguments['beStrictAboutChangesToGlobalState'] = $arguments['beStrictAboutChangesToGlobalState'] ?? null; + $arguments['beStrictAboutResourceUsageDuringSmallTests'] = $arguments['beStrictAboutResourceUsageDuringSmallTests'] ?? \false; + $arguments['cacheResult'] = $arguments['cacheResult'] ?? \true; + $arguments['colors'] = $arguments['colors'] ?? \PHPUnit\TextUI\DefaultResultPrinter::COLOR_DEFAULT; + $arguments['columns'] = $arguments['columns'] ?? 80; + $arguments['convertDeprecationsToExceptions'] = $arguments['convertDeprecationsToExceptions'] ?? \false; + $arguments['convertErrorsToExceptions'] = $arguments['convertErrorsToExceptions'] ?? \true; + $arguments['convertNoticesToExceptions'] = $arguments['convertNoticesToExceptions'] ?? \true; + $arguments['convertWarningsToExceptions'] = $arguments['convertWarningsToExceptions'] ?? \true; + $arguments['crap4jThreshold'] = $arguments['crap4jThreshold'] ?? 30; + $arguments['disallowTestOutput'] = $arguments['disallowTestOutput'] ?? \false; + $arguments['disallowTodoAnnotatedTests'] = $arguments['disallowTodoAnnotatedTests'] ?? \false; + $arguments['defaultTimeLimit'] = $arguments['defaultTimeLimit'] ?? 0; + $arguments['enforceTimeLimit'] = $arguments['enforceTimeLimit'] ?? \false; + $arguments['excludeGroups'] = $arguments['excludeGroups'] ?? []; + $arguments['executionOrder'] = $arguments['executionOrder'] ?? TestSuiteSorter::ORDER_DEFAULT; + $arguments['executionOrderDefects'] = $arguments['executionOrderDefects'] ?? TestSuiteSorter::ORDER_DEFAULT; + $arguments['failOnIncomplete'] = $arguments['failOnIncomplete'] ?? \false; + $arguments['failOnRisky'] = $arguments['failOnRisky'] ?? \false; + $arguments['failOnSkipped'] = $arguments['failOnSkipped'] ?? \false; + $arguments['failOnWarning'] = $arguments['failOnWarning'] ?? \false; + $arguments['groups'] = $arguments['groups'] ?? []; + $arguments['noInteraction'] = $arguments['noInteraction'] ?? \false; + $arguments['processIsolation'] = $arguments['processIsolation'] ?? \false; + $arguments['randomOrderSeed'] = $arguments['randomOrderSeed'] ?? time(); + $arguments['registerMockObjectsFromTestArgumentsRecursively'] = $arguments['registerMockObjectsFromTestArgumentsRecursively'] ?? \false; + $arguments['repeat'] = $arguments['repeat'] ?? \false; + $arguments['reportHighLowerBound'] = $arguments['reportHighLowerBound'] ?? 90; + $arguments['reportLowUpperBound'] = $arguments['reportLowUpperBound'] ?? 50; + $arguments['reportUselessTests'] = $arguments['reportUselessTests'] ?? \true; + $arguments['reverseList'] = $arguments['reverseList'] ?? \false; + $arguments['resolveDependencies'] = $arguments['resolveDependencies'] ?? \true; + $arguments['stopOnError'] = $arguments['stopOnError'] ?? \false; + $arguments['stopOnFailure'] = $arguments['stopOnFailure'] ?? \false; + $arguments['stopOnIncomplete'] = $arguments['stopOnIncomplete'] ?? \false; + $arguments['stopOnRisky'] = $arguments['stopOnRisky'] ?? \false; + $arguments['stopOnSkipped'] = $arguments['stopOnSkipped'] ?? \false; + $arguments['stopOnWarning'] = $arguments['stopOnWarning'] ?? \false; + $arguments['stopOnDefect'] = $arguments['stopOnDefect'] ?? \false; + $arguments['strictCoverage'] = $arguments['strictCoverage'] ?? \false; + $arguments['testdoxExcludeGroups'] = $arguments['testdoxExcludeGroups'] ?? []; + $arguments['testdoxGroups'] = $arguments['testdoxGroups'] ?? []; + $arguments['timeoutForLargeTests'] = $arguments['timeoutForLargeTests'] ?? 60; + $arguments['timeoutForMediumTests'] = $arguments['timeoutForMediumTests'] ?? 10; + $arguments['timeoutForSmallTests'] = $arguments['timeoutForSmallTests'] ?? 1; + $arguments['verbose'] = $arguments['verbose'] ?? \false; + } + private function processSuiteFilters(TestSuite $suite, array $arguments) : void + { + if (!$arguments['filter'] && empty($arguments['groups']) && empty($arguments['excludeGroups']) && empty($arguments['testsCovering']) && empty($arguments['testsUsing'])) { + return; } - $cloneTrait = ''; - if ($mockedCloneMethod) { - $cloneTrait = \PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\MockedCloneMethod;'; + $filterFactory = new Factory(); + if (!empty($arguments['excludeGroups'])) { + $filterFactory->addFilter(new ReflectionClass(ExcludeGroupFilterIterator::class), $arguments['excludeGroups']); } - if ($unmockedCloneMethod) { - $cloneTrait = \PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\UnmockedCloneMethod;'; + if (!empty($arguments['groups'])) { + $filterFactory->addFilter(new ReflectionClass(IncludeGroupFilterIterator::class), $arguments['groups']); } - $classTemplate->setVar(['prologue' => $prologue ?? '', 'epilogue' => $epilogue ?? '', 'class_declaration' => $this->generateMockClassDeclaration($_mockClassName, $isInterface, $additionalInterfaces), 'clone' => $cloneTrait, 'mock_class_name' => $_mockClassName['className'], 'mocked_methods' => $mockedMethods, 'method' => $method]); - return new \PHPUnit\Framework\MockObject\MockClass($classTemplate->render(), $_mockClassName['className'], $configurable); - } - private function generateClassName(string $type, string $className, string $prefix) : array - { - if ($type[0] === '\\') { - $type = \substr($type, 1); + if (!empty($arguments['testsCovering'])) { + $filterFactory->addFilter(new ReflectionClass(IncludeGroupFilterIterator::class), array_map(static function (string $name) : string { + return '__phpunit_covers_' . $name; + }, $arguments['testsCovering'])); } - $classNameParts = \explode('\\', $type); - if (\count($classNameParts) > 1) { - $type = \array_pop($classNameParts); - $namespaceName = \implode('\\', $classNameParts); - $fullClassName = $namespaceName . '\\' . $type; - } else { - $namespaceName = ''; - $fullClassName = $type; + if (!empty($arguments['testsUsing'])) { + $filterFactory->addFilter(new ReflectionClass(IncludeGroupFilterIterator::class), array_map(static function (string $name) : string { + return '__phpunit_uses_' . $name; + }, $arguments['testsUsing'])); } - if ($className === '') { - do { - $className = $prefix . $type . '_' . \substr(\md5((string) \mt_rand()), 0, 8); - } while (\class_exists($className, \false)); + if ($arguments['filter']) { + $filterFactory->addFilter(new ReflectionClass(NameFilterIterator::class), $arguments['filter']); } - return ['className' => $className, 'originalClassName' => $type, 'fullClassName' => $fullClassName, 'namespaceName' => $namespaceName]; + $suite->injectFilter($filterFactory); } - private function generateMockClassDeclaration(array $mockClassName, bool $isInterface, array $additionalInterfaces = []) : string + private function writeMessage(string $type, string $message) : void { - $buffer = 'class '; - $additionalInterfaces[] = \PHPUnit\Framework\MockObject\MockObject::class; - $interfaces = \implode(', ', $additionalInterfaces); - if ($isInterface) { - $buffer .= \sprintf('%s implements %s', $mockClassName['className'], $interfaces); - if (!\in_array($mockClassName['originalClassName'], $additionalInterfaces, \true)) { - $buffer .= ', '; - if (!empty($mockClassName['namespaceName'])) { - $buffer .= $mockClassName['namespaceName'] . '\\'; - } - $buffer .= $mockClassName['originalClassName']; - } - } else { - $buffer .= \sprintf('%s extends %s%s implements %s', $mockClassName['className'], !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '', $mockClassName['originalClassName'], $interfaces); + if (!$this->messagePrinted) { + $this->write("\n"); } - return $buffer; + $this->write(sprintf("%-15s%s\n", $type . ':', $message)); + $this->messagePrinted = \true; } - private function canMockMethod(\ReflectionMethod $method) : bool + private function createPrinter(string $class, array $arguments) : \PHPUnit\TextUI\ResultPrinter { - return !($this->isConstructor($method) || $method->isFinal() || $method->isPrivate() || $this->isMethodNameExcluded($method->getName())); + $object = new $class(isset($arguments['stderr']) && $arguments['stderr'] === \true ? 'php://stderr' : null, $arguments['verbose'], $arguments['colors'], $arguments['debug'], $arguments['columns'], $arguments['reverseList']); + assert($object instanceof \PHPUnit\TextUI\ResultPrinter); + return $object; } - private function isMethodNameExcluded(string $name) : bool + private function codeCoverageGenerationStart(string $format) : void { - return isset(self::EXCLUDED_METHOD_NAMES[$name]); + $this->printer->write(sprintf("\nGenerating code coverage report in %s format ... ", $format)); + $this->timer->start(); } - private function getTemplate(string $template) : \PHPUnit\SebastianBergmann\Template\Template + private function codeCoverageGenerationSucceeded() : void { - $filename = __DIR__ . \DIRECTORY_SEPARATOR . 'Generator' . \DIRECTORY_SEPARATOR . $template; - if (!isset(self::$templates[$filename])) { - self::$templates[$filename] = new \PHPUnit\SebastianBergmann\Template\Template($filename); - } - return self::$templates[$filename]; + $this->printer->write(sprintf("done [%s]\n", $this->timer->stop()->asString())); } - /** - * @see https://github.com/sebastianbergmann/phpunit/issues/4139#issuecomment-605409765 - */ - private function isConstructor(\ReflectionMethod $method) : bool + private function codeCoverageGenerationFailed(\Exception $e) : void { - $methodName = \strtolower($method->getName()); - if ($methodName === '__construct') { - return \true; - } - if (\PHP_MAJOR_VERSION >= 8) { - return \false; - } - $className = \strtolower($method->getDeclaringClass()->getName()); - return $methodName === $className; + $this->printer->write(sprintf("failed [%s]\n%s\n", $this->timer->stop()->asString(), $e->getMessage())); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; + +/** + * @internal This interface is not covered by the backward compatibility promise for PHPUnit + */ +final class RuntimeException extends \RuntimeException implements \PHPUnit\TextUI\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; -use function array_diff; -use function array_merge; use function sprintf; -use PHPUnit\Framework\TestCase; -use ReflectionClass; -use ReflectionException; +use RuntimeException; /** - * @psalm-template MockedType + * @internal This interface is not covered by the backward compatibility promise for PHPUnit + */ +final class TestDirectoryNotFoundException extends RuntimeException implements \PHPUnit\TextUI\Exception +{ + public function __construct(string $path) + { + parent::__construct(sprintf('Test directory "%s" not found', $path)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; + +use Throwable; +/** + * @internal This interface is not covered by the backward compatibility promise for PHPUnit + */ +interface Exception extends Throwable +{ +} + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; + +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestResult; +/** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class MockBuilder +interface ResultPrinter extends TestListener { + public function printResult(TestResult $result) : void; + public function write(string $buffer) : void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; + +use const PHP_EOL; +use function array_map; +use function array_reverse; +use function count; +use function floor; +use function implode; +use function in_array; +use function is_int; +use function max; +use function preg_split; +use function sprintf; +use function str_pad; +use function str_repeat; +use function strlen; +use function vsprintf; +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\InvalidArgumentException; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestFailure; +use PHPUnit\Framework\TestResult; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Framework\Warning; +use PHPUnit\Runner\PhptTestCase; +use PHPUnit\Util\Color; +use PHPUnit\Util\Printer; +use PHPUnit\SebastianBergmann\Environment\Console; +use PHPUnit\SebastianBergmann\Timer\ResourceUsageFormatter; +use PHPUnit\SebastianBergmann\Timer\Timer; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +class DefaultResultPrinter extends Printer implements \PHPUnit\TextUI\ResultPrinter +{ + public const EVENT_TEST_START = 0; + public const EVENT_TEST_END = 1; + public const EVENT_TESTSUITE_START = 2; + public const EVENT_TESTSUITE_END = 3; + public const COLOR_NEVER = 'never'; + public const COLOR_AUTO = 'auto'; + public const COLOR_ALWAYS = 'always'; + public const COLOR_DEFAULT = self::COLOR_NEVER; + private const AVAILABLE_COLORS = [self::COLOR_NEVER, self::COLOR_AUTO, self::COLOR_ALWAYS]; /** - * @var TestCase - */ - private $testCase; - /** - * @var string + * @var int */ - private $type; + protected $column = 0; /** - * @var null|string[] + * @var int */ - private $methods = []; + protected $maxColumn; /** * @var bool */ - private $emptyMethodsArray = \false; + protected $lastTestFailed = \false; /** - * @var string + * @var int */ - private $mockClassName = ''; + protected $numAssertions = 0; /** - * @var array + * @var int */ - private $constructorArgs = []; + protected $numTests = -1; /** - * @var bool + * @var int */ - private $originalConstructor = \true; + protected $numTestsRun = 0; /** - * @var bool + * @var int */ - private $originalClone = \true; + protected $numTestsWidth; /** * @var bool */ - private $autoload = \true; + protected $colors = \false; /** * @var bool */ - private $cloneArguments = \false; + protected $debug = \false; /** * @var bool */ - private $callOriginalMethods = \false; + protected $verbose = \false; /** - * @var ?object + * @var int */ - private $proxyTarget; + private $numberOfColumns; /** * @var bool */ - private $allowMockingUnknownTypes = \true; + private $reverse; /** * @var bool */ - private $returnValueGeneration = \true; - /** - * @var Generator - */ - private $generator; - /** - * @param string|string[] $type - * - * @psalm-param class-string|string|string[] $type - */ - public function __construct(\PHPUnit\Framework\TestCase $testCase, $type) - { - $this->testCase = $testCase; - $this->type = $type; - $this->generator = new \PHPUnit\Framework\MockObject\Generator(); - } - /** - * Creates a mock object using a fluent interface. - * - * @throws RuntimeException - * - * @psalm-return MockObject&MockedType - */ - public function getMock() : \PHPUnit\Framework\MockObject\MockObject - { - $object = $this->generator->getMock($this->type, !$this->emptyMethodsArray ? $this->methods : null, $this->constructorArgs, $this->mockClassName, $this->originalConstructor, $this->originalClone, $this->autoload, $this->cloneArguments, $this->callOriginalMethods, $this->proxyTarget, $this->allowMockingUnknownTypes, $this->returnValueGeneration); - $this->testCase->registerMockObject($object); - return $object; - } - /** - * Creates a mock object for an abstract class using a fluent interface. - * - * @throws \PHPUnit\Framework\Exception - * @throws RuntimeException - * - * @psalm-return MockObject&MockedType - */ - public function getMockForAbstractClass() : \PHPUnit\Framework\MockObject\MockObject - { - $object = $this->generator->getMockForAbstractClass($this->type, $this->constructorArgs, $this->mockClassName, $this->originalConstructor, $this->originalClone, $this->autoload, $this->methods, $this->cloneArguments); - $this->testCase->registerMockObject($object); - return $object; - } + private $defectListPrinted = \false; /** - * Creates a mock object for a trait using a fluent interface. - * - * @throws \PHPUnit\Framework\Exception - * @throws RuntimeException - * - * @psalm-return MockObject&MockedType + * @var Timer */ - public function getMockForTrait() : \PHPUnit\Framework\MockObject\MockObject - { - $object = $this->generator->getMockForTrait($this->type, $this->constructorArgs, $this->mockClassName, $this->originalConstructor, $this->originalClone, $this->autoload, $this->methods, $this->cloneArguments); - $this->testCase->registerMockObject($object); - return $object; - } + private $timer; /** - * Specifies the subset of methods to mock. Default is to mock none of them. + * Constructor. * - * @deprecated https://github.com/sebastianbergmann/phpunit/pull/3687 + * @param null|resource|string $out + * @param int|string $numberOfColumns * - * @return $this + * @throws Exception */ - public function setMethods(?array $methods = null) : self + public function __construct($out = null, bool $verbose = \false, string $colors = self::COLOR_DEFAULT, bool $debug = \false, $numberOfColumns = 80, bool $reverse = \false) { - if ($methods === null) { - $this->methods = $methods; - } else { - $this->methods = \array_merge($this->methods ?? [], $methods); + parent::__construct($out); + if (!in_array($colors, self::AVAILABLE_COLORS, \true)) { + throw InvalidArgumentException::create(3, vsprintf('value from "%s", "%s" or "%s"', self::AVAILABLE_COLORS)); } - return $this; - } - /** - * Specifies the subset of methods to mock, requiring each to exist in the class. - * - * @param string[] $methods - * - * @throws RuntimeException - * - * @return $this - */ - public function onlyMethods(array $methods) : self - { - if (empty($methods)) { - $this->emptyMethodsArray = \true; - return $this; + if (!is_int($numberOfColumns) && $numberOfColumns !== 'max') { + throw InvalidArgumentException::create(5, 'integer or "max"'); } - try { - $reflector = new \ReflectionClass($this->type); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); + $console = new Console(); + $maxNumberOfColumns = $console->getNumberOfColumns(); + if ($numberOfColumns === 'max' || $numberOfColumns !== 80 && $numberOfColumns > $maxNumberOfColumns) { + $numberOfColumns = $maxNumberOfColumns; } - // @codeCoverageIgnoreEnd - foreach ($methods as $method) { - if (!$reflector->hasMethod($method)) { - throw new \PHPUnit\Framework\MockObject\RuntimeException(\sprintf('Trying to set mock method "%s" with onlyMethods, but it does not exist in class "%s". Use addMethods() for methods that don\'t exist in the class.', $method, $this->type)); - } + $this->numberOfColumns = $numberOfColumns; + $this->verbose = $verbose; + $this->debug = $debug; + $this->reverse = $reverse; + if ($colors === self::COLOR_AUTO && $console->hasColorSupport()) { + $this->colors = \true; + } else { + $this->colors = self::COLOR_ALWAYS === $colors; } - $this->methods = \array_merge($this->methods ?? [], $methods); - return $this; + $this->timer = new Timer(); + $this->timer->start(); } - /** - * Specifies methods that don't exist in the class which you want to mock. - * - * @param string[] $methods - * - * @throws RuntimeException - * - * @return $this - */ - public function addMethods(array $methods) : self + public function printResult(TestResult $result) : void { - if (empty($methods)) { - $this->emptyMethodsArray = \true; - return $this; - } - try { - $reflector = new \ReflectionClass($this->type); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - foreach ($methods as $method) { - if ($reflector->hasMethod($method)) { - throw new \PHPUnit\Framework\MockObject\RuntimeException(\sprintf('Trying to set mock method "%s" with addMethods(), but it exists in class "%s". Use onlyMethods() for methods that exist in the class.', $method, $this->type)); - } + $this->printHeader($result); + $this->printErrors($result); + $this->printWarnings($result); + $this->printFailures($result); + $this->printRisky($result); + if ($this->verbose) { + $this->printIncompletes($result); + $this->printSkipped($result); } - $this->methods = \array_merge($this->methods ?? [], $methods); - return $this; - } - /** - * Specifies the subset of methods to not mock. Default is to mock all of them. - */ - public function setMethodsExcept(array $methods = []) : self - { - return $this->setMethods(\array_diff($this->generator->getClassMethods($this->type), $methods)); - } - /** - * Specifies the arguments for the constructor. - * - * @return $this - */ - public function setConstructorArgs(array $args) : self - { - $this->constructorArgs = $args; - return $this; - } - /** - * Specifies the name for the mock class. - * - * @return $this - */ - public function setMockClassName(string $name) : self - { - $this->mockClassName = $name; - return $this; - } - /** - * Disables the invocation of the original constructor. - * - * @return $this - */ - public function disableOriginalConstructor() : self - { - $this->originalConstructor = \false; - return $this; - } - /** - * Enables the invocation of the original constructor. - * - * @return $this - */ - public function enableOriginalConstructor() : self - { - $this->originalConstructor = \true; - return $this; + $this->printFooter($result); } /** - * Disables the invocation of the original clone constructor. - * - * @return $this + * An error occurred. */ - public function disableOriginalClone() : self + public function addError(Test $test, Throwable $t, float $time) : void { - $this->originalClone = \false; - return $this; + $this->writeProgressWithColor('fg-red, bold', 'E'); + $this->lastTestFailed = \true; } /** - * Enables the invocation of the original clone constructor. - * - * @return $this + * A failure occurred. */ - public function enableOriginalClone() : self + public function addFailure(Test $test, AssertionFailedError $e, float $time) : void { - $this->originalClone = \true; - return $this; + $this->writeProgressWithColor('bg-red, fg-white', 'F'); + $this->lastTestFailed = \true; } /** - * Disables the use of class autoloading while creating the mock object. - * - * @return $this + * A warning occurred. */ - public function disableAutoload() : self + public function addWarning(Test $test, Warning $e, float $time) : void { - $this->autoload = \false; - return $this; + $this->writeProgressWithColor('fg-yellow, bold', 'W'); + $this->lastTestFailed = \true; } /** - * Enables the use of class autoloading while creating the mock object. - * - * @return $this + * Incomplete test. */ - public function enableAutoload() : self + public function addIncompleteTest(Test $test, Throwable $t, float $time) : void { - $this->autoload = \true; - return $this; + $this->writeProgressWithColor('fg-yellow, bold', 'I'); + $this->lastTestFailed = \true; } /** - * Disables the cloning of arguments passed to mocked methods. - * - * @return $this + * Risky test. */ - public function disableArgumentCloning() : self + public function addRiskyTest(Test $test, Throwable $t, float $time) : void { - $this->cloneArguments = \false; - return $this; + $this->writeProgressWithColor('fg-yellow, bold', 'R'); + $this->lastTestFailed = \true; } /** - * Enables the cloning of arguments passed to mocked methods. - * - * @return $this + * Skipped test. */ - public function enableArgumentCloning() : self + public function addSkippedTest(Test $test, Throwable $t, float $time) : void { - $this->cloneArguments = \true; - return $this; + $this->writeProgressWithColor('fg-cyan, bold', 'S'); + $this->lastTestFailed = \true; } /** - * Enables the invocation of the original methods. - * - * @return $this + * A testsuite started. */ - public function enableProxyingToOriginalMethods() : self + public function startTestSuite(TestSuite $suite) : void { - $this->callOriginalMethods = \true; - return $this; + if ($this->numTests == -1) { + $this->numTests = count($suite); + $this->numTestsWidth = strlen((string) $this->numTests); + $this->maxColumn = $this->numberOfColumns - strlen(' / (XXX%)') - 2 * $this->numTestsWidth; + } } /** - * Disables the invocation of the original methods. - * - * @return $this + * A testsuite ended. */ - public function disableProxyingToOriginalMethods() : self + public function endTestSuite(TestSuite $suite) : void { - $this->callOriginalMethods = \false; - $this->proxyTarget = null; - return $this; } /** - * Sets the proxy target. - * - * @return $this + * A test started. */ - public function setProxyTarget(object $object) : self + public function startTest(Test $test) : void { - $this->proxyTarget = $object; - return $this; + if ($this->debug) { + $this->write(sprintf("Test '%s' started\n", \PHPUnit\Util\Test::describeAsString($test))); + } } /** - * @return $this + * A test ended. */ - public function allowMockingUnknownTypes() : self + public function endTest(Test $test, float $time) : void { - $this->allowMockingUnknownTypes = \true; - return $this; + if ($this->debug) { + $this->write(sprintf("Test '%s' ended\n", \PHPUnit\Util\Test::describeAsString($test))); + } + if (!$this->lastTestFailed) { + $this->writeProgress('.'); + } + if ($test instanceof TestCase) { + $this->numAssertions += $test->getNumAssertions(); + } elseif ($test instanceof PhptTestCase) { + $this->numAssertions++; + } + $this->lastTestFailed = \false; + if ($test instanceof TestCase && !$test->hasExpectationOnOutput()) { + $this->write($test->getActualOutput()); + } + } + protected function printDefects(array $defects, string $type) : void + { + $count = count($defects); + if ($count == 0) { + return; + } + if ($this->defectListPrinted) { + $this->write("\n--\n\n"); + } + $this->write(sprintf("There %s %d %s%s:\n", $count == 1 ? 'was' : 'were', $count, $type, $count == 1 ? '' : 's')); + $i = 1; + if ($this->reverse) { + $defects = array_reverse($defects); + } + foreach ($defects as $defect) { + $this->printDefect($defect, $i++); + } + $this->defectListPrinted = \true; + } + protected function printDefect(TestFailure $defect, int $count) : void + { + $this->printDefectHeader($defect, $count); + $this->printDefectTrace($defect); + } + protected function printDefectHeader(TestFailure $defect, int $count) : void + { + $this->write(sprintf("\n%d) %s\n", $count, $defect->getTestName())); + } + protected function printDefectTrace(TestFailure $defect) : void + { + $e = $defect->thrownException(); + $this->write((string) $e); + while ($e = $e->getPrevious()) { + $this->write("\nCaused by\n" . $e); + } + } + protected function printErrors(TestResult $result) : void + { + $this->printDefects($result->errors(), 'error'); + } + protected function printFailures(TestResult $result) : void + { + $this->printDefects($result->failures(), 'failure'); + } + protected function printWarnings(TestResult $result) : void + { + $this->printDefects($result->warnings(), 'warning'); + } + protected function printIncompletes(TestResult $result) : void + { + $this->printDefects($result->notImplemented(), 'incomplete test'); + } + protected function printRisky(TestResult $result) : void + { + $this->printDefects($result->risky(), 'risky test'); + } + protected function printSkipped(TestResult $result) : void + { + $this->printDefects($result->skipped(), 'skipped test'); + } + protected function printHeader(TestResult $result) : void + { + if (count($result) > 0) { + $this->write(\PHP_EOL . \PHP_EOL . (new ResourceUsageFormatter())->resourceUsage($this->timer->stop()) . \PHP_EOL . \PHP_EOL); + } + } + protected function printFooter(TestResult $result) : void + { + if (count($result) === 0) { + $this->writeWithColor('fg-black, bg-yellow', 'No tests executed!'); + return; + } + if ($result->wasSuccessfulAndNoTestIsRiskyOrSkippedOrIncomplete()) { + $this->writeWithColor('fg-black, bg-green', sprintf('OK (%d test%s, %d assertion%s)', count($result), count($result) === 1 ? '' : 's', $this->numAssertions, $this->numAssertions === 1 ? '' : 's')); + return; + } + $color = 'fg-black, bg-yellow'; + if ($result->wasSuccessful()) { + if ($this->verbose || !$result->allHarmless()) { + $this->write("\n"); + } + $this->writeWithColor($color, 'OK, but incomplete, skipped, or risky tests!'); + } else { + $this->write("\n"); + if ($result->errorCount()) { + $color = 'fg-white, bg-red'; + $this->writeWithColor($color, 'ERRORS!'); + } elseif ($result->failureCount()) { + $color = 'fg-white, bg-red'; + $this->writeWithColor($color, 'FAILURES!'); + } elseif ($result->warningCount()) { + $color = 'fg-black, bg-yellow'; + $this->writeWithColor($color, 'WARNINGS!'); + } + } + $this->writeCountString(count($result), 'Tests', $color, \true); + $this->writeCountString($this->numAssertions, 'Assertions', $color, \true); + $this->writeCountString($result->errorCount(), 'Errors', $color); + $this->writeCountString($result->failureCount(), 'Failures', $color); + $this->writeCountString($result->warningCount(), 'Warnings', $color); + $this->writeCountString($result->skippedCount(), 'Skipped', $color); + $this->writeCountString($result->notImplementedCount(), 'Incomplete', $color); + $this->writeCountString($result->riskyCount(), 'Risky', $color); + $this->writeWithColor($color, '.'); + } + protected function writeProgress(string $progress) : void + { + if ($this->debug) { + return; + } + $this->write($progress); + $this->column++; + $this->numTestsRun++; + if ($this->column == $this->maxColumn || $this->numTestsRun == $this->numTests) { + if ($this->numTestsRun == $this->numTests) { + $this->write(str_repeat(' ', $this->maxColumn - $this->column)); + } + $this->write(sprintf(' %' . $this->numTestsWidth . 'd / %' . $this->numTestsWidth . 'd (%3s%%)', $this->numTestsRun, $this->numTests, floor($this->numTestsRun / $this->numTests * 100))); + if ($this->column == $this->maxColumn) { + $this->writeNewLine(); + } + } + } + protected function writeNewLine() : void + { + $this->column = 0; + $this->write("\n"); } /** - * @return $this + * Formats a buffer with a specified ANSI color sequence if colors are + * enabled. */ - public function disallowMockingUnknownTypes() : self + protected function colorizeTextBox(string $color, string $buffer) : string { - $this->allowMockingUnknownTypes = \false; - return $this; + if (!$this->colors) { + return $buffer; + } + $lines = preg_split('/\\r\\n|\\r|\\n/', $buffer); + $padding = max(array_map('\\strlen', $lines)); + $styledLines = []; + foreach ($lines as $line) { + $styledLines[] = Color::colorize($color, str_pad($line, $padding)); + } + return implode(\PHP_EOL, $styledLines); } /** - * @return $this + * Writes a buffer out with a color sequence if colors are enabled. */ - public function enableAutoReturnValueGeneration() : self + protected function writeWithColor(string $color, string $buffer, bool $lf = \true) : void { - $this->returnValueGeneration = \true; - return $this; + $this->write($this->colorizeTextBox($color, $buffer)); + if ($lf) { + $this->write(\PHP_EOL); + } } /** - * @return $this + * Writes progress with a color sequence if colors are enabled. */ - public function disableAutoReturnValueGeneration() : self + protected function writeProgressWithColor(string $color, string $buffer) : void { - $this->returnValueGeneration = \false; - return $this; + $buffer = $this->colorizeTextBox($color, $buffer); + $this->writeProgress($buffer); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use PHPUnit\Framework\MockObject\Builder\InvocationMocker as BuilderInvocationMocker; -use PHPUnit\Framework\MockObject\Rule\InvocationOrder; -/** - * @method BuilderInvocationMocker method($constraint) - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface MockObject extends \PHPUnit\Framework\MockObject\Stub -{ - public function __phpunit_setOriginalObject($originalObject) : void; - public function __phpunit_verify(bool $unsetInvocationMocker = \true) : void; - public function expects(\PHPUnit\Framework\MockObject\Rule\InvocationOrder $invocationRule) : \PHPUnit\Framework\MockObject\Builder\InvocationMocker; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use function call_user_func_array; -use function func_get_args; -use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount; -/** - * @internal This trait is not covered by the backward compatibility promise for PHPUnit - */ -trait Method -{ - public function method() + private function writeCountString(int $count, string $name, string $color, bool $always = \false) : void { - $expects = $this->expects(new \PHPUnit\Framework\MockObject\Rule\AnyInvokedCount()); - return \call_user_func_array([$expects, 'method'], \func_get_args()); + static $first = \true; + if ($always || $count > 0) { + $this->writeWithColor($color, sprintf('%s%s: %d', !$first ? ', ' : '', $name, $count), \false); + $first = \false; + } } } __phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); - parent::__clone(); + $result = ['extensions' => [], 'listGroups' => \false, 'listSuites' => \false, 'listTests' => \false, 'listTestsXml' => \false, 'loader' => null, 'useDefaultConfiguration' => \true, 'loadedExtensions' => [], 'unavailableExtensions' => [], 'notLoadedExtensions' => []]; + if ($arguments->hasColors()) { + $result['colors'] = $arguments->colors(); + } + if ($arguments->hasBootstrap()) { + $result['bootstrap'] = $arguments->bootstrap(); + } + if ($arguments->hasCacheResult()) { + $result['cacheResult'] = $arguments->cacheResult(); + } + if ($arguments->hasCacheResultFile()) { + $result['cacheResultFile'] = $arguments->cacheResultFile(); + } + if ($arguments->hasColumns()) { + $result['columns'] = $arguments->columns(); + } + if ($arguments->hasConfiguration()) { + $result['configuration'] = $arguments->configuration(); + } + if ($arguments->hasCoverageCacheDirectory()) { + $result['coverageCacheDirectory'] = $arguments->coverageCacheDirectory(); + } + if ($arguments->hasWarmCoverageCache()) { + $result['warmCoverageCache'] = $arguments->warmCoverageCache(); + } + if ($arguments->hasCoverageClover()) { + $result['coverageClover'] = $arguments->coverageClover(); + } + if ($arguments->hasCoverageCobertura()) { + $result['coverageCobertura'] = $arguments->coverageCobertura(); + } + if ($arguments->hasCoverageCrap4J()) { + $result['coverageCrap4J'] = $arguments->coverageCrap4J(); + } + if ($arguments->hasCoverageHtml()) { + $result['coverageHtml'] = $arguments->coverageHtml(); + } + if ($arguments->hasCoveragePhp()) { + $result['coveragePHP'] = $arguments->coveragePhp(); + } + if ($arguments->hasCoverageText()) { + $result['coverageText'] = $arguments->coverageText(); + } + if ($arguments->hasCoverageTextShowUncoveredFiles()) { + $result['coverageTextShowUncoveredFiles'] = $arguments->hasCoverageTextShowUncoveredFiles(); + } + if ($arguments->hasCoverageTextShowOnlySummary()) { + $result['coverageTextShowOnlySummary'] = $arguments->coverageTextShowOnlySummary(); + } + if ($arguments->hasCoverageXml()) { + $result['coverageXml'] = $arguments->coverageXml(); + } + if ($arguments->hasPathCoverage()) { + $result['pathCoverage'] = $arguments->pathCoverage(); + } + if ($arguments->hasDebug()) { + $result['debug'] = $arguments->debug(); + } + if ($arguments->hasHelp()) { + $result['help'] = $arguments->help(); + } + if ($arguments->hasFilter()) { + $result['filter'] = $arguments->filter(); + } + if ($arguments->hasTestSuite()) { + $result['testsuite'] = $arguments->testSuite(); + } + if ($arguments->hasGroups()) { + $result['groups'] = $arguments->groups(); + } + if ($arguments->hasExcludeGroups()) { + $result['excludeGroups'] = $arguments->excludeGroups(); + } + if ($arguments->hasTestsCovering()) { + $result['testsCovering'] = $arguments->testsCovering(); + } + if ($arguments->hasTestsUsing()) { + $result['testsUsing'] = $arguments->testsUsing(); + } + if ($arguments->hasTestSuffixes()) { + $result['testSuffixes'] = $arguments->testSuffixes(); + } + if ($arguments->hasIncludePath()) { + $result['includePath'] = $arguments->includePath(); + } + if ($arguments->hasListGroups()) { + $result['listGroups'] = $arguments->listGroups(); + } + if ($arguments->hasListSuites()) { + $result['listSuites'] = $arguments->listSuites(); + } + if ($arguments->hasListTests()) { + $result['listTests'] = $arguments->listTests(); + } + if ($arguments->hasListTestsXml()) { + $result['listTestsXml'] = $arguments->listTestsXml(); + } + if ($arguments->hasPrinter()) { + $result['printer'] = $arguments->printer(); + } + if ($arguments->hasLoader()) { + $result['loader'] = $arguments->loader(); + } + if ($arguments->hasJunitLogfile()) { + $result['junitLogfile'] = $arguments->junitLogfile(); + } + if ($arguments->hasTeamcityLogfile()) { + $result['teamcityLogfile'] = $arguments->teamcityLogfile(); + } + if ($arguments->hasExecutionOrder()) { + $result['executionOrder'] = $arguments->executionOrder(); + } + if ($arguments->hasExecutionOrderDefects()) { + $result['executionOrderDefects'] = $arguments->executionOrderDefects(); + } + if ($arguments->hasExtensions()) { + $result['extensions'] = $arguments->extensions(); + } + if ($arguments->hasUnavailableExtensions()) { + $result['unavailableExtensions'] = $arguments->unavailableExtensions(); + } + if ($arguments->hasResolveDependencies()) { + $result['resolveDependencies'] = $arguments->resolveDependencies(); + } + if ($arguments->hasProcessIsolation()) { + $result['processIsolation'] = $arguments->processIsolation(); + } + if ($arguments->hasRepeat()) { + $result['repeat'] = $arguments->repeat(); + } + if ($arguments->hasStderr()) { + $result['stderr'] = $arguments->stderr(); + } + if ($arguments->hasStopOnDefect()) { + $result['stopOnDefect'] = $arguments->stopOnDefect(); + } + if ($arguments->hasStopOnError()) { + $result['stopOnError'] = $arguments->stopOnError(); + } + if ($arguments->hasStopOnFailure()) { + $result['stopOnFailure'] = $arguments->stopOnFailure(); + } + if ($arguments->hasStopOnWarning()) { + $result['stopOnWarning'] = $arguments->stopOnWarning(); + } + if ($arguments->hasStopOnIncomplete()) { + $result['stopOnIncomplete'] = $arguments->stopOnIncomplete(); + } + if ($arguments->hasStopOnRisky()) { + $result['stopOnRisky'] = $arguments->stopOnRisky(); + } + if ($arguments->hasStopOnSkipped()) { + $result['stopOnSkipped'] = $arguments->stopOnSkipped(); + } + if ($arguments->hasFailOnEmptyTestSuite()) { + $result['failOnEmptyTestSuite'] = $arguments->failOnEmptyTestSuite(); + } + if ($arguments->hasFailOnIncomplete()) { + $result['failOnIncomplete'] = $arguments->failOnIncomplete(); + } + if ($arguments->hasFailOnRisky()) { + $result['failOnRisky'] = $arguments->failOnRisky(); + } + if ($arguments->hasFailOnSkipped()) { + $result['failOnSkipped'] = $arguments->failOnSkipped(); + } + if ($arguments->hasFailOnWarning()) { + $result['failOnWarning'] = $arguments->failOnWarning(); + } + if ($arguments->hasTestdoxGroups()) { + $result['testdoxGroups'] = $arguments->testdoxGroups(); + } + if ($arguments->hasTestdoxExcludeGroups()) { + $result['testdoxExcludeGroups'] = $arguments->testdoxExcludeGroups(); + } + if ($arguments->hasTestdoxHtmlFile()) { + $result['testdoxHTMLFile'] = $arguments->testdoxHtmlFile(); + } + if ($arguments->hasTestdoxTextFile()) { + $result['testdoxTextFile'] = $arguments->testdoxTextFile(); + } + if ($arguments->hasTestdoxXmlFile()) { + $result['testdoxXMLFile'] = $arguments->testdoxXmlFile(); + } + if ($arguments->hasUseDefaultConfiguration()) { + $result['useDefaultConfiguration'] = $arguments->useDefaultConfiguration(); + } + if ($arguments->hasNoExtensions()) { + $result['noExtensions'] = $arguments->noExtensions(); + } + if ($arguments->hasNoCoverage()) { + $result['noCoverage'] = $arguments->noCoverage(); + } + if ($arguments->hasNoLogging()) { + $result['noLogging'] = $arguments->noLogging(); + } + if ($arguments->hasNoInteraction()) { + $result['noInteraction'] = $arguments->noInteraction(); + } + if ($arguments->hasBackupGlobals()) { + $result['backupGlobals'] = $arguments->backupGlobals(); + } + if ($arguments->hasBackupStaticAttributes()) { + $result['backupStaticAttributes'] = $arguments->backupStaticAttributes(); + } + if ($arguments->hasVerbose()) { + $result['verbose'] = $arguments->verbose(); + } + if ($arguments->hasReportUselessTests()) { + $result['reportUselessTests'] = $arguments->reportUselessTests(); + } + if ($arguments->hasStrictCoverage()) { + $result['strictCoverage'] = $arguments->strictCoverage(); + } + if ($arguments->hasDisableCodeCoverageIgnore()) { + $result['disableCodeCoverageIgnore'] = $arguments->disableCodeCoverageIgnore(); + } + if ($arguments->hasBeStrictAboutChangesToGlobalState()) { + $result['beStrictAboutChangesToGlobalState'] = $arguments->beStrictAboutChangesToGlobalState(); + } + if ($arguments->hasDisallowTestOutput()) { + $result['disallowTestOutput'] = $arguments->disallowTestOutput(); + } + if ($arguments->hasBeStrictAboutResourceUsageDuringSmallTests()) { + $result['beStrictAboutResourceUsageDuringSmallTests'] = $arguments->beStrictAboutResourceUsageDuringSmallTests(); + } + if ($arguments->hasDefaultTimeLimit()) { + $result['defaultTimeLimit'] = $arguments->defaultTimeLimit(); + } + if ($arguments->hasEnforceTimeLimit()) { + $result['enforceTimeLimit'] = $arguments->enforceTimeLimit(); + } + if ($arguments->hasDisallowTodoAnnotatedTests()) { + $result['disallowTodoAnnotatedTests'] = $arguments->disallowTodoAnnotatedTests(); + } + if ($arguments->hasReverseList()) { + $result['reverseList'] = $arguments->reverseList(); + } + if ($arguments->hasCoverageFilter()) { + $result['coverageFilter'] = $arguments->coverageFilter(); + } + if ($arguments->hasRandomOrderSeed()) { + $result['randomOrderSeed'] = $arguments->randomOrderSeed(); + } + if ($arguments->hasXdebugFilterFile()) { + $result['xdebugFilterFile'] = $arguments->xdebugFilterFile(); + } + return $result; } } __phpunit_originalObject = $originalObject; - } - /** @noinspection MagicMethodsValidityInspection */ - public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration) : void - { - $this->__phpunit_returnValueGeneration = $returnValueGeneration; - } - /** @noinspection MagicMethodsValidityInspection */ - public function __phpunit_getInvocationHandler() : \PHPUnit\Framework\MockObject\InvocationHandler - { - if ($this->__phpunit_invocationMocker === null) { - $this->__phpunit_invocationMocker = new \PHPUnit\Framework\MockObject\InvocationHandler(static::$__phpunit_configurableMethods, $this->__phpunit_returnValueGeneration); - } - return $this->__phpunit_invocationMocker; - } - /** @noinspection MagicMethodsValidityInspection */ - public function __phpunit_hasMatchers() : bool - { - return $this->__phpunit_getInvocationHandler()->hasMatchers(); - } - /** @noinspection MagicMethodsValidityInspection */ - public function __phpunit_verify(bool $unsetInvocationMocker = \true) : void - { - $this->__phpunit_getInvocationHandler()->verify(); - if ($unsetInvocationMocker) { - $this->__phpunit_invocationMocker = null; - } - } - public function expects(\PHPUnit\Framework\MockObject\Rule\InvocationOrder $matcher) : \PHPUnit\Framework\MockObject\Builder\InvocationMocker - { - return $this->__phpunit_getInvocationHandler()->expects($matcher); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -/** - * @internal This trait is not covered by the backward compatibility promise for PHPUnit - */ -trait MockedCloneMethod -{ - public function __clone() - { - $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use function sprintf; -use function strtolower; -use Exception; -use PHPUnit\Framework\MockObject\Builder\InvocationMocker; -use PHPUnit\Framework\MockObject\Rule\InvocationOrder; -use Throwable; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class InvocationHandler -{ + private $backupStaticAttributes; /** - * @var Matcher[] + * @var ?bool */ - private $matchers = []; + private $beStrictAboutChangesToGlobalState; /** - * @var Matcher[] + * @var ?bool */ - private $matcherMap = []; + private $beStrictAboutResourceUsageDuringSmallTests; /** - * @var ConfigurableMethod[] + * @var ?string */ - private $configurableMethods; + private $bootstrap; /** - * @var bool + * @var ?bool */ - private $returnValueGeneration; + private $cacheResult; /** - * @var Throwable + * @var ?string */ - private $deferredError; - public function __construct(array $configurableMethods, bool $returnValueGeneration) - { - $this->configurableMethods = $configurableMethods; - $this->returnValueGeneration = $returnValueGeneration; - } - public function hasMatchers() : bool - { - foreach ($this->matchers as $matcher) { - if ($matcher->hasMatchers()) { - return \true; - } - } - return \false; - } + private $cacheResultFile; /** - * Looks up the match builder with identification $id and returns it. - * - * @param string $id The identification of the match builder + * @var ?bool */ - public function lookupMatcher(string $id) : ?\PHPUnit\Framework\MockObject\Matcher - { - if (isset($this->matcherMap[$id])) { - return $this->matcherMap[$id]; - } - return null; - } + private $checkVersion; /** - * Registers a matcher with the identification $id. The matcher can later be - * looked up using lookupMatcher() to figure out if it has been invoked. - * - * @param string $id The identification of the matcher - * @param Matcher $matcher The builder which is being registered - * - * @throws RuntimeException + * @var ?string */ - public function registerMatcher(string $id, \PHPUnit\Framework\MockObject\Matcher $matcher) : void - { - if (isset($this->matcherMap[$id])) { - throw new \PHPUnit\Framework\MockObject\RuntimeException('Matcher with id <' . $id . '> is already registered.'); - } - $this->matcherMap[$id] = $matcher; - } - public function expects(\PHPUnit\Framework\MockObject\Rule\InvocationOrder $rule) : \PHPUnit\Framework\MockObject\Builder\InvocationMocker - { - $matcher = new \PHPUnit\Framework\MockObject\Matcher($rule); - $this->addMatcher($matcher); - return new \PHPUnit\Framework\MockObject\Builder\InvocationMocker($this, $matcher, ...$this->configurableMethods); - } + private $colors; /** - * @throws RuntimeException - * @throws Exception + * @var null|int|string */ - public function invoke(\PHPUnit\Framework\MockObject\Invocation $invocation) - { - $exception = null; - $hasReturnValue = \false; - $returnValue = null; - foreach ($this->matchers as $match) { - try { - if ($match->matches($invocation)) { - $value = $match->invoked($invocation); - if (!$hasReturnValue) { - $returnValue = $value; - $hasReturnValue = \true; - } - } - } catch (\Exception $e) { - $exception = $e; - } - } - if ($exception !== null) { - throw $exception; - } - if ($hasReturnValue) { - return $returnValue; - } - if (!$this->returnValueGeneration) { - $exception = new \PHPUnit\Framework\MockObject\RuntimeException(\sprintf('Return value inference disabled and no expectation set up for %s::%s()', $invocation->getClassName(), $invocation->getMethodName())); - if (\strtolower($invocation->getMethodName()) === '__tostring') { - $this->deferredError = $exception; - return ''; - } - throw $exception; - } - return $invocation->generateReturnValue(); - } - public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool - { - foreach ($this->matchers as $matcher) { - if (!$matcher->matches($invocation)) { - return \false; - } - } - return \true; - } + private $columns; /** - * @throws Throwable + * @var ?string */ - public function verify() : void - { - foreach ($this->matchers as $matcher) { - $matcher->verify(); - } - if ($this->deferredError) { - throw $this->deferredError; - } - } - private function addMatcher(\PHPUnit\Framework\MockObject\Matcher $matcher) : void - { - $this->matchers[] = $matcher; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class IncompatibleReturnValueException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class BadMethodCallException extends \BadMethodCallException implements \PHPUnit\Framework\MockObject\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ConfigurableMethodsAlreadyInitializedException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use Throwable; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface Exception extends \Throwable -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class RuntimeException extends \RuntimeException implements \PHPUnit\Framework\MockObject\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use PHPUnit\Framework\ExpectationFailedException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface Verifiable -{ + private $configuration; + /** + * @var null|string[] + */ + private $coverageFilter; + /** + * @var ?string + */ + private $coverageClover; + /** + * @var ?string + */ + private $coverageCobertura; + /** + * @var ?string + */ + private $coverageCrap4J; + /** + * @var ?string + */ + private $coverageHtml; + /** + * @var ?string + */ + private $coveragePhp; + /** + * @var ?string + */ + private $coverageText; + /** + * @var ?bool + */ + private $coverageTextShowUncoveredFiles; + /** + * @var ?bool + */ + private $coverageTextShowOnlySummary; + /** + * @var ?string + */ + private $coverageXml; + /** + * @var ?bool + */ + private $pathCoverage; + /** + * @var ?string + */ + private $coverageCacheDirectory; + /** + * @var ?bool + */ + private $warmCoverageCache; + /** + * @var ?bool + */ + private $debug; + /** + * @var ?int + */ + private $defaultTimeLimit; + /** + * @var ?bool + */ + private $disableCodeCoverageIgnore; + /** + * @var ?bool + */ + private $disallowTestOutput; + /** + * @var ?bool + */ + private $disallowTodoAnnotatedTests; + /** + * @var ?bool + */ + private $enforceTimeLimit; + /** + * @var null|string[] + */ + private $excludeGroups; + /** + * @var ?int + */ + private $executionOrder; + /** + * @var ?int + */ + private $executionOrderDefects; + /** + * @var null|Extension[] + */ + private $extensions; + /** + * @var null|string[] + */ + private $unavailableExtensions; + /** + * @var ?bool + */ + private $failOnEmptyTestSuite; + /** + * @var ?bool + */ + private $failOnIncomplete; + /** + * @var ?bool + */ + private $failOnRisky; + /** + * @var ?bool + */ + private $failOnSkipped; + /** + * @var ?bool + */ + private $failOnWarning; + /** + * @var ?string + */ + private $filter; /** - * Verifies that the current expectation is valid. If everything is OK the - * code should just return, if not it must throw an exception. - * - * @throws ExpectationFailedException + * @var ?bool */ - public function verify() : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use function assert; -use function implode; -use function sprintf; -use Exception; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount; -use PHPUnit\Framework\MockObject\Rule\AnyParameters; -use PHPUnit\Framework\MockObject\Rule\InvocationOrder; -use PHPUnit\Framework\MockObject\Rule\InvokedCount; -use PHPUnit\Framework\MockObject\Rule\MethodName; -use PHPUnit\Framework\MockObject\Rule\ParametersRule; -use PHPUnit\Framework\MockObject\Stub\Stub; -use PHPUnit\Framework\TestFailure; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Matcher -{ + private $generateConfiguration; /** - * @var InvocationOrder + * @var ?bool */ - private $invocationRule; + private $migrateConfiguration; /** - * @var mixed + * @var null|string[] */ - private $afterMatchBuilderId; + private $groups; /** - * @var bool + * @var null|string[] */ - private $afterMatchBuilderIsInvoked = \false; + private $testsCovering; /** - * @var MethodName + * @var null|string[] */ - private $methodNameRule; + private $testsUsing; /** - * @var ParametersRule + * @var ?bool */ - private $parametersRule; + private $help; /** - * @var Stub + * @var ?string */ - private $stub; - public function __construct(\PHPUnit\Framework\MockObject\Rule\InvocationOrder $rule) - { - $this->invocationRule = $rule; - } - public function hasMatchers() : bool - { - return !$this->invocationRule instanceof \PHPUnit\Framework\MockObject\Rule\AnyInvokedCount; - } - public function hasMethodNameRule() : bool - { - return $this->methodNameRule !== null; - } - public function getMethodNameRule() : \PHPUnit\Framework\MockObject\Rule\MethodName - { - return $this->methodNameRule; - } - public function setMethodNameRule(\PHPUnit\Framework\MockObject\Rule\MethodName $rule) : void - { - $this->methodNameRule = $rule; - } - public function hasParametersRule() : bool - { - return $this->parametersRule !== null; - } - public function setParametersRule(\PHPUnit\Framework\MockObject\Rule\ParametersRule $rule) : void - { - $this->parametersRule = $rule; - } - public function setStub(\PHPUnit\Framework\MockObject\Stub\Stub $stub) : void - { - $this->stub = $stub; - } - public function setAfterMatchBuilderId(string $id) : void - { - $this->afterMatchBuilderId = $id; - } + private $includePath; /** - * @throws Exception - * @throws RuntimeException - * @throws ExpectationFailedException + * @var null|string[] */ - public function invoked(\PHPUnit\Framework\MockObject\Invocation $invocation) - { - if ($this->methodNameRule === null) { - throw new \PHPUnit\Framework\MockObject\RuntimeException('No method rule is set'); - } - if ($this->afterMatchBuilderId !== null) { - $matcher = $invocation->getObject()->__phpunit_getInvocationHandler()->lookupMatcher($this->afterMatchBuilderId); - if (!$matcher) { - throw new \PHPUnit\Framework\MockObject\RuntimeException(\sprintf('No builder found for match builder identification <%s>', $this->afterMatchBuilderId)); - } - \assert($matcher instanceof self); - if ($matcher->invocationRule->hasBeenInvoked()) { - $this->afterMatchBuilderIsInvoked = \true; - } - } - $this->invocationRule->invoked($invocation); - try { - if ($this->parametersRule !== null) { - $this->parametersRule->apply($invocation); - } - } catch (\PHPUnit\Framework\ExpectationFailedException $e) { - throw new \PHPUnit\Framework\ExpectationFailedException(\sprintf("Expectation failed for %s when %s\n%s", $this->methodNameRule->toString(), $this->invocationRule->toString(), $e->getMessage()), $e->getComparisonFailure()); - } - if ($this->stub) { - return $this->stub->invoke($invocation); - } - return $invocation->generateReturnValue(); - } + private $iniSettings; /** - * @throws RuntimeException - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var ?string */ - public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool - { - if ($this->afterMatchBuilderId !== null) { - $matcher = $invocation->getObject()->__phpunit_getInvocationHandler()->lookupMatcher($this->afterMatchBuilderId); - if (!$matcher) { - throw new \PHPUnit\Framework\MockObject\RuntimeException(\sprintf('No builder found for match builder identification <%s>', $this->afterMatchBuilderId)); - } - \assert($matcher instanceof self); - if (!$matcher->invocationRule->hasBeenInvoked()) { - return \false; - } - } - if ($this->methodNameRule === null) { - throw new \PHPUnit\Framework\MockObject\RuntimeException('No method rule is set'); - } - if (!$this->invocationRule->matches($invocation)) { - return \false; - } - try { - if (!$this->methodNameRule->matches($invocation)) { - return \false; - } - } catch (\PHPUnit\Framework\ExpectationFailedException $e) { - throw new \PHPUnit\Framework\ExpectationFailedException(\sprintf("Expectation failed for %s when %s\n%s", $this->methodNameRule->toString(), $this->invocationRule->toString(), $e->getMessage()), $e->getComparisonFailure()); - } - return \true; - } + private $junitLogfile; /** - * @throws RuntimeException - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var ?bool */ - public function verify() : void - { - if ($this->methodNameRule === null) { - throw new \PHPUnit\Framework\MockObject\RuntimeException('No method rule is set'); - } - try { - $this->invocationRule->verify(); - if ($this->parametersRule === null) { - $this->parametersRule = new \PHPUnit\Framework\MockObject\Rule\AnyParameters(); - } - $invocationIsAny = $this->invocationRule instanceof \PHPUnit\Framework\MockObject\Rule\AnyInvokedCount; - $invocationIsNever = $this->invocationRule instanceof \PHPUnit\Framework\MockObject\Rule\InvokedCount && $this->invocationRule->isNever(); - if (!$invocationIsAny && !$invocationIsNever) { - $this->parametersRule->verify(); - } - } catch (\PHPUnit\Framework\ExpectationFailedException $e) { - throw new \PHPUnit\Framework\ExpectationFailedException(\sprintf("Expectation failed for %s when %s.\n%s", $this->methodNameRule->toString(), $this->invocationRule->toString(), \PHPUnit\Framework\TestFailure::exceptionToString($e))); - } - } - public function toString() : string - { - $list = []; - if ($this->invocationRule !== null) { - $list[] = $this->invocationRule->toString(); - } - if ($this->methodNameRule !== null) { - $list[] = 'where ' . $this->methodNameRule->toString(); - } - if ($this->parametersRule !== null) { - $list[] = 'and ' . $this->parametersRule->toString(); - } - if ($this->afterMatchBuilderId !== null) { - $list[] = 'after ' . $this->afterMatchBuilderId; - } - if ($this->stub !== null) { - $list[] = 'will ' . $this->stub->toString(); - } - return \implode(' ', $list); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use function is_string; -use function sprintf; -use function strtolower; -use PHPUnit\Framework\Constraint\Constraint; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MethodNameConstraint extends \PHPUnit\Framework\Constraint\Constraint -{ + private $listGroups; /** - * @var string + * @var ?bool */ - private $methodName; - public function __construct(string $methodName) - { - $this->methodName = $methodName; - } - public function toString() : string - { - return \sprintf('is "%s"', $this->methodName); - } - protected function matches($other) : bool - { - if (!\is_string($other)) { - return \false; - } - return \strtolower($this->methodName) === \strtolower($other); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use function array_pop; -use function count; -use function is_array; -use PHPUnit\Framework\MockObject\Invocation; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ReturnValueMap implements \PHPUnit\Framework\MockObject\Stub\Stub -{ + private $listSuites; /** - * @var array + * @var ?bool */ - private $valueMap; - public function __construct(array $valueMap) - { - $this->valueMap = $valueMap; - } - public function invoke(\PHPUnit\Framework\MockObject\Invocation $invocation) - { - $parameterCount = \count($invocation->getParameters()); - foreach ($this->valueMap as $map) { - if (!\is_array($map) || $parameterCount !== \count($map) - 1) { - continue; - } - $return = \array_pop($map); - if ($invocation->getParameters() === $map) { - return $return; - } - } - } - public function toString() : string - { - return 'return value from a map'; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use function array_shift; -use function sprintf; -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnit\SebastianBergmann\Exporter\Exporter; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ConsecutiveCalls implements \PHPUnit\Framework\MockObject\Stub\Stub -{ + private $listTests; /** - * @var array + * @var ?string */ - private $stack; + private $listTestsXml; /** - * @var mixed + * @var ?string */ - private $value; - public function __construct(array $stack) - { - $this->stack = $stack; - } - public function invoke(\PHPUnit\Framework\MockObject\Invocation $invocation) - { - $this->value = \array_shift($this->stack); - if ($this->value instanceof \PHPUnit\Framework\MockObject\Stub\Stub) { - $this->value = $this->value->invoke($invocation); - } - return $this->value; - } - public function toString() : string - { - $exporter = new \PHPUnit\SebastianBergmann\Exporter\Exporter(); - return \sprintf('return user-specified value %s', $exporter->export($this->value)); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use function sprintf; -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnit\SebastianBergmann\Exporter\Exporter; -use Throwable; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Exception implements \PHPUnit\Framework\MockObject\Stub\Stub -{ - private $exception; - public function __construct(\Throwable $exception) - { - $this->exception = $exception; - } + private $loader; /** - * @throws Throwable + * @var ?bool */ - public function invoke(\PHPUnit\Framework\MockObject\Invocation $invocation) : void - { - throw $this->exception; - } - public function toString() : string - { - $exporter = new \PHPUnit\SebastianBergmann\Exporter\Exporter(); - return \sprintf('raise user-specified exception %s', $exporter->export($this->exception)); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnit\Framework\SelfDescribing; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface Stub extends \PHPUnit\Framework\SelfDescribing -{ + private $noCoverage; /** - * Fakes the processing of the invocation $invocation by returning a - * specific value. - * - * @param Invocation $invocation The invocation which was mocked and matched by the current method and argument matchers + * @var ?bool */ - public function invoke(\PHPUnit\Framework\MockObject\Invocation $invocation); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use function sprintf; -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnit\SebastianBergmann\Exporter\Exporter; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ReturnReference implements \PHPUnit\Framework\MockObject\Stub\Stub -{ + private $noExtensions; /** - * @var mixed + * @var ?bool */ - private $reference; - public function __construct(&$reference) - { - $this->reference =& $reference; - } - public function invoke(\PHPUnit\Framework\MockObject\Invocation $invocation) - { - return $this->reference; - } - public function toString() : string - { - $exporter = new \PHPUnit\SebastianBergmann\Exporter\Exporter(); - return \sprintf('return user-specified reference %s', $exporter->export($this->reference)); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use function call_user_func_array; -use function get_class; -use function is_array; -use function is_object; -use function sprintf; -use PHPUnit\Framework\MockObject\Invocation; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ReturnCallback implements \PHPUnit\Framework\MockObject\Stub\Stub -{ - private $callback; - public function __construct($callback) - { - $this->callback = $callback; - } - public function invoke(\PHPUnit\Framework\MockObject\Invocation $invocation) - { - return \call_user_func_array($this->callback, $invocation->getParameters()); - } - public function toString() : string - { - if (\is_array($this->callback)) { - if (\is_object($this->callback[0])) { - $class = \get_class($this->callback[0]); - $type = '->'; - } else { - $class = $this->callback[0]; - $type = '::'; - } - return \sprintf('return result of user defined callback %s%s%s() with the ' . 'passed arguments', $class, $type, $this->callback[1]); - } - return 'return result of user defined callback ' . $this->callback . ' with the passed arguments'; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use function sprintf; -use PHPUnit\Framework\MockObject\Invocation; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ReturnArgument implements \PHPUnit\Framework\MockObject\Stub\Stub -{ + private $noInteraction; + /** + * @var ?bool + */ + private $noLogging; + /** + * @var ?string + */ + private $printer; + /** + * @var ?bool + */ + private $processIsolation; + /** + * @var ?int + */ + private $randomOrderSeed; + /** + * @var ?int + */ + private $repeat; + /** + * @var ?bool + */ + private $reportUselessTests; + /** + * @var ?bool + */ + private $resolveDependencies; + /** + * @var ?bool + */ + private $reverseList; + /** + * @var ?bool + */ + private $stderr; /** - * @var int + * @var ?bool */ - private $argumentIndex; - public function __construct($argumentIndex) - { - $this->argumentIndex = $argumentIndex; - } - public function invoke(\PHPUnit\Framework\MockObject\Invocation $invocation) - { - if (isset($invocation->getParameters()[$this->argumentIndex])) { - return $invocation->getParameters()[$this->argumentIndex]; - } - } - public function toString() : string - { - return \sprintf('return argument #%d', $this->argumentIndex); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnit\Framework\MockObject\RuntimeException; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ReturnSelf implements \PHPUnit\Framework\MockObject\Stub\Stub -{ + private $strictCoverage; /** - * @throws RuntimeException + * @var ?bool */ - public function invoke(\PHPUnit\Framework\MockObject\Invocation $invocation) - { - return $invocation->getObject(); - } - public function toString() : string - { - return 'return the current object'; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use function sprintf; -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnit\SebastianBergmann\Exporter\Exporter; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ReturnStub implements \PHPUnit\Framework\MockObject\Stub\Stub -{ + private $stopOnDefect; /** - * @var mixed + * @var ?bool */ - private $value; - public function __construct($value) - { - $this->value = $value; - } - public function invoke(\PHPUnit\Framework\MockObject\Invocation $invocation) - { - return $this->value; - } - public function toString() : string - { - $exporter = new \PHPUnit\SebastianBergmann\Exporter\Exporter(); - return \sprintf('return user-specified value %s', $exporter->export($this->value)); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use PHPUnit\Framework\MockObject\Builder\InvocationStubber; -/** - * @method InvocationStubber method($constraint) - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface Stub -{ - public function __phpunit_getInvocationHandler() : \PHPUnit\Framework\MockObject\InvocationHandler; - public function __phpunit_hasMatchers() : bool; - public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration) : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use function call_user_func; -use function class_exists; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MockClass implements \PHPUnit\Framework\MockObject\MockType -{ + private $stopOnError; /** - * @var string + * @var ?bool */ - private $classCode; + private $stopOnFailure; /** - * @var class-string + * @var ?bool */ - private $mockName; + private $stopOnIncomplete; /** - * @var ConfigurableMethod[] + * @var ?bool */ - private $configurableMethods; + private $stopOnRisky; /** - * @psalm-param class-string $mockName + * @var ?bool */ - public function __construct(string $classCode, string $mockName, array $configurableMethods) - { - $this->classCode = $classCode; - $this->mockName = $mockName; - $this->configurableMethods = $configurableMethods; - } + private $stopOnSkipped; /** - * @psalm-return class-string + * @var ?bool */ - public function generate() : string - { - if (!\class_exists($this->mockName, \false)) { - eval($this->classCode); - \call_user_func([$this->mockName, '__phpunit_initConfigurableMethods'], ...$this->configurableMethods); - } - return $this->mockName; - } - public function getClassCode() : string - { - return $this->classCode; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use function array_map; -use function explode; -use function get_class; -use function implode; -use function is_object; -use function sprintf; -use function strpos; -use function strtolower; -use function substr; -use PHPUnit\Doctrine\Instantiator\Instantiator; -use PHPUnit\Framework\SelfDescribing; -use PHPUnit\Util\Type; -use PHPUnit\SebastianBergmann\Exporter\Exporter; -use stdClass; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Invocation implements \PHPUnit\Framework\SelfDescribing -{ + private $stopOnWarning; /** - * @var string + * @var ?string */ - private $className; + private $teamcityLogfile; /** - * @var string + * @var null|string[] */ - private $methodName; + private $testdoxExcludeGroups; /** - * @var array + * @var null|string[] */ - private $parameters; + private $testdoxGroups; /** - * @var string + * @var ?string */ - private $returnType; + private $testdoxHtmlFile; /** - * @var bool + * @var ?string */ - private $isReturnTypeNullable = \false; + private $testdoxTextFile; /** - * @var bool + * @var ?string */ - private $proxiedCall; + private $testdoxXmlFile; /** - * @var object + * @var null|string[] */ - private $object; - public function __construct(string $className, string $methodName, array $parameters, string $returnType, object $object, bool $cloneObjects = \false, bool $proxiedCall = \false) - { - $this->className = $className; - $this->methodName = $methodName; - $this->parameters = $parameters; - $this->object = $object; - $this->proxiedCall = $proxiedCall; - if (\strtolower($methodName) === '__tostring') { - $returnType = 'string'; - } - if (\strpos($returnType, '?') === 0) { - $returnType = \substr($returnType, 1); - $this->isReturnTypeNullable = \true; - } - $this->returnType = $returnType; - if (!$cloneObjects) { - return; - } - foreach ($this->parameters as $key => $value) { - if (\is_object($value)) { - $this->parameters[$key] = $this->cloneObject($value); - } - } - } - public function getClassName() : string - { - return $this->className; - } - public function getMethodName() : string - { - return $this->methodName; - } - public function getParameters() : array - { - return $this->parameters; - } + private $testSuffixes; /** - * @throws RuntimeException - * - * @return mixed Mocked return value + * @var ?string */ - public function generateReturnValue() - { - if ($this->isReturnTypeNullable || $this->proxiedCall) { - return; - } - $returnType = $this->returnType; - if (\strpos($returnType, '|') !== \false) { - $types = \explode('|', $returnType); - $returnType = $types[0]; - foreach ($types as $type) { - if ($type === 'null') { - return; - } - } - } - switch (\strtolower($returnType)) { - case '': - case 'void': - return; - case 'string': - return ''; - case 'float': - return 0.0; - case 'int': - return 0; - case 'bool': - return \false; - case 'array': - return []; - case 'static': - return (new \PHPUnit\Doctrine\Instantiator\Instantiator())->instantiate(\get_class($this->object)); - case 'object': - return new \stdClass(); - case 'callable': - case 'closure': - return static function () : void { - }; - case 'traversable': - case 'generator': - case 'iterable': - $generator = static function () : \Generator { - yield; - }; - return $generator(); - case 'mixed': - return null; - default: - return (new \PHPUnit\Framework\MockObject\Generator())->getMock($this->returnType, [], [], '', \false); - } - } - public function toString() : string - { - $exporter = new \PHPUnit\SebastianBergmann\Exporter\Exporter(); - return \sprintf('%s::%s(%s)%s', $this->className, $this->methodName, \implode(', ', \array_map([$exporter, 'shortenedExport'], $this->parameters)), $this->returnType ? \sprintf(': %s', $this->returnType) : ''); - } - public function getObject() : object - { - return $this->object; - } - private function cloneObject(object $original) : object - { - if (\PHPUnit\Util\Type::isCloneable($original)) { - return clone $original; - } - return $original; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use PHPUnit\SebastianBergmann\Type\Type; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ConfigurableMethod -{ + private $testSuite; /** - * @var string + * @var string[] */ - private $name; + private $unrecognizedOptions; /** - * @var Type + * @var ?string */ - private $returnType; - public function __construct(string $name, \PHPUnit\SebastianBergmann\Type\Type $returnType) - { - $this->name = $name; - $this->returnType = $returnType; - } - public function getName() : string - { - return $this->name; - } - public function mayReturn($value) : bool - { - if ($value === null && $this->returnType->allowsNull()) { - return \true; - } - return $this->returnType->isAssignable(\PHPUnit\SebastianBergmann\Type\Type::fromValue($value, \false)); - } - public function getReturnTypeDeclaration() : string - { - return $this->returnType->asString(); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Rule; - -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class AnyInvokedCount extends \PHPUnit\Framework\MockObject\Rule\InvocationOrder -{ - public function toString() : string - { - return 'invoked zero or more times'; - } - public function verify() : void - { - } - public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool - { - return \true; - } - protected function invokedDo(\PHPUnit\Framework\MockObject\Invocation $invocation) : void + private $unrecognizedOrderBy; + /** + * @var ?bool + */ + private $useDefaultConfiguration; + /** + * @var ?bool + */ + private $verbose; + /** + * @var ?bool + */ + private $version; + /** + * @var ?string + */ + private $xdebugFilterFile; + /** + * @param null|int|string $columns + */ + public function __construct(?string $argument, ?string $atLeastVersion, ?bool $backupGlobals, ?bool $backupStaticAttributes, ?bool $beStrictAboutChangesToGlobalState, ?bool $beStrictAboutResourceUsageDuringSmallTests, ?string $bootstrap, ?bool $cacheResult, ?string $cacheResultFile, ?bool $checkVersion, ?string $colors, $columns, ?string $configuration, ?string $coverageClover, ?string $coverageCobertura, ?string $coverageCrap4J, ?string $coverageHtml, ?string $coveragePhp, ?string $coverageText, ?bool $coverageTextShowUncoveredFiles, ?bool $coverageTextShowOnlySummary, ?string $coverageXml, ?bool $pathCoverage, ?string $coverageCacheDirectory, ?bool $warmCoverageCache, ?bool $debug, ?int $defaultTimeLimit, ?bool $disableCodeCoverageIgnore, ?bool $disallowTestOutput, ?bool $disallowTodoAnnotatedTests, ?bool $enforceTimeLimit, ?array $excludeGroups, ?int $executionOrder, ?int $executionOrderDefects, ?array $extensions, ?array $unavailableExtensions, ?bool $failOnEmptyTestSuite, ?bool $failOnIncomplete, ?bool $failOnRisky, ?bool $failOnSkipped, ?bool $failOnWarning, ?string $filter, ?bool $generateConfiguration, ?bool $migrateConfiguration, ?array $groups, ?array $testsCovering, ?array $testsUsing, ?bool $help, ?string $includePath, ?array $iniSettings, ?string $junitLogfile, ?bool $listGroups, ?bool $listSuites, ?bool $listTests, ?string $listTestsXml, ?string $loader, ?bool $noCoverage, ?bool $noExtensions, ?bool $noInteraction, ?bool $noLogging, ?string $printer, ?bool $processIsolation, ?int $randomOrderSeed, ?int $repeat, ?bool $reportUselessTests, ?bool $resolveDependencies, ?bool $reverseList, ?bool $stderr, ?bool $strictCoverage, ?bool $stopOnDefect, ?bool $stopOnError, ?bool $stopOnFailure, ?bool $stopOnIncomplete, ?bool $stopOnRisky, ?bool $stopOnSkipped, ?bool $stopOnWarning, ?string $teamcityLogfile, ?array $testdoxExcludeGroups, ?array $testdoxGroups, ?string $testdoxHtmlFile, ?string $testdoxTextFile, ?string $testdoxXmlFile, ?array $testSuffixes, ?string $testSuite, array $unrecognizedOptions, ?string $unrecognizedOrderBy, ?bool $useDefaultConfiguration, ?bool $verbose, ?bool $version, ?array $coverageFilter, ?string $xdebugFilterFile) { - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Rule; - -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class InvokedAtLeastOnce extends \PHPUnit\Framework\MockObject\Rule\InvocationOrder -{ - public function toString() : string + $this->argument = $argument; + $this->atLeastVersion = $atLeastVersion; + $this->backupGlobals = $backupGlobals; + $this->backupStaticAttributes = $backupStaticAttributes; + $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; + $this->beStrictAboutResourceUsageDuringSmallTests = $beStrictAboutResourceUsageDuringSmallTests; + $this->bootstrap = $bootstrap; + $this->cacheResult = $cacheResult; + $this->cacheResultFile = $cacheResultFile; + $this->checkVersion = $checkVersion; + $this->colors = $colors; + $this->columns = $columns; + $this->configuration = $configuration; + $this->coverageFilter = $coverageFilter; + $this->coverageClover = $coverageClover; + $this->coverageCobertura = $coverageCobertura; + $this->coverageCrap4J = $coverageCrap4J; + $this->coverageHtml = $coverageHtml; + $this->coveragePhp = $coveragePhp; + $this->coverageText = $coverageText; + $this->coverageTextShowUncoveredFiles = $coverageTextShowUncoveredFiles; + $this->coverageTextShowOnlySummary = $coverageTextShowOnlySummary; + $this->coverageXml = $coverageXml; + $this->pathCoverage = $pathCoverage; + $this->coverageCacheDirectory = $coverageCacheDirectory; + $this->warmCoverageCache = $warmCoverageCache; + $this->debug = $debug; + $this->defaultTimeLimit = $defaultTimeLimit; + $this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore; + $this->disallowTestOutput = $disallowTestOutput; + $this->disallowTodoAnnotatedTests = $disallowTodoAnnotatedTests; + $this->enforceTimeLimit = $enforceTimeLimit; + $this->excludeGroups = $excludeGroups; + $this->executionOrder = $executionOrder; + $this->executionOrderDefects = $executionOrderDefects; + $this->extensions = $extensions; + $this->unavailableExtensions = $unavailableExtensions; + $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; + $this->failOnIncomplete = $failOnIncomplete; + $this->failOnRisky = $failOnRisky; + $this->failOnSkipped = $failOnSkipped; + $this->failOnWarning = $failOnWarning; + $this->filter = $filter; + $this->generateConfiguration = $generateConfiguration; + $this->migrateConfiguration = $migrateConfiguration; + $this->groups = $groups; + $this->testsCovering = $testsCovering; + $this->testsUsing = $testsUsing; + $this->help = $help; + $this->includePath = $includePath; + $this->iniSettings = $iniSettings; + $this->junitLogfile = $junitLogfile; + $this->listGroups = $listGroups; + $this->listSuites = $listSuites; + $this->listTests = $listTests; + $this->listTestsXml = $listTestsXml; + $this->loader = $loader; + $this->noCoverage = $noCoverage; + $this->noExtensions = $noExtensions; + $this->noInteraction = $noInteraction; + $this->noLogging = $noLogging; + $this->printer = $printer; + $this->processIsolation = $processIsolation; + $this->randomOrderSeed = $randomOrderSeed; + $this->repeat = $repeat; + $this->reportUselessTests = $reportUselessTests; + $this->resolveDependencies = $resolveDependencies; + $this->reverseList = $reverseList; + $this->stderr = $stderr; + $this->strictCoverage = $strictCoverage; + $this->stopOnDefect = $stopOnDefect; + $this->stopOnError = $stopOnError; + $this->stopOnFailure = $stopOnFailure; + $this->stopOnIncomplete = $stopOnIncomplete; + $this->stopOnRisky = $stopOnRisky; + $this->stopOnSkipped = $stopOnSkipped; + $this->stopOnWarning = $stopOnWarning; + $this->teamcityLogfile = $teamcityLogfile; + $this->testdoxExcludeGroups = $testdoxExcludeGroups; + $this->testdoxGroups = $testdoxGroups; + $this->testdoxHtmlFile = $testdoxHtmlFile; + $this->testdoxTextFile = $testdoxTextFile; + $this->testdoxXmlFile = $testdoxXmlFile; + $this->testSuffixes = $testSuffixes; + $this->testSuite = $testSuite; + $this->unrecognizedOptions = $unrecognizedOptions; + $this->unrecognizedOrderBy = $unrecognizedOrderBy; + $this->useDefaultConfiguration = $useDefaultConfiguration; + $this->verbose = $verbose; + $this->version = $version; + $this->xdebugFilterFile = $xdebugFilterFile; + } + public function hasArgument() : bool { - return 'invoked at least once'; + return $this->argument !== null; } /** - * Verifies that the current expectation is valid. If everything is OK the - * code should just return, if not it must throw an exception. - * - * @throws ExpectationFailedException + * @throws Exception */ - public function verify() : void + public function argument() : string { - $count = $this->getInvocationCount(); - if ($count < 1) { - throw new \PHPUnit\Framework\ExpectationFailedException('Expected invocation at least once but it never occurred.'); + if ($this->argument === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->argument; } - public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool - { - return \true; - } - protected function invokedDo(\PHPUnit\Framework\MockObject\Invocation $invocation) : void + public function hasAtLeastVersion() : bool { + return $this->atLeastVersion !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Rule; - -use function count; -use function get_class; -use function sprintf; -use Exception; -use PHPUnit\Framework\Constraint\Constraint; -use PHPUnit\Framework\Constraint\IsAnything; -use PHPUnit\Framework\Constraint\IsEqual; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Parameters implements \PHPUnit\Framework\MockObject\Rule\ParametersRule -{ - /** - * @var Constraint[] - */ - private $parameters = []; - /** - * @var BaseInvocation - */ - private $invocation; - /** - * @var bool|ExpectationFailedException - */ - private $parameterVerificationResult; /** - * @throws \PHPUnit\Framework\Exception + * @throws Exception */ - public function __construct(array $parameters) + public function atLeastVersion() : string { - foreach ($parameters as $parameter) { - if (!$parameter instanceof \PHPUnit\Framework\Constraint\Constraint) { - $parameter = new \PHPUnit\Framework\Constraint\IsEqual($parameter); - } - $this->parameters[] = $parameter; + if ($this->atLeastVersion === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->atLeastVersion; } - public function toString() : string + public function hasBackupGlobals() : bool { - $text = 'with parameter'; - foreach ($this->parameters as $index => $parameter) { - if ($index > 0) { - $text .= ' and'; - } - $text .= ' ' . $index . ' ' . $parameter->toString(); - } - return $text; + return $this->backupGlobals !== null; } /** * @throws Exception */ - public function apply(\PHPUnit\Framework\MockObject\Invocation $invocation) : void + public function backupGlobals() : bool { - $this->invocation = $invocation; - $this->parameterVerificationResult = null; - try { - $this->parameterVerificationResult = $this->doVerify(); - } catch (\PHPUnit\Framework\ExpectationFailedException $e) { - $this->parameterVerificationResult = $e; - throw $this->parameterVerificationResult; + if ($this->backupGlobals === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->backupGlobals; } - /** - * Checks if the invocation $invocation matches the current rules. If it - * does the rule will get the invoked() method called which should check - * if an expectation is met. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function verify() : void + public function hasBackupStaticAttributes() : bool { - $this->doVerify(); + return $this->backupStaticAttributes !== null; } /** - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception */ - private function doVerify() : bool + public function backupStaticAttributes() : bool { - if (isset($this->parameterVerificationResult)) { - return $this->guardAgainstDuplicateEvaluationOfParameterConstraints(); - } - if ($this->invocation === null) { - throw new \PHPUnit\Framework\ExpectationFailedException('Mocked method does not exist.'); - } - if (\count($this->invocation->getParameters()) < \count($this->parameters)) { - $message = 'Parameter count for invocation %s is too low.'; - // The user called `->with($this->anything())`, but may have meant - // `->withAnyParameters()`. - // - // @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/199 - if (\count($this->parameters) === 1 && \get_class($this->parameters[0]) === \PHPUnit\Framework\Constraint\IsAnything::class) { - $message .= "\nTo allow 0 or more parameters with any value, omit ->with() or use ->withAnyParameters() instead."; - } - throw new \PHPUnit\Framework\ExpectationFailedException(\sprintf($message, $this->invocation->toString())); - } - foreach ($this->parameters as $i => $parameter) { - $parameter->evaluate($this->invocation->getParameters()[$i], \sprintf('Parameter %s for invocation %s does not match expected ' . 'value.', $i, $this->invocation->toString())); + if ($this->backupStaticAttributes === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return \true; + return $this->backupStaticAttributes; + } + public function hasBeStrictAboutChangesToGlobalState() : bool + { + return $this->beStrictAboutChangesToGlobalState !== null; } /** - * @throws ExpectationFailedException + * @throws Exception */ - private function guardAgainstDuplicateEvaluationOfParameterConstraints() : bool + public function beStrictAboutChangesToGlobalState() : bool { - if ($this->parameterVerificationResult instanceof \PHPUnit\Framework\ExpectationFailedException) { - throw $this->parameterVerificationResult; + if ($this->beStrictAboutChangesToGlobalState === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return (bool) $this->parameterVerificationResult; + return $this->beStrictAboutChangesToGlobalState; + } + public function hasBeStrictAboutResourceUsageDuringSmallTests() : bool + { + return $this->beStrictAboutResourceUsageDuringSmallTests !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Rule; - -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class InvokedAtLeastCount extends \PHPUnit\Framework\MockObject\Rule\InvocationOrder -{ - /** - * @var int - */ - private $requiredInvocations; /** - * @param int $requiredInvocations + * @throws Exception */ - public function __construct($requiredInvocations) + public function beStrictAboutResourceUsageDuringSmallTests() : bool { - $this->requiredInvocations = $requiredInvocations; + if ($this->beStrictAboutResourceUsageDuringSmallTests === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->beStrictAboutResourceUsageDuringSmallTests; } - public function toString() : string + public function hasBootstrap() : bool { - return 'invoked at least ' . $this->requiredInvocations . ' times'; + return $this->bootstrap !== null; } /** - * Verifies that the current expectation is valid. If everything is OK the - * code should just return, if not it must throw an exception. - * - * @throws ExpectationFailedException + * @throws Exception */ - public function verify() : void + public function bootstrap() : string { - $count = $this->getInvocationCount(); - if ($count < $this->requiredInvocations) { - throw new \PHPUnit\Framework\ExpectationFailedException('Expected invocation at least ' . $this->requiredInvocations . ' times but it occurred ' . $count . ' time(s).'); + if ($this->bootstrap === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->bootstrap; } - public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool - { - return \true; - } - protected function invokedDo(\PHPUnit\Framework\MockObject\Invocation $invocation) : void + public function hasCacheResult() : bool { + return $this->cacheResult !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Rule; - -use function sprintf; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4297 - * @codeCoverageIgnore - */ -final class InvokedAtIndex extends \PHPUnit\Framework\MockObject\Rule\InvocationOrder -{ /** - * @var int + * @throws Exception */ - private $sequenceIndex; + public function cacheResult() : bool + { + if ($this->cacheResult === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->cacheResult; + } + public function hasCacheResultFile() : bool + { + return $this->cacheResultFile !== null; + } /** - * @var int + * @throws Exception */ - private $currentIndex = -1; + public function cacheResultFile() : string + { + if ($this->cacheResultFile === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->cacheResultFile; + } + public function hasCheckVersion() : bool + { + return $this->checkVersion !== null; + } /** - * @param int $sequenceIndex + * @throws Exception */ - public function __construct($sequenceIndex) + public function checkVersion() : bool { - $this->sequenceIndex = $sequenceIndex; + if ($this->checkVersion === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->checkVersion; } - public function toString() : string + public function hasColors() : bool { - return 'invoked at sequence index ' . $this->sequenceIndex; + return $this->colors !== null; } - public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool + /** + * @throws Exception + */ + public function colors() : string { - $this->currentIndex++; - return $this->currentIndex == $this->sequenceIndex; + if ($this->colors === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->colors; + } + public function hasColumns() : bool + { + return $this->columns !== null; } /** - * Verifies that the current expectation is valid. If everything is OK the - * code should just return, if not it must throw an exception. - * - * @throws ExpectationFailedException + * @throws Exception */ - public function verify() : void + public function columns() { - if ($this->currentIndex < $this->sequenceIndex) { - throw new \PHPUnit\Framework\ExpectationFailedException(\sprintf('The expected invocation at index %s was never reached.', $this->sequenceIndex)); + if ($this->columns === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->columns; } - protected function invokedDo(\PHPUnit\Framework\MockObject\Invocation $invocation) : void + public function hasConfiguration() : bool { + return $this->configuration !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Rule; - -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; -use PHPUnit\Framework\MockObject\Verifiable; -use PHPUnit\Framework\SelfDescribing; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface ParametersRule extends \PHPUnit\Framework\SelfDescribing, \PHPUnit\Framework\MockObject\Verifiable -{ /** - * @throws ExpectationFailedException if the invocation violates the rule + * @throws Exception */ - public function apply(\PHPUnit\Framework\MockObject\Invocation $invocation) : void; - public function verify() : void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Rule; - -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class AnyParameters implements \PHPUnit\Framework\MockObject\Rule\ParametersRule -{ - public function toString() : string + public function configuration() : string { - return 'with any parameters'; + if ($this->configuration === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->configuration; } - public function apply(\PHPUnit\Framework\MockObject\Invocation $invocation) : void + public function hasCoverageFilter() : bool { + return $this->coverageFilter !== null; } - public function verify() : void + /** + * @throws Exception + */ + public function coverageFilter() : array { + if ($this->coverageFilter === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageFilter; + } + public function hasCoverageClover() : bool + { + return $this->coverageClover !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Rule; - -use function sprintf; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class InvokedCount extends \PHPUnit\Framework\MockObject\Rule\InvocationOrder -{ /** - * @var int + * @throws Exception */ - private $expectedCount; + public function coverageClover() : string + { + if ($this->coverageClover === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageClover; + } + public function hasCoverageCobertura() : bool + { + return $this->coverageCobertura !== null; + } /** - * @param int $expectedCount + * @throws Exception */ - public function __construct($expectedCount) + public function coverageCobertura() : string { - $this->expectedCount = $expectedCount; + if ($this->coverageCobertura === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageCobertura; } - public function isNever() : bool + public function hasCoverageCrap4J() : bool { - return $this->expectedCount === 0; + return $this->coverageCrap4J !== null; } - public function toString() : string + /** + * @throws Exception + */ + public function coverageCrap4J() : string { - return 'invoked ' . $this->expectedCount . ' time(s)'; + if ($this->coverageCrap4J === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageCrap4J; } - public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool + public function hasCoverageHtml() : bool { - return \true; + return $this->coverageHtml !== null; } /** - * Verifies that the current expectation is valid. If everything is OK the - * code should just return, if not it must throw an exception. - * - * @throws ExpectationFailedException + * @throws Exception */ - public function verify() : void + public function coverageHtml() : string { - $count = $this->getInvocationCount(); - if ($count !== $this->expectedCount) { - throw new \PHPUnit\Framework\ExpectationFailedException(\sprintf('Method was expected to be called %d times, ' . 'actually called %d times.', $this->expectedCount, $count)); + if ($this->coverageHtml === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->coverageHtml; + } + public function hasCoveragePhp() : bool + { + return $this->coveragePhp !== null; } /** - * @throws ExpectationFailedException + * @throws Exception */ - protected function invokedDo(\PHPUnit\Framework\MockObject\Invocation $invocation) : void + public function coveragePhp() : string { - $count = $this->getInvocationCount(); - if ($count > $this->expectedCount) { - $message = $invocation->toString() . ' '; - switch ($this->expectedCount) { - case 0: - $message .= 'was not expected to be called.'; - break; - case 1: - $message .= 'was not expected to be called more than once.'; - break; - default: - $message .= \sprintf('was not expected to be called more than %d times.', $this->expectedCount); - } - throw new \PHPUnit\Framework\ExpectationFailedException($message); + if ($this->coveragePhp === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->coveragePhp; + } + public function hasCoverageText() : bool + { + return $this->coverageText !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Rule; - -use function is_string; -use PHPUnit\Framework\Constraint\Constraint; -use PHPUnit\Framework\InvalidArgumentException; -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; -use PHPUnit\Framework\MockObject\MethodNameConstraint; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MethodName -{ /** - * @var Constraint + * @throws Exception */ - private $constraint; - /** - * @param Constraint|string $constraint - * - * @throws InvalidArgumentException + public function coverageText() : string + { + if ($this->coverageText === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageText; + } + public function hasCoverageTextShowUncoveredFiles() : bool + { + return $this->coverageTextShowUncoveredFiles !== null; + } + /** + * @throws Exception */ - public function __construct($constraint) + public function coverageTextShowUncoveredFiles() : bool { - if (\is_string($constraint)) { - $constraint = new \PHPUnit\Framework\MockObject\MethodNameConstraint($constraint); - } - if (!$constraint instanceof \PHPUnit\Framework\Constraint\Constraint) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'PHPUnit\\Framework\\Constraint\\Constraint object or string'); + if ($this->coverageTextShowUncoveredFiles === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $this->constraint = $constraint; + return $this->coverageTextShowUncoveredFiles; } - public function toString() : string + public function hasCoverageTextShowOnlySummary() : bool { - return 'method name ' . $this->constraint->toString(); + return $this->coverageTextShowOnlySummary !== null; } /** - * @throws \PHPUnit\Framework\ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception */ - public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool + public function coverageTextShowOnlySummary() : bool { - return $this->matchesName($invocation->getMethodName()); + if ($this->coverageTextShowOnlySummary === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageTextShowOnlySummary; } - public function matchesName(string $methodName) : bool + public function hasCoverageXml() : bool { - return (bool) $this->constraint->evaluate($methodName, '', \true); + return $this->coverageXml !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Rule; - -use function count; -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; -use PHPUnit\Framework\MockObject\Verifiable; -use PHPUnit\Framework\SelfDescribing; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -abstract class InvocationOrder implements \PHPUnit\Framework\SelfDescribing, \PHPUnit\Framework\MockObject\Verifiable -{ /** - * @var BaseInvocation[] + * @throws Exception */ - private $invocations = []; - public function getInvocationCount() : int - { - return \count($this->invocations); - } - public function hasBeenInvoked() : bool + public function coverageXml() : string { - return \count($this->invocations) > 0; + if ($this->coverageXml === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->coverageXml; } - public final function invoked(\PHPUnit\Framework\MockObject\Invocation $invocation) + public function hasPathCoverage() : bool { - $this->invocations[] = $invocation; - return $this->invokedDo($invocation); + return $this->pathCoverage !== null; } - public abstract function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool; - protected abstract function invokedDo(\PHPUnit\Framework\MockObject\Invocation $invocation); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Rule; - -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class InvokedAtMostCount extends \PHPUnit\Framework\MockObject\Rule\InvocationOrder -{ - /** - * @var int - */ - private $allowedInvocations; /** - * @param int $allowedInvocations + * @throws Exception */ - public function __construct($allowedInvocations) + public function pathCoverage() : bool { - $this->allowedInvocations = $allowedInvocations; + if ($this->pathCoverage === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->pathCoverage; } - public function toString() : string + public function hasCoverageCacheDirectory() : bool { - return 'invoked at most ' . $this->allowedInvocations . ' times'; + return $this->coverageCacheDirectory !== null; } /** - * Verifies that the current expectation is valid. If everything is OK the - * code should just return, if not it must throw an exception. - * - * @throws ExpectationFailedException + * @throws Exception */ - public function verify() : void + public function coverageCacheDirectory() : string { - $count = $this->getInvocationCount(); - if ($count > $this->allowedInvocations) { - throw new \PHPUnit\Framework\ExpectationFailedException('Expected invocation at most ' . $this->allowedInvocations . ' times but it occurred ' . $count . ' time(s).'); + if ($this->coverageCacheDirectory === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->coverageCacheDirectory; } - public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool - { - return \true; - } - protected function invokedDo(\PHPUnit\Framework\MockObject\Invocation $invocation) : void + public function hasWarmCoverageCache() : bool { + return $this->warmCoverageCache !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Rule; - -use function count; -use function gettype; -use function is_iterable; -use function sprintf; -use PHPUnit\Framework\Constraint\Constraint; -use PHPUnit\Framework\Constraint\IsEqual; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\InvalidParameterGroupException; -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ConsecutiveParameters implements \PHPUnit\Framework\MockObject\Rule\ParametersRule -{ - /** - * @var array - */ - private $parameterGroups = []; - /** - * @var array - */ - private $invocations = []; /** - * @throws \PHPUnit\Framework\Exception + * @throws Exception */ - public function __construct(array $parameterGroups) + public function warmCoverageCache() : bool { - foreach ($parameterGroups as $index => $parameters) { - if (!\is_iterable($parameters)) { - throw new \PHPUnit\Framework\InvalidParameterGroupException(\sprintf('Parameter group #%d must be an array or Traversable, got %s', $index, \gettype($parameters))); - } - foreach ($parameters as $parameter) { - if (!$parameter instanceof \PHPUnit\Framework\Constraint\Constraint) { - $parameter = new \PHPUnit\Framework\Constraint\IsEqual($parameter); - } - $this->parameterGroups[$index][] = $parameter; - } + if ($this->warmCoverageCache === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->warmCoverageCache; } - public function toString() : string + public function hasDebug() : bool { - return 'with consecutive parameters'; + return $this->debug !== null; } /** - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception */ - public function apply(\PHPUnit\Framework\MockObject\Invocation $invocation) : void + public function debug() : bool { - $this->invocations[] = $invocation; - $callIndex = \count($this->invocations) - 1; - $this->verifyInvocation($invocation, $callIndex); + if ($this->debug === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->debug; + } + public function hasDefaultTimeLimit() : bool + { + return $this->defaultTimeLimit !== null; } /** - * @throws \PHPUnit\Framework\ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception */ - public function verify() : void + public function defaultTimeLimit() : int { - foreach ($this->invocations as $callIndex => $invocation) { - $this->verifyInvocation($invocation, $callIndex); + if ($this->defaultTimeLimit === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->defaultTimeLimit; + } + public function hasDisableCodeCoverageIgnore() : bool + { + return $this->disableCodeCoverageIgnore !== null; } /** - * Verify a single invocation. - * - * @param int $callIndex - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception */ - private function verifyInvocation(\PHPUnit\Framework\MockObject\Invocation $invocation, $callIndex) : void + public function disableCodeCoverageIgnore() : bool { - if (!isset($this->parameterGroups[$callIndex])) { - // no parameter assertion for this call index - return; - } - $parameters = $this->parameterGroups[$callIndex]; - if (\count($invocation->getParameters()) < \count($parameters)) { - throw new \PHPUnit\Framework\ExpectationFailedException(\sprintf('Parameter count for invocation %s is too low.', $invocation->toString())); - } - foreach ($parameters as $i => $parameter) { - $parameter->evaluate($invocation->getParameters()[$i], \sprintf('Parameter %s for invocation #%d %s does not match expected ' . 'value.', $i, $callIndex, $invocation->toString())); + if ($this->disableCodeCoverageIgnore === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->disableCodeCoverageIgnore; } -} - - @trigger_error({deprecation}, E_USER_DEPRECATED); - - {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} + public function hasDisallowTestOutput() : bool { - $__phpunit_arguments = [{arguments_call}]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > {arguments_count}) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}, true - ) - ); - - call_user_func_array(array($this->__phpunit_originalObject, "{method_name}"), $__phpunit_arguments); + return $this->disallowTestOutput !== null; } - - {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} - {{deprecation} - $__phpunit_arguments = [{arguments_call}]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > {arguments_count}) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } + /** + * @throws Exception + */ + public function disallowTestOutput() : bool + { + if ($this->disallowTestOutput === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - - $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} - ) - ); - - return $__phpunit_result; + return $this->disallowTestOutput; } - - {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} + public function hasDisallowTodoAnnotatedTests() : bool { - throw new \PHPUnit\Framework\MockObject\BadMethodCallException('Static method "{method_name}" cannot be invoked on mock object'); + return $this->disallowTodoAnnotatedTests !== null; } -declare(strict_types=1); - -{prologue}{class_declaration} -{ - use \PHPUnit\Framework\MockObject\Api;{method}{clone} -{mocked_methods}}{epilogue} -declare(strict_types=1); - -{namespace}class {class_name} extends \SoapClient -{ - public function __construct($wsdl, array $options) + /** + * @throws Exception + */ + public function disallowTodoAnnotatedTests() : bool { - parent::__construct('{wsdl}', $options); + if ($this->disallowTodoAnnotatedTests === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->disallowTodoAnnotatedTests; } -{methods}} - - {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} + public function hasEnforceTimeLimit() : bool { - $__phpunit_arguments = [{arguments_call}]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > {arguments_count}) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}, true - ) - ); - - return call_user_func_array(array($this->__phpunit_originalObject, "{method_name}"), $__phpunit_arguments); + return $this->enforceTimeLimit !== null; } - - {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} - {{deprecation} - $__phpunit_arguments = [{arguments_call}]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > {arguments_count}) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } + /** + * @throws Exception + */ + public function enforceTimeLimit() : bool + { + if ($this->enforceTimeLimit === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - - $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} - ) - ); + return $this->enforceTimeLimit; } -declare(strict_types=1); - -{prologue}class {class_name} -{ - use {trait_name}; -} - - public function {method_name}({arguments}) + public function hasExcludeGroups() : bool { + return $this->excludeGroups !== null; } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface Identity -{ - /** - * Sets the identification of the expectation to $id. - * - * @note The identifier is unique per mock object. - * - * @param string $id unique identification of expectation - */ - public function id($id); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -use PHPUnit\Framework\MockObject\Stub\Stub as BaseStub; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface Stub extends \PHPUnit\Framework\MockObject\Builder\Identity -{ - /** - * Stubs the matching method with the stub object $stub. Any invocations of - * the matched method will now be handled by the stub instead. - */ - public function will(\PHPUnit\Framework\MockObject\Stub\Stub $stub) : \PHPUnit\Framework\MockObject\Builder\Identity; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface ParametersMatch extends \PHPUnit\Framework\MockObject\Builder\Stub -{ - /** - * Defines the expectation which must occur before the current is valid. - * - * @param string $id the identification of the expectation that should - * occur before this one - * - * @return Stub - */ - public function after($id); - /** - * Sets the parameters to match for, each parameter to this function will - * be part of match. To perform specific matches or constraints create a - * new PHPUnit\Framework\Constraint\Constraint and use it for the parameter. - * If the parameter value is not a constraint it will use the - * PHPUnit\Framework\Constraint\IsEqual for the value. - * - * Some examples: - * - * // match first parameter with value 2 - * $b->with(2); - * // match first parameter with value 'smock' and second identical to 42 - * $b->with('smock', new PHPUnit\Framework\Constraint\IsEqual(42)); - * - * - * @return ParametersMatch - */ - public function with(...$arguments); /** - * Sets a rule which allows any kind of parameters. - * - * Some examples: - * - * // match any number of parameters - * $b->withAnyParameters(); - * - * - * @return ParametersMatch - */ - public function withAnyParameters(); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -use function array_map; -use function array_merge; -use function count; -use function get_class; -use function gettype; -use function in_array; -use function is_object; -use function is_string; -use function sprintf; -use function strtolower; -use PHPUnit\Framework\Constraint\Constraint; -use PHPUnit\Framework\MockObject\ConfigurableMethod; -use PHPUnit\Framework\MockObject\IncompatibleReturnValueException; -use PHPUnit\Framework\MockObject\InvocationHandler; -use PHPUnit\Framework\MockObject\Matcher; -use PHPUnit\Framework\MockObject\Rule; -use PHPUnit\Framework\MockObject\RuntimeException; -use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls; -use PHPUnit\Framework\MockObject\Stub\Exception; -use PHPUnit\Framework\MockObject\Stub\ReturnArgument; -use PHPUnit\Framework\MockObject\Stub\ReturnCallback; -use PHPUnit\Framework\MockObject\Stub\ReturnReference; -use PHPUnit\Framework\MockObject\Stub\ReturnSelf; -use PHPUnit\Framework\MockObject\Stub\ReturnStub; -use PHPUnit\Framework\MockObject\Stub\ReturnValueMap; -use PHPUnit\Framework\MockObject\Stub\Stub; -use Throwable; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class InvocationMocker implements \PHPUnit\Framework\MockObject\Builder\InvocationStubber, \PHPUnit\Framework\MockObject\Builder\MethodNameMatch -{ + * @throws Exception + */ + public function excludeGroups() : array + { + if ($this->excludeGroups === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->excludeGroups; + } + public function hasExecutionOrder() : bool + { + return $this->executionOrder !== null; + } /** - * @var InvocationHandler + * @throws Exception */ - private $invocationHandler; + public function executionOrder() : int + { + if ($this->executionOrder === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->executionOrder; + } + public function hasExecutionOrderDefects() : bool + { + return $this->executionOrderDefects !== null; + } /** - * @var Matcher + * @throws Exception */ - private $matcher; + public function executionOrderDefects() : int + { + if ($this->executionOrderDefects === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->executionOrderDefects; + } + public function hasFailOnEmptyTestSuite() : bool + { + return $this->failOnEmptyTestSuite !== null; + } /** - * @var ConfigurableMethod[] + * @throws Exception */ - private $configurableMethods; - public function __construct(\PHPUnit\Framework\MockObject\InvocationHandler $handler, \PHPUnit\Framework\MockObject\Matcher $matcher, \PHPUnit\Framework\MockObject\ConfigurableMethod ...$configurableMethods) + public function failOnEmptyTestSuite() : bool { - $this->invocationHandler = $handler; - $this->matcher = $matcher; - $this->configurableMethods = $configurableMethods; + if ($this->failOnEmptyTestSuite === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->failOnEmptyTestSuite; + } + public function hasFailOnIncomplete() : bool + { + return $this->failOnIncomplete !== null; } /** - * @return $this + * @throws Exception */ - public function id($id) : self + public function failOnIncomplete() : bool { - $this->invocationHandler->registerMatcher($id, $this->matcher); - return $this; + if ($this->failOnIncomplete === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->failOnIncomplete; + } + public function hasFailOnRisky() : bool + { + return $this->failOnRisky !== null; } /** - * @return $this + * @throws Exception */ - public function will(\PHPUnit\Framework\MockObject\Stub\Stub $stub) : \PHPUnit\Framework\MockObject\Builder\Identity + public function failOnRisky() : bool { - $this->matcher->setStub($stub); - return $this; + if ($this->failOnRisky === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->failOnRisky; } - public function willReturn($value, ...$nextValues) : self + public function hasFailOnSkipped() : bool { - if (\count($nextValues) === 0) { - $this->ensureTypeOfReturnValues([$value]); - $stub = $value instanceof \PHPUnit\Framework\MockObject\Stub\Stub ? $value : new \PHPUnit\Framework\MockObject\Stub\ReturnStub($value); - } else { - $values = \array_merge([$value], $nextValues); - $this->ensureTypeOfReturnValues($values); - $stub = new \PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls($values); + return $this->failOnSkipped !== null; + } + /** + * @throws Exception + */ + public function failOnSkipped() : bool + { + if ($this->failOnSkipped === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $this->will($stub); + return $this->failOnSkipped; } - public function willReturnReference(&$reference) : self + public function hasFailOnWarning() : bool { - $stub = new \PHPUnit\Framework\MockObject\Stub\ReturnReference($reference); - return $this->will($stub); + return $this->failOnWarning !== null; } - public function willReturnMap(array $valueMap) : self + /** + * @throws Exception + */ + public function failOnWarning() : bool { - $stub = new \PHPUnit\Framework\MockObject\Stub\ReturnValueMap($valueMap); - return $this->will($stub); + if ($this->failOnWarning === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->failOnWarning; } - public function willReturnArgument($argumentIndex) : self + public function hasFilter() : bool { - $stub = new \PHPUnit\Framework\MockObject\Stub\ReturnArgument($argumentIndex); - return $this->will($stub); + return $this->filter !== null; } - public function willReturnCallback($callback) : self + /** + * @throws Exception + */ + public function filter() : string { - $stub = new \PHPUnit\Framework\MockObject\Stub\ReturnCallback($callback); - return $this->will($stub); + if ($this->filter === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->filter; } - public function willReturnSelf() : self + public function hasGenerateConfiguration() : bool { - $stub = new \PHPUnit\Framework\MockObject\Stub\ReturnSelf(); - return $this->will($stub); + return $this->generateConfiguration !== null; } - public function willReturnOnConsecutiveCalls(...$values) : self + /** + * @throws Exception + */ + public function generateConfiguration() : bool { - $stub = new \PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls($values); - return $this->will($stub); + if ($this->generateConfiguration === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->generateConfiguration; } - public function willThrowException(\Throwable $exception) : self + public function hasMigrateConfiguration() : bool { - $stub = new \PHPUnit\Framework\MockObject\Stub\Exception($exception); - return $this->will($stub); + return $this->migrateConfiguration !== null; } /** - * @return $this + * @throws Exception */ - public function after($id) : self + public function migrateConfiguration() : bool { - $this->matcher->setAfterMatchBuilderId($id); - return $this; + if ($this->migrateConfiguration === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->migrateConfiguration; + } + public function hasGroups() : bool + { + return $this->groups !== null; } /** - * @throws RuntimeException - * - * @return $this + * @throws Exception */ - public function with(...$arguments) : self + public function groups() : array { - $this->canDefineParameters(); - $this->matcher->setParametersRule(new \PHPUnit\Framework\MockObject\Rule\Parameters($arguments)); - return $this; + if ($this->groups === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->groups; + } + public function hasTestsCovering() : bool + { + return $this->testsCovering !== null; } /** - * @param array ...$arguments - * - * @throws RuntimeException - * - * @return $this + * @throws Exception */ - public function withConsecutive(...$arguments) : self + public function testsCovering() : array { - $this->canDefineParameters(); - $this->matcher->setParametersRule(new \PHPUnit\Framework\MockObject\Rule\ConsecutiveParameters($arguments)); - return $this; + if ($this->testsCovering === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->testsCovering; + } + public function hasTestsUsing() : bool + { + return $this->testsUsing !== null; } /** - * @throws RuntimeException - * - * @return $this + * @throws Exception */ - public function withAnyParameters() : self + public function testsUsing() : array { - $this->canDefineParameters(); - $this->matcher->setParametersRule(new \PHPUnit\Framework\MockObject\Rule\AnyParameters()); - return $this; + if ($this->testsUsing === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->testsUsing; + } + public function hasHelp() : bool + { + return $this->help !== null; } /** - * @param Constraint|string $constraint - * - * @throws RuntimeException - * - * @return $this + * @throws Exception */ - public function method($constraint) : self + public function help() : bool { - if ($this->matcher->hasMethodNameRule()) { - throw new \PHPUnit\Framework\MockObject\RuntimeException('Rule for method name is already defined, cannot redefine'); + if ($this->help === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $configurableMethodNames = \array_map(static function (\PHPUnit\Framework\MockObject\ConfigurableMethod $configurable) { - return \strtolower($configurable->getName()); - }, $this->configurableMethods); - if (\is_string($constraint) && !\in_array(\strtolower($constraint), $configurableMethodNames, \true)) { - throw new \PHPUnit\Framework\MockObject\RuntimeException(\sprintf('Trying to configure method "%s" which cannot be configured because it does not exist, has not been specified, is final, or is static', $constraint)); + return $this->help; + } + public function hasIncludePath() : bool + { + return $this->includePath !== null; + } + /** + * @throws Exception + */ + public function includePath() : string + { + if ($this->includePath === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $this->matcher->setMethodNameRule(new \PHPUnit\Framework\MockObject\Rule\MethodName($constraint)); - return $this; + return $this->includePath; + } + public function hasIniSettings() : bool + { + return $this->iniSettings !== null; } /** - * Validate that a parameters rule can be defined, throw exceptions otherwise. - * - * @throws RuntimeException + * @throws Exception */ - private function canDefineParameters() : void + public function iniSettings() : array { - if (!$this->matcher->hasMethodNameRule()) { - throw new \PHPUnit\Framework\MockObject\RuntimeException('Rule for method name is not defined, cannot define rule for parameters ' . 'without one'); + if ($this->iniSettings === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - if ($this->matcher->hasParametersRule()) { - throw new \PHPUnit\Framework\MockObject\RuntimeException('Rule for parameters is already defined, cannot redefine'); + return $this->iniSettings; + } + public function hasJunitLogfile() : bool + { + return $this->junitLogfile !== null; + } + /** + * @throws Exception + */ + public function junitLogfile() : string + { + if ($this->junitLogfile === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->junitLogfile; } - private function getConfiguredMethod() : ?\PHPUnit\Framework\MockObject\ConfigurableMethod + public function hasListGroups() : bool { - $configuredMethod = null; - foreach ($this->configurableMethods as $configurableMethod) { - if ($this->matcher->getMethodNameRule()->matchesName($configurableMethod->getName())) { - if ($configuredMethod !== null) { - return null; - } - $configuredMethod = $configurableMethod; - } + return $this->listGroups !== null; + } + /** + * @throws Exception + */ + public function listGroups() : bool + { + if ($this->listGroups === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $configuredMethod; + return $this->listGroups; } - private function ensureTypeOfReturnValues(array $values) : void + public function hasListSuites() : bool { - $configuredMethod = $this->getConfiguredMethod(); - if ($configuredMethod === null) { - return; + return $this->listSuites !== null; + } + /** + * @throws Exception + */ + public function listSuites() : bool + { + if ($this->listSuites === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - foreach ($values as $value) { - if (!$configuredMethod->mayReturn($value)) { - throw new \PHPUnit\Framework\MockObject\IncompatibleReturnValueException(\sprintf('Method %s may not return value of type %s, its return declaration is "%s"', $configuredMethod->getName(), \is_object($value) ? \get_class($value) : \gettype($value), $configuredMethod->getReturnTypeDeclaration())); - } + return $this->listSuites; + } + public function hasListTests() : bool + { + return $this->listTests !== null; + } + /** + * @throws Exception + */ + public function listTests() : bool + { + if ($this->listTests === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->listTests; + } + public function hasListTestsXml() : bool + { + return $this->listTestsXml !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -use PHPUnit\Framework\MockObject\Stub\Stub; -use Throwable; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface InvocationStubber -{ - public function will(\PHPUnit\Framework\MockObject\Stub\Stub $stub) : \PHPUnit\Framework\MockObject\Builder\Identity; - /** @return self */ - public function willReturn($value, ...$nextValues); /** - * @param mixed $reference - * - * @return self + * @throws Exception */ - public function willReturnReference(&$reference); + public function listTestsXml() : string + { + if ($this->listTestsXml === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->listTestsXml; + } + public function hasLoader() : bool + { + return $this->loader !== null; + } /** - * @param array> $valueMap - * - * @return self + * @throws Exception */ - public function willReturnMap(array $valueMap); + public function loader() : string + { + if ($this->loader === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->loader; + } + public function hasNoCoverage() : bool + { + return $this->noCoverage !== null; + } /** - * @param int $argumentIndex - * - * @return self + * @throws Exception */ - public function willReturnArgument($argumentIndex); + public function noCoverage() : bool + { + if ($this->noCoverage === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->noCoverage; + } + public function hasNoExtensions() : bool + { + return $this->noExtensions !== null; + } /** - * @param callable $callback - * - * @return self + * @throws Exception */ - public function willReturnCallback($callback); - /** @return self */ - public function willReturnSelf(); + public function noExtensions() : bool + { + if ($this->noExtensions === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->noExtensions; + } + public function hasExtensions() : bool + { + return $this->extensions !== null; + } /** - * @param mixed $values - * - * @return self + * @throws Exception */ - public function willReturnOnConsecutiveCalls(...$values); - /** @return self */ - public function willThrowException(\Throwable $exception); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface MethodNameMatch extends \PHPUnit\Framework\MockObject\Builder\ParametersMatch -{ + public function extensions() : array + { + if ($this->extensions === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->extensions; + } + public function hasUnavailableExtensions() : bool + { + return $this->unavailableExtensions !== null; + } /** - * Adds a new method name match and returns the parameter match object for - * further matching possibilities. - * - * @param \PHPUnit\Framework\Constraint\Constraint $name Constraint for matching method, if a string is passed it will use the PHPUnit_Framework_Constraint_IsEqual - * - * @return ParametersMatch + * @throws Exception */ - public function method($name); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use function class_exists; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MockTrait implements \PHPUnit\Framework\MockObject\MockType -{ + public function unavailableExtensions() : array + { + if ($this->unavailableExtensions === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->unavailableExtensions; + } + public function hasNoInteraction() : bool + { + return $this->noInteraction !== null; + } /** - * @var string + * @throws Exception */ - private $classCode; + public function noInteraction() : bool + { + if ($this->noInteraction === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->noInteraction; + } + public function hasNoLogging() : bool + { + return $this->noLogging !== null; + } + /** + * @throws Exception + */ + public function noLogging() : bool + { + if ($this->noLogging === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->noLogging; + } + public function hasPrinter() : bool + { + return $this->printer !== null; + } + /** + * @throws Exception + */ + public function printer() : string + { + if ($this->printer === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->printer; + } + public function hasProcessIsolation() : bool + { + return $this->processIsolation !== null; + } /** - * @var class-string + * @throws Exception */ - private $mockName; + public function processIsolation() : bool + { + if ($this->processIsolation === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->processIsolation; + } + public function hasRandomOrderSeed() : bool + { + return $this->randomOrderSeed !== null; + } /** - * @psalm-param class-string $mockName + * @throws Exception */ - public function __construct(string $classCode, string $mockName) + public function randomOrderSeed() : int { - $this->classCode = $classCode; - $this->mockName = $mockName; + if ($this->randomOrderSeed === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->randomOrderSeed; + } + public function hasRepeat() : bool + { + return $this->repeat !== null; } /** - * @psalm-return class-string + * @throws Exception */ - public function generate() : string + public function repeat() : int { - if (!\class_exists($this->mockName, \false)) { - eval($this->classCode); + if ($this->repeat === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return $this->mockName; + return $this->repeat; } - public function getClassCode() : string + public function hasReportUselessTests() : bool { - return $this->classCode; + return $this->reportUselessTests !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use function array_key_exists; -use function array_values; -use function strtolower; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MockMethodSet -{ /** - * @var MockMethod[] + * @throws Exception */ - private $methods = []; - public function addMethods(\PHPUnit\Framework\MockObject\MockMethod ...$methods) : void + public function reportUselessTests() : bool { - foreach ($methods as $method) { - $this->methods[\strtolower($method->getName())] = $method; + if ($this->reportUselessTests === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } + return $this->reportUselessTests; + } + public function hasResolveDependencies() : bool + { + return $this->resolveDependencies !== null; } /** - * @return MockMethod[] + * @throws Exception */ - public function asArray() : array + public function resolveDependencies() : bool { - return \array_values($this->methods); + if ($this->resolveDependencies === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->resolveDependencies; } - public function hasMethod(string $methodName) : bool + public function hasReverseList() : bool { - return \array_key_exists(\strtolower($methodName), $this->methods); + return $this->reverseList !== null; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use const DIRECTORY_SEPARATOR; -use function implode; -use function is_string; -use function preg_match; -use function preg_replace; -use function sprintf; -use function substr_count; -use function trim; -use function var_export; -use ReflectionException; -use ReflectionMethod; -use ReflectionNamedType; -use ReflectionParameter; -use ReflectionUnionType; -use PHPUnit\SebastianBergmann\Template\Template; -use PHPUnit\SebastianBergmann\Type\ReflectionMapper; -use PHPUnit\SebastianBergmann\Type\Type; -use PHPUnit\SebastianBergmann\Type\UnknownType; -use PHPUnit\SebastianBergmann\Type\VoidType; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MockMethod -{ /** - * @var Template[] + * @throws Exception */ - private static $templates = []; + public function reverseList() : bool + { + if ($this->reverseList === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->reverseList; + } + public function hasStderr() : bool + { + return $this->stderr !== null; + } /** - * @var string + * @throws Exception */ - private $className; + public function stderr() : bool + { + if ($this->stderr === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->stderr; + } + public function hasStrictCoverage() : bool + { + return $this->strictCoverage !== null; + } /** - * @var string + * @throws Exception */ - private $methodName; + public function strictCoverage() : bool + { + if ($this->strictCoverage === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->strictCoverage; + } + public function hasStopOnDefect() : bool + { + return $this->stopOnDefect !== null; + } /** - * @var bool + * @throws Exception */ - private $cloneArguments; + public function stopOnDefect() : bool + { + if ($this->stopOnDefect === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->stopOnDefect; + } + public function hasStopOnError() : bool + { + return $this->stopOnError !== null; + } /** - * @var string string + * @throws Exception */ - private $modifier; + public function stopOnError() : bool + { + if ($this->stopOnError === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->stopOnError; + } + public function hasStopOnFailure() : bool + { + return $this->stopOnFailure !== null; + } /** - * @var string + * @throws Exception */ - private $argumentsForDeclaration; + public function stopOnFailure() : bool + { + if ($this->stopOnFailure === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->stopOnFailure; + } + public function hasStopOnIncomplete() : bool + { + return $this->stopOnIncomplete !== null; + } /** - * @var string + * @throws Exception */ - private $argumentsForCall; + public function stopOnIncomplete() : bool + { + if ($this->stopOnIncomplete === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->stopOnIncomplete; + } + public function hasStopOnRisky() : bool + { + return $this->stopOnRisky !== null; + } /** - * @var Type + * @throws Exception */ - private $returnType; + public function stopOnRisky() : bool + { + if ($this->stopOnRisky === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->stopOnRisky; + } + public function hasStopOnSkipped() : bool + { + return $this->stopOnSkipped !== null; + } /** - * @var string + * @throws Exception */ - private $reference; + public function stopOnSkipped() : bool + { + if ($this->stopOnSkipped === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->stopOnSkipped; + } + public function hasStopOnWarning() : bool + { + return $this->stopOnWarning !== null; + } /** - * @var bool + * @throws Exception */ - private $callOriginalMethod; + public function stopOnWarning() : bool + { + if ($this->stopOnWarning === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->stopOnWarning; + } + public function hasTeamcityLogfile() : bool + { + return $this->teamcityLogfile !== null; + } /** - * @var bool + * @throws Exception */ - private $static; + public function teamcityLogfile() : string + { + if ($this->teamcityLogfile === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->teamcityLogfile; + } + public function hasTestdoxExcludeGroups() : bool + { + return $this->testdoxExcludeGroups !== null; + } /** - * @var ?string + * @throws Exception */ - private $deprecation; + public function testdoxExcludeGroups() : array + { + if ($this->testdoxExcludeGroups === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->testdoxExcludeGroups; + } + public function hasTestdoxGroups() : bool + { + return $this->testdoxGroups !== null; + } /** - * @throws RuntimeException + * @throws Exception */ - public static function fromReflection(\ReflectionMethod $method, bool $callOriginalMethod, bool $cloneArguments) : self + public function testdoxGroups() : array { - if ($method->isPrivate()) { - $modifier = 'private'; - } elseif ($method->isProtected()) { - $modifier = 'protected'; - } else { - $modifier = 'public'; + if ($this->testdoxGroups === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - if ($method->isStatic()) { - $modifier .= ' static'; + return $this->testdoxGroups; + } + public function hasTestdoxHtmlFile() : bool + { + return $this->testdoxHtmlFile !== null; + } + /** + * @throws Exception + */ + public function testdoxHtmlFile() : string + { + if ($this->testdoxHtmlFile === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - if ($method->returnsReference()) { - $reference = '&'; - } else { - $reference = ''; + return $this->testdoxHtmlFile; + } + public function hasTestdoxTextFile() : bool + { + return $this->testdoxTextFile !== null; + } + /** + * @throws Exception + */ + public function testdoxTextFile() : string + { + if ($this->testdoxTextFile === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $docComment = $method->getDocComment(); - if (\is_string($docComment) && \preg_match('#\\*[ \\t]*+@deprecated[ \\t]*+(.*?)\\r?+\\n[ \\t]*+\\*(?:[ \\t]*+@|/$)#s', $docComment, $deprecation)) { - $deprecation = \trim(\preg_replace('#[ \\t]*\\r?\\n[ \\t]*+\\*[ \\t]*+#', ' ', $deprecation[1])); - } else { - $deprecation = null; + return $this->testdoxTextFile; + } + public function hasTestdoxXmlFile() : bool + { + return $this->testdoxXmlFile !== null; + } + /** + * @throws Exception + */ + public function testdoxXmlFile() : string + { + if ($this->testdoxXmlFile === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return new self($method->getDeclaringClass()->getName(), $method->getName(), $cloneArguments, $modifier, self::getMethodParametersForDeclaration($method), self::getMethodParametersForCall($method), (new \PHPUnit\SebastianBergmann\Type\ReflectionMapper())->fromMethodReturnType($method), $reference, $callOriginalMethod, $method->isStatic(), $deprecation); + return $this->testdoxXmlFile; } - public static function fromName(string $fullClassName, string $methodName, bool $cloneArguments) : self + public function hasTestSuffixes() : bool { - return new self($fullClassName, $methodName, $cloneArguments, 'public', '', '', new \PHPUnit\SebastianBergmann\Type\UnknownType(), '', \false, \false, null); + return $this->testSuffixes !== null; } - public function __construct(string $className, string $methodName, bool $cloneArguments, string $modifier, string $argumentsForDeclaration, string $argumentsForCall, \PHPUnit\SebastianBergmann\Type\Type $returnType, string $reference, bool $callOriginalMethod, bool $static, ?string $deprecation) + /** + * @throws Exception + */ + public function testSuffixes() : array { - $this->className = $className; - $this->methodName = $methodName; - $this->cloneArguments = $cloneArguments; - $this->modifier = $modifier; - $this->argumentsForDeclaration = $argumentsForDeclaration; - $this->argumentsForCall = $argumentsForCall; - $this->returnType = $returnType; - $this->reference = $reference; - $this->callOriginalMethod = $callOriginalMethod; - $this->static = $static; - $this->deprecation = $deprecation; + if ($this->testSuffixes === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); + } + return $this->testSuffixes; } - public function getName() : string + public function hasTestSuite() : bool { - return $this->methodName; + return $this->testSuite !== null; } /** - * @throws RuntimeException + * @throws Exception */ - public function generateCode() : string + public function testSuite() : string { - if ($this->static) { - $templateFile = 'mocked_static_method.tpl'; - } elseif ($this->returnType instanceof \PHPUnit\SebastianBergmann\Type\VoidType) { - $templateFile = \sprintf('%s_method_void.tpl', $this->callOriginalMethod ? 'proxied' : 'mocked'); - } else { - $templateFile = \sprintf('%s_method.tpl', $this->callOriginalMethod ? 'proxied' : 'mocked'); + if ($this->testSuite === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $deprecation = $this->deprecation; - if (null !== $this->deprecation) { - $deprecation = "The {$this->className}::{$this->methodName} method is deprecated ({$this->deprecation})."; - $deprecationTemplate = $this->getTemplate('deprecation.tpl'); - $deprecationTemplate->setVar(['deprecation' => \var_export($deprecation, \true)]); - $deprecation = $deprecationTemplate->render(); + return $this->testSuite; + } + public function unrecognizedOptions() : array + { + return $this->unrecognizedOptions; + } + public function hasUnrecognizedOrderBy() : bool + { + return $this->unrecognizedOrderBy !== null; + } + /** + * @throws Exception + */ + public function unrecognizedOrderBy() : string + { + if ($this->unrecognizedOrderBy === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - $template = $this->getTemplate($templateFile); - $template->setVar(['arguments_decl' => $this->argumentsForDeclaration, 'arguments_call' => $this->argumentsForCall, 'return_declaration' => !empty($this->returnType->asString()) ? ': ' . $this->returnType->asString() : '', 'return_type' => $this->returnType->asString(), 'arguments_count' => !empty($this->argumentsForCall) ? \substr_count($this->argumentsForCall, ',') + 1 : 0, 'class_name' => $this->className, 'method_name' => $this->methodName, 'modifier' => $this->modifier, 'reference' => $this->reference, 'clone_arguments' => $this->cloneArguments ? 'true' : 'false', 'deprecation' => $deprecation]); - return $template->render(); + return $this->unrecognizedOrderBy; } - public function getReturnType() : \PHPUnit\SebastianBergmann\Type\Type + public function hasUseDefaultConfiguration() : bool { - return $this->returnType; + return $this->useDefaultConfiguration !== null; } - private function getTemplate(string $template) : \PHPUnit\SebastianBergmann\Template\Template + /** + * @throws Exception + */ + public function useDefaultConfiguration() : bool { - $filename = __DIR__ . \DIRECTORY_SEPARATOR . 'Generator' . \DIRECTORY_SEPARATOR . $template; - if (!isset(self::$templates[$filename])) { - self::$templates[$filename] = new \PHPUnit\SebastianBergmann\Template\Template($filename); + if ($this->useDefaultConfiguration === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return self::$templates[$filename]; + return $this->useDefaultConfiguration; + } + public function hasVerbose() : bool + { + return $this->verbose !== null; } /** - * Returns the parameters of a function or method. - * - * @throws RuntimeException + * @throws Exception */ - private static function getMethodParametersForDeclaration(\ReflectionMethod $method) : string + public function verbose() : bool { - $parameters = []; - foreach ($method->getParameters() as $i => $parameter) { - $name = '$' . $parameter->getName(); - /* Note: PHP extensions may use empty names for reference arguments - * or "..." for methods taking a variable number of arguments. - */ - if ($name === '$' || $name === '$...') { - $name = '$arg' . $i; - } - $nullable = ''; - $default = ''; - $reference = ''; - $typeDeclaration = ''; - $type = null; - $typeName = null; - if ($parameter->hasType()) { - $type = $parameter->getType(); - if ($type instanceof \ReflectionNamedType) { - $typeName = $type->getName(); - } - } - if ($parameter->isVariadic()) { - $name = '...' . $name; - } elseif ($parameter->isDefaultValueAvailable()) { - $default = ' = ' . self::exportDefaultValue($parameter); - } elseif ($parameter->isOptional()) { - $default = ' = null'; - } - if ($type !== null) { - if ($typeName !== 'mixed' && $parameter->allowsNull() && !$type instanceof \ReflectionUnionType) { - $nullable = '?'; - } - if ($typeName === 'self') { - $typeDeclaration = $method->getDeclaringClass()->getName() . ' '; - } elseif ($typeName !== null) { - $typeDeclaration = $typeName . ' '; - } elseif ($type instanceof \ReflectionUnionType) { - $typeDeclaration = self::unionTypeAsString($type, $method->getDeclaringClass()->getName()); - } - } - if ($parameter->isPassedByReference()) { - $reference = '&'; - } - $parameters[] = $nullable . $typeDeclaration . $reference . $name . $default; + if ($this->verbose === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return \implode(', ', $parameters); + return $this->verbose; + } + public function hasVersion() : bool + { + return $this->version !== null; } /** - * Returns the parameters of a function or method. - * - * @throws RuntimeException + * @throws Exception */ - private static function getMethodParametersForCall(\ReflectionMethod $method) : string + public function version() : bool { - $parameters = []; - foreach ($method->getParameters() as $i => $parameter) { - $name = '$' . $parameter->getName(); - /* Note: PHP extensions may use empty names for reference arguments - * or "..." for methods taking a variable number of arguments. - */ - if ($name === '$' || $name === '$...') { - $name = '$arg' . $i; - } - if ($parameter->isVariadic()) { - continue; - } - if ($parameter->isPassedByReference()) { - $parameters[] = '&' . $name; - } else { - $parameters[] = $name; - } + if ($this->version === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return \implode(', ', $parameters); + return $this->version; + } + public function hasXdebugFilterFile() : bool + { + return $this->xdebugFilterFile !== null; } /** - * @throws RuntimeException + * @throws Exception */ - private static function exportDefaultValue(\ReflectionParameter $parameter) : string - { - try { - return (string) \var_export($parameter->getDefaultValue(), \true); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - } - private static function unionTypeAsString(\ReflectionUnionType $union, string $self) : string + public function xdebugFilterFile() : string { - $types = []; - foreach ($union->getTypes() as $type) { - if ((string) $type === 'self') { - $types[] = $self; - } else { - $types[] = $type; - } + if ($this->xdebugFilterFile === null) { + throw new \PHPUnit\TextUI\CliArguments\Exception(); } - return \implode('|', $types) . ' '; + return $this->xdebugFilterFile; } } parse($parameters, self::SHORT_OPTIONS, array_merge(self::LONG_OPTIONS, $additionalLongOptions)); + } catch (CliParserException $e) { + throw new \PHPUnit\TextUI\CliArguments\Exception($e->getMessage(), (int) $e->getCode(), $e); + } + $argument = null; + $atLeastVersion = null; + $backupGlobals = null; + $backupStaticAttributes = null; + $beStrictAboutChangesToGlobalState = null; + $beStrictAboutResourceUsageDuringSmallTests = null; + $bootstrap = null; + $cacheResult = null; + $cacheResultFile = null; + $checkVersion = null; + $colors = null; + $columns = null; + $configuration = null; + $coverageCacheDirectory = null; + $warmCoverageCache = null; + $coverageFilter = null; + $coverageClover = null; + $coverageCobertura = null; + $coverageCrap4J = null; + $coverageHtml = null; + $coveragePhp = null; + $coverageText = null; + $coverageTextShowUncoveredFiles = null; + $coverageTextShowOnlySummary = null; + $coverageXml = null; + $pathCoverage = null; + $debug = null; + $defaultTimeLimit = null; + $disableCodeCoverageIgnore = null; + $disallowTestOutput = null; + $disallowTodoAnnotatedTests = null; + $enforceTimeLimit = null; + $excludeGroups = null; + $executionOrder = null; + $executionOrderDefects = null; + $extensions = []; + $unavailableExtensions = []; + $failOnEmptyTestSuite = null; + $failOnIncomplete = null; + $failOnRisky = null; + $failOnSkipped = null; + $failOnWarning = null; + $filter = null; + $generateConfiguration = null; + $migrateConfiguration = null; + $groups = null; + $testsCovering = null; + $testsUsing = null; + $help = null; + $includePath = null; + $iniSettings = []; + $junitLogfile = null; + $listGroups = null; + $listSuites = null; + $listTests = null; + $listTestsXml = null; + $loader = null; + $noCoverage = null; + $noExtensions = null; + $noInteraction = null; + $noLogging = null; + $printer = null; + $processIsolation = null; + $randomOrderSeed = null; + $repeat = null; + $reportUselessTests = null; + $resolveDependencies = null; + $reverseList = null; + $stderr = null; + $strictCoverage = null; + $stopOnDefect = null; + $stopOnError = null; + $stopOnFailure = null; + $stopOnIncomplete = null; + $stopOnRisky = null; + $stopOnSkipped = null; + $stopOnWarning = null; + $teamcityLogfile = null; + $testdoxExcludeGroups = null; + $testdoxGroups = null; + $testdoxHtmlFile = null; + $testdoxTextFile = null; + $testdoxXmlFile = null; + $testSuffixes = null; + $testSuite = null; + $unrecognizedOptions = []; + $unrecognizedOrderBy = null; + $useDefaultConfiguration = null; + $verbose = null; + $version = null; + $xdebugFilterFile = null; + if (isset($options[1][0])) { + $argument = $options[1][0]; + } + foreach ($options[0] as $option) { + switch ($option[0]) { + case '--colors': + $colors = $option[1] ?: DefaultResultPrinter::COLOR_AUTO; + break; + case '--bootstrap': + $bootstrap = $option[1]; + break; + case '--cache-result': + $cacheResult = \true; + break; + case '--do-not-cache-result': + $cacheResult = \false; + break; + case '--cache-result-file': + $cacheResultFile = $option[1]; + break; + case '--columns': + if (is_numeric($option[1])) { + $columns = (int) $option[1]; + } elseif ($option[1] === 'max') { + $columns = 'max'; + } + break; + case 'c': + case '--configuration': + $configuration = $option[1]; + break; + case '--coverage-cache': + $coverageCacheDirectory = $option[1]; + break; + case '--warm-coverage-cache': + $warmCoverageCache = \true; + break; + case '--coverage-clover': + $coverageClover = $option[1]; + break; + case '--coverage-cobertura': + $coverageCobertura = $option[1]; + break; + case '--coverage-crap4j': + $coverageCrap4J = $option[1]; + break; + case '--coverage-html': + $coverageHtml = $option[1]; + break; + case '--coverage-php': + $coveragePhp = $option[1]; + break; + case '--coverage-text': + if ($option[1] === null) { + $option[1] = 'php://stdout'; + } + $coverageText = $option[1]; + $coverageTextShowUncoveredFiles = \false; + $coverageTextShowOnlySummary = \false; + break; + case '--coverage-xml': + $coverageXml = $option[1]; + break; + case '--path-coverage': + $pathCoverage = \true; + break; + case 'd': + $tmp = explode('=', $option[1]); + if (isset($tmp[0])) { + if (isset($tmp[1])) { + $iniSettings[$tmp[0]] = $tmp[1]; + } else { + $iniSettings[$tmp[0]] = '1'; + } + } + break; + case '--debug': + $debug = \true; + break; + case 'h': + case '--help': + $help = \true; + break; + case '--filter': + $filter = $option[1]; + break; + case '--testsuite': + $testSuite = $option[1]; + break; + case '--generate-configuration': + $generateConfiguration = \true; + break; + case '--migrate-configuration': + $migrateConfiguration = \true; + break; + case '--group': + $groups = explode(',', $option[1]); + break; + case '--exclude-group': + $excludeGroups = explode(',', $option[1]); + break; + case '--covers': + $testsCovering = array_map('strtolower', explode(',', $option[1])); + break; + case '--uses': + $testsUsing = array_map('strtolower', explode(',', $option[1])); + break; + case '--test-suffix': + $testSuffixes = explode(',', $option[1]); + break; + case '--include-path': + $includePath = $option[1]; + break; + case '--list-groups': + $listGroups = \true; + break; + case '--list-suites': + $listSuites = \true; + break; + case '--list-tests': + $listTests = \true; + break; + case '--list-tests-xml': + $listTestsXml = $option[1]; + break; + case '--printer': + $printer = $option[1]; + break; + case '--loader': + $loader = $option[1]; + break; + case '--log-junit': + $junitLogfile = $option[1]; + break; + case '--log-teamcity': + $teamcityLogfile = $option[1]; + break; + case '--order-by': + foreach (explode(',', $option[1]) as $order) { + switch ($order) { + case 'default': + $executionOrder = TestSuiteSorter::ORDER_DEFAULT; + $executionOrderDefects = TestSuiteSorter::ORDER_DEFAULT; + $resolveDependencies = \true; + break; + case 'defects': + $executionOrderDefects = TestSuiteSorter::ORDER_DEFECTS_FIRST; + break; + case 'depends': + $resolveDependencies = \true; + break; + case 'duration': + $executionOrder = TestSuiteSorter::ORDER_DURATION; + break; + case 'no-depends': + $resolveDependencies = \false; + break; + case 'random': + $executionOrder = TestSuiteSorter::ORDER_RANDOMIZED; + break; + case 'reverse': + $executionOrder = TestSuiteSorter::ORDER_REVERSED; + break; + case 'size': + $executionOrder = TestSuiteSorter::ORDER_SIZE; + break; + default: + $unrecognizedOrderBy = $order; + } + } + break; + case '--process-isolation': + $processIsolation = \true; + break; + case '--repeat': + $repeat = (int) $option[1]; + break; + case '--stderr': + $stderr = \true; + break; + case '--stop-on-defect': + $stopOnDefect = \true; + break; + case '--stop-on-error': + $stopOnError = \true; + break; + case '--stop-on-failure': + $stopOnFailure = \true; + break; + case '--stop-on-warning': + $stopOnWarning = \true; + break; + case '--stop-on-incomplete': + $stopOnIncomplete = \true; + break; + case '--stop-on-risky': + $stopOnRisky = \true; + break; + case '--stop-on-skipped': + $stopOnSkipped = \true; + break; + case '--fail-on-empty-test-suite': + $failOnEmptyTestSuite = \true; + break; + case '--fail-on-incomplete': + $failOnIncomplete = \true; + break; + case '--fail-on-risky': + $failOnRisky = \true; + break; + case '--fail-on-skipped': + $failOnSkipped = \true; + break; + case '--fail-on-warning': + $failOnWarning = \true; + break; + case '--teamcity': + $printer = TeamCity::class; + break; + case '--testdox': + $printer = CliTestDoxPrinter::class; + break; + case '--testdox-group': + $testdoxGroups = explode(',', $option[1]); + break; + case '--testdox-exclude-group': + $testdoxExcludeGroups = explode(',', $option[1]); + break; + case '--testdox-html': + $testdoxHtmlFile = $option[1]; + break; + case '--testdox-text': + $testdoxTextFile = $option[1]; + break; + case '--testdox-xml': + $testdoxXmlFile = $option[1]; + break; + case '--no-configuration': + $useDefaultConfiguration = \false; + break; + case '--extensions': + foreach (explode(',', $option[1]) as $extensionClass) { + if (!class_exists($extensionClass)) { + $unavailableExtensions[] = $extensionClass; + continue; + } + $extensions[] = new Extension($extensionClass, '', []); + } + break; + case '--no-extensions': + $noExtensions = \true; + break; + case '--no-coverage': + $noCoverage = \true; + break; + case '--no-logging': + $noLogging = \true; + break; + case '--no-interaction': + $noInteraction = \true; + break; + case '--globals-backup': + $backupGlobals = \true; + break; + case '--static-backup': + $backupStaticAttributes = \true; + break; + case 'v': + case '--verbose': + $verbose = \true; + break; + case '--atleast-version': + $atLeastVersion = $option[1]; + break; + case '--version': + $version = \true; + break; + case '--dont-report-useless-tests': + $reportUselessTests = \false; + break; + case '--strict-coverage': + $strictCoverage = \true; + break; + case '--disable-coverage-ignore': + $disableCodeCoverageIgnore = \true; + break; + case '--strict-global-state': + $beStrictAboutChangesToGlobalState = \true; + break; + case '--disallow-test-output': + $disallowTestOutput = \true; + break; + case '--disallow-resource-usage': + $beStrictAboutResourceUsageDuringSmallTests = \true; + break; + case '--default-time-limit': + $defaultTimeLimit = (int) $option[1]; + break; + case '--enforce-time-limit': + $enforceTimeLimit = \true; + break; + case '--disallow-todo-tests': + $disallowTodoAnnotatedTests = \true; + break; + case '--reverse-list': + $reverseList = \true; + break; + case '--check-version': + $checkVersion = \true; + break; + case '--coverage-filter': + case '--whitelist': + if ($coverageFilter === null) { + $coverageFilter = []; + } + $coverageFilter[] = $option[1]; + break; + case '--random-order': + $executionOrder = TestSuiteSorter::ORDER_RANDOMIZED; + break; + case '--random-order-seed': + $randomOrderSeed = (int) $option[1]; + break; + case '--resolve-dependencies': + $resolveDependencies = \true; + break; + case '--ignore-dependencies': + $resolveDependencies = \false; + break; + case '--reverse-order': + $executionOrder = TestSuiteSorter::ORDER_REVERSED; + break; + case '--dump-xdebug-filter': + $xdebugFilterFile = $option[1]; + break; + default: + $unrecognizedOptions[str_replace('--', '', $option[0])] = $option[1]; + } + } + if (empty($extensions)) { + $extensions = null; + } + if (empty($unavailableExtensions)) { + $unavailableExtensions = null; + } + if (empty($iniSettings)) { + $iniSettings = null; + } + if (empty($coverageFilter)) { + $coverageFilter = null; + } + return new \PHPUnit\TextUI\CliArguments\Configuration($argument, $atLeastVersion, $backupGlobals, $backupStaticAttributes, $beStrictAboutChangesToGlobalState, $beStrictAboutResourceUsageDuringSmallTests, $bootstrap, $cacheResult, $cacheResultFile, $checkVersion, $colors, $columns, $configuration, $coverageClover, $coverageCobertura, $coverageCrap4J, $coverageHtml, $coveragePhp, $coverageText, $coverageTextShowUncoveredFiles, $coverageTextShowOnlySummary, $coverageXml, $pathCoverage, $coverageCacheDirectory, $warmCoverageCache, $debug, $defaultTimeLimit, $disableCodeCoverageIgnore, $disallowTestOutput, $disallowTodoAnnotatedTests, $enforceTimeLimit, $excludeGroups, $executionOrder, $executionOrderDefects, $extensions, $unavailableExtensions, $failOnEmptyTestSuite, $failOnIncomplete, $failOnRisky, $failOnSkipped, $failOnWarning, $filter, $generateConfiguration, $migrateConfiguration, $groups, $testsCovering, $testsUsing, $help, $includePath, $iniSettings, $junitLogfile, $listGroups, $listSuites, $listTests, $listTestsXml, $loader, $noCoverage, $noExtensions, $noInteraction, $noLogging, $printer, $processIsolation, $randomOrderSeed, $repeat, $reportUselessTests, $resolveDependencies, $reverseList, $stderr, $strictCoverage, $stopOnDefect, $stopOnError, $stopOnFailure, $stopOnIncomplete, $stopOnRisky, $stopOnSkipped, $stopOnWarning, $teamcityLogfile, $testdoxExcludeGroups, $testdoxGroups, $testdoxHtmlFile, $testdoxTextFile, $testdoxXmlFile, $testSuffixes, $testSuite, $unrecognizedOptions, $unrecognizedOrderBy, $useDefaultConfiguration, $verbose, $version, $coverageFilter, $xdebugFilterFile); + } } > - */ - protected $backupStaticAttributesExcludeList = []; - /** - * @var array> - * - * @deprecated Use $backupStaticAttributesExcludeList instead - */ - protected $backupStaticAttributesBlacklist = []; - /** - * @var bool - */ - protected $runTestInSeparateProcess; - /** - * @var bool - */ - protected $preserveGlobalState = \true; /** - * @var list - */ - protected $providedTests = []; - /** - * @var bool - */ - private $runClassInSeparateProcess; - /** - * @var bool - */ - private $inIsolation = \false; - /** - * @var array - */ - private $data; - /** - * @var int|string - */ - private $dataName; - /** - * @var null|string - */ - private $expectedException; - /** - * @var null|string - */ - private $expectedExceptionMessage; - /** - * @var null|string - */ - private $expectedExceptionMessageRegExp; - /** - * @var null|int|string - */ - private $expectedExceptionCode; - /** - * @var string - */ - private $name = ''; - /** - * @var list - */ - private $dependencies = []; - /** - * @var array - */ - private $dependencyInput = []; - /** - * @var array - */ - private $iniSettings = []; - /** - * @var array - */ - private $locale = []; - /** - * @var MockObject[] - */ - private $mockObjects = []; - /** - * @var MockGenerator - */ - private $mockObjectGenerator; - /** - * @var int - */ - private $status = \PHPUnit\Runner\BaseTestRunner::STATUS_UNKNOWN; - /** - * @var string - */ - private $statusMessage = ''; - /** - * @var int - */ - private $numAssertions = 0; - /** - * @var TestResult - */ - private $result; - /** - * @var mixed - */ - private $testResult; - /** - * @var string - */ - private $output = ''; - /** - * @var string - */ - private $outputExpectedRegex; - /** - * @var string - */ - private $outputExpectedString; - /** - * @var mixed - */ - private $outputCallback = \false; - /** - * @var bool - */ - private $outputBufferingActive = \false; - /** - * @var int - */ - private $outputBufferingLevel; - /** - * @var bool - */ - private $outputRetrievedForAssertion = \false; - /** - * @var Snapshot - */ - private $snapshot; - /** - * @var \Prophecy\Prophet + * @var array */ - private $prophet; + protected $arguments = []; /** - * @var bool + * @var array */ - private $beStrictAboutChangesToGlobalState = \false; + protected $longOptions = []; /** * @var bool */ - private $registerMockObjectsFromTestArgumentsRecursively = \false; + private $versionStringPrinted = \false; /** - * @var string[] + * @psalm-var list */ private $warnings = []; /** - * @var string[] - */ - private $groups = []; - /** - * @var bool - */ - private $doesNotPerformAssertions = \false; - /** - * @var Comparator[] - */ - private $customComparators = []; - /** - * @var string[] - */ - private $doubledTypes = []; - /** - * Returns a matcher that matches when the method is executed - * zero or more times. - */ - public static function any() : \PHPUnit\Framework\MockObject\Rule\AnyInvokedCount - { - return new \PHPUnit\Framework\MockObject\Rule\AnyInvokedCount(); - } - /** - * Returns a matcher that matches when the method is never executed. + * @throws Exception */ - public static function never() : \PHPUnit\Framework\MockObject\Rule\InvokedCount + public static function main(bool $exit = \true) : int { - return new \PHPUnit\Framework\MockObject\Rule\InvokedCount(0); + try { + return (new static())->run($_SERVER['argv'], $exit); + } catch (Throwable $t) { + throw new \PHPUnit\TextUI\RuntimeException($t->getMessage(), (int) $t->getCode(), $t); + } } /** - * Returns a matcher that matches when the method is executed - * at least N times. + * @throws Exception */ - public static function atLeast(int $requiredInvocations) : \PHPUnit\Framework\MockObject\Rule\InvokedAtLeastCount + public function run(array $argv, bool $exit = \true) : int { - return new \PHPUnit\Framework\MockObject\Rule\InvokedAtLeastCount($requiredInvocations); + $this->handleArguments($argv); + $runner = $this->createRunner(); + if ($this->arguments['test'] instanceof TestSuite) { + $suite = $this->arguments['test']; + } else { + $suite = $runner->getTest($this->arguments['test'], $this->arguments['testSuffixes']); + } + if ($this->arguments['listGroups']) { + return $this->handleListGroups($suite, $exit); + } + if ($this->arguments['listSuites']) { + return $this->handleListSuites($exit); + } + if ($this->arguments['listTests']) { + return $this->handleListTests($suite, $exit); + } + if ($this->arguments['listTestsXml']) { + return $this->handleListTestsXml($suite, $this->arguments['listTestsXml'], $exit); + } + unset($this->arguments['test'], $this->arguments['testFile']); + try { + $result = $runner->run($suite, $this->arguments, $this->warnings, $exit); + } catch (Throwable $t) { + print $t->getMessage() . \PHP_EOL; + } + $return = \PHPUnit\TextUI\TestRunner::FAILURE_EXIT; + if (isset($result) && $result->wasSuccessful()) { + $return = \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; + } elseif (!isset($result) || $result->errorCount() > 0) { + $return = \PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT; + } + if ($exit) { + exit($return); + } + return $return; } /** - * Returns a matcher that matches when the method is executed at least once. + * Create a TestRunner, override in subclasses. */ - public static function atLeastOnce() : \PHPUnit\Framework\MockObject\Rule\InvokedAtLeastOnce + protected function createRunner() : \PHPUnit\TextUI\TestRunner { - return new \PHPUnit\Framework\MockObject\Rule\InvokedAtLeastOnce(); + return new \PHPUnit\TextUI\TestRunner($this->arguments['loader']); } /** - * Returns a matcher that matches when the method is executed exactly once. + * Handles the command-line arguments. + * + * A child class of PHPUnit\TextUI\Command can hook into the argument + * parsing by adding the switch(es) to the $longOptions array and point to a + * callback method that handles the switch(es) in the child class like this + * + * + * longOptions['my-switch'] = 'myHandler'; + * // my-secondswitch will accept a value - note the equals sign + * $this->longOptions['my-secondswitch='] = 'myOtherHandler'; + * } + * + * // --my-switch -> myHandler() + * protected function myHandler() + * { + * } + * + * // --my-secondswitch foo -> myOtherHandler('foo') + * protected function myOtherHandler ($value) + * { + * } + * + * // You will also need this - the static keyword in the + * // PHPUnit\TextUI\Command will mean that it'll be + * // PHPUnit\TextUI\Command that gets instantiated, + * // not MyCommand + * public static function main($exit = true) + * { + * $command = new static; + * + * return $command->run($_SERVER['argv'], $exit); + * } + * + * } + * + * + * @throws Exception */ - public static function once() : \PHPUnit\Framework\MockObject\Rule\InvokedCount + protected function handleArguments(array $argv) : void { - return new \PHPUnit\Framework\MockObject\Rule\InvokedCount(1); + try { + $arguments = (new Builder())->fromParameters($argv, array_keys($this->longOptions)); + } catch (ArgumentsException $e) { + $this->exitWithErrorMessage($e->getMessage()); + } + assert(isset($arguments) && $arguments instanceof Configuration); + if ($arguments->hasGenerateConfiguration() && $arguments->generateConfiguration()) { + $this->generateConfiguration(); + } + if ($arguments->hasAtLeastVersion()) { + if (version_compare(Version::id(), $arguments->atLeastVersion(), '>=')) { + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + } + exit(\PHPUnit\TextUI\TestRunner::FAILURE_EXIT); + } + if ($arguments->hasVersion() && $arguments->version()) { + $this->printVersionString(); + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + } + if ($arguments->hasCheckVersion() && $arguments->checkVersion()) { + $this->handleVersionCheck(); + } + if ($arguments->hasHelp()) { + $this->showHelp(); + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + } + if ($arguments->hasUnrecognizedOrderBy()) { + $this->exitWithErrorMessage(sprintf('unrecognized --order-by option: %s', $arguments->unrecognizedOrderBy())); + } + if ($arguments->hasIniSettings()) { + foreach ($arguments->iniSettings() as $name => $value) { + ini_set($name, $value); + } + } + if ($arguments->hasIncludePath()) { + ini_set('include_path', $arguments->includePath() . \PATH_SEPARATOR . ini_get('include_path')); + } + $this->arguments = (new Mapper())->mapToLegacyArray($arguments); + $this->handleCustomOptions($arguments->unrecognizedOptions()); + $this->handleCustomTestSuite(); + if (!isset($this->arguments['testSuffixes'])) { + $this->arguments['testSuffixes'] = ['Test.php', '.phpt']; + } + if (!isset($this->arguments['test']) && $arguments->hasArgument()) { + $this->arguments['test'] = realpath($arguments->argument()); + if ($this->arguments['test'] === \false) { + $this->exitWithErrorMessage(sprintf('Cannot open file "%s".', $arguments->argument())); + } + } + if ($this->arguments['loader'] !== null) { + $this->arguments['loader'] = $this->handleLoader($this->arguments['loader']); + } + if (isset($this->arguments['configuration'])) { + if (is_dir($this->arguments['configuration'])) { + $candidate = $this->configurationFileInDirectory($this->arguments['configuration']); + if ($candidate !== null) { + $this->arguments['configuration'] = $candidate; + } + } + } elseif ($this->arguments['useDefaultConfiguration']) { + $candidate = $this->configurationFileInDirectory(getcwd()); + if ($candidate !== null) { + $this->arguments['configuration'] = $candidate; + } + } + if ($arguments->hasMigrateConfiguration() && $arguments->migrateConfiguration()) { + if (!isset($this->arguments['configuration'])) { + print 'No configuration file found to migrate.' . \PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + } + $this->migrateConfiguration(realpath($this->arguments['configuration'])); + } + if (isset($this->arguments['configuration'])) { + try { + $this->arguments['configurationObject'] = (new Loader())->load($this->arguments['configuration']); + } catch (Throwable $e) { + print $e->getMessage() . \PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::FAILURE_EXIT); + } + $phpunitConfiguration = $this->arguments['configurationObject']->phpunit(); + (new PhpHandler())->handle($this->arguments['configurationObject']->php()); + if (isset($this->arguments['bootstrap'])) { + $this->handleBootstrap($this->arguments['bootstrap']); + } elseif ($phpunitConfiguration->hasBootstrap()) { + $this->handleBootstrap($phpunitConfiguration->bootstrap()); + } + if (!isset($this->arguments['stderr'])) { + $this->arguments['stderr'] = $phpunitConfiguration->stderr(); + } + if (!isset($this->arguments['noExtensions']) && $phpunitConfiguration->hasExtensionsDirectory() && extension_loaded('phar')) { + $result = (new PharLoader())->loadPharExtensionsInDirectory($phpunitConfiguration->extensionsDirectory()); + $this->arguments['loadedExtensions'] = $result['loadedExtensions']; + $this->arguments['notLoadedExtensions'] = $result['notLoadedExtensions']; + unset($result); + } + if (!isset($this->arguments['columns'])) { + $this->arguments['columns'] = $phpunitConfiguration->columns(); + } + if (!isset($this->arguments['printer']) && $phpunitConfiguration->hasPrinterClass()) { + $file = $phpunitConfiguration->hasPrinterFile() ? $phpunitConfiguration->printerFile() : ''; + $this->arguments['printer'] = $this->handlePrinter($phpunitConfiguration->printerClass(), $file); + } + if ($phpunitConfiguration->hasTestSuiteLoaderClass()) { + $file = $phpunitConfiguration->hasTestSuiteLoaderFile() ? $phpunitConfiguration->testSuiteLoaderFile() : ''; + $this->arguments['loader'] = $this->handleLoader($phpunitConfiguration->testSuiteLoaderClass(), $file); + } + if (!isset($this->arguments['testsuite']) && $phpunitConfiguration->hasDefaultTestSuite()) { + $this->arguments['testsuite'] = $phpunitConfiguration->defaultTestSuite(); + } + if (!isset($this->arguments['test'])) { + try { + $this->arguments['test'] = (new \PHPUnit\TextUI\TestSuiteMapper())->map($this->arguments['configurationObject']->testSuite(), $this->arguments['testsuite'] ?? ''); + } catch (\PHPUnit\TextUI\Exception $e) { + $this->printVersionString(); + print $e->getMessage() . \PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + } + } + } elseif (isset($this->arguments['bootstrap'])) { + $this->handleBootstrap($this->arguments['bootstrap']); + } + if (isset($this->arguments['printer']) && is_string($this->arguments['printer'])) { + $this->arguments['printer'] = $this->handlePrinter($this->arguments['printer']); + } + if (isset($this->arguments['configurationObject'], $this->arguments['warmCoverageCache'])) { + $this->handleWarmCoverageCache($this->arguments['configurationObject']); + } + if (!isset($this->arguments['test'])) { + $this->showHelp(); + exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + } } /** - * Returns a matcher that matches when the method is executed - * exactly $count times. + * Handles the loading of the PHPUnit\Runner\TestSuiteLoader implementation. + * + * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 */ - public static function exactly(int $count) : \PHPUnit\Framework\MockObject\Rule\InvokedCount + protected function handleLoader(string $loaderClass, string $loaderFile = '') : ?TestSuiteLoader { - return new \PHPUnit\Framework\MockObject\Rule\InvokedCount($count); + $this->warnings[] = 'Using a custom test suite loader is deprecated'; + if (!class_exists($loaderClass, \false)) { + if ($loaderFile == '') { + $loaderFile = Filesystem::classNameToFilename($loaderClass); + } + $loaderFile = stream_resolve_include_path($loaderFile); + if ($loaderFile) { + /** + * @noinspection PhpIncludeInspection + * @psalm-suppress UnresolvableInclude + */ + require $loaderFile; + } + } + if (class_exists($loaderClass, \false)) { + try { + $class = new ReflectionClass($loaderClass); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\TextUI\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if ($class->implementsInterface(TestSuiteLoader::class) && $class->isInstantiable()) { + $object = $class->newInstance(); + assert($object instanceof TestSuiteLoader); + return $object; + } + } + if ($loaderClass == StandardTestSuiteLoader::class) { + return null; + } + $this->exitWithErrorMessage(sprintf('Could not use "%s" as loader.', $loaderClass)); + return null; } /** - * Returns a matcher that matches when the method is executed - * at most N times. + * Handles the loading of the PHPUnit\Util\Printer implementation. + * + * @return null|Printer|string */ - public static function atMost(int $allowedInvocations) : \PHPUnit\Framework\MockObject\Rule\InvokedAtMostCount + protected function handlePrinter(string $printerClass, string $printerFile = '') { - return new \PHPUnit\Framework\MockObject\Rule\InvokedAtMostCount($allowedInvocations); + if (!class_exists($printerClass, \false)) { + if ($printerFile === '') { + $printerFile = Filesystem::classNameToFilename($printerClass); + } + $printerFile = stream_resolve_include_path($printerFile); + if ($printerFile) { + /** + * @noinspection PhpIncludeInspection + * @psalm-suppress UnresolvableInclude + */ + require $printerFile; + } + } + if (!class_exists($printerClass)) { + $this->exitWithErrorMessage(sprintf('Could not use "%s" as printer: class does not exist', $printerClass)); + } + try { + $class = new ReflectionClass($printerClass); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\TextUI\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + // @codeCoverageIgnoreEnd + } + if (!$class->implementsInterface(\PHPUnit\TextUI\ResultPrinter::class)) { + $this->exitWithErrorMessage(sprintf('Could not use "%s" as printer: class does not implement %s', $printerClass, \PHPUnit\TextUI\ResultPrinter::class)); + } + if (!$class->isInstantiable()) { + $this->exitWithErrorMessage(sprintf('Could not use "%s" as printer: class cannot be instantiated', $printerClass)); + } + if ($class->isSubclassOf(\PHPUnit\TextUI\ResultPrinter::class)) { + return $printerClass; + } + $outputStream = isset($this->arguments['stderr']) ? 'php://stderr' : null; + return $class->newInstance($outputStream); } /** - * Returns a matcher that matches when the method is executed - * at the given index. - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4297 - * @codeCoverageIgnore + * Loads a bootstrap file. */ - public static function at(int $index) : \PHPUnit\Framework\MockObject\Rule\InvokedAtIndex + protected function handleBootstrap(string $filename) : void { - $stack = \debug_backtrace(); - while (!empty($stack)) { - $frame = \array_pop($stack); - if (isset($frame['object']) && $frame['object'] instanceof self) { - $frame['object']->addWarning('The at() matcher has been deprecated. It will be removed in PHPUnit 10. Please refactor your test to not rely on the order in which methods are invoked.'); - break; + try { + FileLoader::checkAndLoad($filename); + } catch (Throwable $t) { + if ($t instanceof \PHPUnit\Exception) { + $this->exitWithErrorMessage($t->getMessage()); } + $this->exitWithErrorMessage(sprintf('Error in bootstrap script: %s:%s%s', get_class($t), \PHP_EOL, $t->getMessage())); } - return new \PHPUnit\Framework\MockObject\Rule\InvokedAtIndex($index); - } - public static function returnValue($value) : \PHPUnit\Framework\MockObject\Stub\ReturnStub - { - return new \PHPUnit\Framework\MockObject\Stub\ReturnStub($value); } - public static function returnValueMap(array $valueMap) : \PHPUnit\Framework\MockObject\Stub\ReturnValueMap - { - return new \PHPUnit\Framework\MockObject\Stub\ReturnValueMap($valueMap); - } - public static function returnArgument(int $argumentIndex) : \PHPUnit\Framework\MockObject\Stub\ReturnArgument + protected function handleVersionCheck() : void { - return new \PHPUnit\Framework\MockObject\Stub\ReturnArgument($argumentIndex); + $this->printVersionString(); + $latestVersion = file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit'); + $isOutdated = version_compare($latestVersion, Version::id(), '>'); + if ($isOutdated) { + printf('You are not using the latest version of PHPUnit.' . \PHP_EOL . 'The latest version is PHPUnit %s.' . \PHP_EOL, $latestVersion); + } else { + print 'You are using the latest version of PHPUnit.' . \PHP_EOL; + } + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); } - public static function returnCallback($callback) : \PHPUnit\Framework\MockObject\Stub\ReturnCallback + /** + * Show the help message. + */ + protected function showHelp() : void { - return new \PHPUnit\Framework\MockObject\Stub\ReturnCallback($callback); + $this->printVersionString(); + (new \PHPUnit\TextUI\Help())->writeToConsole(); } /** - * Returns the current object. - * - * This method is useful when mocking a fluent interface. + * Custom callback for test suite discovery. */ - public static function returnSelf() : \PHPUnit\Framework\MockObject\Stub\ReturnSelf + protected function handleCustomTestSuite() : void { - return new \PHPUnit\Framework\MockObject\Stub\ReturnSelf(); } - public static function throwException(\Throwable $exception) : \PHPUnit\Framework\MockObject\Stub\Exception + private function printVersionString() : void { - return new \PHPUnit\Framework\MockObject\Stub\Exception($exception); + if ($this->versionStringPrinted) { + return; + } + print Version::getVersionString() . \PHP_EOL . \PHP_EOL; + $this->versionStringPrinted = \true; } - public static function onConsecutiveCalls(...$args) : \PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls + private function exitWithErrorMessage(string $message) : void { - return new \PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls($args); + $this->printVersionString(); + print $message . \PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::FAILURE_EXIT); } - /** - * @param int|string $dataName - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function __construct(?string $name = null, array $data = [], $dataName = '') + private function handleListGroups(TestSuite $suite, bool $exit) : int { - if ($name !== null) { - $this->setName($name); + $this->printVersionString(); + print 'Available test group(s):' . \PHP_EOL; + $groups = $suite->getGroups(); + sort($groups); + foreach ($groups as $group) { + if (strpos($group, '__phpunit_') === 0) { + continue; + } + printf(' - %s' . \PHP_EOL, $group); } - $this->data = $data; - $this->dataName = $dataName; + if ($exit) { + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + } + return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; } /** - * This method is called before the first test of this test class is run. + * @throws \PHPUnit\Framework\Exception + * @throws \PHPUnit\TextUI\XmlConfiguration\Exception */ - public static function setUpBeforeClass() : void + private function handleListSuites(bool $exit) : int { + $this->printVersionString(); + print 'Available test suite(s):' . \PHP_EOL; + foreach ($this->arguments['configurationObject']->testSuite() as $testSuite) { + printf(' - %s' . \PHP_EOL, $testSuite->name()); + } + if ($exit) { + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + } + return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; } /** - * This method is called after the last test of this test class is run. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public static function tearDownAfterClass() : void + private function handleListTests(TestSuite $suite, bool $exit) : int { + $this->printVersionString(); + $renderer = new TextTestListRenderer(); + print $renderer->render($suite); + if ($exit) { + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + } + return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; } /** - * This method is called before each test. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - protected function setUp() : void + private function handleListTestsXml(TestSuite $suite, string $target, bool $exit) : int { + $this->printVersionString(); + $renderer = new XmlTestListRenderer(); + file_put_contents($target, $renderer->render($suite)); + printf('Wrote list of tests that would have been run to %s' . \PHP_EOL, $target); + if ($exit) { + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); + } + return \PHPUnit\TextUI\TestRunner::SUCCESS_EXIT; } - /** - * This method is called after each test. - */ - protected function tearDown() : void + private function generateConfiguration() : void { + $this->printVersionString(); + print 'Generating phpunit.xml in ' . getcwd() . \PHP_EOL . \PHP_EOL; + print 'Bootstrap script (relative to path shown above; default: vendor/autoload.php): '; + $bootstrapScript = trim(fgets(\STDIN)); + print 'Tests directory (relative to path shown above; default: tests): '; + $testsDirectory = trim(fgets(\STDIN)); + print 'Source directory (relative to path shown above; default: src): '; + $src = trim(fgets(\STDIN)); + print 'Cache directory (relative to path shown above; default: .phpunit.cache): '; + $cacheDirectory = trim(fgets(\STDIN)); + if ($bootstrapScript === '') { + $bootstrapScript = 'vendor/autoload.php'; + } + if ($testsDirectory === '') { + $testsDirectory = 'tests'; + } + if ($src === '') { + $src = 'src'; + } + if ($cacheDirectory === '') { + $cacheDirectory = '.phpunit.cache'; + } + $generator = new Generator(); + file_put_contents('phpunit.xml', $generator->generateDefaultConfiguration(Version::series(), $bootstrapScript, $testsDirectory, $src, $cacheDirectory)); + print \PHP_EOL . 'Generated phpunit.xml in ' . getcwd() . '.' . \PHP_EOL; + print 'Make sure to exclude the ' . $cacheDirectory . ' directory from version control.' . \PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); } - /** - * Returns a string representation of the test case. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws Exception - */ - public function toString() : string + private function migrateConfiguration(string $filename) : void { + $this->printVersionString(); + if (!(new SchemaDetector())->detect($filename)->detected()) { + print $filename . ' does not need to be migrated.' . \PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + } + copy($filename, $filename . '.bak'); + print 'Created backup: ' . $filename . '.bak' . \PHP_EOL; try { - $class = new \ReflectionClass($this); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); + file_put_contents($filename, (new Migrator())->migrate($filename)); + print 'Migrated configuration: ' . $filename . \PHP_EOL; + } catch (Throwable $t) { + print 'Migration failed: ' . $t->getMessage() . \PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); } - // @codeCoverageIgnoreEnd - $buffer = \sprintf('%s::%s', $class->name, $this->getName(\false)); - return $buffer . $this->getDataSetAsString(); - } - public function count() : int - { - return 1; + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); } - public function getActualOutputForAssertion() : string + private function handleCustomOptions(array $unrecognizedOptions) : void { - $this->outputRetrievedForAssertion = \true; - return $this->getActualOutput(); + foreach ($unrecognizedOptions as $name => $value) { + if (isset($this->longOptions[$name])) { + $handler = $this->longOptions[$name]; + } + $name .= '='; + if (isset($this->longOptions[$name])) { + $handler = $this->longOptions[$name]; + } + if (isset($handler) && is_callable([$this, $handler])) { + $this->{$handler}($value); + unset($handler); + } + } } - public function expectOutputRegex(string $expectedRegex) : void + private function handleWarmCoverageCache(\PHPUnit\TextUI\XmlConfiguration\Configuration $configuration) : void { - $this->outputExpectedRegex = $expectedRegex; + $this->printVersionString(); + if (isset($this->arguments['coverageCacheDirectory'])) { + $cacheDirectory = $this->arguments['coverageCacheDirectory']; + } elseif ($configuration->codeCoverage()->hasCacheDirectory()) { + $cacheDirectory = $configuration->codeCoverage()->cacheDirectory()->path(); + } else { + print 'Cache for static analysis has not been configured' . \PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + } + $filter = new Filter(); + if ($configuration->codeCoverage()->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) { + (new FilterMapper())->map($filter, $configuration->codeCoverage()); + } elseif (isset($this->arguments['coverageFilter'])) { + if (!\is_array($this->arguments['coverageFilter'])) { + $coverageFilterDirectories = [$this->arguments['coverageFilter']]; + } else { + $coverageFilterDirectories = $this->arguments['coverageFilter']; + } + foreach ($coverageFilterDirectories as $coverageFilterDirectory) { + $filter->includeDirectory($coverageFilterDirectory); + } + } else { + print 'Filter for code coverage has not been configured' . \PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::EXCEPTION_EXIT); + } + $timer = new Timer(); + $timer->start(); + print 'Warming cache for static analysis ... '; + (new CacheWarmer())->warmCache($cacheDirectory, !$configuration->codeCoverage()->disableCodeCoverageIgnore(), $configuration->codeCoverage()->ignoreDeprecatedCodeUnits(), $filter); + print 'done [' . $timer->stop()->asString() . ']' . \PHP_EOL; + exit(\PHPUnit\TextUI\TestRunner::SUCCESS_EXIT); } - public function expectOutputString(string $expectedString) : void + private function configurationFileInDirectory(string $directory) : ?string { - $this->outputExpectedString = $expectedString; + $candidates = [$directory . '/phpunit.xml', $directory . '/phpunit.xml.dist']; + foreach ($candidates as $candidate) { + if (is_file($candidate)) { + return realpath($candidate); + } + } + return null; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; + +use const PHP_VERSION; +use function explode; +use function in_array; +use function is_dir; +use function is_file; +use function strpos; +use function version_compare; +use PHPUnit\Framework\Exception as FrameworkException; +use PHPUnit\Framework\TestSuite as TestSuiteObject; +use PHPUnit\TextUI\XmlConfiguration\TestSuiteCollection; +use PHPUnit\SebastianBergmann\FileIterator\Facade; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestSuiteMapper +{ /** - * @psalm-param class-string<\Throwable> $exception + * @throws RuntimeException + * @throws TestDirectoryNotFoundException + * @throws TestFileNotFoundException */ - public function expectException(string $exception) : void + public function map(TestSuiteCollection $configuration, string $filter) : TestSuiteObject { - // @codeCoverageIgnoreStart - switch ($exception) { - case \PHPUnit\Framework\Error\Deprecated::class: - $this->addWarning('Support for using expectException() with PHPUnit\\Framework\\Error\\Deprecated is deprecated and will be removed in PHPUnit 10. Use expectDeprecation() instead.'); - break; - case \PHPUnit\Framework\Error\Error::class: - $this->addWarning('Support for using expectException() with PHPUnit\\Framework\\Error\\Error is deprecated and will be removed in PHPUnit 10. Use expectError() instead.'); - break; - case \PHPUnit\Framework\Error\Notice::class: - $this->addWarning('Support for using expectException() with PHPUnit\\Framework\\Error\\Notice is deprecated and will be removed in PHPUnit 10. Use expectNotice() instead.'); - break; - case \PHPUnit\Framework\Error\Warning::class: - $this->addWarning('Support for using expectException() with PHPUnit\\Framework\\Error\\Warning is deprecated and will be removed in PHPUnit 10. Use expectWarning() instead.'); - break; + try { + $filterAsArray = $filter ? explode(',', $filter) : []; + $result = new TestSuiteObject(); + foreach ($configuration as $testSuiteConfiguration) { + if (!empty($filterAsArray) && !in_array($testSuiteConfiguration->name(), $filterAsArray, \true)) { + continue; + } + $testSuite = new TestSuiteObject($testSuiteConfiguration->name()); + $testSuiteEmpty = \true; + foreach ($testSuiteConfiguration->directories() as $directory) { + if (!version_compare(\PHP_VERSION, $directory->phpVersion(), $directory->phpVersionOperator()->asString())) { + continue; + } + $exclude = []; + foreach ($testSuiteConfiguration->exclude()->asArray() as $file) { + $exclude[] = $file->path(); + } + $files = (new Facade())->getFilesAsArray($directory->path(), $directory->suffix(), $directory->prefix(), $exclude); + if (!empty($files)) { + $testSuite->addTestFiles($files); + $testSuiteEmpty = \false; + } elseif (strpos($directory->path(), '*') === \false && !is_dir($directory->path())) { + throw new \PHPUnit\TextUI\TestDirectoryNotFoundException($directory->path()); + } + } + foreach ($testSuiteConfiguration->files() as $file) { + if (!is_file($file->path())) { + throw new \PHPUnit\TextUI\TestFileNotFoundException($file->path()); + } + if (!version_compare(\PHP_VERSION, $file->phpVersion(), $file->phpVersionOperator()->asString())) { + continue; + } + $testSuite->addTestFile($file->path()); + $testSuiteEmpty = \false; + } + if (!$testSuiteEmpty) { + $result->addTest($testSuite); + } + } + return $result; + } catch (FrameworkException $e) { + throw new \PHPUnit\TextUI\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); } - // @codeCoverageIgnoreEnd - $this->expectedException = $exception; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ErrorTestCase extends \PHPUnit\Framework\TestCase +{ /** - * @param int|string $code + * @var bool */ - public function expectExceptionCode($code) : void - { - $this->expectedExceptionCode = $code; - } - public function expectExceptionMessage(string $message) : void - { - $this->expectedExceptionMessage = $message; - } - public function expectExceptionMessageMatches(string $regularExpression) : void - { - $this->expectedExceptionMessageRegExp = $regularExpression; - } + protected $backupGlobals = \false; /** - * Sets up an expectation for an exception to be raised by the code under test. - * Information for expected exception class, expected exception message, and - * expected exception code are retrieved from a given Exception object. + * @var bool */ - public function expectExceptionObject(\Exception $exception) : void - { - $this->expectException(\get_class($exception)); - $this->expectExceptionMessage($exception->getMessage()); - $this->expectExceptionCode($exception->getCode()); - } - public function expectNotToPerformAssertions() : void - { - $this->doesNotPerformAssertions = \true; - } - public function expectDeprecation() : void - { - $this->expectedException = \PHPUnit\Framework\Error\Deprecated::class; - } - public function expectDeprecationMessage(string $message) : void - { - $this->expectExceptionMessage($message); - } - public function expectDeprecationMessageMatches(string $regularExpression) : void - { - $this->expectExceptionMessageMatches($regularExpression); - } - public function expectNotice() : void - { - $this->expectedException = \PHPUnit\Framework\Error\Notice::class; - } - public function expectNoticeMessage(string $message) : void - { - $this->expectExceptionMessage($message); - } - public function expectNoticeMessageMatches(string $regularExpression) : void - { - $this->expectExceptionMessageMatches($regularExpression); - } - public function expectWarning() : void - { - $this->expectedException = \PHPUnit\Framework\Error\Warning::class; - } - public function expectWarningMessage(string $message) : void - { - $this->expectExceptionMessage($message); - } - public function expectWarningMessageMatches(string $regularExpression) : void - { - $this->expectExceptionMessageMatches($regularExpression); - } - public function expectError() : void - { - $this->expectedException = \PHPUnit\Framework\Error\Error::class; - } - public function expectErrorMessage(string $message) : void - { - $this->expectExceptionMessage($message); - } - public function expectErrorMessageMatches(string $regularExpression) : void - { - $this->expectExceptionMessageMatches($regularExpression); - } - public function getStatus() : int - { - return $this->status; - } - public function markAsRisky() : void - { - $this->status = \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY; - } - public function getStatusMessage() : string + protected $backupStaticAttributes = \false; + /** + * @var bool + */ + protected $runTestInSeparateProcess = \false; + /** + * @var string + */ + private $message; + public function __construct(string $message = '') { - return $this->statusMessage; + $this->message = $message; + parent::__construct('Error'); } - public function hasFailed() : bool + public function getMessage() : string { - $status = $this->getStatus(); - return $status === \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE || $status === \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR; + return $this->message; } /** - * Runs the test case and collects the results in a TestResult object. - * If no TestResult object is passed a new one will be created. - * - * @throws CodeCoverageException - * @throws UtilException - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * Returns a string representation of the test case. */ - public function run(\PHPUnit\Framework\TestResult $result = null) : \PHPUnit\Framework\TestResult + public function toString() : string { - if ($result === null) { - $result = $this->createResult(); - } - if (!$this instanceof \PHPUnit\Framework\WarningTestCase) { - $this->setTestResultObject($result); - } - if (!$this instanceof \PHPUnit\Framework\WarningTestCase && !$this instanceof \PHPUnit\Framework\SkippedTestCase && !$this->handleDependencies()) { - return $result; - } - if ($this->runInSeparateProcess()) { - $runEntireClass = $this->runClassInSeparateProcess && !$this->runTestInSeparateProcess; - try { - $class = new \ReflectionClass($this); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($runEntireClass) { - $template = new \PHPUnit\SebastianBergmann\Template\Template(__DIR__ . '/../Util/PHP/Template/TestCaseClass.tpl'); - } else { - $template = new \PHPUnit\SebastianBergmann\Template\Template(__DIR__ . '/../Util/PHP/Template/TestCaseMethod.tpl'); - } - if ($this->preserveGlobalState) { - $constants = \PHPUnit\Util\GlobalState::getConstantsAsString(); - $globals = \PHPUnit\Util\GlobalState::getGlobalsAsString(); - $includedFiles = \PHPUnit\Util\GlobalState::getIncludedFilesAsString(); - $iniSettings = \PHPUnit\Util\GlobalState::getIniSettingsAsString(); - } else { - $constants = ''; - if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { - $globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . \var_export($GLOBALS['__PHPUNIT_BOOTSTRAP'], \true) . ";\n"; - } else { - $globals = ''; - } - $includedFiles = ''; - $iniSettings = ''; - } - $coverage = $result->getCollectCodeCoverageInformation() ? 'true' : 'false'; - $isStrictAboutTestsThatDoNotTestAnything = $result->isStrictAboutTestsThatDoNotTestAnything() ? 'true' : 'false'; - $isStrictAboutOutputDuringTests = $result->isStrictAboutOutputDuringTests() ? 'true' : 'false'; - $enforcesTimeLimit = $result->enforcesTimeLimit() ? 'true' : 'false'; - $isStrictAboutTodoAnnotatedTests = $result->isStrictAboutTodoAnnotatedTests() ? 'true' : 'false'; - $isStrictAboutResourceUsageDuringSmallTests = $result->isStrictAboutResourceUsageDuringSmallTests() ? 'true' : 'false'; - if (\defined('PHPUNIT_COMPOSER_INSTALL')) { - $composerAutoload = \var_export(PHPUNIT_COMPOSER_INSTALL, \true); - } else { - $composerAutoload = '\'\''; - } - if (\defined('__PHPUNIT_PHAR__')) { - $phar = \var_export(__PHPUNIT_PHAR__, \true); - } else { - $phar = '\'\''; - } - $codeCoverage = $result->getCodeCoverage(); - $codeCoverageFilter = null; - $cachesStaticAnalysis = 'false'; - $codeCoverageCacheDirectory = null; - $driverMethod = 'forLineCoverage'; - if ($codeCoverage) { - $codeCoverageFilter = $codeCoverage->filter(); - if ($codeCoverage->collectsBranchAndPathCoverage()) { - $driverMethod = 'forLineAndPathCoverage'; - } - if ($codeCoverage->cachesStaticAnalysis()) { - $cachesStaticAnalysis = 'true'; - $codeCoverageCacheDirectory = $codeCoverage->cacheDirectory(); - } - } - $data = \var_export(\serialize($this->data), \true); - $dataName = \var_export($this->dataName, \true); - $dependencyInput = \var_export(\serialize($this->dependencyInput), \true); - $includePath = \var_export(\get_include_path(), \true); - $codeCoverageFilter = \var_export(\serialize($codeCoverageFilter), \true); - $codeCoverageCacheDirectory = \var_export(\serialize($codeCoverageCacheDirectory), \true); - // must do these fixes because TestCaseMethod.tpl has unserialize('{data}') in it, and we can't break BC - // the lines above used to use addcslashes() rather than var_export(), which breaks null byte escape sequences - $data = "'." . $data . ".'"; - $dataName = "'.(" . $dataName . ").'"; - $dependencyInput = "'." . $dependencyInput . ".'"; - $includePath = "'." . $includePath . ".'"; - $codeCoverageFilter = "'." . $codeCoverageFilter . ".'"; - $codeCoverageCacheDirectory = "'." . $codeCoverageCacheDirectory . ".'"; - $configurationFilePath = $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] ?? ''; - $var = ['composerAutoload' => $composerAutoload, 'phar' => $phar, 'filename' => $class->getFileName(), 'className' => $class->getName(), 'collectCodeCoverageInformation' => $coverage, 'cachesStaticAnalysis' => $cachesStaticAnalysis, 'codeCoverageCacheDirectory' => $codeCoverageCacheDirectory, 'driverMethod' => $driverMethod, 'data' => $data, 'dataName' => $dataName, 'dependencyInput' => $dependencyInput, 'constants' => $constants, 'globals' => $globals, 'include_path' => $includePath, 'included_files' => $includedFiles, 'iniSettings' => $iniSettings, 'isStrictAboutTestsThatDoNotTestAnything' => $isStrictAboutTestsThatDoNotTestAnything, 'isStrictAboutOutputDuringTests' => $isStrictAboutOutputDuringTests, 'enforcesTimeLimit' => $enforcesTimeLimit, 'isStrictAboutTodoAnnotatedTests' => $isStrictAboutTodoAnnotatedTests, 'isStrictAboutResourceUsageDuringSmallTests' => $isStrictAboutResourceUsageDuringSmallTests, 'codeCoverageFilter' => $codeCoverageFilter, 'configurationFilePath' => $configurationFilePath, 'name' => $this->getName(\false)]; - if (!$runEntireClass) { - $var['methodName'] = $this->name; - } - $template->setVar($var); - $php = \PHPUnit\Util\PHP\AbstractPhpProcess::factory(); - $php->runTestJob($template->render(), $this, $result); - } else { - $result->run($this); - } - $this->result = null; - return $result; + return 'Error'; } /** - * Returns a builder object to create mock objects using a fluent interface. + * @throws Exception * - * @psalm-template RealInstanceType of object - * @psalm-param class-string $className - * @psalm-return MockBuilder + * @psalm-return never-return */ - public function getMockBuilder(string $className) : \PHPUnit\Framework\MockObject\MockBuilder - { - $this->recordDoubledType($className); - return new \PHPUnit\Framework\MockObject\MockBuilder($this, $className); - } - public function registerComparator(\PHPUnit\SebastianBergmann\Comparator\Comparator $comparator) : void + protected function runTest() : void { - \PHPUnit\SebastianBergmann\Comparator\Factory::getInstance()->register($comparator); - $this->customComparators[] = $comparator; + throw new \PHPUnit\Framework\Error($this->message); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface SkippedTest extends Throwable +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const PHP_EOL; +use function array_diff; +use function array_keys; +use function array_map; +use function array_merge; +use function array_unique; +use function basename; +use function call_user_func; +use function class_exists; +use function count; +use function dirname; +use function get_declared_classes; +use function implode; +use function is_bool; +use function is_callable; +use function is_file; +use function is_object; +use function is_string; +use function method_exists; +use function preg_match; +use function preg_quote; +use function sprintf; +use function strpos; +use function substr; +use Iterator; +use IteratorAggregate; +use PHPUnit\Runner\BaseTestRunner; +use PHPUnit\Runner\Filter\Factory; +use PHPUnit\Runner\PhptTestCase; +use PHPUnit\Util\FileLoader; +use PHPUnit\Util\Test as TestUtil; +use ReflectionClass; +use ReflectionException; +use ReflectionMethod; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +class TestSuite implements IteratorAggregate, \PHPUnit\Framework\Reorderable, \PHPUnit\Framework\SelfDescribing, \PHPUnit\Framework\Test +{ /** - * @return string[] + * Enable or disable the backup and restoration of the $GLOBALS array. * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function doubledTypes() : array - { - return \array_unique($this->doubledTypes); - } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function getGroups() : array - { - return $this->groups; - } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function setGroups(array $groups) : void - { - $this->groups = $groups; - } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var bool */ - public function getAnnotations() : array - { - return \PHPUnit\Util\Test::parseTestMethodAnnotations(static::class, $this->name); - } + protected $backupGlobals; /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * Enable or disable the backup and restoration of static attributes. * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var bool */ - public function getName(bool $withDataSet = \true) : string - { - if ($withDataSet) { - return $this->name . $this->getDataSetAsString(\false); - } - return $this->name; - } + protected $backupStaticAttributes; /** - * Returns the size of the test. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var bool */ - public function getSize() : int - { - return \PHPUnit\Util\Test::getSize(static::class, $this->getName(\false)); - } + protected $runTestInSeparateProcess = \false; /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * The name of the test suite. * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var string */ - public function hasSize() : bool - { - return $this->getSize() !== \PHPUnit\Util\Test::UNKNOWN; - } + protected $name = ''; /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * The test groups of the test suite. * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @psalm-var array> */ - public function isSmall() : bool - { - return $this->getSize() === \PHPUnit\Util\Test::SMALL; - } + protected $groups = []; /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * The tests in the test suite. * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var Test[] */ - public function isMedium() : bool - { - return $this->getSize() === \PHPUnit\Util\Test::MEDIUM; - } + protected $tests = []; /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * The number of tests in the test suite. * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function isLarge() : bool - { - return $this->getSize() === \PHPUnit\Util\Test::LARGE; - } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var int */ - public function getActualOutput() : string - { - if (!$this->outputBufferingActive) { - return $this->output; - } - return (string) \ob_get_contents(); - } + protected $numTests = -1; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var bool */ - public function hasOutput() : bool - { - if ($this->output === '') { - return \false; - } - if ($this->hasExpectationOnOutput()) { - return \false; - } - return \true; - } + protected $testCase = \false; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var string[] */ - public function doesNotPerformAssertions() : bool - { - return $this->doesNotPerformAssertions; - } + protected $foundClasses = []; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var null|list */ - public function hasExpectationOnOutput() : bool - { - return \is_string($this->outputExpectedString) || \is_string($this->outputExpectedRegex) || $this->outputRetrievedForAssertion; - } + protected $providedTests; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var null|list */ - public function getExpectedException() : ?string - { - return $this->expectedException; - } + protected $requiredTests; /** - * @return null|int|string - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var bool */ - public function getExpectedExceptionCode() - { - return $this->expectedExceptionCode; - } + private $beStrictAboutChangesToGlobalState; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var Factory */ - public function getExpectedExceptionMessage() : ?string - { - return $this->expectedExceptionMessage; - } + private $iteratorFilter; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @var string[] */ - public function getExpectedExceptionMessageRegExp() : ?string - { - return $this->expectedExceptionMessageRegExp; - } + private $declaredClasses; /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @psalm-var array */ - public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag) : void - { - $this->registerMockObjectsFromTestArgumentsRecursively = $flag; - } + private $warnings = []; /** - * @throws Throwable + * Constructs a new TestSuite. * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * - PHPUnit\Framework\TestSuite() constructs an empty TestSuite. + * + * - PHPUnit\Framework\TestSuite(ReflectionClass) constructs a + * TestSuite from the given class. + * + * - PHPUnit\Framework\TestSuite(ReflectionClass, String) + * constructs a TestSuite from the given class with the given + * name. + * + * - PHPUnit\Framework\TestSuite(String) either constructs a + * TestSuite from the given class (if the passed string is the + * name of an existing class) or constructs an empty TestSuite + * with the given name. + * + * @param ReflectionClass|string $theClass + * + * @throws Exception */ - public function runBare() : void + public function __construct($theClass = '', string $name = '') { - $this->numAssertions = 0; - $this->snapshotGlobalState(); - $this->startOutputBuffering(); - \clearstatcache(); - $currentWorkingDirectory = \getcwd(); - $hookMethods = \PHPUnit\Util\Test::getHookMethods(static::class); - $hasMetRequirements = \false; - try { - $this->checkRequirements(); - $hasMetRequirements = \true; - if ($this->inIsolation) { - foreach ($hookMethods['beforeClass'] as $method) { - $this->{$method}(); - } - } - $this->setDoesNotPerformAssertionsFromAnnotation(); - foreach ($hookMethods['before'] as $method) { - $this->{$method}(); - } - foreach ($hookMethods['preCondition'] as $method) { - $this->{$method}(); - } - $this->testResult = $this->runTest(); - $this->verifyMockObjects(); - foreach ($hookMethods['postCondition'] as $method) { - $this->{$method}(); - } - if (!empty($this->warnings)) { - throw new \PHPUnit\Framework\Warning(\implode("\n", \array_unique($this->warnings))); - } - $this->status = \PHPUnit\Runner\BaseTestRunner::STATUS_PASSED; - } catch (\PHPUnit\Framework\IncompleteTest $e) { - $this->status = \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE; - $this->statusMessage = $e->getMessage(); - } catch (\PHPUnit\Framework\SkippedTest $e) { - $this->status = \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED; - $this->statusMessage = $e->getMessage(); - } catch (\PHPUnit\Framework\Warning $e) { - $this->status = \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING; - $this->statusMessage = $e->getMessage(); - } catch (\PHPUnit\Framework\AssertionFailedError $e) { - $this->status = \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE; - $this->statusMessage = $e->getMessage(); - } catch (\Prophecy\Exception\Prediction\PredictionException $e) { - $this->status = \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE; - $this->statusMessage = $e->getMessage(); - } catch (\Throwable $_e) { - $e = $_e; - $this->status = \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR; - $this->statusMessage = $_e->getMessage(); + if (!is_string($theClass) && !$theClass instanceof ReflectionClass) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'ReflectionClass object or string'); } - $this->mockObjects = []; - $this->prophet = null; - // Tear down the fixture. An exception raised in tearDown() will be - // caught and passed on when no exception was raised before. - try { - if ($hasMetRequirements) { - foreach ($hookMethods['after'] as $method) { - $this->{$method}(); + $this->declaredClasses = get_declared_classes(); + if (!$theClass instanceof ReflectionClass) { + if (class_exists($theClass, \true)) { + if ($name === '') { + $name = $theClass; } - if ($this->inIsolation) { - foreach ($hookMethods['afterClass'] as $method) { - $this->{$method}(); - } + try { + $theClass = new ReflectionClass($theClass); + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); } + // @codeCoverageIgnoreEnd + } else { + $this->setName($theClass); + return; } - } catch (\Throwable $_e) { - $e = $e ?? $_e; } - try { - $this->stopOutputBuffering(); - } catch (\PHPUnit\Framework\RiskyTestError $_e) { - $e = $e ?? $_e; + if (!$theClass->isSubclassOf(\PHPUnit\Framework\TestCase::class)) { + $this->setName((string) $theClass); + return; } - if (isset($_e)) { - $this->status = \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR; - $this->statusMessage = $_e->getMessage(); + if ($name !== '') { + $this->setName($name); + } else { + $this->setName($theClass->getName()); } - \clearstatcache(); - if ($currentWorkingDirectory !== \getcwd()) { - \chdir($currentWorkingDirectory); + $constructor = $theClass->getConstructor(); + if ($constructor !== null && !$constructor->isPublic()) { + $this->addTest(new \PHPUnit\Framework\WarningTestCase(sprintf('Class "%s" has no public constructor.', $theClass->getName()))); + return; } - $this->restoreGlobalState(); - $this->unregisterCustomComparators(); - $this->cleanupIniSettings(); - $this->cleanupLocaleSettings(); - \libxml_clear_errors(); - // Perform assertion on output. - if (!isset($e)) { - try { - if ($this->outputExpectedRegex !== null) { - $this->assertMatchesRegularExpression($this->outputExpectedRegex, $this->output); - } elseif ($this->outputExpectedString !== null) { - $this->assertEquals($this->outputExpectedString, $this->output); - } - } catch (\Throwable $_e) { - $e = $_e; + foreach ($theClass->getMethods() as $method) { + if ($method->getDeclaringClass()->getName() === \PHPUnit\Framework\Assert::class) { + continue; } - } - // Workaround for missing "finally". - if (isset($e)) { - if ($e instanceof \Prophecy\Exception\Prediction\PredictionException) { - $e = new \PHPUnit\Framework\AssertionFailedError($e->getMessage()); + if ($method->getDeclaringClass()->getName() === \PHPUnit\Framework\TestCase::class) { + continue; } - $this->onNotSuccessfulTest($e); + if (!TestUtil::isTestMethod($method)) { + continue; + } + $this->addTestMethod($theClass, $method); } - } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function setName(string $name) : void - { - $this->name = $name; - if (\is_callable($this->sortId(), \true)) { - $this->providedTests = [new \PHPUnit\Framework\ExecutionOrderDependency($this->sortId())]; + if (empty($this->tests)) { + $this->addTest(new \PHPUnit\Framework\WarningTestCase(sprintf('No tests found in class "%s".', $theClass->getName()))); } + $this->testCase = \true; } /** - * @param list $dependencies - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function setDependencies(array $dependencies) : void - { - $this->dependencies = $dependencies; - } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function setDependencyInput(array $dependencyInput) : void - { - $this->dependencyInput = $dependencyInput; - } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * Returns a string representation of the test suite. */ - public function setBeStrictAboutChangesToGlobalState(?bool $beStrictAboutChangesToGlobalState) : void + public function toString() : string { - $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; + return $this->getName(); } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * Adds a test to the suite. + * + * @param array $groups */ - public function setBackupGlobals(?bool $backupGlobals) : void + public function addTest(\PHPUnit\Framework\Test $test, $groups = []) : void { - if ($this->backupGlobals === null && $backupGlobals !== null) { - $this->backupGlobals = $backupGlobals; + try { + $class = new ReflectionClass($test); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); } - } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function setBackupStaticAttributes(?bool $backupStaticAttributes) : void - { - if ($this->backupStaticAttributes === null && $backupStaticAttributes !== null) { - $this->backupStaticAttributes = $backupStaticAttributes; + // @codeCoverageIgnoreEnd + if (!$class->isAbstract()) { + $this->tests[] = $test; + $this->clearCaches(); + if ($test instanceof self && empty($groups)) { + $groups = $test->getGroups(); + } + if ($this->containsOnlyVirtualGroups($groups)) { + $groups[] = 'default'; + } + foreach ($groups as $group) { + if (!isset($this->groups[$group])) { + $this->groups[$group] = [$test]; + } else { + $this->groups[$group][] = $test; + } + } + if ($test instanceof \PHPUnit\Framework\TestCase) { + $test->setGroups($groups); + } } } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * Adds the tests from the given class to the suite. + * + * @psalm-param object|class-string $testClass + * + * @throws Exception */ - public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess) : void + public function addTestSuite($testClass) : void { - if ($this->runTestInSeparateProcess === null) { - $this->runTestInSeparateProcess = $runTestInSeparateProcess; + if (!(is_object($testClass) || is_string($testClass) && class_exists($testClass))) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'class name or object'); } - } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function setRunClassInSeparateProcess(bool $runClassInSeparateProcess) : void - { - if ($this->runClassInSeparateProcess === null) { - $this->runClassInSeparateProcess = $runClassInSeparateProcess; + if (!is_object($testClass)) { + try { + $testClass = new ReflectionClass($testClass); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + if ($testClass instanceof self) { + $this->addTest($testClass); + } elseif ($testClass instanceof ReflectionClass) { + $suiteMethod = \false; + if (!$testClass->isAbstract() && $testClass->hasMethod(BaseTestRunner::SUITE_METHODNAME)) { + try { + $method = $testClass->getMethod(BaseTestRunner::SUITE_METHODNAME); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if ($method->isStatic()) { + $this->addTest($method->invoke(null, $testClass->getName())); + $suiteMethod = \true; + } + } + if (!$suiteMethod && !$testClass->isAbstract() && $testClass->isSubclassOf(\PHPUnit\Framework\TestCase::class)) { + $this->addTest(new self($testClass)); + } + } else { + throw new \PHPUnit\Framework\Exception(); } } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function setPreserveGlobalState(bool $preserveGlobalState) : void - { - $this->preserveGlobalState = $preserveGlobalState; - } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function setInIsolation(bool $inIsolation) : void + public function addWarning(string $warning) : void { - $this->inIsolation = $inIsolation; + $this->warnings[] = $warning; } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * Wraps both addTest() and addTestSuite + * as well as the separate import statements for the user's convenience. + * + * If the named file cannot be read or there are no new tests that can be + * added, a PHPUnit\Framework\WarningTestCase will be created instead, + * leaving the current test run untouched. + * + * @throws Exception */ - public function isInIsolation() : bool + public function addTestFile(string $filename) : void { - return $this->inIsolation; + if (is_file($filename) && substr($filename, -5) === '.phpt') { + $this->addTest(new PhptTestCase($filename)); + $this->declaredClasses = get_declared_classes(); + return; + } + $numTests = count($this->tests); + // The given file may contain further stub classes in addition to the + // test class itself. Figure out the actual test class. + $filename = FileLoader::checkAndLoad($filename); + $newClasses = array_diff(get_declared_classes(), $this->declaredClasses); + // The diff is empty in case a parent class (with test methods) is added + // AFTER a child class that inherited from it. To account for that case, + // accumulate all discovered classes, so the parent class may be found in + // a later invocation. + if (!empty($newClasses)) { + // On the assumption that test classes are defined first in files, + // process discovered classes in approximate LIFO order, so as to + // avoid unnecessary reflection. + $this->foundClasses = array_merge($newClasses, $this->foundClasses); + $this->declaredClasses = get_declared_classes(); + } + // The test class's name must match the filename, either in full, or as + // a PEAR/PSR-0 prefixed short name ('NameSpace_ShortName'), or as a + // PSR-1 local short name ('NameSpace\ShortName'). The comparison must be + // anchored to prevent false-positive matches (e.g., 'OtherShortName'). + $shortName = basename($filename, '.php'); + $shortNameRegEx = '/(?:^|_|\\\\)' . preg_quote($shortName, '/') . '$/'; + foreach ($this->foundClasses as $i => $className) { + if (preg_match($shortNameRegEx, $className)) { + try { + $class = new ReflectionClass($className); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if ($class->getFileName() == $filename) { + $newClasses = [$className]; + unset($this->foundClasses[$i]); + break; + } + } + } + foreach ($newClasses as $className) { + try { + $class = new ReflectionClass($className); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if (dirname($class->getFileName()) === __DIR__) { + continue; + } + if (!$class->isAbstract()) { + if ($class->hasMethod(BaseTestRunner::SUITE_METHODNAME)) { + try { + $method = $class->getMethod(BaseTestRunner::SUITE_METHODNAME); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if ($method->isStatic()) { + $this->addTest($method->invoke(null, $className)); + } + } elseif ($class->implementsInterface(\PHPUnit\Framework\Test::class)) { + $expectedClassName = $shortName; + if (($pos = strpos($expectedClassName, '.')) !== \false) { + $expectedClassName = substr($expectedClassName, 0, $pos); + } + if ($class->getShortName() !== $expectedClassName) { + $this->addWarning(sprintf("Test case class not matching filename is deprecated\n in %s\n Class name was '%s', expected '%s'", $filename, $class->getShortName(), $expectedClassName)); + } + $this->addTestSuite($class); + } + } + } + if (count($this->tests) > ++$numTests) { + $this->addWarning(sprintf("Multiple test case classes per file is deprecated\n in %s", $filename)); + } + $this->numTests = -1; } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * Wrapper for addTestFile() that adds multiple test files. + * + * @throws Exception */ - public function getResult() + public function addTestFiles(iterable $fileNames) : void { - return $this->testResult; + foreach ($fileNames as $filename) { + $this->addTestFile((string) $filename); + } } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * Counts the number of test cases that will be run by this test. + * + * @todo refactor usage of numTests in DefaultResultPrinter */ - public function setResult($result) : void + public function count() : int { - $this->testResult = $result; + $this->numTests = 0; + foreach ($this as $test) { + $this->numTests += count($test); + } + return $this->numTests; } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * Returns the name of the suite. */ - public function setOutputCallback(callable $callback) : void + public function getName() : string { - $this->outputCallback = $callback; + return $this->name; } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * Returns the test groups of the suite. + * + * @psalm-return list */ - public function getTestResultObject() : ?\PHPUnit\Framework\TestResult + public function getGroups() : array { - return $this->result; + return array_map(static function ($key) : string { + return (string) $key; + }, array_keys($this->groups)); } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function setTestResultObject(\PHPUnit\Framework\TestResult $result) : void + public function getGroupDetails() : array { - $this->result = $result; + return $this->groups; } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * Set tests groups of the test case. */ - public function registerMockObject(\PHPUnit\Framework\MockObject\MockObject $mockObject) : void + public function setGroupDetails(array $groups) : void { - $this->mockObjects[] = $mockObject; + $this->groups = $groups; } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * Runs the tests and collects their result in a TestResult. + * + * @throws \PHPUnit\Framework\CodeCoverageException + * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException + * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Warning */ - public function addToAssertionCount(int $count) : void + public function run(\PHPUnit\Framework\TestResult $result = null) : \PHPUnit\Framework\TestResult { - $this->numAssertions += $count; + if ($result === null) { + $result = $this->createResult(); + } + if (count($this) === 0) { + return $result; + } + /** @psalm-var class-string $className */ + $className = $this->name; + $hookMethods = TestUtil::getHookMethods($className); + $result->startTestSuite($this); + $test = null; + if ($this->testCase && class_exists($this->name, \false)) { + try { + foreach ($hookMethods['beforeClass'] as $beforeClassMethod) { + if (method_exists($this->name, $beforeClassMethod)) { + if ($missingRequirements = TestUtil::getMissingRequirements($this->name, $beforeClassMethod)) { + $this->markTestSuiteSkipped(implode(\PHP_EOL, $missingRequirements)); + } + call_user_func([$this->name, $beforeClassMethod]); + } + } + } catch (\PHPUnit\Framework\SkippedTestSuiteError $error) { + foreach ($this->tests() as $test) { + $result->startTest($test); + $result->addFailure($test, $error, 0); + $result->endTest($test, 0); + } + $result->endTestSuite($this); + return $result; + } catch (Throwable $t) { + $errorAdded = \false; + foreach ($this->tests() as $test) { + if ($result->shouldStop()) { + break; + } + $result->startTest($test); + if (!$errorAdded) { + $result->addError($test, $t, 0); + $errorAdded = \true; + } else { + $result->addFailure($test, new \PHPUnit\Framework\SkippedTestError('Test skipped because of an error in hook method'), 0); + } + $result->endTest($test, 0); + } + $result->endTestSuite($this); + return $result; + } + } + foreach ($this as $test) { + if ($result->shouldStop()) { + break; + } + if ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof self) { + $test->setBeStrictAboutChangesToGlobalState($this->beStrictAboutChangesToGlobalState); + $test->setBackupGlobals($this->backupGlobals); + $test->setBackupStaticAttributes($this->backupStaticAttributes); + $test->setRunTestInSeparateProcess($this->runTestInSeparateProcess); + } + $test->run($result); + } + if ($this->testCase && class_exists($this->name, \false)) { + foreach ($hookMethods['afterClass'] as $afterClassMethod) { + if (method_exists($this->name, $afterClassMethod)) { + try { + call_user_func([$this->name, $afterClassMethod]); + } catch (Throwable $t) { + $message = "Exception in {$this->name}::{$afterClassMethod}" . \PHP_EOL . $t->getMessage(); + $error = new \PHPUnit\Framework\SyntheticError($message, 0, $t->getFile(), $t->getLine(), $t->getTrace()); + $placeholderTest = clone $test; + $placeholderTest->setName($afterClassMethod); + $result->startTest($placeholderTest); + $result->addFailure($placeholderTest, $error, 0); + $result->endTest($placeholderTest, 0); + } + } + } + } + $result->endTestSuite($this); + return $result; } - /** - * Returns the number of assertions performed by this test. - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function getNumAssertions() : int + public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess) : void { - return $this->numAssertions; + $this->runTestInSeparateProcess = $runTestInSeparateProcess; } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function usesDataProvider() : bool + public function setName(string $name) : void { - return !empty($this->data); + $this->name = $name; } /** - * @return int|string + * Returns the tests as an enumeration. * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @return Test[] */ - public function dataName() + public function tests() : array { - return $this->dataName; + return $this->tests; } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * Set tests of the test suite. + * + * @param Test[] $tests */ - public function getDataSetAsString(bool $includeData = \true) : string + public function setTests(array $tests) : void { - $buffer = ''; - if (!empty($this->data)) { - if (\is_int($this->dataName)) { - $buffer .= \sprintf(' with data set #%d', $this->dataName); - } else { - $buffer .= \sprintf(' with data set "%s"', $this->dataName); - } - $exporter = new \PHPUnit\SebastianBergmann\Exporter\Exporter(); - if ($includeData) { - $buffer .= \sprintf(' (%s)', $exporter->shortenedRecursiveExport($this->data)); - } - } - return $buffer; + $this->tests = $tests; } /** - * Gets the data set of a TestCase. + * Mark the test suite as skipped. * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @param string $message + * + * @throws SkippedTestSuiteError + * + * @psalm-return never-return */ - public function getProvidedData() : array + public function markTestSuiteSkipped($message = '') : void { - return $this->data; + throw new \PHPUnit\Framework\SkippedTestSuiteError($message); } /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @param bool $beStrictAboutChangesToGlobalState */ - public function addWarning(string $warning) : void - { - $this->warnings[] = $warning; - } - public function sortId() : string + public function setBeStrictAboutChangesToGlobalState($beStrictAboutChangesToGlobalState) : void { - $id = $this->name; - if (\strpos($id, '::') === \false) { - $id = static::class . '::' . $id; - } - if ($this->usesDataProvider()) { - $id .= $this->getDataSetAsString(\false); + if (null === $this->beStrictAboutChangesToGlobalState && is_bool($beStrictAboutChangesToGlobalState)) { + $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; } - return $id; } /** - * Returns the normalized test name as class::method. - * - * @return list + * @param bool $backupGlobals */ - public function provides() : array + public function setBackupGlobals($backupGlobals) : void { - return $this->providedTests; + if (null === $this->backupGlobals && is_bool($backupGlobals)) { + $this->backupGlobals = $backupGlobals; + } } /** - * Returns a list of normalized dependency names, class::method. - * - * This list can differ from the raw dependencies as the resolver has - * no need for the [!][shallow]clone prefix that is filtered out - * during normalization. - * - * @return list + * @param bool $backupStaticAttributes */ - public function requires() : array + public function setBackupStaticAttributes($backupStaticAttributes) : void { - return $this->dependencies; + if (null === $this->backupStaticAttributes && is_bool($backupStaticAttributes)) { + $this->backupStaticAttributes = $backupStaticAttributes; + } } /** - * Override to run the test and assert its state. - * - * @throws AssertionFailedError - * @throws Exception - * @throws ExpectationFailedException - * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException - * @throws Throwable + * Returns an iterator for this test suite. */ - protected function runTest() + public function getIterator() : Iterator { - if (\trim($this->name) === '') { - throw new \PHPUnit\Framework\Exception('PHPUnit\\Framework\\TestCase::$name must be a non-blank string.'); + $iterator = new \PHPUnit\Framework\TestSuiteIterator($this); + if ($this->iteratorFilter !== null) { + $iterator = $this->iteratorFilter->factory($iterator, $this); } - $testArguments = \array_merge($this->data, $this->dependencyInput); - $this->registerMockObjectsFromTestArguments($testArguments); - try { - $testResult = $this->{$this->name}(...\array_values($testArguments)); - } catch (\Throwable $exception) { - if (!$this->checkExceptionExpectations($exception)) { - throw $exception; - } - if ($this->expectedException !== null) { - $this->assertThat($exception, new \PHPUnit\Framework\Constraint\Exception($this->expectedException)); - } - if ($this->expectedExceptionMessage !== null) { - $this->assertThat($exception, new \PHPUnit\Framework\Constraint\ExceptionMessage($this->expectedExceptionMessage)); - } - if ($this->expectedExceptionMessageRegExp !== null) { - $this->assertThat($exception, new \PHPUnit\Framework\Constraint\ExceptionMessageRegularExpression($this->expectedExceptionMessageRegExp)); - } - if ($this->expectedExceptionCode !== null) { - $this->assertThat($exception, new \PHPUnit\Framework\Constraint\ExceptionCode($this->expectedExceptionCode)); + return $iterator; + } + public function injectFilter(Factory $filter) : void + { + $this->iteratorFilter = $filter; + foreach ($this as $test) { + if ($test instanceof self) { + $test->injectFilter($filter); } - return; - } - if ($this->expectedException !== null) { - $this->assertThat(null, new \PHPUnit\Framework\Constraint\Exception($this->expectedException)); - } elseif ($this->expectedExceptionMessage !== null) { - $this->numAssertions++; - throw new \PHPUnit\Framework\AssertionFailedError(\sprintf('Failed asserting that exception with message "%s" is thrown', $this->expectedExceptionMessage)); - } elseif ($this->expectedExceptionMessageRegExp !== null) { - $this->numAssertions++; - throw new \PHPUnit\Framework\AssertionFailedError(\sprintf('Failed asserting that exception with message matching "%s" is thrown', $this->expectedExceptionMessageRegExp)); - } elseif ($this->expectedExceptionCode !== null) { - $this->numAssertions++; - throw new \PHPUnit\Framework\AssertionFailedError(\sprintf('Failed asserting that exception with code "%s" is thrown', $this->expectedExceptionCode)); } - return $testResult; } /** - * This method is a wrapper for the ini_set() function that automatically - * resets the modified php.ini setting to its original value after the - * test is run. - * - * @throws Exception + * @psalm-return array */ - protected function iniSet(string $varName, string $newValue) : void + public function warnings() : array { - $currentValue = \ini_set($varName, $newValue); - if ($currentValue !== \false) { - $this->iniSettings[$varName] = $currentValue; - } else { - throw new \PHPUnit\Framework\Exception(\sprintf('INI setting "%s" could not be set to "%s".', $varName, $newValue)); - } + return array_unique($this->warnings); } /** - * This method is a wrapper for the setlocale() function that automatically - * resets the locale to its original value after the test is run. - * - * @throws Exception + * @return list */ - protected function setLocale(...$args) : void + public function provides() : array { - if (\count($args) < 2) { - throw new \PHPUnit\Framework\Exception(); - } - [$category, $locale] = $args; - if (!\in_array($category, self::LOCALE_CATEGORIES, \true)) { - throw new \PHPUnit\Framework\Exception(); - } - if (!\is_array($locale) && !\is_string($locale)) { - throw new \PHPUnit\Framework\Exception(); - } - $this->locale[$category] = \setlocale($category, 0); - $result = \setlocale(...$args); - if ($result === \false) { - throw new \PHPUnit\Framework\Exception('The locale functionality is not implemented on your platform, ' . 'the specified locale does not exist or the category name is ' . 'invalid.'); + if ($this->providedTests === null) { + $this->providedTests = []; + if (is_callable($this->sortId(), \true)) { + $this->providedTests[] = new \PHPUnit\Framework\ExecutionOrderDependency($this->sortId()); + } + foreach ($this->tests as $test) { + if (!$test instanceof \PHPUnit\Framework\Reorderable) { + // @codeCoverageIgnoreStart + continue; + // @codeCoverageIgnoreEnd + } + $this->providedTests = \PHPUnit\Framework\ExecutionOrderDependency::mergeUnique($this->providedTests, $test->provides()); + } } + return $this->providedTests; } /** - * Makes configurable stub for the specified class. - * - * @psalm-template RealInstanceType of object - * @psalm-param class-string $originalClassName - * @psalm-return Stub&RealInstanceType + * @return list */ - protected function createStub(string $originalClassName) : \PHPUnit\Framework\MockObject\Stub + public function requires() : array { - return $this->createMockObject($originalClassName); + if ($this->requiredTests === null) { + $this->requiredTests = []; + foreach ($this->tests as $test) { + if (!$test instanceof \PHPUnit\Framework\Reorderable) { + // @codeCoverageIgnoreStart + continue; + // @codeCoverageIgnoreEnd + } + $this->requiredTests = \PHPUnit\Framework\ExecutionOrderDependency::mergeUnique(\PHPUnit\Framework\ExecutionOrderDependency::filterInvalid($this->requiredTests), $test->requires()); + } + $this->requiredTests = \PHPUnit\Framework\ExecutionOrderDependency::diff($this->requiredTests, $this->provides()); + } + return $this->requiredTests; } - /** - * Returns a mock object for the specified class. - * - * @psalm-template RealInstanceType of object - * @psalm-param class-string $originalClassName - * @psalm-return MockObject&RealInstanceType - */ - protected function createMock(string $originalClassName) : \PHPUnit\Framework\MockObject\MockObject + public function sortId() : string { - return $this->createMockObject($originalClassName); + return $this->getName() . '::class'; } /** - * Returns a configured mock object for the specified class. - * - * @psalm-template RealInstanceType of object - * @psalm-param class-string $originalClassName - * @psalm-return MockObject&RealInstanceType + * Creates a default TestResult object. */ - protected function createConfiguredMock(string $originalClassName, array $configuration) : \PHPUnit\Framework\MockObject\MockObject + protected function createResult() : \PHPUnit\Framework\TestResult { - $o = $this->createMockObject($originalClassName); - foreach ($configuration as $method => $return) { - $o->method($method)->willReturn($return); - } - return $o; + return new \PHPUnit\Framework\TestResult(); } /** - * Returns a partial mock object for the specified class. - * - * @param string[] $methods - * - * @psalm-template RealInstanceType of object - * @psalm-param class-string $originalClassName - * @psalm-return MockObject&RealInstanceType + * @throws Exception */ - protected function createPartialMock(string $originalClassName, array $methods) : \PHPUnit\Framework\MockObject\MockObject + protected function addTestMethod(ReflectionClass $class, ReflectionMethod $method) : void { - try { - $reflector = new \ReflectionClass($originalClassName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - $mockedMethodsThatDontExist = \array_filter($methods, static function (string $method) use($reflector) { - return !$reflector->hasMethod($method); - }); - if ($mockedMethodsThatDontExist) { - $this->addWarning(\sprintf('createPartialMock() called with method(s) %s that do not exist in %s. This will not be allowed in future versions of PHPUnit.', \implode(', ', $mockedMethodsThatDontExist), $originalClassName)); + $methodName = $method->getName(); + $test = (new \PHPUnit\Framework\TestBuilder())->build($class, $methodName); + if ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof \PHPUnit\Framework\DataProviderTestSuite) { + $test->setDependencies(TestUtil::getDependencies($class->getName(), $methodName)); } - return $this->getMockBuilder($originalClassName)->disableOriginalConstructor()->disableOriginalClone()->disableArgumentCloning()->disallowMockingUnknownTypes()->setMethods(empty($methods) ? null : $methods)->getMock(); + $this->addTest($test, TestUtil::getGroups($class->getName(), $methodName)); } - /** - * Returns a test proxy for the specified class. - * - * @psalm-template RealInstanceType of object - * @psalm-param class-string $originalClassName - * @psalm-return MockObject&RealInstanceType - */ - protected function createTestProxy(string $originalClassName, array $constructorArguments = []) : \PHPUnit\Framework\MockObject\MockObject + private function clearCaches() : void { - return $this->getMockBuilder($originalClassName)->setConstructorArgs($constructorArguments)->enableProxyingToOriginalMethods()->getMock(); + $this->numTests = -1; + $this->providedTests = null; + $this->requiredTests = null; } - /** - * Mocks the specified class and returns the name of the mocked class. - * - * @param null|array $methods $methods - * - * @psalm-template RealInstanceType of object - * @psalm-param class-string|string $originalClassName - * @psalm-return class-string - */ - protected function getMockClass(string $originalClassName, $methods = [], array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \false, bool $callOriginalClone = \true, bool $callAutoload = \true, bool $cloneArguments = \false) : string + private function containsOnlyVirtualGroups(array $groups) : bool { - $this->recordDoubledType($originalClassName); - $mock = $this->getMockObjectGenerator()->getMock($originalClassName, $methods, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $cloneArguments); - return \get_class($mock); + foreach ($groups as $group) { + if (strpos($group, '__phpunit_') !== 0) { + return \false; + } + } + return \true; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function explode; +use PHPUnit\Util\Test as TestUtil; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class DataProviderTestSuite extends \PHPUnit\Framework\TestSuite +{ /** - * Returns a mock object for the specified abstract class with all abstract - * methods of the class mocked. Concrete methods are not mocked by default. - * To mock concrete methods, use the 7th parameter ($mockedMethods). - * - * @psalm-template RealInstanceType of object - * @psalm-param class-string $originalClassName - * @psalm-return MockObject&RealInstanceType + * @var list */ - protected function getMockForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = [], bool $cloneArguments = \false) : \PHPUnit\Framework\MockObject\MockObject - { - $this->recordDoubledType($originalClassName); - $mockObject = $this->getMockObjectGenerator()->getMockForAbstractClass($originalClassName, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); - $this->registerMockObject($mockObject); - return $mockObject; - } + private $dependencies = []; /** - * Returns a mock object based on the given WSDL file. - * - * @psalm-template RealInstanceType of object - * @psalm-param class-string|string $originalClassName - * @psalm-return MockObject&RealInstanceType + * @param list $dependencies */ - protected function getMockFromWsdl(string $wsdlFile, string $originalClassName = '', string $mockClassName = '', array $methods = [], bool $callOriginalConstructor = \true, array $options = []) : \PHPUnit\Framework\MockObject\MockObject + public function setDependencies(array $dependencies) : void { - $this->recordDoubledType(\SoapClient::class); - if ($originalClassName === '') { - $fileName = \pathinfo(\basename(\parse_url($wsdlFile, \PHP_URL_PATH)), \PATHINFO_FILENAME); - $originalClassName = \preg_replace('/\\W/', '', $fileName); - } - if (!\class_exists($originalClassName)) { - eval($this->getMockObjectGenerator()->generateClassFromWsdl($wsdlFile, $originalClassName, $methods, $options)); + $this->dependencies = $dependencies; + foreach ($this->tests as $test) { + if (!$test instanceof \PHPUnit\Framework\TestCase) { + // @codeCoverageIgnoreStart + continue; + // @codeCoverageIgnoreStart + } + $test->setDependencies($dependencies); } - $mockObject = $this->getMockObjectGenerator()->getMock($originalClassName, $methods, ['', $options], $mockClassName, $callOriginalConstructor, \false, \false); - $this->registerMockObject($mockObject); - return $mockObject; - } - /** - * Returns a mock object for the specified trait with all abstract methods - * of the trait mocked. Concrete methods to mock can be specified with the - * `$mockedMethods` parameter. - * - * @psalm-param trait-string $traitName - */ - protected function getMockForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = [], bool $cloneArguments = \false) : \PHPUnit\Framework\MockObject\MockObject - { - $this->recordDoubledType($traitName); - $mockObject = $this->getMockObjectGenerator()->getMockForTrait($traitName, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); - $this->registerMockObject($mockObject); - return $mockObject; } /** - * Returns an object for the specified trait. - * - * @psalm-param trait-string $traitName + * @return list */ - protected function getObjectForTrait(string $traitName, array $arguments = [], string $traitClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true) : object + public function provides() : array { - $this->recordDoubledType($traitName); - return $this->getMockObjectGenerator()->getObjectForTrait($traitName, $traitClassName, $callAutoload, $callOriginalConstructor, $arguments); + if ($this->providedTests === null) { + $this->providedTests = [new \PHPUnit\Framework\ExecutionOrderDependency($this->getName())]; + } + return $this->providedTests; } /** - * @throws \Prophecy\Exception\Doubler\ClassNotFoundException - * @throws \Prophecy\Exception\Doubler\DoubleException - * @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException - * - * @psalm-param class-string|null $classOrInterface + * @return list */ - protected function prophesize(?string $classOrInterface = null) : \Prophecy\Prophecy\ObjectProphecy + public function requires() : array { - $this->addWarning('PHPUnit\\Framework\\TestCase::prophesize() is deprecated and will be removed in PHPUnit 10. Please use the trait provided by phpspec/prophecy-phpunit.'); - if (\is_string($classOrInterface)) { - $this->recordDoubledType($classOrInterface); - } - return $this->getProphet()->prophesize($classOrInterface); + // A DataProviderTestSuite does not have to traverse its child tests + // as these are inherited and cannot reference dataProvider rows directly + return $this->dependencies; } /** - * Creates a default TestResult object. + * Returns the size of the each test created using the data provider(s). * - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - protected function createResult() : \PHPUnit\Framework\TestResult + public function getSize() : int { - return new \PHPUnit\Framework\TestResult(); + [$className, $methodName] = explode('::', $this->getName()); + return TestUtil::getSize($className, $methodName); } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface IncompleteTest extends Throwable +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function get_class; +use function sprintf; +use function trim; +use PHPUnit\Framework\Error\Error; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestFailure +{ /** - * Performs assertions shared by all tests of a test case. - * - * This method is called between setUp() and test. + * @var null|Test */ - protected function assertPreConditions() : void - { - } + private $failedTest; /** - * Performs assertions shared by all tests of a test case. - * - * This method is called between test and tearDown(). + * @var Throwable */ - protected function assertPostConditions() : void - { - } + private $thrownException; /** - * This method is called when a test method did not execute successfully. - * - * @throws Throwable + * @var string */ - protected function onNotSuccessfulTest(\Throwable $t) : void - { - throw $t; - } - protected function recordDoubledType(string $originalClassName) : void - { - $this->doubledTypes[] = $originalClassName; - } + private $testName; /** - * @throws Throwable + * Returns a description for an exception. */ - private function verifyMockObjects() : void + public static function exceptionToString(Throwable $e) : string { - foreach ($this->mockObjects as $mockObject) { - if ($mockObject->__phpunit_hasMatchers()) { - $this->numAssertions++; + if ($e instanceof \PHPUnit\Framework\SelfDescribing) { + $buffer = $e->toString(); + if ($e instanceof \PHPUnit\Framework\ExpectationFailedException && $e->getComparisonFailure()) { + $buffer .= $e->getComparisonFailure()->getDiff(); } - $mockObject->__phpunit_verify($this->shouldInvocationMockerBeReset($mockObject)); - } - if ($this->prophet !== null) { - try { - $this->prophet->checkPredictions(); - } finally { - foreach ($this->prophet->getProphecies() as $objectProphecy) { - foreach ($objectProphecy->getMethodProphecies() as $methodProphecies) { - foreach ($methodProphecies as $methodProphecy) { - /* @var MethodProphecy $methodProphecy */ - $this->numAssertions += \count($methodProphecy->getCheckedPredictions()); - } - } - } + if ($e instanceof \PHPUnit\Framework\PHPTAssertionFailedError) { + $buffer .= $e->getDiff(); + } + if (!empty($buffer)) { + $buffer = trim($buffer) . "\n"; } + return $buffer; } - } - /** - * @throws Warning - * @throws SkippedTestError - * @throws SyntheticSkippedError - */ - private function checkRequirements() : void - { - if (!$this->name || !\method_exists($this, $this->name)) { - return; + if ($e instanceof Error) { + return $e->getMessage() . "\n"; } - $missingRequirements = \PHPUnit\Util\Test::getMissingRequirements(static::class, $this->name); - if (!empty($missingRequirements)) { - $this->markTestSkipped(\implode(\PHP_EOL, $missingRequirements)); + if ($e instanceof \PHPUnit\Framework\ExceptionWrapper) { + return $e->getClassName() . ': ' . $e->getMessage() . "\n"; } + return get_class($e) . ': ' . $e->getMessage() . "\n"; } - private function handleDependencies() : bool + /** + * Constructs a TestFailure with the given test and exception. + */ + public function __construct(\PHPUnit\Framework\Test $failedTest, Throwable $t) { - if ([] === $this->dependencies || $this->inIsolation) { - return \true; - } - $passed = $this->result->passed(); - $passedKeys = \array_keys($passed); - $numKeys = \count($passedKeys); - for ($i = 0; $i < $numKeys; $i++) { - $pos = \strpos($passedKeys[$i], ' with data set'); - if ($pos !== \false) { - $passedKeys[$i] = \substr($passedKeys[$i], 0, $pos); - } + if ($failedTest instanceof \PHPUnit\Framework\SelfDescribing) { + $this->testName = $failedTest->toString(); + } else { + $this->testName = get_class($failedTest); } - $passedKeys = \array_flip(\array_unique($passedKeys)); - foreach ($this->dependencies as $dependency) { - if (!$dependency->isValid()) { - $this->markSkippedForNotSpecifyingDependency(); - return \false; - } - if ($dependency->targetIsClass()) { - $dependencyClassName = $dependency->getTargetClassName(); - if (\array_search($dependencyClassName, $this->result->passedClasses(), \true) === \false) { - $this->markSkippedForMissingDependency($dependency); - return \false; - } - continue; - } - $dependencyTarget = $dependency->getTarget(); - if (!isset($passedKeys[$dependencyTarget])) { - if (!$this->isCallableTestMethod($dependencyTarget)) { - $this->markWarningForUncallableDependency($dependency); - } else { - $this->markSkippedForMissingDependency($dependency); - } - return \false; - } - if (isset($passed[$dependencyTarget])) { - if ($passed[$dependencyTarget]['size'] != \PHPUnit\Util\Test::UNKNOWN && $this->getSize() != \PHPUnit\Util\Test::UNKNOWN && $passed[$dependencyTarget]['size'] > $this->getSize()) { - $this->result->addError($this, new \PHPUnit\Framework\SkippedTestError('This test depends on a test that is larger than itself.'), 0); - return \false; - } - if ($dependency->useDeepClone()) { - $deepCopy = new \PHPUnit\DeepCopy\DeepCopy(); - $deepCopy->skipUncloneable(\false); - $this->dependencyInput[$dependencyTarget] = $deepCopy->copy($passed[$dependencyTarget]['result']); - } elseif ($dependency->useShallowClone()) { - $this->dependencyInput[$dependencyTarget] = clone $passed[$dependencyTarget]['result']; - } else { - $this->dependencyInput[$dependencyTarget] = $passed[$dependencyTarget]['result']; - } - } else { - $this->dependencyInput[$dependencyTarget] = null; - } + if (!$failedTest instanceof \PHPUnit\Framework\TestCase || !$failedTest->isInIsolation()) { + $this->failedTest = $failedTest; } - return \true; - } - private function markSkippedForNotSpecifyingDependency() : void - { - $this->status = \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED; - $this->result->startTest($this); - $this->result->addError($this, new \PHPUnit\Framework\SkippedTestError(\sprintf('This method has an invalid @depends annotation.')), 0); - $this->result->endTest($this, 0); - } - private function markSkippedForMissingDependency(\PHPUnit\Framework\ExecutionOrderDependency $dependency) : void - { - $this->status = \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED; - $this->result->startTest($this); - $this->result->addError($this, new \PHPUnit\Framework\SkippedTestError(\sprintf('This test depends on "%s" to pass.', $dependency->getTarget())), 0); - $this->result->endTest($this, 0); - } - private function markWarningForUncallableDependency(\PHPUnit\Framework\ExecutionOrderDependency $dependency) : void - { - $this->status = \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING; - $this->result->startTest($this); - $this->result->addWarning($this, new \PHPUnit\Framework\Warning(\sprintf('This test depends on "%s" which does not exist.', $dependency->getTarget())), 0); - $this->result->endTest($this, 0); + $this->thrownException = $t; } /** - * Get the mock object generator, creating it if it doesn't exist. + * Returns a short description of the failure. */ - private function getMockObjectGenerator() : \PHPUnit\Framework\MockObject\Generator + public function toString() : string { - if ($this->mockObjectGenerator === null) { - $this->mockObjectGenerator = new \PHPUnit\Framework\MockObject\Generator(); - } - return $this->mockObjectGenerator; + return sprintf('%s: %s', $this->testName, $this->thrownException->getMessage()); } - private function startOutputBuffering() : void + /** + * Returns a description for the thrown exception. + */ + public function getExceptionAsString() : string { - \ob_start(); - $this->outputBufferingActive = \true; - $this->outputBufferingLevel = \ob_get_level(); + return self::exceptionToString($this->thrownException); } /** - * @throws RiskyTestError + * Returns the name of the failing test (including data set, if any). */ - private function stopOutputBuffering() : void + public function getTestName() : string { - if (\ob_get_level() !== $this->outputBufferingLevel) { - while (\ob_get_level() >= $this->outputBufferingLevel) { - \ob_end_clean(); - } - throw new \PHPUnit\Framework\RiskyTestError('Test code or tested code did not (only) close its own output buffers'); - } - $this->output = \ob_get_contents(); - if ($this->outputCallback !== \false) { - $this->output = (string) \call_user_func($this->outputCallback, $this->output); - } - \ob_end_clean(); - $this->outputBufferingActive = \false; - $this->outputBufferingLevel = \ob_get_level(); + return $this->testName; } - private function snapshotGlobalState() : void + /** + * Returns the failing test. + * + * Note: The test object is not set when the test is executed in process + * isolation. + * + * @see Exception + */ + public function failedTest() : ?\PHPUnit\Framework\Test { - if ($this->runTestInSeparateProcess || $this->inIsolation || !$this->backupGlobals && !$this->backupStaticAttributes) { - return; - } - $this->snapshot = $this->createGlobalStateSnapshot($this->backupGlobals === \true); + return $this->failedTest; } /** - * @throws RiskyTestError - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * Gets the thrown exception. */ - private function restoreGlobalState() : void + public function thrownException() : Throwable { - if (!$this->snapshot instanceof \PHPUnit\SebastianBergmann\GlobalState\Snapshot) { - return; - } - if ($this->beStrictAboutChangesToGlobalState) { - try { - $this->compareGlobalStateSnapshots($this->snapshot, $this->createGlobalStateSnapshot($this->backupGlobals === \true)); - } catch (\PHPUnit\Framework\RiskyTestError $rte) { - // Intentionally left empty - } - } - $restorer = new \PHPUnit\SebastianBergmann\GlobalState\Restorer(); - if ($this->backupGlobals) { - $restorer->restoreGlobalVariables($this->snapshot); - } - if ($this->backupStaticAttributes) { - $restorer->restoreStaticAttributes($this->snapshot); - } - $this->snapshot = null; - if (isset($rte)) { - throw $rte; - } + return $this->thrownException; } - private function createGlobalStateSnapshot(bool $backupGlobals) : \PHPUnit\SebastianBergmann\GlobalState\Snapshot + /** + * Returns the exception's message. + */ + public function exceptionMessage() : string { - $excludeList = new \PHPUnit\SebastianBergmann\GlobalState\ExcludeList(); - foreach ($this->backupGlobalsExcludeList as $globalVariable) { - $excludeList->addGlobalVariable($globalVariable); - } - if (!empty($this->backupGlobalsBlacklist)) { - $this->addWarning('PHPUnit\\Framework\\TestCase::$backupGlobalsBlacklist is deprecated and will be removed in PHPUnit 10. Please use PHPUnit\\Framework\\TestCase::$backupGlobalsExcludeList instead.'); - foreach ($this->backupGlobalsBlacklist as $globalVariable) { - $excludeList->addGlobalVariable($globalVariable); - } - } - if (!\defined('PHPUNIT_TESTSUITE')) { - $excludeList->addClassNamePrefix('PHPUnit'); - $excludeList->addClassNamePrefix('PHPUnit\\SebastianBergmann\\CodeCoverage'); - $excludeList->addClassNamePrefix('PHPUnit\\SebastianBergmann\\FileIterator'); - $excludeList->addClassNamePrefix('PHPUnit\\SebastianBergmann\\Invoker'); - $excludeList->addClassNamePrefix('PHPUnit\\SebastianBergmann\\Template'); - $excludeList->addClassNamePrefix('PHPUnit\\SebastianBergmann\\Timer'); - $excludeList->addClassNamePrefix('Symfony'); - $excludeList->addClassNamePrefix('PHPUnit\\Doctrine\\Instantiator'); - $excludeList->addClassNamePrefix('Prophecy'); - $excludeList->addStaticAttribute(\PHPUnit\SebastianBergmann\Comparator\Factory::class, 'instance'); - foreach ($this->backupStaticAttributesExcludeList as $class => $attributes) { - foreach ($attributes as $attribute) { - $excludeList->addStaticAttribute($class, $attribute); - } - } - if (!empty($this->backupStaticAttributesBlacklist)) { - $this->addWarning('PHPUnit\\Framework\\TestCase::$backupStaticAttributesBlacklist is deprecated and will be removed in PHPUnit 10. Please use PHPUnit\\Framework\\TestCase::$backupStaticAttributesExcludeList instead.'); - foreach ($this->backupStaticAttributesBlacklist as $class => $attributes) { - foreach ($attributes as $attribute) { - $excludeList->addStaticAttribute($class, $attribute); - } - } - } - } - return new \PHPUnit\SebastianBergmann\GlobalState\Snapshot($excludeList, $backupGlobals, (bool) $this->backupStaticAttributes, \false, \false, \false, \false, \false, \false, \false); + return $this->thrownException()->getMessage(); } /** - * @throws RiskyTestError - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * Returns true if the thrown exception + * is of type AssertionFailedError. */ - private function compareGlobalStateSnapshots(\PHPUnit\SebastianBergmann\GlobalState\Snapshot $before, \PHPUnit\SebastianBergmann\GlobalState\Snapshot $after) : void + public function isFailure() : bool { - $backupGlobals = $this->backupGlobals === null || $this->backupGlobals; - if ($backupGlobals) { - $this->compareGlobalStateSnapshotPart($before->globalVariables(), $after->globalVariables(), "--- Global variables before the test\n+++ Global variables after the test\n"); - $this->compareGlobalStateSnapshotPart($before->superGlobalVariables(), $after->superGlobalVariables(), "--- Super-global variables before the test\n+++ Super-global variables after the test\n"); - } - if ($this->backupStaticAttributes) { - $this->compareGlobalStateSnapshotPart($before->staticAttributes(), $after->staticAttributes(), "--- Static attributes before the test\n+++ Static attributes after the test\n"); - } + return $this->thrownException() instanceof \PHPUnit\Framework\AssertionFailedError; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class SkippedTestCase extends \PHPUnit\Framework\TestCase +{ /** - * @throws RiskyTestError + * @var bool */ - private function compareGlobalStateSnapshotPart(array $before, array $after, string $header) : void + protected $backupGlobals = \false; + /** + * @var bool + */ + protected $backupStaticAttributes = \false; + /** + * @var bool + */ + protected $runTestInSeparateProcess = \false; + /** + * @var string + */ + private $message; + public function __construct(string $className, string $methodName, string $message = '') { - if ($before != $after) { - $differ = new \PHPUnit\SebastianBergmann\Diff\Differ($header); - $exporter = new \PHPUnit\SebastianBergmann\Exporter\Exporter(); - $diff = $differ->diff($exporter->export($before), $exporter->export($after)); - throw new \PHPUnit\Framework\RiskyTestError($diff); - } + parent::__construct($className . '::' . $methodName); + $this->message = $message; } - private function getProphet() : \Prophecy\Prophet + public function getMessage() : string { - if ($this->prophet === null) { - $this->prophet = new \Prophecy\Prophet(); - } - return $this->prophet; + return $this->message; } /** - * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException + * Returns a string representation of the test case. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - private function shouldInvocationMockerBeReset(\PHPUnit\Framework\MockObject\MockObject $mock) : bool + public function toString() : string { - $enumerator = new \PHPUnit\SebastianBergmann\ObjectEnumerator\Enumerator(); - foreach ($enumerator->enumerate($this->dependencyInput) as $object) { - if ($mock === $object) { - return \false; - } - } - if (!\is_array($this->testResult) && !\is_object($this->testResult)) { - return \true; - } - return !\in_array($mock, $enumerator->enumerate($this->testResult), \true); + return $this->getName(); } /** - * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException - * @throws \SebastianBergmann\ObjectReflector\InvalidArgumentException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception */ - private function registerMockObjectsFromTestArguments(array $testArguments, array &$visited = []) : void - { - if ($this->registerMockObjectsFromTestArgumentsRecursively) { - foreach ((new \PHPUnit\SebastianBergmann\ObjectEnumerator\Enumerator())->enumerate($testArguments) as $object) { - if ($object instanceof \PHPUnit\Framework\MockObject\MockObject) { - $this->registerMockObject($object); - } - } - } else { - foreach ($testArguments as $testArgument) { - if ($testArgument instanceof \PHPUnit\Framework\MockObject\MockObject) { - if (\PHPUnit\Util\Type::isCloneable($testArgument)) { - $testArgument = clone $testArgument; - } - $this->registerMockObject($testArgument); - } elseif (\is_array($testArgument) && !\in_array($testArgument, $visited, \true)) { - $visited[] = $testArgument; - $this->registerMockObjectsFromTestArguments($testArgument, $visited); - } - } - } - } - private function setDoesNotPerformAssertionsFromAnnotation() : void - { - $annotations = $this->getAnnotations(); - if (isset($annotations['method']['doesNotPerformAssertions'])) { - $this->doesNotPerformAssertions = \true; - } - } - private function unregisterCustomComparators() : void + protected function runTest() : void { - $factory = \PHPUnit\SebastianBergmann\Comparator\Factory::getInstance(); - foreach ($this->customComparators as $comparator) { - $factory->unregister($comparator); - } - $this->customComparators = []; + $this->markTestSkipped($this->message); } - private function cleanupIniSettings() : void +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class PHPTAssertionFailedError extends \PHPUnit\Framework\SyntheticError +{ + /** + * @var string + */ + private $diff; + public function __construct(string $message, int $code, string $file, int $line, array $trace, string $diff) { - foreach ($this->iniSettings as $varName => $oldValue) { - \ini_set($varName, $oldValue); - } - $this->iniSettings = []; + parent::__construct($message, $code, $file, $line, $trace); + $this->diff = $diff; } - private function cleanupLocaleSettings() : void + public function getDiff() : string { - foreach ($this->locale as $category => $locale) { - \setlocale($category, $locale); - } - $this->locale = []; + return $this->diff; } - /** - * @throws Exception - */ - private function checkExceptionExpectations(\Throwable $throwable) : bool +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const PHP_EOL; +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ComparisonMethodDoesNotAcceptParameterTypeException extends \PHPUnit\Framework\Exception +{ + public function __construct(string $className, string $methodName, string $type) { - $result = \false; - if ($this->expectedException !== null || $this->expectedExceptionCode !== null || $this->expectedExceptionMessage !== null || $this->expectedExceptionMessageRegExp !== null) { - $result = \true; - } - if ($throwable instanceof \PHPUnit\Framework\Exception) { - $result = \false; - } - if (\is_string($this->expectedException)) { - try { - $reflector = new \ReflectionClass($this->expectedException); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - if ($this->expectedException === 'PHPUnit\\Framework\\Exception' || $this->expectedException === '\\PHPUnit\\Framework\\Exception' || $reflector->isSubclassOf(\PHPUnit\Framework\Exception::class)) { - $result = \true; - } - } - return $result; + parent::__construct(sprintf('%s is not an accepted argument type for comparison method %s::%s().', $type, $className, $methodName), 0, null); } - private function runInSeparateProcess() : bool + public function __toString() : string { - return ($this->runTestInSeparateProcess || $this->runClassInSeparateProcess) && !$this->inIsolation && !$this instanceof \PHPUnit\Runner\PhptTestCase; + return $this->getMessage() . \PHP_EOL; } - private function isCallableTestMethod(string $dependency) : bool +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class SyntheticSkippedError extends \PHPUnit\Framework\SyntheticError implements \PHPUnit\Framework\SkippedTest +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const PHP_EOL; +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ComparisonMethodDoesNotExistException extends \PHPUnit\Framework\Exception +{ + public function __construct(string $className, string $methodName) { - [$className, $methodName] = \explode('::', $dependency); - if (!\class_exists($className)) { - return \false; - } - try { - $class = new \ReflectionClass($className); - } catch (\ReflectionException $e) { - return \false; - } - if (!$class->isSubclassOf(__CLASS__)) { - return \false; - } - if (!$class->hasMethod($methodName)) { - return \false; - } - try { - $method = $class->getMethod($methodName); - } catch (\ReflectionException $e) { - return \false; - } - return \PHPUnit\Util\Test::isTestMethod($method); + parent::__construct(sprintf('Comparison method %s::%s() does not exist.', $className, $methodName), 0, null); } - /** - * @psalm-template RealInstanceType of object - * @psalm-param class-string $originalClassName - * @psalm-return MockObject&RealInstanceType - */ - private function createMockObject(string $originalClassName) : \PHPUnit\Framework\MockObject\MockObject + public function __toString() : string { - return $this->getMockBuilder($originalClassName)->disableOriginalConstructor()->disableOriginalClone()->disableArgumentCloning()->disallowMockingUnknownTypes()->getMock(); + return $this->getMessage() . \PHP_EOL; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class IncompleteTestError extends \PHPUnit\Framework\AssertionFailedError implements \PHPUnit\Framework\IncompleteTest { } * - * Unlike PHPUnit\Framework_\Exception, the complete stack of previous Exceptions - * is processed. + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +class RiskyTestError extends \PHPUnit\Framework\AssertionFailedError +{ +} + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class ExceptionWrapper extends \PHPUnit\Framework\Exception +final class Error extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\SelfDescribing { /** - * @var string - */ - protected $className; - /** - * @var null|ExceptionWrapper - */ - protected $previous; - public function __construct(\Throwable $t) - { - // PDOException::getCode() is a string. - // @see https://php.net/manual/en/class.pdoexception.php#95812 - parent::__construct($t->getMessage(), (int) $t->getCode()); - $this->setOriginalException($t); - } - public function __toString() : string - { - $string = \PHPUnit\Framework\TestFailure::exceptionToString($this); - if ($trace = \PHPUnit\Util\Filter::getFilteredStacktrace($this)) { - $string .= "\n" . $trace; - } - if ($this->previous) { - $string .= "\nCaused by\n" . $this->previous; - } - return $string; - } - public function getClassName() : string - { - return $this->className; - } - public function getPreviousWrapped() : ?self - { - return $this->previous; - } - public function setClassName(string $className) : void - { - $this->className = $className; - } - public function setOriginalException(\Throwable $t) : void - { - $this->originalException($t); - $this->className = \get_class($t); - $this->file = $t->getFile(); - $this->line = $t->getLine(); - $this->serializableTrace = $t->getTrace(); - foreach (\array_keys($this->serializableTrace) as $key) { - unset($this->serializableTrace[$key]['args']); - } - if ($t->getPrevious()) { - $this->previous = new self($t->getPrevious()); - } - } - public function getOriginalException() : ?\Throwable - { - return $this->originalException(); - } - /** - * Method to contain static originalException to exclude it from stacktrace to prevent the stacktrace contents, - * which can be quite big, from being garbage-collected, thus blocking memory until shutdown. - * - * Approach works both for var_dump() and var_export() and print_r(). + * Wrapper for getMessage() which is declared as final. */ - private function originalException(\Throwable $exceptionToStore = null) : ?\Throwable + public function toString() : string { - static $originalExceptions; - $instanceId = \spl_object_hash($this); - if ($exceptionToStore) { - $originalExceptions[$instanceId] = $exceptionToStore; - } - return $originalExceptions[$instanceId] ?? null; + return $this->getMessage(); } } getMessage(); } } matches($other)) { - $success = \true; - } - if ($returnResult) { - return $success; - } - if (!$success) { - $this->fail($other, $description); - } - return null; - } - /** - * Counts the number of constraint elements. - */ - public function count() : int - { - return 1; - } - protected function exporter() : \PHPUnit\SebastianBergmann\Exporter\Exporter - { - if ($this->exporter === null) { - $this->exporter = new \PHPUnit\SebastianBergmann\Exporter\Exporter(); - } - return $this->exporter; - } - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * This method can be overridden to implement the evaluation algorithm. - * - * @param mixed $other value or object to evaluate - * @codeCoverageIgnore - */ - protected function matches($other) : bool - { - return \false; - } - /** - * Throws an exception for the given compared value and test description. - * - * @param mixed $other evaluated value or object - * @param string $description Additional information about the test - * @param ComparisonFailure $comparisonFailure - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-return never-return - */ - protected function fail($other, $description, \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure $comparisonFailure = null) : void - { - $failureDescription = \sprintf('Failed asserting that %s.', $this->failureDescription($other)); - $additionalFailureDescription = $this->additionalFailureDescription($other); - if ($additionalFailureDescription) { - $failureDescription .= "\n" . $additionalFailureDescription; - } - if (!empty($description)) { - $failureDescription = $description . "\n" . $failureDescription; - } - throw new \PHPUnit\Framework\ExpectationFailedException($failureDescription, $comparisonFailure); - } - /** - * Return additional failure description where needed. - * - * The function can be overridden to provide additional failure - * information like a diff - * - * @param mixed $other evaluated value or object - */ - protected function additionalFailureDescription($other) : string - { - return ''; - } - /** - * Returns the description of the failure. - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * To provide additional failure information additionalFailureDescription - * can be used. - * - * @param mixed $other evaluated value or object - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - protected function failureDescription($other) : string + public function __construct(string $className, string $methodName) { - return $this->exporter()->export($other) . ' ' . $this->toString(); + parent::__construct(sprintf('Comparison method %s::%s() does not declare exactly one parameter.', $className, $methodName), 0, null); } - /** - * Returns a custom string representation of the constraint object when it - * appears in context of an $operator expression. - * - * The purpose of this method is to provide meaningful descriptive string - * in context of operators such as LogicalNot. Native PHPUnit constraints - * are supported out of the box by LogicalNot, but externally developed - * ones had no way to provide correct strings in this context. - * - * The method shall return empty string, when it does not handle - * customization by itself. - * - * @param Operator $operator the $operator of the expression - * @param mixed $role role of $this constraint in the $operator expression - */ - protected function toStringInContext(\PHPUnit\Framework\Constraint\Operator $operator, $role) : string + public function __toString() : string { - return ''; + return $this->getMessage() . \PHP_EOL; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use Exception; +use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; +/** + * Exception for expectations which failed their check. + * + * The exception contains the error message and optionally a + * SebastianBergmann\Comparator\ComparisonFailure which is used to + * generate diff output of the failed expectations. + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ExpectationFailedException extends \PHPUnit\Framework\AssertionFailedError +{ /** - * Returns the description of the failure when this constraint appears in - * context of an $operator expression. - * - * The purpose of this method is to provide meaningful failue description - * in context of operators such as LogicalNot. Native PHPUnit constraints - * are supported out of the box by LogicalNot, but externally developed - * ones had no way to provide correct messages in this context. - * - * The method shall return empty string, when it does not handle - * customization by itself. - * - * @param Operator $operator the $operator of the expression - * @param mixed $role role of $this constraint in the $operator expression - * @param mixed $other evaluated value or object + * @var ComparisonFailure */ - protected function failureDescriptionInContext(\PHPUnit\Framework\Constraint\Operator $operator, $role, $other) : string + protected $comparisonFailure; + public function __construct(string $message, ComparisonFailure $comparisonFailure = null, Exception $previous = null) { - $string = $this->toStringInContext($operator, $role); - if ($string === '') { - return ''; - } - return $this->exporter()->export($other) . ' ' . $string; + $this->comparisonFailure = $comparisonFailure; + parent::__construct($message, 0, $previous); } - /** - * Reduces the sub-expression starting at $this by skipping degenerate - * sub-expression and returns first descendant constraint that starts - * a non-reducible sub-expression. - * - * Returns $this for terminal constraints and for operators that start - * non-reducible sub-expression, or the nearest descendant of $this that - * starts a non-reducible sub-expression. - * - * A constraint expression may be modelled as a tree with non-terminal - * nodes (operators) and terminal nodes. For example: - * - * LogicalOr (operator, non-terminal) - * + LogicalAnd (operator, non-terminal) - * | + IsType('int') (terminal) - * | + GreaterThan(10) (terminal) - * + LogicalNot (operator, non-terminal) - * + IsType('array') (terminal) - * - * A degenerate sub-expression is a part of the tree, that effectively does - * not contribute to the evaluation of the expression it appears in. An example - * of degenerate sub-expression is a BinaryOperator constructed with single - * operand or nested BinaryOperators, each with single operand. An - * expression involving a degenerate sub-expression is equivalent to a - * reduced expression with the degenerate sub-expression removed, for example - * - * LogicalAnd (operator) - * + LogicalOr (degenerate operator) - * | + LogicalAnd (degenerate operator) - * | + IsType('int') (terminal) - * + GreaterThan(10) (terminal) - * - * is equivalent to - * - * LogicalAnd (operator) - * + IsType('int') (terminal) - * + GreaterThan(10) (terminal) - * - * because the subexpression - * - * + LogicalOr - * + LogicalAnd - * + - - * - * is degenerate. Calling reduce() on the LogicalOr object above, as well - * as on LogicalAnd, shall return the IsType('int') instance. - * - * Other specific reductions can be implemented, for example cascade of - * LogicalNot operators - * - * + LogicalNot - * + LogicalNot - * +LogicalNot - * + IsTrue - * - * can be reduced to - * - * LogicalNot - * + IsTrue - */ - protected function reduce() : self + public function getComparisonFailure() : ?ComparisonFailure { - return $this; + return $this->comparisonFailure; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MissingCoversAnnotationException extends \PHPUnit\Framework\RiskyTestError +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; +use const PHP_EOL; /** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class GreaterThan extends \PHPUnit\Framework\Constraint\Constraint +final class ActualValueIsNotAnObjectException extends \PHPUnit\Framework\Exception { - /** - * @var float|int - */ - private $value; - /** - * @param float|int $value - */ - public function __construct($value) - { - $this->value = $value; - } - /** - * Returns a string representation of the constraint. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function toString() : string + public function __construct() { - return 'is greater than ' . $this->exporter()->export($this->value); + parent::__construct('Actual value is not an object', 0, null); } - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other) : bool + public function __toString() : string { - return $this->value < $other; + return $this->getMessage() . \PHP_EOL; } } value = $value; - } + protected $syntheticLine = 0; /** - * Returns a string representation of the constraint. + * The synthetic trace. * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @var array */ - public function toString() : string + protected $syntheticTrace = []; + public function __construct(string $message, int $code, string $file, int $line, array $trace) { - return 'is less than ' . $this->exporter()->export($this->value); + parent::__construct($message, $code); + $this->syntheticFile = $file; + $this->syntheticLine = $line; + $this->syntheticTrace = $trace; } - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other) : bool + public function getSyntheticFile() : string { - return $this->value > $other; + return $this->syntheticFile; + } + public function getSyntheticLine() : int + { + return $this->syntheticLine; + } + public function getSyntheticTrace() : array + { + return $this->syntheticTrace; } } toString()); - } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function array_keys; +use function get_object_vars; +use PHPUnit\Util\Filter; +use RuntimeException; +use Throwable; +/** + * Base class for all PHPUnit Framework exceptions. + * + * Ensures that exceptions thrown during a test run do not leave stray + * references behind. + * + * Every Exception contains a stack trace. Each stack frame contains the 'args' + * of the called function. The function arguments can contain references to + * instantiated objects. The references prevent the objects from being + * destructed (until test results are eventually printed), so memory cannot be + * freed up. + * + * With enabled process isolation, test results are serialized in the child + * process and unserialized in the parent process. The stack trace of Exceptions + * may contain objects that cannot be serialized or unserialized (e.g., PDO + * connections). Unserializing user-space objects from the child process into + * the parent would break the intended encapsulation of process isolation. + * + * @see http://fabien.potencier.org/article/9/php-serialization-stack-traces-and-exceptions + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +class Exception extends RuntimeException implements \PHPUnit\Exception { /** - * @var int - */ - private $expectedCount; - public function __construct(int $expected) - { - $this->expectedCount = $expected; - } - public function toString() : string - { - return \sprintf('count matches %d', $this->expectedCount); - } - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @throws Exception + * @var array */ - protected function matches($other) : bool + protected $serializableTrace; + public function __construct($message = '', $code = 0, Throwable $previous = null) { - return $this->expectedCount === $this->getCountOf($other); + parent::__construct($message, $code, $previous); + $this->serializableTrace = $this->getTrace(); + foreach (array_keys($this->serializableTrace) as $key) { + unset($this->serializableTrace[$key]['args']); + } } - /** - * @throws Exception - */ - protected function getCountOf($other) : ?int + public function __toString() : string { - if ($other instanceof \Countable || \is_array($other)) { - return \count($other); - } - if ($other instanceof \EmptyIterator) { - return 0; - } - if ($other instanceof \Traversable) { - while ($other instanceof \IteratorAggregate) { - try { - $other = $other->getIterator(); - } catch (\Exception $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), $e->getCode(), $e); - } - } - $iterator = $other; - if ($iterator instanceof \Generator) { - return $this->getCountOfGenerator($iterator); - } - if (!$iterator instanceof \Iterator) { - return \iterator_count($iterator); - } - $key = $iterator->key(); - $count = \iterator_count($iterator); - // Manually rewind $iterator to previous key, since iterator_count - // moves pointer. - if ($key !== null) { - $iterator->rewind(); - while ($iterator->valid() && $key !== $iterator->key()) { - $iterator->next(); - } - } - return $count; + $string = \PHPUnit\Framework\TestFailure::exceptionToString($this); + if ($trace = Filter::getFilteredStacktrace($this)) { + $string .= "\n" . $trace; } - return null; + return $string; } - /** - * Returns the total number of iterations from a generator. - * This will fully exhaust the generator. - */ - protected function getCountOfGenerator(\Generator $generator) : int + public function __sleep() : array { - for ($count = 0; $generator->valid(); $generator->next()) { - $count++; - } - return $count; + return array_keys(get_object_vars($this)); } /** - * Returns the description of the failure. - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object + * Returns the serializable trace (without 'args'). */ - protected function failureDescription($other) : string + public function getSerializableTrace() : array { - return \sprintf('actual size %d matches expected size %d', (int) $this->getCountOf($other), $this->expectedCount); + return $this->serializableTrace; } } getCountOf($expected)); + return $this->getMessage(); } } className = $className; - } - /** - * Returns a string representation of the constraint. - */ - public function toString() : string - { - return \sprintf('is instance of %s "%s"', $this->getType(), $this->className); - } - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other) : bool - { - return $other instanceof $this->className; - } - /** - * Returns the description of the failure. - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - protected function failureDescription($other) : string + public function __construct(string $className, string $methodName) { - return \sprintf('%s is an instance of %s "%s"', $this->exporter()->shortenedExport($other), $this->getType(), $this->className); + parent::__construct(sprintf('Comparison method %s::%s() does not declare bool return type.', $className, $methodName), 0, null); } - private function getType() : string + public function __toString() : string { - try { - $reflection = new \ReflectionClass($this->className); - if ($reflection->isInterface()) { - return 'interface'; - } - } catch (\ReflectionException $e) { - } - return 'class'; + return $this->getMessage() . \PHP_EOL; } } - */ - private const KNOWN_TYPES = ['array' => \true, 'boolean' => \true, 'bool' => \true, 'double' => \true, 'float' => \true, 'integer' => \true, 'int' => \true, 'null' => \true, 'numeric' => \true, 'object' => \true, 'real' => \true, 'resource' => \true, 'resource (closed)' => \true, 'string' => \true, 'scalar' => \true, 'callable' => \true, 'iterable' => \true]; - /** - * @var string - */ - private $type; - /** - * @throws \PHPUnit\Framework\Exception - */ - public function __construct(string $type) + public function __construct(string $className, string $methodName) { - if (!isset(self::KNOWN_TYPES[$type])) { - throw new \PHPUnit\Framework\Exception(\sprintf('Type specified for PHPUnit\\Framework\\Constraint\\IsType <%s> ' . 'is not a valid type.', $type)); - } - $this->type = $type; + parent::__construct(sprintf('Parameter of comparison method %s::%s() does not have a declared type.', $className, $methodName), 0, null); } - /** - * Returns a string representation of the constraint. - */ - public function toString() : string + public function __toString() : string { - return \sprintf('is of type "%s"', $this->type); + return $this->getMessage() . \PHP_EOL; } - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other) : bool - { - switch ($this->type) { - case 'numeric': - return \is_numeric($other); - case 'integer': - case 'int': - return \is_int($other); - case 'double': - case 'float': - case 'real': - return \is_float($other); - case 'string': - return \is_string($other); - case 'boolean': - case 'bool': - return \is_bool($other); - case 'null': - return null === $other; - case 'array': - return \is_array($other); - case 'object': - return \is_object($other); - case 'resource': - $type = \gettype($other); - return $type === 'resource' || $type === 'resource (closed)'; - case 'resource (closed)': - return \gettype($other) === 'resource (closed)'; - case 'scalar': - return \is_scalar($other); - case 'callable': - return \is_callable($other); - case 'iterable': - return \is_iterable($other); - default: - return \false; - } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function debug_backtrace; +use function in_array; +use function lcfirst; +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvalidArgumentException extends \PHPUnit\Framework\Exception +{ + public static function create(int $argument, string $type) : self + { + $stack = debug_backtrace(); + return new self(sprintf('Argument #%d of %s::%s() must be %s %s', $argument, $stack[1]['class'], $stack[1]['function'], in_array(lcfirst($type)[0], ['a', 'e', 'i', 'o', 'u'], \true) ? 'an' : 'a', $type)); + } + private function __construct(string $message = '', int $code = 0, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); } } callback = $callback; + \PHPUnit\Framework\Assert::assertArrayHasKey(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertArrayNotHasKey')) { /** - * Returns a string representation of the constraint. + * Asserts that an array does not have a specified key. + * + * @param int|string $key + * @param array|ArrayAccess $array + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertArrayNotHasKey */ - public function toString() : string + function assertArrayNotHasKey($key, $array, string $message = '') : void { - return 'is accepted by specified callback'; + \PHPUnit\Framework\Assert::assertArrayNotHasKey(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertContains')) { /** - * Evaluates the constraint for parameter $value. Returns true if the - * constraint is met, false otherwise. + * Asserts that a haystack contains a needle. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContains */ - protected function matches($other) : bool + function assertContains($needle, iterable $haystack, string $message = '') : void { - return \call_user_func($this->callback, $other); + \PHPUnit\Framework\Assert::assertContains(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_string; -use function sprintf; -use function strpos; -use function trim; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -use PHPUnit\SebastianBergmann\Comparator\Factory as ComparatorFactory; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsEqual extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertContainsEquals')) { + function assertContainsEquals($needle, iterable $haystack, string $message = '') : void + { + \PHPUnit\Framework\Assert::assertContainsEquals(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\assertNotContains')) { /** - * @var mixed + * Asserts that a haystack does not contain a needle. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotContains */ - private $value; + function assertNotContains($needle, iterable $haystack, string $message = '') : void + { + \PHPUnit\Framework\Assert::assertNotContains(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\assertNotContainsEquals')) { + function assertNotContainsEquals($needle, iterable $haystack, string $message = '') : void + { + \PHPUnit\Framework\Assert::assertNotContainsEquals(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\assertContainsOnly')) { /** - * @var float + * Asserts that a haystack contains only values of a given type. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnly */ - private $delta; + function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '') : void + { + \PHPUnit\Framework\Assert::assertContainsOnly(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\assertContainsOnlyInstancesOf')) { /** - * @var bool + * Asserts that a haystack contains only instances of a given class name. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnlyInstancesOf */ - private $canonicalize; + function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = '') : void + { + \PHPUnit\Framework\Assert::assertContainsOnlyInstancesOf(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\assertNotContainsOnly')) { /** - * @var bool + * Asserts that a haystack does not contain only values of a given type. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotContainsOnly */ - private $ignoreCase; - public function __construct($value, float $delta = 0.0, bool $canonicalize = \false, bool $ignoreCase = \false) + function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = '') : void { - $this->value = $value; - $this->delta = $delta; - $this->canonicalize = $canonicalize; - $this->ignoreCase = $ignoreCase; + \PHPUnit\Framework\Assert::assertNotContainsOnly(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertCount')) { /** - * Evaluates the constraint for parameter $other. - * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. + * Asserts the number of elements of an array, Countable or Traversable. * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. + * @param Countable|iterable $haystack * * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * - * @return bool + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertCount */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + function assertCount(int $expectedCount, $haystack, string $message = '') : void { - // If $this->value and $other are identical, they are also equal. - // This is the most common path and will allow us to skip - // initialization of all the comparators. - if ($this->value === $other) { - return \true; - } - $comparatorFactory = \PHPUnit\SebastianBergmann\Comparator\Factory::getInstance(); - try { - $comparator = $comparatorFactory->getComparatorFor($this->value, $other); - $comparator->assertEquals($this->value, $other, $this->delta, $this->canonicalize, $this->ignoreCase); - } catch (\PHPUnit\SebastianBergmann\Comparator\ComparisonFailure $f) { - if ($returnResult) { - return \false; - } - throw new \PHPUnit\Framework\ExpectationFailedException(\trim($description . "\n" . $f->getMessage()), $f); - } - return \true; + \PHPUnit\Framework\Assert::assertCount(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertNotCount')) { /** - * Returns a string representation of the constraint. + * Asserts the number of elements of an array, Countable or Traversable. * + * @param Countable|iterable $haystack + * + * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotCount */ - public function toString() : string + function assertNotCount(int $expectedCount, $haystack, string $message = '') : void { - $delta = ''; - if (\is_string($this->value)) { - if (\strpos($this->value, "\n") !== \false) { - return 'is equal to '; - } - return \sprintf("is equal to '%s'", $this->value); - } - if ($this->delta != 0) { - $delta = \sprintf(' with delta <%F>', $this->delta); - } - return \sprintf('is equal to %s%s', $this->exporter()->export($this->value), $delta); + \PHPUnit\Framework\Assert::assertNotCount(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_string; -use function sprintf; -use function strpos; -use function trim; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -use PHPUnit\SebastianBergmann\Comparator\Factory as ComparatorFactory; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsEqualCanonicalizing extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertEquals')) { /** - * @var mixed + * Asserts that two variables are equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertEquals */ - private $value; - public function __construct($value) + function assertEquals($expected, $actual, string $message = '') : void { - $this->value = $value; + \PHPUnit\Framework\Assert::assertEquals(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertEqualsCanonicalizing')) { /** - * Evaluates the constraint for parameter $other. + * Asserts that two variables are equal (canonicalizing). * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @throws ExpectationFailedException + * @see Assert::assertEqualsCanonicalizing */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + function assertEqualsCanonicalizing($expected, $actual, string $message = '') : void { - // If $this->value and $other are identical, they are also equal. - // This is the most common path and will allow us to skip - // initialization of all the comparators. - if ($this->value === $other) { - return \true; - } - $comparatorFactory = \PHPUnit\SebastianBergmann\Comparator\Factory::getInstance(); - try { - $comparator = $comparatorFactory->getComparatorFor($this->value, $other); - $comparator->assertEquals($this->value, $other, 0.0, \true, \false); - } catch (\PHPUnit\SebastianBergmann\Comparator\ComparisonFailure $f) { - if ($returnResult) { - return \false; - } - throw new \PHPUnit\Framework\ExpectationFailedException(\trim($description . "\n" . $f->getMessage()), $f); - } - return \true; + \PHPUnit\Framework\Assert::assertEqualsCanonicalizing(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertEqualsIgnoringCase')) { /** - * Returns a string representation of the constraint. + * Asserts that two variables are equal (ignoring case). * + * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertEqualsIgnoringCase */ - public function toString() : string + function assertEqualsIgnoringCase($expected, $actual, string $message = '') : void { - if (\is_string($this->value)) { - if (\strpos($this->value, "\n") !== \false) { - return 'is equal to '; - } - return \sprintf("is equal to '%s'", $this->value); - } - return \sprintf('is equal to %s', $this->exporter()->export($this->value)); + \PHPUnit\Framework\Assert::assertEqualsIgnoringCase(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function sprintf; -use function trim; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -use PHPUnit\SebastianBergmann\Comparator\Factory as ComparatorFactory; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsEqualWithDelta extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertEqualsWithDelta')) { /** - * @var mixed + * Asserts that two variables are equal (with delta). + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertEqualsWithDelta */ - private $value; + function assertEqualsWithDelta($expected, $actual, float $delta, string $message = '') : void + { + \PHPUnit\Framework\Assert::assertEqualsWithDelta(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\assertNotEquals')) { /** - * @var float + * Asserts that two variables are not equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotEquals */ - private $delta; - public function __construct($value, float $delta) + function assertNotEquals($expected, $actual, string $message = '') : void { - $this->value = $value; - $this->delta = $delta; + \PHPUnit\Framework\Assert::assertNotEquals(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertNotEqualsCanonicalizing')) { /** - * Evaluates the constraint for parameter $other. + * Asserts that two variables are not equal (canonicalizing). * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotEqualsCanonicalizing + */ + function assertNotEqualsCanonicalizing($expected, $actual, string $message = '') : void + { + \PHPUnit\Framework\Assert::assertNotEqualsCanonicalizing(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\assertNotEqualsIgnoringCase')) { + /** + * Asserts that two variables are not equal (ignoring case). * * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotEqualsIgnoringCase */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + function assertNotEqualsIgnoringCase($expected, $actual, string $message = '') : void { - // If $this->value and $other are identical, they are also equal. - // This is the most common path and will allow us to skip - // initialization of all the comparators. - if ($this->value === $other) { - return \true; - } - $comparatorFactory = \PHPUnit\SebastianBergmann\Comparator\Factory::getInstance(); - try { - $comparator = $comparatorFactory->getComparatorFor($this->value, $other); - $comparator->assertEquals($this->value, $other, $this->delta); - } catch (\PHPUnit\SebastianBergmann\Comparator\ComparisonFailure $f) { - if ($returnResult) { - return \false; - } - throw new \PHPUnit\Framework\ExpectationFailedException(\trim($description . "\n" . $f->getMessage()), $f); - } - return \true; + \PHPUnit\Framework\Assert::assertNotEqualsIgnoringCase(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertNotEqualsWithDelta')) { /** - * Returns a string representation of the constraint. + * Asserts that two variables are not equal (with delta). * + * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotEqualsWithDelta */ - public function toString() : string + function assertNotEqualsWithDelta($expected, $actual, float $delta, string $message = '') : void { - return \sprintf('is equal to %s with delta <%F>>', $this->exporter()->export($this->value), $this->delta); + \PHPUnit\Framework\Assert::assertNotEqualsWithDelta(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_string; -use function sprintf; -use function strpos; -use function trim; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -use PHPUnit\SebastianBergmann\Comparator\Factory as ComparatorFactory; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsEqualIgnoringCase extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertObjectEquals')) { /** - * @var mixed + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertObjectEquals + */ + function assertObjectEquals(object $expected, object $actual, string $method = 'equals', string $message = '') : void + { + \PHPUnit\Framework\Assert::assertObjectEquals(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\assertEmpty')) { + /** + * Asserts that a variable is empty. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert empty $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertEmpty */ - private $value; - public function __construct($value) + function assertEmpty($actual, string $message = '') : void { - $this->value = $value; + \PHPUnit\Framework\Assert::assertEmpty(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertNotEmpty')) { /** - * Evaluates the constraint for parameter $other. + * Asserts that a variable is not empty. * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. + * @psalm-assert !empty $actual * - * @throws ExpectationFailedException + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotEmpty */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + function assertNotEmpty($actual, string $message = '') : void { - // If $this->value and $other are identical, they are also equal. - // This is the most common path and will allow us to skip - // initialization of all the comparators. - if ($this->value === $other) { - return \true; - } - $comparatorFactory = \PHPUnit\SebastianBergmann\Comparator\Factory::getInstance(); - try { - $comparator = $comparatorFactory->getComparatorFor($this->value, $other); - $comparator->assertEquals($this->value, $other, 0.0, \false, \true); - } catch (\PHPUnit\SebastianBergmann\Comparator\ComparisonFailure $f) { - if ($returnResult) { - return \false; - } - throw new \PHPUnit\Framework\ExpectationFailedException(\trim($description . "\n" . $f->getMessage()), $f); - } - return \true; + \PHPUnit\Framework\Assert::assertNotEmpty(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertGreaterThan')) { /** - * Returns a string representation of the constraint. + * Asserts that a value is greater than another value. * + * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertGreaterThan */ - public function toString() : string + function assertGreaterThan($expected, $actual, string $message = '') : void { - if (\is_string($this->value)) { - if (\strpos($this->value, "\n") !== \false) { - return 'is equal to '; - } - return \sprintf("is equal to '%s'", $this->value); - } - return \sprintf('is equal to %s', $this->exporter()->export($this->value)); + \PHPUnit\Framework\Assert::assertGreaterThan(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function json_decode; -use function sprintf; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Util\Json; -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class JsonMatches extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertGreaterThanOrEqual')) { /** - * @var string + * Asserts that a value is greater than or equal to another value. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertGreaterThanOrEqual */ - private $value; - public function __construct(string $value) + function assertGreaterThanOrEqual($expected, $actual, string $message = '') : void { - $this->value = $value; + \PHPUnit\Framework\Assert::assertGreaterThanOrEqual(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertLessThan')) { /** - * Returns a string representation of the object. + * Asserts that a value is smaller than another value. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertLessThan */ - public function toString() : string + function assertLessThan($expected, $actual, string $message = '') : void { - return \sprintf('matches JSON string "%s"', $this->value); + \PHPUnit\Framework\Assert::assertLessThan(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertLessThanOrEqual')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a value is smaller than or equal to another value. * - * This method can be overridden to implement the evaluation algorithm. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @param mixed $other value or object to evaluate + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertLessThanOrEqual */ - protected function matches($other) : bool + function assertLessThanOrEqual($expected, $actual, string $message = '') : void { - [$error, $recodedOther] = \PHPUnit\Util\Json::canonicalize($other); - if ($error) { - return \false; - } - [$error, $recodedValue] = \PHPUnit\Util\Json::canonicalize($this->value); - if ($error) { - return \false; - } - return $recodedOther == $recodedValue; + \PHPUnit\Framework\Assert::assertLessThanOrEqual(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertFileEquals')) { /** - * Throws an exception for the given compared value and test description. - * - * @param mixed $other evaluated value or object - * @param string $description Additional information about the test - * @param ComparisonFailure $comparisonFailure + * Asserts that the contents of one file is equal to the contents of another + * file. * * @throws ExpectationFailedException - * @throws \PHPUnit\Framework\Exception * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @psalm-return never-return + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileEquals */ - protected function fail($other, $description, \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure $comparisonFailure = null) : void + function assertFileEquals(string $expected, string $actual, string $message = '') : void { - if ($comparisonFailure === null) { - [$error, $recodedOther] = \PHPUnit\Util\Json::canonicalize($other); - if ($error) { - parent::fail($other, $description); - } - [$error, $recodedValue] = \PHPUnit\Util\Json::canonicalize($this->value); - if ($error) { - parent::fail($other, $description); - } - $comparisonFailure = new \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure(\json_decode($this->value), \json_decode($other), \PHPUnit\Util\Json::prettify($recodedValue), \PHPUnit\Util\Json::prettify($recodedOther), \false, 'Failed asserting that two json values are equal.'); - } - parent::fail($other, $description, $comparisonFailure); + \PHPUnit\Framework\Assert::assertFileEquals(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function sprintf; -use Throwable; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class ExceptionCode extends \PHPUnit\Framework\Constraint\Constraint -{ - /** - * @var int|string - */ - private $expectedCode; +if (!\function_exists('PHPUnit\\Framework\\assertFileEqualsCanonicalizing')) { /** - * @param int|string $expected + * Asserts that the contents of one file is equal to the contents of another + * file (canonicalizing). + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileEqualsCanonicalizing */ - public function __construct($expected) - { - $this->expectedCode = $expected; - } - public function toString() : string + function assertFileEqualsCanonicalizing(string $expected, string $actual, string $message = '') : void { - return 'exception code is '; + \PHPUnit\Framework\Assert::assertFileEqualsCanonicalizing(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertFileEqualsIgnoringCase')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that the contents of one file is equal to the contents of another + * file (ignoring case). * - * @param Throwable $other + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileEqualsIgnoringCase */ - protected function matches($other) : bool + function assertFileEqualsIgnoringCase(string $expected, string $actual, string $message = '') : void { - return (string) $other->getCode() === (string) $this->expectedCode; + \PHPUnit\Framework\Assert::assertFileEqualsIgnoringCase(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertFileNotEquals')) { /** - * Returns the description of the failure. + * Asserts that the contents of one file is not equal to the contents of + * another file. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @see Assert::assertFileNotEquals */ - protected function failureDescription($other) : string + function assertFileNotEquals(string $expected, string $actual, string $message = '') : void { - return \sprintf('%s is equal to expected exception code %s', $this->exporter()->export($other->getCode()), $this->exporter()->export($this->expectedCode)); + \PHPUnit\Framework\Assert::assertFileNotEquals(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function get_class; -use function sprintf; -use PHPUnit\Util\Filter; -use Throwable; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class Exception extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertFileNotEqualsCanonicalizing')) { /** - * @var string + * Asserts that the contents of one file is not equal to the contents of another + * file (canonicalizing). + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileNotEqualsCanonicalizing */ - private $className; - public function __construct(string $className) + function assertFileNotEqualsCanonicalizing(string $expected, string $actual, string $message = '') : void { - $this->className = $className; + \PHPUnit\Framework\Assert::assertFileNotEqualsCanonicalizing(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertFileNotEqualsIgnoringCase')) { /** - * Returns a string representation of the constraint. + * Asserts that the contents of one file is not equal to the contents of another + * file (ignoring case). + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileNotEqualsIgnoringCase */ - public function toString() : string + function assertFileNotEqualsIgnoringCase(string $expected, string $actual, string $message = '') : void { - return \sprintf('exception of type "%s"', $this->className); + \PHPUnit\Framework\Assert::assertFileNotEqualsIgnoringCase(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertStringEqualsFile')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that the contents of a string is equal + * to the contents of a file. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringEqualsFile */ - protected function matches($other) : bool + function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = '') : void { - return $other instanceof $this->className; + \PHPUnit\Framework\Assert::assertStringEqualsFile(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertStringEqualsFileCanonicalizing')) { /** - * Returns the description of the failure. + * Asserts that the contents of a string is equal + * to the contents of a file (canonicalizing). * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringEqualsFileCanonicalizing */ - protected function failureDescription($other) : string + function assertStringEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '') : void { - if ($other !== null) { - $message = ''; - if ($other instanceof \Throwable) { - $message = '. Message was: "' . $other->getMessage() . '" at' . "\n" . \PHPUnit\Util\Filter::getFilteredStacktrace($other); - } - return \sprintf('exception of type "%s" matches expected exception "%s"%s', \get_class($other), $this->className, $message); - } - return \sprintf('exception of type "%s" is thrown', $this->className); + \PHPUnit\Framework\Assert::assertStringEqualsFileCanonicalizing(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function sprintf; -use function strpos; -use Throwable; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class ExceptionMessage extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertStringEqualsFileIgnoringCase')) { /** - * @var string + * Asserts that the contents of a string is equal + * to the contents of a file (ignoring case). + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringEqualsFileIgnoringCase */ - private $expectedMessage; - public function __construct(string $expected) - { - $this->expectedMessage = $expected; - } - public function toString() : string + function assertStringEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = '') : void { - if ($this->expectedMessage === '') { - return 'exception message is empty'; - } - return 'exception message contains '; + \PHPUnit\Framework\Assert::assertStringEqualsFileIgnoringCase(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertStringNotEqualsFile')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that the contents of a string is not equal + * to the contents of a file. * - * @param Throwable $other + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotEqualsFile */ - protected function matches($other) : bool + function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = '') : void { - if ($this->expectedMessage === '') { - return $other->getMessage() === ''; - } - return \strpos((string) $other->getMessage(), $this->expectedMessage) !== \false; + \PHPUnit\Framework\Assert::assertStringNotEqualsFile(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertStringNotEqualsFileCanonicalizing')) { /** - * Returns the description of the failure. + * Asserts that the contents of a string is not equal + * to the contents of a file (canonicalizing). * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotEqualsFileCanonicalizing */ - protected function failureDescription($other) : string + function assertStringNotEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = '') : void { - if ($this->expectedMessage === '') { - return \sprintf("exception message is empty but is '%s'", $other->getMessage()); - } - return \sprintf("exception message '%s' contains '%s'", $other->getMessage(), $this->expectedMessage); + \PHPUnit\Framework\Assert::assertStringNotEqualsFileCanonicalizing(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function sprintf; -use Exception; -use PHPUnit\Util\RegularExpression as RegularExpressionUtil; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class ExceptionMessageRegularExpression extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertStringNotEqualsFileIgnoringCase')) { /** - * @var string + * Asserts that the contents of a string is not equal + * to the contents of a file (ignoring case). + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotEqualsFileIgnoringCase */ - private $expectedMessageRegExp; - public function __construct(string $expected) - { - $this->expectedMessageRegExp = $expected; - } - public function toString() : string + function assertStringNotEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = '') : void { - return 'exception message matches '; + \PHPUnit\Framework\Assert::assertStringNotEqualsFileIgnoringCase(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsReadable')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a file/dir is readable. * - * @param \PHPUnit\Framework\Exception $other + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @throws Exception - * @throws \PHPUnit\Framework\Exception + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsReadable */ - protected function matches($other) : bool + function assertIsReadable(string $filename, string $message = '') : void { - $match = \PHPUnit\Util\RegularExpression::safeMatch($this->expectedMessageRegExp, $other->getMessage()); - if ($match === \false) { - throw new \PHPUnit\Framework\Exception("Invalid expected exception message regex given: '{$this->expectedMessageRegExp}'"); - } - return $match === 1; + \PHPUnit\Framework\Assert::assertIsReadable(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsNotReadable')) { /** - * Returns the description of the failure. + * Asserts that a file/dir exists and is not readable. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotReadable */ - protected function failureDescription($other) : string + function assertIsNotReadable(string $filename, string $message = '') : void { - return \sprintf("exception message '%s' matches '%s'", $other->getMessage(), $this->expectedMessageRegExp); + \PHPUnit\Framework\Assert::assertIsNotReadable(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function abs; -use function get_class; -use function is_array; -use function is_float; -use function is_infinite; -use function is_nan; -use function is_object; -use function is_string; -use function sprintf; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsIdentical extends \PHPUnit\Framework\Constraint\Constraint -{ - /** - * @var float - */ - private const EPSILON = 1.0E-10; +if (!\function_exists('PHPUnit\\Framework\\assertNotIsReadable')) { /** - * @var mixed + * Asserts that a file/dir exists and is not readable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4062 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotIsReadable */ - private $value; - public function __construct($value) + function assertNotIsReadable(string $filename, string $message = '') : void { - $this->value = $value; + \PHPUnit\Framework\Assert::assertNotIsReadable(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsWritable')) { /** - * Evaluates the constraint for parameter $other. - * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. - * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. + * Asserts that a file/dir exists and is writable. * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsWritable */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + function assertIsWritable(string $filename, string $message = '') : void { - if (\is_float($this->value) && \is_float($other) && !\is_infinite($this->value) && !\is_infinite($other) && !\is_nan($this->value) && !\is_nan($other)) { - $success = \abs($this->value - $other) < self::EPSILON; - } else { - $success = $this->value === $other; - } - if ($returnResult) { - return $success; - } - if (!$success) { - $f = null; - // if both values are strings, make sure a diff is generated - if (\is_string($this->value) && \is_string($other)) { - $f = new \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure($this->value, $other, \sprintf("'%s'", $this->value), \sprintf("'%s'", $other)); - } - // if both values are array, make sure a diff is generated - if (\is_array($this->value) && \is_array($other)) { - $f = new \PHPUnit\SebastianBergmann\Comparator\ComparisonFailure($this->value, $other, $this->exporter()->export($this->value), $this->exporter()->export($other)); - } - $this->fail($other, $description, $f); - } - return null; + \PHPUnit\Framework\Assert::assertIsWritable(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsNotWritable')) { /** - * Returns a string representation of the constraint. + * Asserts that a file/dir exists and is not writable. * + * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotWritable */ - public function toString() : string + function assertIsNotWritable(string $filename, string $message = '') : void { - if (\is_object($this->value)) { - return 'is identical to an object of class "' . \get_class($this->value) . '"'; - } - return 'is identical to ' . $this->exporter()->export($this->value); + \PHPUnit\Framework\Assert::assertIsNotWritable(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertNotIsWritable')) { /** - * Returns the description of the failure. + * Asserts that a file/dir exists and is not writable. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @param mixed $other evaluated value or object + * @codeCoverageIgnore * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4065 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotIsWritable */ - protected function failureDescription($other) : string + function assertNotIsWritable(string $filename, string $message = '') : void { - if (\is_object($this->value) && \is_object($other)) { - return 'two variables reference the same object'; - } - if (\is_string($this->value) && \is_string($other)) { - return 'two strings are identical'; - } - if (\is_array($this->value) && \is_array($other)) { - return 'two arrays are identical'; - } - return parent::failureDescription($other); + \PHPUnit\Framework\Assert::assertNotIsWritable(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function array_reduce; -use function array_shift; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class LogicalXor extends \PHPUnit\Framework\Constraint\BinaryOperator -{ +if (!\function_exists('PHPUnit\\Framework\\assertDirectoryExists')) { /** - * Returns the name of this operator. + * Asserts that a directory exists. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryExists */ - public function operator() : string + function assertDirectoryExists(string $directory, string $message = '') : void { - return 'xor'; + \PHPUnit\Framework\Assert::assertDirectoryExists(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertDirectoryDoesNotExist')) { /** - * Returns this operator's precedence. + * Asserts that a directory does not exist. * - * @see https://www.php.net/manual/en/language.operators.precedence.php. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryDoesNotExist */ - public function precedence() : int + function assertDirectoryDoesNotExist(string $directory, string $message = '') : void { - return 23; + \PHPUnit\Framework\Assert::assertDirectoryDoesNotExist(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertDirectoryNotExists')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a directory does not exist. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4068 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryNotExists */ - public function matches($other) : bool + function assertDirectoryNotExists(string $directory, string $message = '') : void { - $constraints = $this->constraints(); - $initial = \array_shift($constraints); - if ($initial === null) { - return \false; - } - return \array_reduce($constraints, static function (bool $matches, \PHPUnit\Framework\Constraint\Constraint $constraint) use($other) : bool { - return $matches xor $constraint->evaluate($other, '', \true); - }, $initial->evaluate($other, '', \true)); + \PHPUnit\Framework\Assert::assertDirectoryNotExists(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class LogicalOr extends \PHPUnit\Framework\Constraint\BinaryOperator -{ +if (!\function_exists('PHPUnit\\Framework\\assertDirectoryIsReadable')) { /** - * Returns the name of this operator. + * Asserts that a directory exists and is readable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryIsReadable */ - public function operator() : string + function assertDirectoryIsReadable(string $directory, string $message = '') : void { - return 'or'; + \PHPUnit\Framework\Assert::assertDirectoryIsReadable(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertDirectoryIsNotReadable')) { /** - * Returns this operator's precedence. + * Asserts that a directory exists and is not readable. * - * @see https://www.php.net/manual/en/language.operators.precedence.php + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryIsNotReadable */ - public function precedence() : int + function assertDirectoryIsNotReadable(string $directory, string $message = '') : void { - return 24; + \PHPUnit\Framework\Assert::assertDirectoryIsNotReadable(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertDirectoryNotIsReadable')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a directory exists and is not readable. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4071 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryNotIsReadable */ - public function matches($other) : bool + function assertDirectoryNotIsReadable(string $directory, string $message = '') : void { - foreach ($this->constraints() as $constraint) { - if ($constraint->evaluate($other, '', \true)) { - return \true; - } - } - return \false; + \PHPUnit\Framework\Assert::assertDirectoryNotIsReadable(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function array_map; -use function count; -use function preg_match; -use function preg_quote; -use function preg_replace; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class LogicalNot extends \PHPUnit\Framework\Constraint\UnaryOperator -{ - public static function negate(string $string) : string +if (!\function_exists('PHPUnit\\Framework\\assertDirectoryIsWritable')) { + /** + * Asserts that a directory exists and is writable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryIsWritable + */ + function assertDirectoryIsWritable(string $directory, string $message = '') : void { - $positives = ['contains ', 'exists', 'has ', 'is ', 'are ', 'matches ', 'starts with ', 'ends with ', 'reference ', 'not not ']; - $negatives = ['does not contain ', 'does not exist', 'does not have ', 'is not ', 'are not ', 'does not match ', 'starts not with ', 'ends not with ', 'don\'t reference ', 'not ']; - \preg_match('/(\'[\\w\\W]*\')([\\w\\W]*)("[\\w\\W]*")/i', $string, $matches); - $positives = \array_map(function (string $s) { - return '/\\b' . \preg_quote($s, '/') . '/'; - }, $positives); - if (\count($matches) > 0) { - $nonInput = $matches[2]; - $negatedString = \preg_replace('/' . \preg_quote($nonInput, '/') . '/', \preg_replace($positives, $negatives, $nonInput), $string); - } else { - $negatedString = \preg_replace($positives, $negatives, $string); - } - return $negatedString; + \PHPUnit\Framework\Assert::assertDirectoryIsWritable(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertDirectoryIsNotWritable')) { /** - * Returns the name of this operator. + * Asserts that a directory exists and is not writable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryIsNotWritable */ - public function operator() : string + function assertDirectoryIsNotWritable(string $directory, string $message = '') : void { - return 'not'; + \PHPUnit\Framework\Assert::assertDirectoryIsNotWritable(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertDirectoryNotIsWritable')) { /** - * Returns this operator's precedence. + * Asserts that a directory exists and is not writable. * - * @see https://www.php.net/manual/en/language.operators.precedence.php + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4074 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertDirectoryNotIsWritable */ - public function precedence() : int + function assertDirectoryNotIsWritable(string $directory, string $message = '') : void { - return 5; + \PHPUnit\Framework\Assert::assertDirectoryNotIsWritable(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertFileExists')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a file exists. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileExists */ - protected function matches($other) : bool + function assertFileExists(string $filename, string $message = '') : void { - return !$this->constraint()->evaluate($other, '', \true); + \PHPUnit\Framework\Assert::assertFileExists(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertFileDoesNotExist')) { /** - * Applies additional transformation to strings returned by toString() or - * failureDescription(). + * Asserts that a file does not exist. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileDoesNotExist */ - protected function transformString(string $string) : string + function assertFileDoesNotExist(string $filename, string $message = '') : void { - return self::negate($string); + \PHPUnit\Framework\Assert::assertFileDoesNotExist(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertFileNotExists')) { /** - * Reduces the sub-expression starting at $this by skipping degenerate - * sub-expression and returns first descendant constraint that starts - * a non-reducible sub-expression. + * Asserts that a file does not exist. * - * See Constraint::reduce() for more. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4077 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileNotExists */ - protected function reduce() : \PHPUnit\Framework\Constraint\Constraint + function assertFileNotExists(string $filename, string $message = '') : void { - $constraint = $this->constraint(); - if ($constraint instanceof self) { - return $constraint->constraint()->reduce(); - } - return parent::reduce(); + \PHPUnit\Framework\Assert::assertFileNotExists(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class LogicalAnd extends \PHPUnit\Framework\Constraint\BinaryOperator -{ +if (!\function_exists('PHPUnit\\Framework\\assertFileIsReadable')) { /** - * Returns the name of this operator. + * Asserts that a file exists and is readable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileIsReadable */ - public function operator() : string + function assertFileIsReadable(string $file, string $message = '') : void { - return 'and'; + \PHPUnit\Framework\Assert::assertFileIsReadable(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertFileIsNotReadable')) { /** - * Returns this operator's precedence. + * Asserts that a file exists and is not readable. * - * @see https://www.php.net/manual/en/language.operators.precedence.php + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileIsNotReadable */ - public function precedence() : int + function assertFileIsNotReadable(string $file, string $message = '') : void { - return 22; + \PHPUnit\Framework\Assert::assertFileIsNotReadable(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertFileNotIsReadable')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a file exists and is not readable. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4080 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileNotIsReadable */ - protected function matches($other) : bool + function assertFileNotIsReadable(string $file, string $message = '') : void { - foreach ($this->constraints() as $constraint) { - if (!$constraint->evaluate($other, '', \true)) { - return \false; - } - } - return [] !== $this->constraints(); + \PHPUnit\Framework\Assert::assertFileNotIsReadable(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function array_map; -use function array_values; -use function count; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -abstract class BinaryOperator extends \PHPUnit\Framework\Constraint\Operator -{ +if (!\function_exists('PHPUnit\\Framework\\assertFileIsWritable')) { /** - * @var Constraint[] + * Asserts that a file exists and is writable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileIsWritable */ - private $constraints = []; - public static function fromConstraints(\PHPUnit\Framework\Constraint\Constraint ...$constraints) : self + function assertFileIsWritable(string $file, string $message = '') : void { - $constraint = new static(); - $constraint->constraints = $constraints; - return $constraint; + \PHPUnit\Framework\Assert::assertFileIsWritable(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertFileIsNotWritable')) { /** - * @param mixed[] $constraints + * Asserts that a file exists and is not writable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileIsNotWritable */ - public function setConstraints(array $constraints) : void + function assertFileIsNotWritable(string $file, string $message = '') : void { - $this->constraints = \array_map(function ($constraint) : Constraint { - return $this->checkConstraint($constraint); - }, \array_values($constraints)); + \PHPUnit\Framework\Assert::assertFileIsNotWritable(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertFileNotIsWritable')) { /** - * Returns the number of operands (constraints). + * Asserts that a file exists and is not writable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4083 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFileNotIsWritable */ - public final function arity() : int + function assertFileNotIsWritable(string $file, string $message = '') : void { - return \count($this->constraints); + \PHPUnit\Framework\Assert::assertFileNotIsWritable(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertTrue')) { /** - * Returns a string representation of the constraint. + * Asserts that a condition is true. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert true $condition + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertTrue */ - public function toString() : string + function assertTrue($condition, string $message = '') : void { - $reduced = $this->reduce(); - if ($reduced !== $this) { - return $reduced->toString(); - } - $text = ''; - foreach ($this->constraints as $key => $constraint) { - $constraint = $constraint->reduce(); - $text .= $this->constraintToString($constraint, $key); - } - return $text; + \PHPUnit\Framework\Assert::assertTrue(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertNotTrue')) { /** - * Counts the number of constraint elements. + * Asserts that a condition is not true. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !true $condition + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotTrue */ - public function count() : int + function assertNotTrue($condition, string $message = '') : void { - $count = 0; - foreach ($this->constraints as $constraint) { - $count += \count($constraint); - } - return $count; + \PHPUnit\Framework\Assert::assertNotTrue(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertFalse')) { /** - * Returns the nested constraints. + * Asserts that a condition is false. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert false $condition + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFalse */ - protected final function constraints() : array + function assertFalse($condition, string $message = '') : void { - return $this->constraints; + \PHPUnit\Framework\Assert::assertFalse(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertNotFalse')) { /** - * Returns true if the $constraint needs to be wrapped with braces. + * Asserts that a condition is not false. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !false $condition + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotFalse */ - protected final function constraintNeedsParentheses(\PHPUnit\Framework\Constraint\Constraint $constraint) : bool + function assertNotFalse($condition, string $message = '') : void { - return $this->arity() > 1 && parent::constraintNeedsParentheses($constraint); + \PHPUnit\Framework\Assert::assertNotFalse(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertNull')) { /** - * Reduces the sub-expression starting at $this by skipping degenerate - * sub-expression and returns first descendant constraint that starts - * a non-reducible sub-expression. + * Asserts that a variable is null. * - * See Constraint::reduce() for more. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert null $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNull */ - protected function reduce() : \PHPUnit\Framework\Constraint\Constraint + function assertNull($actual, string $message = '') : void { - if ($this->arity() === 1 && $this->constraints[0] instanceof \PHPUnit\Framework\Constraint\Operator) { - return $this->constraints[0]->reduce(); - } - return parent::reduce(); + \PHPUnit\Framework\Assert::assertNull(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertNotNull')) { /** - * Returns string representation of given operand in context of this operator. + * Asserts that a variable is not null. * - * @param Constraint $constraint operand constraint - * @param int $position position of $constraint in this expression + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !null $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotNull */ - private function constraintToString(\PHPUnit\Framework\Constraint\Constraint $constraint, int $position) : string + function assertNotNull($actual, string $message = '') : void { - $prefix = ''; - if ($position > 0) { - $prefix = ' ' . $this->operator() . ' '; - } - if ($this->constraintNeedsParentheses($constraint)) { - return $prefix . '( ' . $constraint->toString() . ' )'; - } - $string = $constraint->toStringInContext($this, $position); - if ($string === '') { - $string = $constraint->toString(); - } - return $prefix . $string; + \PHPUnit\Framework\Assert::assertNotNull(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -abstract class Operator extends \PHPUnit\Framework\Constraint\Constraint -{ - /** - * Returns the name of this operator. - */ - public abstract function operator() : string; +if (!\function_exists('PHPUnit\\Framework\\assertFinite')) { /** - * Returns this operator's precedence. + * Asserts that a variable is finite. * - * @see https://www.php.net/manual/en/language.operators.precedence.php - */ - public abstract function precedence() : int; - /** - * Returns the number of operands. - */ - public abstract function arity() : int; - /** - * Validates $constraint argument. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertFinite */ - protected function checkConstraint($constraint) : \PHPUnit\Framework\Constraint\Constraint + function assertFinite($actual, string $message = '') : void { - if (!$constraint instanceof \PHPUnit\Framework\Constraint\Constraint) { - return new \PHPUnit\Framework\Constraint\IsEqual($constraint); - } - return $constraint; + \PHPUnit\Framework\Assert::assertFinite(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertInfinite')) { /** - * Returns true if the $constraint needs to be wrapped with braces. + * Asserts that a variable is infinite. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertInfinite */ - protected function constraintNeedsParentheses(\PHPUnit\Framework\Constraint\Constraint $constraint) : bool + function assertInfinite($actual, string $message = '') : void { - return $constraint instanceof self && $constraint->arity() > 1 && $this->precedence() <= $constraint->precedence(); + \PHPUnit\Framework\Assert::assertInfinite(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function count; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -abstract class UnaryOperator extends \PHPUnit\Framework\Constraint\Operator -{ - /** - * @var Constraint - */ - private $constraint; +if (!\function_exists('PHPUnit\\Framework\\assertNan')) { /** - * @param Constraint|mixed $constraint + * Asserts that a variable is nan. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNan */ - public function __construct($constraint) + function assertNan($actual, string $message = '') : void { - $this->constraint = $this->checkConstraint($constraint); + \PHPUnit\Framework\Assert::assertNan(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertClassHasAttribute')) { /** - * Returns the number of operands (constraints). + * Asserts that a class has a specified attribute. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertClassHasAttribute */ - public function arity() : int + function assertClassHasAttribute(string $attributeName, string $className, string $message = '') : void { - return 1; + \PHPUnit\Framework\Assert::assertClassHasAttribute(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertClassNotHasAttribute')) { /** - * Returns a string representation of the constraint. + * Asserts that a class does not have a specified attribute. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertClassNotHasAttribute */ - public function toString() : string + function assertClassNotHasAttribute(string $attributeName, string $className, string $message = '') : void { - $reduced = $this->reduce(); - if ($reduced !== $this) { - return $reduced->toString(); - } - $constraint = $this->constraint->reduce(); - if ($this->constraintNeedsParentheses($constraint)) { - return $this->operator() . '( ' . $constraint->toString() . ' )'; - } - $string = $constraint->toStringInContext($this, 0); - if ($string === '') { - return $this->transformString($constraint->toString()); - } - return $string; + \PHPUnit\Framework\Assert::assertClassNotHasAttribute(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertClassHasStaticAttribute')) { /** - * Counts the number of constraint elements. + * Asserts that a class has a specified static attribute. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertClassHasStaticAttribute */ - public function count() : int + function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = '') : void { - return \count($this->constraint); + \PHPUnit\Framework\Assert::assertClassHasStaticAttribute(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertClassNotHasStaticAttribute')) { /** - * Returns the description of the failure. + * Asserts that a class does not have a specified static attribute. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @see Assert::assertClassNotHasStaticAttribute */ - protected function failureDescription($other) : string + function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = '') : void { - $reduced = $this->reduce(); - if ($reduced !== $this) { - return $reduced->failureDescription($other); - } - $constraint = $this->constraint->reduce(); - if ($this->constraintNeedsParentheses($constraint)) { - return $this->operator() . '( ' . $constraint->failureDescription($other) . ' )'; - } - $string = $constraint->failureDescriptionInContext($this, 0, $other); - if ($string === '') { - return $this->transformString($constraint->failureDescription($other)); - } - return $string; + \PHPUnit\Framework\Assert::assertClassNotHasStaticAttribute(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertObjectHasAttribute')) { /** - * Transforms string returned by the memeber constraint's toString() or - * failureDescription() such that it reflects constraint's participation in - * this expression. + * Asserts that an object has a specified attribute. * - * The method may be overwritten in a subclass to apply default - * transformation in case the operand constraint does not provide its own - * custom strings via toStringInContext() or failureDescriptionInContext(). + * @param object $object * - * @param string $string the string to be transformed + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertObjectHasAttribute */ - protected function transformString(string $string) : string + function assertObjectHasAttribute(string $attributeName, $object, string $message = '') : void { - return $string; + \PHPUnit\Framework\Assert::assertObjectHasAttribute(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertObjectNotHasAttribute')) { /** - * Provides access to $this->constraint for subclasses. + * Asserts that an object does not have a specified attribute. + * + * @param object $object + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertObjectNotHasAttribute */ - protected final function constraint() : \PHPUnit\Framework\Constraint\Constraint + function assertObjectNotHasAttribute(string $attributeName, $object, string $message = '') : void { - return $this->constraint; + \PHPUnit\Framework\Assert::assertObjectNotHasAttribute(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertSame')) { /** - * Returns true if the $constraint needs to be wrapped with parentheses. + * Asserts that two variables have the same type and value. + * Used on objects, it asserts that two variables reference + * the same object. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-template ExpectedType + * @psalm-param ExpectedType $expected + * @psalm-assert =ExpectedType $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertSame */ - protected function constraintNeedsParentheses(\PHPUnit\Framework\Constraint\Constraint $constraint) : bool + function assertSame($expected, $actual, string $message = '') : void { - $constraint = $constraint->reduce(); - return $constraint instanceof self || parent::constraintNeedsParentheses($constraint); + \PHPUnit\Framework\Assert::assertSame(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_infinite; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsInfinite extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertNotSame')) { /** - * Returns a string representation of the constraint. + * Asserts that two variables do not have the same type and value. + * Used on objects, it asserts that two variables do not reference + * the same object. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotSame */ - public function toString() : string + function assertNotSame($expected, $actual, string $message = '') : void { - return 'is infinite'; + \PHPUnit\Framework\Assert::assertNotSame(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertInstanceOf')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a variable is of a given type. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * + * @psalm-template ExpectedType of object + * @psalm-param class-string $expected + * @psalm-assert =ExpectedType $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertInstanceOf */ - protected function matches($other) : bool + function assertInstanceOf(string $expected, $actual, string $message = '') : void { - return \is_infinite($other); + \PHPUnit\Framework\Assert::assertInstanceOf(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_finite; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsFinite extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertNotInstanceOf')) { /** - * Returns a string representation of the constraint. + * Asserts that a variable is not of a given type. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * + * @psalm-template ExpectedType of object + * @psalm-param class-string $expected + * @psalm-assert !ExpectedType $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotInstanceOf */ - public function toString() : string + function assertNotInstanceOf(string $expected, $actual, string $message = '') : void { - return 'is finite'; + \PHPUnit\Framework\Assert::assertNotInstanceOf(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsArray')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a variable is of type array. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert array $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsArray */ - protected function matches($other) : bool + function assertIsArray($actual, string $message = '') : void { - return \is_finite($other); + \PHPUnit\Framework\Assert::assertIsArray(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_nan; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsNan extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertIsBool')) { /** - * Returns a string representation of the constraint. + * Asserts that a variable is of type bool. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert bool $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsBool */ - public function toString() : string + function assertIsBool($actual, string $message = '') : void { - return 'is nan'; + \PHPUnit\Framework\Assert::assertIsBool(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsFloat')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a variable is of type float. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert float $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsFloat */ - protected function matches($other) : bool + function assertIsFloat($actual, string $message = '') : void { - return \is_nan($other); + \PHPUnit\Framework\Assert::assertIsFloat(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function preg_match; -use function sprintf; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -class RegularExpression extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertIsInt')) { /** - * @var string + * Asserts that a variable is of type int. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert int $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsInt */ - private $pattern; - public function __construct(string $pattern) + function assertIsInt($actual, string $message = '') : void { - $this->pattern = $pattern; + \PHPUnit\Framework\Assert::assertIsInt(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsNumeric')) { /** - * Returns a string representation of the constraint. + * Asserts that a variable is of type numeric. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert numeric $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNumeric */ - public function toString() : string + function assertIsNumeric($actual, string $message = '') : void { - return \sprintf('matches PCRE pattern "%s"', $this->pattern); + \PHPUnit\Framework\Assert::assertIsNumeric(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsObject')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a variable is of type object. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert object $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsObject */ - protected function matches($other) : bool + function assertIsObject($actual, string $message = '') : void { - return \preg_match($this->pattern, $other) > 0; + \PHPUnit\Framework\Assert::assertIsObject(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function mb_stripos; -use function mb_strtolower; -use function sprintf; -use function strpos; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class StringContains extends \PHPUnit\Framework\Constraint\Constraint -{ - /** - * @var string - */ - private $string; +if (!\function_exists('PHPUnit\\Framework\\assertIsResource')) { /** - * @var bool + * Asserts that a variable is of type resource. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert resource $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsResource */ - private $ignoreCase; - public function __construct(string $string, bool $ignoreCase = \false) + function assertIsResource($actual, string $message = '') : void { - $this->string = $string; - $this->ignoreCase = $ignoreCase; + \PHPUnit\Framework\Assert::assertIsResource(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsClosedResource')) { /** - * Returns a string representation of the constraint. + * Asserts that a variable is of type resource and is closed. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert resource $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsClosedResource */ - public function toString() : string + function assertIsClosedResource($actual, string $message = '') : void { - if ($this->ignoreCase) { - $string = \mb_strtolower($this->string, 'UTF-8'); - } else { - $string = $this->string; - } - return \sprintf('contains "%s"', $string); + \PHPUnit\Framework\Assert::assertIsClosedResource(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsString')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a variable is of type string. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert string $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsString */ - protected function matches($other) : bool + function assertIsString($actual, string $message = '') : void { - if ('' === $this->string) { - return \true; - } - if ($this->ignoreCase) { - /* - * We must use the multi byte safe version so we can accurately compare non latin upper characters with - * their lowercase equivalents. - */ - return \mb_stripos($other, $this->string, 0, 'UTF-8') !== \false; - } - /* - * Use the non multi byte safe functions to see if the string is contained in $other. - * - * This function is very fast and we don't care about the character position in the string. - * - * Additionally, we want this method to be binary safe so we can check if some binary data is in other binary - * data. - */ - return \strpos($other, $this->string) !== \false; + \PHPUnit\Framework\Assert::assertIsString(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function json_decode; -use function json_last_error; -use function sprintf; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsJson extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertIsScalar')) { /** - * Returns a string representation of the constraint. + * Asserts that a variable is of type scalar. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert scalar $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsScalar */ - public function toString() : string + function assertIsScalar($actual, string $message = '') : void { - return 'is valid JSON'; + \PHPUnit\Framework\Assert::assertIsScalar(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsCallable')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a variable is of type callable. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert callable $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsCallable */ - protected function matches($other) : bool + function assertIsCallable($actual, string $message = '') : void { - if ($other === '') { - return \false; - } - \json_decode($other); - if (\json_last_error()) { - return \false; - } - return \true; + \PHPUnit\Framework\Assert::assertIsCallable(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsIterable')) { /** - * Returns the description of the failure. + * Asserts that a variable is of type iterable. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @param mixed $other evaluated value or object + * @psalm-assert iterable $actual * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsIterable */ - protected function failureDescription($other) : string + function assertIsIterable($actual, string $message = '') : void { - if ($other === '') { - return 'an empty string is valid JSON'; - } - \json_decode($other); - $error = (string) \PHPUnit\Framework\Constraint\JsonMatchesErrorMessageProvider::determineJsonError((string) \json_last_error()); - return \sprintf('%s is valid JSON (%s)', $this->exporter()->shortenedExport($other), $error); + \PHPUnit\Framework\Assert::assertIsIterable(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function strlen; -use function substr; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class StringEndsWith extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertIsNotArray')) { /** - * @var string + * Asserts that a variable is not of type array. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !array $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotArray */ - private $suffix; - public function __construct(string $suffix) + function assertIsNotArray($actual, string $message = '') : void { - $this->suffix = $suffix; + \PHPUnit\Framework\Assert::assertIsNotArray(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsNotBool')) { /** - * Returns a string representation of the constraint. + * Asserts that a variable is not of type bool. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !bool $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotBool */ - public function toString() : string + function assertIsNotBool($actual, string $message = '') : void { - return 'ends with "' . $this->suffix . '"'; + \PHPUnit\Framework\Assert::assertIsNotBool(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsNotFloat')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a variable is not of type float. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !float $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotFloat */ - protected function matches($other) : bool + function assertIsNotFloat($actual, string $message = '') : void { - return \substr($other, 0 - \strlen($this->suffix)) === $this->suffix; + \PHPUnit\Framework\Assert::assertIsNotFloat(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use const DIRECTORY_SEPARATOR; -use function explode; -use function implode; -use function preg_match; -use function preg_quote; -use function preg_replace; -use function strtr; -use PHPUnit\SebastianBergmann\Diff\Differ; -use PHPUnit\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class StringMatchesFormatDescription extends \PHPUnit\Framework\Constraint\RegularExpression -{ +if (!\function_exists('PHPUnit\\Framework\\assertIsNotInt')) { /** - * @var string + * Asserts that a variable is not of type int. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !int $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotInt */ - private $string; - public function __construct(string $string) + function assertIsNotInt($actual, string $message = '') : void { - parent::__construct($this->createPatternFromFormat($this->convertNewlines($string))); - $this->string = $string; + \PHPUnit\Framework\Assert::assertIsNotInt(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsNotNumeric')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a variable is not of type numeric. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !numeric $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotNumeric */ - protected function matches($other) : bool - { - return parent::matches($this->convertNewlines($other)); - } - protected function failureDescription($other) : string - { - return 'string matches format description'; - } - protected function additionalFailureDescription($other) : string + function assertIsNotNumeric($actual, string $message = '') : void { - $from = \explode("\n", $this->string); - $to = \explode("\n", $this->convertNewlines($other)); - foreach ($from as $index => $line) { - if (isset($to[$index]) && $line !== $to[$index]) { - $line = $this->createPatternFromFormat($line); - if (\preg_match($line, $to[$index]) > 0) { - $from[$index] = $to[$index]; - } - } - } - $this->string = \implode("\n", $from); - $other = \implode("\n", $to); - return (new \PHPUnit\SebastianBergmann\Diff\Differ(new \PHPUnit\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder("--- Expected\n+++ Actual\n")))->diff($this->string, $other); + \PHPUnit\Framework\Assert::assertIsNotNumeric(...func_get_args()); } - private function createPatternFromFormat(string $string) : string +} +if (!\function_exists('PHPUnit\\Framework\\assertIsNotObject')) { + /** + * Asserts that a variable is not of type object. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !object $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotObject + */ + function assertIsNotObject($actual, string $message = '') : void { - $string = \strtr(\preg_quote($string, '/'), ['%%' => '%', '%e' => '\\' . \DIRECTORY_SEPARATOR, '%s' => '[^\\r\\n]+', '%S' => '[^\\r\\n]*', '%a' => '.+', '%A' => '.*', '%w' => '\\s*', '%i' => '[+-]?\\d+', '%d' => '\\d+', '%x' => '[0-9a-fA-F]+', '%f' => '[+-]?\\.?\\d+\\.?\\d*(?:[Ee][+-]?\\d+)?', '%c' => '.']); - return '/^' . $string . '$/s'; + \PHPUnit\Framework\Assert::assertIsNotObject(...func_get_args()); } - private function convertNewlines(string $text) : string +} +if (!\function_exists('PHPUnit\\Framework\\assertIsNotResource')) { + /** + * Asserts that a variable is not of type resource. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !resource $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotResource + */ + function assertIsNotResource($actual, string $message = '') : void { - return \preg_replace('/\\r\\n/', "\n", $text); + \PHPUnit\Framework\Assert::assertIsNotResource(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function strlen; -use function strpos; -use PHPUnit\Framework\InvalidArgumentException; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class StringStartsWith extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertIsNotClosedResource')) { /** - * @var string + * Asserts that a variable is not of type resource. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !resource $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotClosedResource */ - private $prefix; - public function __construct(string $prefix) + function assertIsNotClosedResource($actual, string $message = '') : void { - if (\strlen($prefix) === 0) { - throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'non-empty string'); - } - $this->prefix = $prefix; + \PHPUnit\Framework\Assert::assertIsNotClosedResource(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsNotString')) { /** - * Returns a string representation of the constraint. + * Asserts that a variable is not of type string. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !string $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotString */ - public function toString() : string + function assertIsNotString($actual, string $message = '') : void { - return 'starts with "' . $this->prefix . '"'; + \PHPUnit\Framework\Assert::assertIsNotString(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsNotScalar')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a variable is not of type scalar. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !scalar $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotScalar */ - protected function matches($other) : bool + function assertIsNotScalar($actual, string $message = '') : void { - return \strpos((string) $other, $this->prefix) === 0; + \PHPUnit\Framework\Assert::assertIsNotScalar(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function array_key_exists; -use function is_array; -use ArrayAccess; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class ArrayHasKey extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertIsNotCallable')) { /** - * @var int|string + * Asserts that a variable is not of type callable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !callable $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotCallable */ - private $key; + function assertIsNotCallable($actual, string $message = '') : void + { + \PHPUnit\Framework\Assert::assertIsNotCallable(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\assertIsNotIterable')) { /** - * @param int|string $key + * Asserts that a variable is not of type iterable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !iterable $actual + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertIsNotIterable */ - public function __construct($key) + function assertIsNotIterable($actual, string $message = '') : void { - $this->key = $key; + \PHPUnit\Framework\Assert::assertIsNotIterable(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertMatchesRegularExpression')) { /** - * Returns a string representation of the constraint. + * Asserts that a string matches a given regular expression. * + * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertMatchesRegularExpression */ - public function toString() : string + function assertMatchesRegularExpression(string $pattern, string $string, string $message = '') : void { - return 'has the key ' . $this->exporter()->export($this->key); + \PHPUnit\Framework\Assert::assertMatchesRegularExpression(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertRegExp')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a string matches a given regular expression. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4086 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertRegExp */ - protected function matches($other) : bool + function assertRegExp(string $pattern, string $string, string $message = '') : void { - if (\is_array($other)) { - return \array_key_exists($this->key, $other); - } - if ($other instanceof \ArrayAccess) { - return $other->offsetExists($this->key); - } - return \false; + \PHPUnit\Framework\Assert::assertRegExp(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertDoesNotMatchRegularExpression')) { /** - * Returns the description of the failure. + * Asserts that a string does not match a given regular expression. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @see Assert::assertDoesNotMatchRegularExpression */ - protected function failureDescription($other) : string + function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = '') : void { - return 'an array ' . $this->toString(); + \PHPUnit\Framework\Assert::assertDoesNotMatchRegularExpression(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use SplObjectStorage; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class TraversableContainsIdentical extends \PHPUnit\Framework\Constraint\TraversableContains -{ +if (!\function_exists('PHPUnit\\Framework\\assertNotRegExp')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a string does not match a given regular expression. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4089 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotRegExp */ - protected function matches($other) : bool + function assertNotRegExp(string $pattern, string $string, string $message = '') : void { - if ($other instanceof \SplObjectStorage) { - return $other->contains($this->value()); - } - foreach ($other as $element) { - if ($this->value() === $element) { - return \true; - } - } - return \false; + \PHPUnit\Framework\Assert::assertNotRegExp(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use PHPUnit\Framework\ExpectationFailedException; -use Traversable; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class TraversableContainsOnly extends \PHPUnit\Framework\Constraint\Constraint -{ - /** - * @var Constraint - */ - private $constraint; - /** - * @var string - */ - private $type; +if (!\function_exists('PHPUnit\\Framework\\assertSameSize')) { /** - * @throws \PHPUnit\Framework\Exception + * Assert that the size of two arrays (or `Countable` or `Traversable` objects) + * is the same. + * + * @param Countable|iterable $expected + * @param Countable|iterable $actual + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertSameSize */ - public function __construct(string $type, bool $isNativeType = \true) + function assertSameSize($expected, $actual, string $message = '') : void { - if ($isNativeType) { - $this->constraint = new \PHPUnit\Framework\Constraint\IsType($type); - } else { - $this->constraint = new \PHPUnit\Framework\Constraint\IsInstanceOf($type); - } - $this->type = $type; + \PHPUnit\Framework\Assert::assertSameSize(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertNotSameSize')) { /** - * Evaluates the constraint for parameter $other. - * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. - * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. + * Assert that the size of two arrays (or `Countable` or `Traversable` objects) + * is not the same. * - * @param mixed|Traversable $other + * @param Countable|iterable $expected + * @param Countable|iterable $actual * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotSameSize */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + function assertNotSameSize($expected, $actual, string $message = '') : void { - $success = \true; - foreach ($other as $item) { - if (!$this->constraint->evaluate($item, '', \true)) { - $success = \false; - break; - } - } - if ($returnResult) { - return $success; - } - if (!$success) { - $this->fail($other, $description); - } - return null; + \PHPUnit\Framework\Assert::assertNotSameSize(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertStringMatchesFormat')) { /** - * Returns a string representation of the constraint. + * Asserts that a string matches a given format string. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringMatchesFormat */ - public function toString() : string + function assertStringMatchesFormat(string $format, string $string, string $message = '') : void { - return 'contains only values of type "' . $this->type . '"'; + \PHPUnit\Framework\Assert::assertStringMatchesFormat(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use SplObjectStorage; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class TraversableContainsEqual extends \PHPUnit\Framework\Constraint\TraversableContains -{ +if (!\function_exists('PHPUnit\\Framework\\assertStringNotMatchesFormat')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a string does not match a given format string. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotMatchesFormat */ - protected function matches($other) : bool + function assertStringNotMatchesFormat(string $format, string $string, string $message = '') : void { - if ($other instanceof \SplObjectStorage) { - return $other->contains($this->value()); - } - foreach ($other as $element) { - /* @noinspection TypeUnsafeComparisonInspection */ - if ($this->value() == $element) { - return \true; - } - } - return \false; + \PHPUnit\Framework\Assert::assertStringNotMatchesFormat(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_array; -use function sprintf; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -abstract class TraversableContains extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertStringMatchesFormatFile')) { /** - * @var mixed + * Asserts that a string matches a given format file. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringMatchesFormatFile */ - private $value; - public function __construct($value) + function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = '') : void { - $this->value = $value; + \PHPUnit\Framework\Assert::assertStringMatchesFormatFile(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertStringNotMatchesFormatFile')) { /** - * Returns a string representation of the constraint. + * Asserts that a string does not match a given format string. * + * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotMatchesFormatFile */ - public function toString() : string + function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = '') : void { - return 'contains ' . $this->exporter()->export($this->value); + \PHPUnit\Framework\Assert::assertStringNotMatchesFormatFile(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertStringStartsWith')) { /** - * Returns the description of the failure. + * Asserts that a string starts with a given prefix. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @see Assert::assertStringStartsWith */ - protected function failureDescription($other) : string - { - return \sprintf('%s %s', \is_array($other) ? 'an array' : 'a traversable', $this->toString()); - } - protected function value() + function assertStringStartsWith(string $prefix, string $string, string $message = '') : void { - return $this->value; + \PHPUnit\Framework\Assert::assertStringStartsWith(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function get_class; -use function is_object; -use PHPUnit\Framework\ActualValueIsNotAnObjectException; -use PHPUnit\Framework\ComparisonMethodDoesNotAcceptParameterTypeException; -use PHPUnit\Framework\ComparisonMethodDoesNotDeclareBoolReturnTypeException; -use PHPUnit\Framework\ComparisonMethodDoesNotDeclareExactlyOneParameterException; -use PHPUnit\Framework\ComparisonMethodDoesNotDeclareParameterTypeException; -use PHPUnit\Framework\ComparisonMethodDoesNotExistException; -use ReflectionNamedType; -use ReflectionObject; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class ObjectEquals extends \PHPUnit\Framework\Constraint\Constraint -{ - /** - * @var object - */ - private $expected; +if (!\function_exists('PHPUnit\\Framework\\assertStringStartsNotWith')) { /** - * @var string + * Asserts that a string starts not with a given prefix. + * + * @param string $prefix + * @param string $string + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringStartsNotWith */ - private $method; - public function __construct(object $object, string $method = 'equals') - { - $this->expected = $object; - $this->method = $method; - } - public function toString() : string + function assertStringStartsNotWith($prefix, $string, string $message = '') : void { - return 'two objects are equal'; + \PHPUnit\Framework\Assert::assertStringStartsNotWith(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertStringContainsString')) { /** - * @throws ActualValueIsNotAnObjectException - * @throws ComparisonMethodDoesNotExistException - * @throws ComparisonMethodDoesNotDeclareBoolReturnTypeException - * @throws ComparisonMethodDoesNotDeclareExactlyOneParameterException - * @throws ComparisonMethodDoesNotDeclareParameterTypeException - * @throws ComparisonMethodDoesNotAcceptParameterTypeException + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringContainsString */ - protected function matches($other) : bool - { - if (!\is_object($other)) { - throw new \PHPUnit\Framework\ActualValueIsNotAnObjectException(); - } - $object = new \ReflectionObject($other); - if (!$object->hasMethod($this->method)) { - throw new \PHPUnit\Framework\ComparisonMethodDoesNotExistException(\get_class($other), $this->method); - } - /** @noinspection PhpUnhandledExceptionInspection */ - $method = $object->getMethod($this->method); - if (!$method->hasReturnType()) { - throw new \PHPUnit\Framework\ComparisonMethodDoesNotDeclareBoolReturnTypeException(\get_class($other), $this->method); - } - $returnType = $method->getReturnType(); - if (!$returnType instanceof \ReflectionNamedType) { - throw new \PHPUnit\Framework\ComparisonMethodDoesNotDeclareBoolReturnTypeException(\get_class($other), $this->method); - } - if ($returnType->allowsNull()) { - throw new \PHPUnit\Framework\ComparisonMethodDoesNotDeclareBoolReturnTypeException(\get_class($other), $this->method); - } - if ($returnType->getName() !== 'bool') { - throw new \PHPUnit\Framework\ComparisonMethodDoesNotDeclareBoolReturnTypeException(\get_class($other), $this->method); - } - if ($method->getNumberOfParameters() !== 1 || $method->getNumberOfRequiredParameters() !== 1) { - throw new \PHPUnit\Framework\ComparisonMethodDoesNotDeclareExactlyOneParameterException(\get_class($other), $this->method); - } - $parameter = $method->getParameters()[0]; - if (!$parameter->hasType()) { - throw new \PHPUnit\Framework\ComparisonMethodDoesNotDeclareParameterTypeException(\get_class($other), $this->method); - } - $type = $parameter->getType(); - if (!$type instanceof \ReflectionNamedType) { - throw new \PHPUnit\Framework\ComparisonMethodDoesNotDeclareParameterTypeException(\get_class($other), $this->method); - } - $typeName = $type->getName(); - if ($typeName === 'self') { - $typeName = \get_class($other); - } - if (!$this->expected instanceof $typeName) { - throw new \PHPUnit\Framework\ComparisonMethodDoesNotAcceptParameterTypeException(\get_class($other), $this->method, \get_class($this->expected)); - } - return $other->{$this->method}($this->expected); - } - protected function failureDescription($other) : string + function assertStringContainsString(string $needle, string $haystack, string $message = '') : void { - return $this->toString(); + \PHPUnit\Framework\Assert::assertStringContainsString(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function sprintf; -use PHPUnit\Framework\Exception; -use ReflectionClass; -use ReflectionException; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class ClassHasStaticAttribute extends \PHPUnit\Framework\Constraint\ClassHasAttribute -{ +if (!\function_exists('PHPUnit\\Framework\\assertStringContainsStringIgnoringCase')) { /** - * Returns a string representation of the constraint. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringContainsStringIgnoringCase */ - public function toString() : string + function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void { - return \sprintf('has static attribute "%s"', $this->attributeName()); + \PHPUnit\Framework\Assert::assertStringContainsStringIgnoringCase(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertStringNotContainsString')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @param mixed $other value or object to evaluate + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotContainsString */ - protected function matches($other) : bool + function assertStringNotContainsString(string $needle, string $haystack, string $message = '') : void { - try { - $class = new \ReflectionClass($other); - if ($class->hasProperty($this->attributeName())) { - return $class->getProperty($this->attributeName())->isStatic(); - } - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd - return \false; + \PHPUnit\Framework\Assert::assertStringNotContainsString(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use ReflectionObject; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class ObjectHasAttribute extends \PHPUnit\Framework\Constraint\ClassHasAttribute -{ +if (!\function_exists('PHPUnit\\Framework\\assertStringNotContainsStringIgnoringCase')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @param mixed $other value or object to evaluate + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringNotContainsStringIgnoringCase */ - protected function matches($other) : bool + function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void { - return (new \ReflectionObject($other))->hasProperty($this->attributeName()); + \PHPUnit\Framework\Assert::assertStringNotContainsStringIgnoringCase(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function get_class; -use function is_object; -use function sprintf; -use PHPUnit\Framework\Exception; -use ReflectionClass; -use ReflectionException; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -class ClassHasAttribute extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertStringEndsWith')) { /** - * @var string + * Asserts that a string ends with a given suffix. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringEndsWith */ - private $attributeName; - public function __construct(string $attributeName) + function assertStringEndsWith(string $suffix, string $string, string $message = '') : void { - $this->attributeName = $attributeName; + \PHPUnit\Framework\Assert::assertStringEndsWith(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertStringEndsNotWith')) { /** - * Returns a string representation of the constraint. + * Asserts that a string ends not with a given suffix. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertStringEndsNotWith */ - public function toString() : string + function assertStringEndsNotWith(string $suffix, string $string, string $message = '') : void { - return \sprintf('has attribute "%s"', $this->attributeName); + \PHPUnit\Framework\Assert::assertStringEndsNotWith(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertXmlFileEqualsXmlFile')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that two XML files are equal. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertXmlFileEqualsXmlFile */ - protected function matches($other) : bool + function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void { - try { - return (new \ReflectionClass($other))->hasProperty($this->attributeName); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); - } - // @codeCoverageIgnoreEnd + \PHPUnit\Framework\Assert::assertXmlFileEqualsXmlFile(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertXmlFileNotEqualsXmlFile')) { /** - * Returns the description of the failure. + * Asserts that two XML files are not equal. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws \PHPUnit\Util\Exception * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertXmlFileNotEqualsXmlFile */ - protected function failureDescription($other) : string - { - return \sprintf('%sclass "%s" %s', \is_object($other) ? 'object of ' : '', \is_object($other) ? \get_class($other) : $other, $this->toString()); - } - protected function attributeName() : string + function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void { - return $this->attributeName; + \PHPUnit\Framework\Assert::assertXmlFileNotEqualsXmlFile(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function file_exists; -use function sprintf; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class FileExists extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertXmlStringEqualsXmlFile')) { /** - * Returns a string representation of the constraint. + * Asserts that two XML documents are equal. + * + * @param DOMDocument|string $actualXml + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws \PHPUnit\Util\Xml\Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertXmlStringEqualsXmlFile */ - public function toString() : string + function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void { - return 'file exists'; + \PHPUnit\Framework\Assert::assertXmlStringEqualsXmlFile(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertXmlStringNotEqualsXmlFile')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that two XML documents are not equal. * - * @param mixed $other value or object to evaluate + * @param DOMDocument|string $actualXml + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws \PHPUnit\Util\Xml\Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertXmlStringNotEqualsXmlFile */ - protected function matches($other) : bool + function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void { - return \file_exists($other); + \PHPUnit\Framework\Assert::assertXmlStringNotEqualsXmlFile(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertXmlStringEqualsXmlString')) { /** - * Returns the description of the failure. + * Asserts that two XML documents are equal. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @param DOMDocument|string $expectedXml + * @param DOMDocument|string $actualXml * - * @param mixed $other evaluated value or object + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws \PHPUnit\Util\Xml\Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertXmlStringEqualsXmlString */ - protected function failureDescription($other) : string + function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = '') : void { - return \sprintf('file "%s" exists', $other); + \PHPUnit\Framework\Assert::assertXmlStringEqualsXmlString(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_writable; -use function sprintf; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsWritable extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertXmlStringNotEqualsXmlString')) { /** - * Returns a string representation of the constraint. + * Asserts that two XML documents are not equal. + * + * @param DOMDocument|string $expectedXml + * @param DOMDocument|string $actualXml + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws \PHPUnit\Util\Xml\Exception + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertXmlStringNotEqualsXmlString */ - public function toString() : string + function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = '') : void { - return 'is writable'; + \PHPUnit\Framework\Assert::assertXmlStringNotEqualsXmlString(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertEqualXMLStructure')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that a hierarchy of DOMElements matches. * - * @param mixed $other value or object to evaluate + * @throws AssertionFailedError + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @codeCoverageIgnore + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4091 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertEqualXMLStructure */ - protected function matches($other) : bool + function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = \false, string $message = '') : void { - return \is_writable($other); + \PHPUnit\Framework\Assert::assertEqualXMLStructure(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertThat')) { /** - * Returns the description of the failure. + * Evaluates a PHPUnit\Framework\Constraint matcher object. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertThat */ - protected function failureDescription($other) : string + function assertThat($value, Constraint $constraint, string $message = '') : void { - return \sprintf('"%s" is writable', $other); + \PHPUnit\Framework\Assert::assertThat(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_dir; -use function sprintf; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class DirectoryExists extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertJson')) { /** - * Returns a string representation of the constraint. + * Asserts that a string is a valid JSON string. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJson */ - public function toString() : string + function assertJson(string $actualJson, string $message = '') : void { - return 'directory exists'; + \PHPUnit\Framework\Assert::assertJson(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertJsonStringEqualsJsonString')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that two given JSON encoded objects or arrays are equal. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJsonStringEqualsJsonString */ - protected function matches($other) : bool + function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = '') : void { - return \is_dir($other); + \PHPUnit\Framework\Assert::assertJsonStringEqualsJsonString(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertJsonStringNotEqualsJsonString')) { /** - * Returns the description of the failure. + * Asserts that two given JSON encoded objects or arrays are not equal. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @param string $expectedJson + * @param string $actualJson * - * @param mixed $other evaluated value or object + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJsonStringNotEqualsJsonString */ - protected function failureDescription($other) : string + function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = '') : void { - return \sprintf('directory "%s" exists', $other); + \PHPUnit\Framework\Assert::assertJsonStringNotEqualsJsonString(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use function is_readable; -use function sprintf; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsReadable extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertJsonStringEqualsJsonFile')) { /** - * Returns a string representation of the constraint. + * Asserts that the generated JSON encoded object and the content of the given file are equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJsonStringEqualsJsonFile */ - public function toString() : string + function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void { - return 'is readable'; + \PHPUnit\Framework\Assert::assertJsonStringEqualsJsonFile(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertJsonStringNotEqualsJsonFile')) { /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. + * Asserts that the generated JSON encoded object and the content of the given file are not equal. * - * @param mixed $other value or object to evaluate + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJsonStringNotEqualsJsonFile */ - protected function matches($other) : bool + function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void { - return \is_readable($other); + \PHPUnit\Framework\Assert::assertJsonStringNotEqualsJsonFile(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\assertJsonFileEqualsJsonFile')) { /** - * Returns the description of the failure. + * Asserts that two JSON files are equal. * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @param mixed $other evaluated value or object + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertJsonFileEqualsJsonFile */ - protected function failureDescription($other) : string + function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void { - return \sprintf('"%s" is readable', $other); + \PHPUnit\Framework\Assert::assertJsonFileEqualsJsonFile(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use PHPUnit\Framework\ExpectationFailedException; -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class IsAnything extends \PHPUnit\Framework\Constraint\Constraint -{ +if (!\function_exists('PHPUnit\\Framework\\assertJsonFileNotEqualsJsonFile')) { /** - * Evaluates the constraint for parameter $other. + * Asserts that two JSON files are not equal. * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @throws ExpectationFailedException + * @see Assert::assertJsonFileNotEqualsJsonFile */ - public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void { - return $returnResult ? \true : null; + \PHPUnit\Framework\Assert::assertJsonFileNotEqualsJsonFile(...func_get_args()); } - /** - * Returns a string representation of the constraint. - */ - public function toString() : string +} +if (!\function_exists('PHPUnit\\Framework\\logicalAnd')) { + function logicalAnd() : LogicalAnd + { + return \PHPUnit\Framework\Assert::logicalAnd(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\logicalOr')) { + function logicalOr() : LogicalOr + { + return \PHPUnit\Framework\Assert::logicalOr(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\logicalNot')) { + function logicalNot(Constraint $constraint) : LogicalNot + { + return \PHPUnit\Framework\Assert::logicalNot(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\logicalXor')) { + function logicalXor() : LogicalXor + { + return \PHPUnit\Framework\Assert::logicalXor(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\anything')) { + function anything() : IsAnything + { + return \PHPUnit\Framework\Assert::anything(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\isTrue')) { + function isTrue() : IsTrue + { + return \PHPUnit\Framework\Assert::isTrue(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\callback')) { + function callback(callable $callback) : Callback + { + return \PHPUnit\Framework\Assert::callback(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\isFalse')) { + function isFalse() : IsFalse + { + return \PHPUnit\Framework\Assert::isFalse(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\isJson')) { + function isJson() : IsJson + { + return \PHPUnit\Framework\Assert::isJson(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\isNull')) { + function isNull() : IsNull + { + return \PHPUnit\Framework\Assert::isNull(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\isFinite')) { + function isFinite() : IsFinite + { + return \PHPUnit\Framework\Assert::isFinite(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\isInfinite')) { + function isInfinite() : IsInfinite + { + return \PHPUnit\Framework\Assert::isInfinite(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\isNan')) { + function isNan() : IsNan + { + return \PHPUnit\Framework\Assert::isNan(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\containsEqual')) { + function containsEqual($value) : TraversableContainsEqual + { + return \PHPUnit\Framework\Assert::containsEqual(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\containsIdentical')) { + function containsIdentical($value) : TraversableContainsIdentical + { + return \PHPUnit\Framework\Assert::containsIdentical(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\containsOnly')) { + function containsOnly(string $type) : TraversableContainsOnly + { + return \PHPUnit\Framework\Assert::containsOnly(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\containsOnlyInstancesOf')) { + function containsOnlyInstancesOf(string $className) : TraversableContainsOnly + { + return \PHPUnit\Framework\Assert::containsOnlyInstancesOf(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\arrayHasKey')) { + function arrayHasKey($key) : ArrayHasKey + { + return \PHPUnit\Framework\Assert::arrayHasKey(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\equalTo')) { + function equalTo($value) : IsEqual + { + return \PHPUnit\Framework\Assert::equalTo(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\equalToCanonicalizing')) { + function equalToCanonicalizing($value) : IsEqualCanonicalizing + { + return \PHPUnit\Framework\Assert::equalToCanonicalizing(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\equalToIgnoringCase')) { + function equalToIgnoringCase($value) : IsEqualIgnoringCase + { + return \PHPUnit\Framework\Assert::equalToIgnoringCase(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\equalToWithDelta')) { + function equalToWithDelta($value, float $delta) : IsEqualWithDelta + { + return \PHPUnit\Framework\Assert::equalToWithDelta(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\isEmpty')) { + function isEmpty() : IsEmpty + { + return \PHPUnit\Framework\Assert::isEmpty(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\isWritable')) { + function isWritable() : IsWritable + { + return \PHPUnit\Framework\Assert::isWritable(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\isReadable')) { + function isReadable() : IsReadable + { + return \PHPUnit\Framework\Assert::isReadable(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\directoryExists')) { + function directoryExists() : DirectoryExists + { + return \PHPUnit\Framework\Assert::directoryExists(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\fileExists')) { + function fileExists() : FileExists + { + return \PHPUnit\Framework\Assert::fileExists(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\greaterThan')) { + function greaterThan($value) : GreaterThan + { + return \PHPUnit\Framework\Assert::greaterThan(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\greaterThanOrEqual')) { + function greaterThanOrEqual($value) : LogicalOr + { + return \PHPUnit\Framework\Assert::greaterThanOrEqual(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\classHasAttribute')) { + function classHasAttribute(string $attributeName) : ClassHasAttribute + { + return \PHPUnit\Framework\Assert::classHasAttribute(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\classHasStaticAttribute')) { + function classHasStaticAttribute(string $attributeName) : ClassHasStaticAttribute + { + return \PHPUnit\Framework\Assert::classHasStaticAttribute(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\objectHasAttribute')) { + function objectHasAttribute($attributeName) : ObjectHasAttribute + { + return \PHPUnit\Framework\Assert::objectHasAttribute(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\identicalTo')) { + function identicalTo($value) : IsIdentical + { + return \PHPUnit\Framework\Assert::identicalTo(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\isInstanceOf')) { + function isInstanceOf(string $className) : IsInstanceOf + { + return \PHPUnit\Framework\Assert::isInstanceOf(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\isType')) { + function isType(string $type) : IsType + { + return \PHPUnit\Framework\Assert::isType(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\lessThan')) { + function lessThan($value) : LessThan + { + return \PHPUnit\Framework\Assert::lessThan(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\lessThanOrEqual')) { + function lessThanOrEqual($value) : LogicalOr + { + return \PHPUnit\Framework\Assert::lessThanOrEqual(...func_get_args()); + } +} +if (!\function_exists('PHPUnit\\Framework\\matchesRegularExpression')) { + function matchesRegularExpression(string $pattern) : RegularExpression { - return 'is anything'; + return \PHPUnit\Framework\Assert::matchesRegularExpression(...func_get_args()); } - /** - * Counts the number of constraint elements. - */ - public function count() : int +} +if (!\function_exists('PHPUnit\\Framework\\matches')) { + function matches(string $string) : StringMatchesFormatDescription { - return 0; + return \PHPUnit\Framework\Assert::matches(...func_get_args()); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use function get_class; -use function sprintf; -use function trim; -use PHPUnit\Framework\Error\Error; -use Throwable; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestFailure -{ - /** - * @var null|Test - */ - private $failedTest; - /** - * @var Throwable - */ - private $thrownException; - /** - * @var string - */ - private $testName; - /** - * Returns a description for an exception. - */ - public static function exceptionToString(\Throwable $e) : string +if (!\function_exists('PHPUnit\\Framework\\stringStartsWith')) { + function stringStartsWith($prefix) : StringStartsWith { - if ($e instanceof \PHPUnit\Framework\SelfDescribing) { - $buffer = $e->toString(); - if ($e instanceof \PHPUnit\Framework\ExpectationFailedException && $e->getComparisonFailure()) { - $buffer .= $e->getComparisonFailure()->getDiff(); - } - if ($e instanceof \PHPUnit\Framework\PHPTAssertionFailedError) { - $buffer .= $e->getDiff(); - } - if (!empty($buffer)) { - $buffer = \trim($buffer) . "\n"; - } - return $buffer; - } - if ($e instanceof \PHPUnit\Framework\Error\Error) { - return $e->getMessage() . "\n"; - } - if ($e instanceof \PHPUnit\Framework\ExceptionWrapper) { - return $e->getClassName() . ': ' . $e->getMessage() . "\n"; - } - return \get_class($e) . ': ' . $e->getMessage() . "\n"; + return \PHPUnit\Framework\Assert::stringStartsWith(...func_get_args()); } - /** - * Constructs a TestFailure with the given test and exception. - */ - public function __construct(\PHPUnit\Framework\Test $failedTest, \Throwable $t) +} +if (!\function_exists('PHPUnit\\Framework\\stringContains')) { + function stringContains(string $string, bool $case = \true) : StringContains { - if ($failedTest instanceof \PHPUnit\Framework\SelfDescribing) { - $this->testName = $failedTest->toString(); - } else { - $this->testName = \get_class($failedTest); - } - if (!$failedTest instanceof \PHPUnit\Framework\TestCase || !$failedTest->isInIsolation()) { - $this->failedTest = $failedTest; - } - $this->thrownException = $t; + return \PHPUnit\Framework\Assert::stringContains(...func_get_args()); } - /** - * Returns a short description of the failure. - */ - public function toString() : string +} +if (!\function_exists('PHPUnit\\Framework\\stringEndsWith')) { + function stringEndsWith(string $suffix) : StringEndsWith { - return \sprintf('%s: %s', $this->testName, $this->thrownException->getMessage()); + return \PHPUnit\Framework\Assert::stringEndsWith(...func_get_args()); } - /** - * Returns a description for the thrown exception. - */ - public function getExceptionAsString() : string +} +if (!\function_exists('PHPUnit\\Framework\\countOf')) { + function countOf(int $count) : Count { - return self::exceptionToString($this->thrownException); + return \PHPUnit\Framework\Assert::countOf(...func_get_args()); } - /** - * Returns the name of the failing test (including data set, if any). - */ - public function getTestName() : string +} +if (!\function_exists('PHPUnit\\Framework\\objectEquals')) { + function objectEquals(object $object, string $method = 'equals') : ObjectEquals { - return $this->testName; + return \PHPUnit\Framework\Assert::objectEquals(...func_get_args()); } +} +if (!\function_exists('PHPUnit\\Framework\\any')) { /** - * Returns the failing test. - * - * Note: The test object is not set when the test is executed in process - * isolation. - * - * @see Exception + * Returns a matcher that matches when the method is executed + * zero or more times. */ - public function failedTest() : ?\PHPUnit\Framework\Test + function any() : AnyInvokedCountMatcher { - return $this->failedTest; + return new AnyInvokedCountMatcher(); } +} +if (!\function_exists('PHPUnit\\Framework\\never')) { /** - * Gets the thrown exception. + * Returns a matcher that matches when the method is never executed. */ - public function thrownException() : \Throwable + function never() : InvokedCountMatcher { - return $this->thrownException; + return new InvokedCountMatcher(0); } +} +if (!\function_exists('PHPUnit\\Framework\\atLeast')) { /** - * Returns the exception's message. + * Returns a matcher that matches when the method is executed + * at least N times. */ - public function exceptionMessage() : string + function atLeast(int $requiredInvocations) : InvokedAtLeastCountMatcher { - return $this->thrownException()->getMessage(); + return new InvokedAtLeastCountMatcher($requiredInvocations); } +} +if (!\function_exists('PHPUnit\\Framework\\atLeastOnce')) { /** - * Returns true if the thrown exception - * is of type AssertionFailedError. + * Returns a matcher that matches when the method is executed at least once. */ - public function isFailure() : bool + function atLeastOnce() : InvokedAtLeastOnceMatcher { - return $this->thrownException() instanceof \PHPUnit\Framework\AssertionFailedError; + return new InvokedAtLeastOnceMatcher(); } } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use function array_filter; -use function array_map; -use function array_values; -use function count; -use function explode; -use function in_array; -use function strpos; -use function trim; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ExecutionOrderDependency -{ - /** - * @var string - */ - private $className = ''; - /** - * @var string - */ - private $methodName = ''; - /** - * @var bool - */ - private $useShallowClone = \false; +if (!\function_exists('PHPUnit\\Framework\\once')) { /** - * @var bool + * Returns a matcher that matches when the method is executed exactly once. */ - private $useDeepClone = \false; - public static function createFromDependsAnnotation(string $className, string $annotation) : self + function once() : InvokedCountMatcher { - // Split clone option and target - $parts = \explode(' ', \trim($annotation), 2); - if (\count($parts) === 1) { - $cloneOption = ''; - $target = $parts[0]; - } else { - $cloneOption = $parts[0]; - $target = $parts[1]; - } - // Prefix provided class for targets assumed to be in scope - if ($target !== '' && \strpos($target, '::') === \false) { - $target = $className . '::' . $target; - } - return new self($target, null, $cloneOption); + return new InvokedCountMatcher(1); } +} +if (!\function_exists('PHPUnit\\Framework\\exactly')) { /** - * @psalm-param list $dependencies - * - * @psalm-return list + * Returns a matcher that matches when the method is executed + * exactly $count times. */ - public static function filterInvalid(array $dependencies) : array + function exactly(int $count) : InvokedCountMatcher { - return \array_values(\array_filter($dependencies, static function (self $d) { - return $d->isValid(); - })); + return new InvokedCountMatcher($count); } +} +if (!\function_exists('PHPUnit\\Framework\\atMost')) { /** - * @psalm-param list $existing - * @psalm-param list $additional - * - * @psalm-return list + * Returns a matcher that matches when the method is executed + * at most N times. */ - public static function mergeUnique(array $existing, array $additional) : array + function atMost(int $allowedInvocations) : InvokedAtMostCountMatcher { - $existingTargets = \array_map(static function ($dependency) { - return $dependency->getTarget(); - }, $existing); - foreach ($additional as $dependency) { - if (\in_array($dependency->getTarget(), $existingTargets, \true)) { - continue; - } - $existingTargets[] = $dependency->getTarget(); - $existing[] = $dependency; - } - return $existing; + return new InvokedAtMostCountMatcher($allowedInvocations); } +} +if (!\function_exists('PHPUnit\\Framework\\at')) { /** - * @psalm-param list $left - * @psalm-param list $right - * - * @psalm-return list + * Returns a matcher that matches when the method is executed + * at the given index. */ - public static function diff(array $left, array $right) : array - { - if ($right === []) { - return $left; - } - if ($left === []) { - return []; - } - $diff = []; - $rightTargets = \array_map(static function ($dependency) { - return $dependency->getTarget(); - }, $right); - foreach ($left as $dependency) { - if (\in_array($dependency->getTarget(), $rightTargets, \true)) { - continue; - } - $diff[] = $dependency; - } - return $diff; - } - public function __construct(string $classOrCallableName, ?string $methodName = null, ?string $option = null) + function at(int $index) : InvokedAtIndexMatcher { - if ($classOrCallableName === '') { - return; - } - if (\strpos($classOrCallableName, '::') !== \false) { - [$this->className, $this->methodName] = \explode('::', $classOrCallableName); - } else { - $this->className = $classOrCallableName; - $this->methodName = !empty($methodName) ? $methodName : 'class'; - } - if ($option === 'clone') { - $this->useDeepClone = \true; - } elseif ($option === 'shallowClone') { - $this->useShallowClone = \true; - } + return new InvokedAtIndexMatcher($index); } - public function __toString() : string +} +if (!\function_exists('PHPUnit\\Framework\\returnValue')) { + function returnValue($value) : ReturnStub { - return $this->getTarget(); + return new ReturnStub($value); } - public function isValid() : bool +} +if (!\function_exists('PHPUnit\\Framework\\returnValueMap')) { + function returnValueMap(array $valueMap) : ReturnValueMapStub { - // Invalid dependencies can be declared and are skipped by the runner - return $this->className !== '' && $this->methodName !== ''; + return new ReturnValueMapStub($valueMap); } - public function useShallowClone() : bool +} +if (!\function_exists('PHPUnit\\Framework\\returnArgument')) { + function returnArgument(int $argumentIndex) : ReturnArgumentStub { - return $this->useShallowClone; + return new ReturnArgumentStub($argumentIndex); } - public function useDeepClone() : bool +} +if (!\function_exists('PHPUnit\\Framework\\returnCallback')) { + function returnCallback($callback) : ReturnCallbackStub { - return $this->useDeepClone; + return new ReturnCallbackStub($callback); } - public function targetIsClass() : bool +} +if (!\function_exists('PHPUnit\\Framework\\returnSelf')) { + /** + * Returns the current object. + * + * This method is useful when mocking a fluent interface. + */ + function returnSelf() : ReturnSelfStub { - return $this->methodName === 'class'; + return new ReturnSelfStub(); } - public function getTarget() : string +} +if (!\function_exists('PHPUnit\\Framework\\throwException')) { + function throwException(Throwable $exception) : ExceptionStub { - return $this->isValid() ? $this->className . '::' . $this->methodName : ''; + return new ExceptionStub($exception); } - public function getTargetClassName() : string +} +if (!\function_exists('PHPUnit\\Framework\\onConsecutiveCalls')) { + function onConsecutiveCalls() : ConsecutiveCallsStub { - return $this->className; + $args = func_get_args(); + return new ConsecutiveCallsStub($args); } } tests = $testSuite->tests(); - } - public function rewind() : void - { - $this->position = 0; - } - public function valid() : bool - { - return $this->position < \count($this->tests); - } - public function key() : int - { - return $this->position; - } - public function current() : \PHPUnit\Framework\Test - { - return $this->tests[$this->position]; - } - public function next() : void - { - $this->position++; - } - /** - * @throws NoChildTestSuiteException + * Runs a test and collects its result in a TestResult instance. */ - public function getChildren() : self - { - if (!$this->hasChildren()) { - throw new \PHPUnit\Framework\NoChildTestSuiteException('The current item is not a TestSuite instance and therefore does not have any children.'); - } - $current = $this->current(); - \assert($current instanceof \PHPUnit\Framework\TestSuite); - return new self($current); - } - public function hasChildren() : bool - { - return $this->valid() && $this->current() instanceof \PHPUnit\Framework\TestSuite; - } + public function run(\PHPUnit\Framework\TestResult $result = null) : \PHPUnit\Framework\TestResult; } $expected * @psalm-assert =ExpectedType $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertInstanceOf */ - function assertInstanceOf(string $expected, $actual, string $message = '') : void + public static function assertInstanceOf(string $expected, $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertInstanceOf(...\func_get_args()); + if (!class_exists($expected) && !interface_exists($expected)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'class or interface name'); + } + static::assertThat($actual, new IsInstanceOf($expected), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertNotInstanceOf')) { /** * Asserts that a variable is not of a given type. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws Exception + * @throws ExpectationFailedException * * @psalm-template ExpectedType of object * @psalm-param class-string $expected * @psalm-assert !ExpectedType $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotInstanceOf */ - function assertNotInstanceOf(string $expected, $actual, string $message = '') : void + public static function assertNotInstanceOf(string $expected, $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertNotInstanceOf(...\func_get_args()); + if (!class_exists($expected) && !interface_exists($expected)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'class or interface name'); + } + static::assertThat($actual, new LogicalNot(new IsInstanceOf($expected)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsArray')) { /** * Asserts that a variable is of type array. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert array $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsArray */ - function assertIsArray($actual, string $message = '') : void + public static function assertIsArray($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsArray(...\func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_ARRAY), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsBool')) { /** * Asserts that a variable is of type bool. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert bool $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsBool */ - function assertIsBool($actual, string $message = '') : void + public static function assertIsBool($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsBool(...\func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_BOOL), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsFloat')) { /** * Asserts that a variable is of type float. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert float $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsFloat */ - function assertIsFloat($actual, string $message = '') : void + public static function assertIsFloat($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsFloat(...\func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_FLOAT), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsInt')) { /** * Asserts that a variable is of type int. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert int $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsInt */ - function assertIsInt($actual, string $message = '') : void + public static function assertIsInt($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsInt(...\func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_INT), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsNumeric')) { /** * Asserts that a variable is of type numeric. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert numeric $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNumeric */ - function assertIsNumeric($actual, string $message = '') : void + public static function assertIsNumeric($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNumeric(...\func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_NUMERIC), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsObject')) { /** * Asserts that a variable is of type object. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert object $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsObject */ - function assertIsObject($actual, string $message = '') : void + public static function assertIsObject($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsObject(...\func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_OBJECT), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsResource')) { /** * Asserts that a variable is of type resource. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert resource $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsResource */ - function assertIsResource($actual, string $message = '') : void + public static function assertIsResource($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsResource(...\func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_RESOURCE), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsClosedResource')) { /** * Asserts that a variable is of type resource and is closed. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert resource $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsClosedResource */ - function assertIsClosedResource($actual, string $message = '') : void + public static function assertIsClosedResource($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsClosedResource(...\func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_CLOSED_RESOURCE), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsString')) { /** * Asserts that a variable is of type string. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert string $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsString */ - function assertIsString($actual, string $message = '') : void + public static function assertIsString($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsString(...\func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_STRING), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsScalar')) { /** * Asserts that a variable is of type scalar. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert scalar $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsScalar */ - function assertIsScalar($actual, string $message = '') : void + public static function assertIsScalar($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsScalar(...\func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_SCALAR), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsCallable')) { /** * Asserts that a variable is of type callable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert callable $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsCallable */ - function assertIsCallable($actual, string $message = '') : void + public static function assertIsCallable($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsCallable(...\func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_CALLABLE), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsIterable')) { /** * Asserts that a variable is of type iterable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert iterable $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsIterable */ - function assertIsIterable($actual, string $message = '') : void + public static function assertIsIterable($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsIterable(...\func_get_args()); + static::assertThat($actual, new IsType(IsType::TYPE_ITERABLE), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsNotArray')) { /** * Asserts that a variable is not of type array. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert !array $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotArray */ - function assertIsNotArray($actual, string $message = '') : void + public static function assertIsNotArray($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotArray(...\func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_ARRAY)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsNotBool')) { /** * Asserts that a variable is not of type bool. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert !bool $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotBool */ - function assertIsNotBool($actual, string $message = '') : void + public static function assertIsNotBool($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotBool(...\func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_BOOL)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsNotFloat')) { /** * Asserts that a variable is not of type float. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert !float $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotFloat */ - function assertIsNotFloat($actual, string $message = '') : void + public static function assertIsNotFloat($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotFloat(...\func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_FLOAT)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsNotInt')) { /** * Asserts that a variable is not of type int. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert !int $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotInt */ - function assertIsNotInt($actual, string $message = '') : void + public static function assertIsNotInt($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotInt(...\func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_INT)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsNotNumeric')) { /** * Asserts that a variable is not of type numeric. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert !numeric $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotNumeric */ - function assertIsNotNumeric($actual, string $message = '') : void + public static function assertIsNotNumeric($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotNumeric(...\func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_NUMERIC)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsNotObject')) { /** * Asserts that a variable is not of type object. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert !object $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotObject */ - function assertIsNotObject($actual, string $message = '') : void + public static function assertIsNotObject($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotObject(...\func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_OBJECT)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsNotResource')) { /** * Asserts that a variable is not of type resource. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert !resource $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotResource */ - function assertIsNotResource($actual, string $message = '') : void + public static function assertIsNotResource($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotResource(...\func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_RESOURCE)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsNotClosedResource')) { /** * Asserts that a variable is not of type resource. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert !resource $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotClosedResource */ - function assertIsNotClosedResource($actual, string $message = '') : void + public static function assertIsNotClosedResource($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotClosedResource(...\func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_CLOSED_RESOURCE)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsNotString')) { /** * Asserts that a variable is not of type string. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * - * @psalm-assert !string $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotString + * @psalm-assert !string $actual */ - function assertIsNotString($actual, string $message = '') : void + public static function assertIsNotString($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotString(...\func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_STRING)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsNotScalar')) { /** * Asserts that a variable is not of type scalar. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert !scalar $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotScalar */ - function assertIsNotScalar($actual, string $message = '') : void + public static function assertIsNotScalar($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotScalar(...\func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_SCALAR)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsNotCallable')) { /** * Asserts that a variable is not of type callable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert !callable $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotCallable */ - function assertIsNotCallable($actual, string $message = '') : void + public static function assertIsNotCallable($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotCallable(...\func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_CALLABLE)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertIsNotIterable')) { /** * Asserts that a variable is not of type iterable. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @psalm-assert !iterable $actual - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertIsNotIterable */ - function assertIsNotIterable($actual, string $message = '') : void + public static function assertIsNotIterable($actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertIsNotIterable(...\func_get_args()); + static::assertThat($actual, new LogicalNot(new IsType(IsType::TYPE_ITERABLE)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertMatchesRegularExpression')) { /** * Asserts that a string matches a given regular expression. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertMatchesRegularExpression + * @throws ExpectationFailedException */ - function assertMatchesRegularExpression(string $pattern, string $string, string $message = '') : void + public static function assertMatchesRegularExpression(string $pattern, string $string, string $message = '') : void { - \PHPUnit\Framework\Assert::assertMatchesRegularExpression(...\func_get_args()); + static::assertThat($string, new RegularExpression($pattern), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertRegExp')) { /** * Asserts that a string matches a given regular expression. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @codeCoverageIgnore * * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4086 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertRegExp */ - function assertRegExp(string $pattern, string $string, string $message = '') : void + public static function assertRegExp(string $pattern, string $string, string $message = '') : void { - \PHPUnit\Framework\Assert::assertRegExp(...\func_get_args()); + self::createWarning('assertRegExp() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertMatchesRegularExpression() instead.'); + static::assertThat($string, new RegularExpression($pattern), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertDoesNotMatchRegularExpression')) { /** * Asserts that a string does not match a given regular expression. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertDoesNotMatchRegularExpression + * @throws ExpectationFailedException */ - function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = '') : void + public static function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = '') : void { - \PHPUnit\Framework\Assert::assertDoesNotMatchRegularExpression(...\func_get_args()); + static::assertThat($string, new LogicalNot(new RegularExpression($pattern)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertNotRegExp')) { /** * Asserts that a string does not match a given regular expression. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException * * @codeCoverageIgnore * * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4089 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotRegExp */ - function assertNotRegExp(string $pattern, string $string, string $message = '') : void + public static function assertNotRegExp(string $pattern, string $string, string $message = '') : void { - \PHPUnit\Framework\Assert::assertNotRegExp(...\func_get_args()); + self::createWarning('assertNotRegExp() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDoesNotMatchRegularExpression() instead.'); + static::assertThat($string, new LogicalNot(new RegularExpression($pattern)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertSameSize')) { /** * Assert that the size of two arrays (or `Countable` or `Traversable` objects) * is the same. @@ -72193,20 +74894,20 @@ if (!\function_exists('PHPUnit\\Framework\\assertSameSize')) { * @param Countable|iterable $expected * @param Countable|iterable $actual * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertSameSize + * @throws ExpectationFailedException */ - function assertSameSize($expected, $actual, string $message = '') : void + public static function assertSameSize($expected, $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertSameSize(...\func_get_args()); + if (!$expected instanceof Countable && !is_iterable($expected)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'countable or iterable'); + } + if (!$actual instanceof Countable && !is_iterable($actual)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'countable or iterable'); + } + static::assertThat($actual, new SameSize($expected), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertNotSameSize')) { /** * Assert that the size of two arrays (or `Countable` or `Traversable` objects) * is not the same. @@ -72214,857 +74915,1644 @@ if (!\function_exists('PHPUnit\\Framework\\assertNotSameSize')) { * @param Countable|iterable $expected * @param Countable|iterable $actual * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertNotSameSize + * @throws ExpectationFailedException */ - function assertNotSameSize($expected, $actual, string $message = '') : void + public static function assertNotSameSize($expected, $actual, string $message = '') : void { - \PHPUnit\Framework\Assert::assertNotSameSize(...\func_get_args()); + if (!$expected instanceof Countable && !is_iterable($expected)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(1, 'countable or iterable'); + } + if (!$actual instanceof Countable && !is_iterable($actual)) { + throw \PHPUnit\Framework\InvalidArgumentException::create(2, 'countable or iterable'); + } + static::assertThat($actual, new LogicalNot(new SameSize($expected)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertStringMatchesFormat')) { /** * Asserts that a string matches a given format string. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringMatchesFormat + * @throws ExpectationFailedException */ - function assertStringMatchesFormat(string $format, string $string, string $message = '') : void + public static function assertStringMatchesFormat(string $format, string $string, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringMatchesFormat(...\func_get_args()); + static::assertThat($string, new StringMatchesFormatDescription($format), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertStringNotMatchesFormat')) { /** * Asserts that a string does not match a given format string. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringNotMatchesFormat + * @throws ExpectationFailedException */ - function assertStringNotMatchesFormat(string $format, string $string, string $message = '') : void + public static function assertStringNotMatchesFormat(string $format, string $string, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringNotMatchesFormat(...\func_get_args()); + static::assertThat($string, new LogicalNot(new StringMatchesFormatDescription($format)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertStringMatchesFormatFile')) { /** * Asserts that a string matches a given format file. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringMatchesFormatFile + * @throws ExpectationFailedException */ - function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = '') : void + public static function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringMatchesFormatFile(...\func_get_args()); + static::assertFileExists($formatFile, $message); + static::assertThat($string, new StringMatchesFormatDescription(file_get_contents($formatFile)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertStringNotMatchesFormatFile')) { /** * Asserts that a string does not match a given format string. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringNotMatchesFormatFile + * @throws ExpectationFailedException */ - function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = '') : void + public static function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringNotMatchesFormatFile(...\func_get_args()); + static::assertFileExists($formatFile, $message); + static::assertThat($string, new LogicalNot(new StringMatchesFormatDescription(file_get_contents($formatFile))), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertStringStartsWith')) { /** * Asserts that a string starts with a given prefix. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringStartsWith + * @throws ExpectationFailedException */ - function assertStringStartsWith(string $prefix, string $string, string $message = '') : void + public static function assertStringStartsWith(string $prefix, string $string, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringStartsWith(...\func_get_args()); + static::assertThat($string, new StringStartsWith($prefix), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertStringStartsNotWith')) { /** * Asserts that a string starts not with a given prefix. * * @param string $prefix * @param string $string * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringStartsNotWith + * @throws ExpectationFailedException */ - function assertStringStartsNotWith($prefix, $string, string $message = '') : void + public static function assertStringStartsNotWith($prefix, $string, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringStartsNotWith(...\func_get_args()); + static::assertThat($string, new LogicalNot(new StringStartsWith($prefix)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertStringContainsString')) { /** - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringContainsString + * @throws ExpectationFailedException */ - function assertStringContainsString(string $needle, string $haystack, string $message = '') : void + public static function assertStringContainsString(string $needle, string $haystack, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringContainsString(...\func_get_args()); + $constraint = new StringContains($needle, \false); + static::assertThat($haystack, $constraint, $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertStringContainsStringIgnoringCase')) { /** - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringContainsStringIgnoringCase + * @throws ExpectationFailedException */ - function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void + public static function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringContainsStringIgnoringCase(...\func_get_args()); + $constraint = new StringContains($needle, \true); + static::assertThat($haystack, $constraint, $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertStringNotContainsString')) { /** - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringNotContainsString + * @throws ExpectationFailedException */ - function assertStringNotContainsString(string $needle, string $haystack, string $message = '') : void + public static function assertStringNotContainsString(string $needle, string $haystack, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringNotContainsString(...\func_get_args()); + $constraint = new LogicalNot(new StringContains($needle)); + static::assertThat($haystack, $constraint, $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertStringNotContainsStringIgnoringCase')) { /** - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringNotContainsStringIgnoringCase + * @throws ExpectationFailedException */ - function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void + public static function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringNotContainsStringIgnoringCase(...\func_get_args()); + $constraint = new LogicalNot(new StringContains($needle, \true)); + static::assertThat($haystack, $constraint, $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertStringEndsWith')) { /** * Asserts that a string ends with a given suffix. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringEndsWith + * @throws ExpectationFailedException */ - function assertStringEndsWith(string $suffix, string $string, string $message = '') : void + public static function assertStringEndsWith(string $suffix, string $string, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringEndsWith(...\func_get_args()); + static::assertThat($string, new StringEndsWith($suffix), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertStringEndsNotWith')) { /** * Asserts that a string ends not with a given suffix. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringEndsNotWith + * @throws ExpectationFailedException */ - function assertStringEndsNotWith(string $suffix, string $string, string $message = '') : void + public static function assertStringEndsNotWith(string $suffix, string $string, string $message = '') : void { - \PHPUnit\Framework\Assert::assertStringEndsNotWith(...\func_get_args()); + static::assertThat($string, new LogicalNot(new StringEndsWith($suffix)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertXmlFileEqualsXmlFile')) { /** * Asserts that two XML files are equal. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertXmlFileEqualsXmlFile + * @throws ExpectationFailedException */ - function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void + public static function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void { - \PHPUnit\Framework\Assert::assertXmlFileEqualsXmlFile(...\func_get_args()); + $expected = (new XmlLoader())->loadFile($expectedFile); + $actual = (new XmlLoader())->loadFile($actualFile); + static::assertEquals($expected, $actual, $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertXmlFileNotEqualsXmlFile')) { /** * Asserts that two XML files are not equal. * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws \PHPUnit\Util\Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertXmlFileNotEqualsXmlFile + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void + public static function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') : void { - \PHPUnit\Framework\Assert::assertXmlFileNotEqualsXmlFile(...\func_get_args()); + $expected = (new XmlLoader())->loadFile($expectedFile); + $actual = (new XmlLoader())->loadFile($actualFile); + static::assertNotEquals($expected, $actual, $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertXmlStringEqualsXmlFile')) { /** * Asserts that two XML documents are equal. * * @param DOMDocument|string $actualXml * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws \PHPUnit\Util\Xml\Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertXmlStringEqualsXmlFile + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void + public static function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void { - \PHPUnit\Framework\Assert::assertXmlStringEqualsXmlFile(...\func_get_args()); + if (!is_string($actualXml)) { + self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + $actual = $actualXml; + } else { + $actual = (new XmlLoader())->load($actualXml); + } + $expected = (new XmlLoader())->loadFile($expectedFile); + static::assertEquals($expected, $actual, $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertXmlStringNotEqualsXmlFile')) { /** * Asserts that two XML documents are not equal. * * @param DOMDocument|string $actualXml * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws \PHPUnit\Util\Xml\Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertXmlStringNotEqualsXmlFile + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void + public static function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') : void { - \PHPUnit\Framework\Assert::assertXmlStringNotEqualsXmlFile(...\func_get_args()); + if (!is_string($actualXml)) { + self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + $actual = $actualXml; + } else { + $actual = (new XmlLoader())->load($actualXml); + } + $expected = (new XmlLoader())->loadFile($expectedFile); + static::assertNotEquals($expected, $actual, $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertXmlStringEqualsXmlString')) { /** * Asserts that two XML documents are equal. * * @param DOMDocument|string $expectedXml * @param DOMDocument|string $actualXml * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws \PHPUnit\Util\Xml\Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertXmlStringEqualsXmlString + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = '') : void + public static function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = '') : void { - \PHPUnit\Framework\Assert::assertXmlStringEqualsXmlString(...\func_get_args()); + if (!is_string($expectedXml)) { + self::createWarning('Passing an argument of type DOMDocument for the $expectedXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + $expected = $expectedXml; + } else { + $expected = (new XmlLoader())->load($expectedXml); + } + if (!is_string($actualXml)) { + self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + $actual = $actualXml; + } else { + $actual = (new XmlLoader())->load($actualXml); + } + static::assertEquals($expected, $actual, $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertXmlStringNotEqualsXmlString')) { /** * Asserts that two XML documents are not equal. * * @param DOMDocument|string $expectedXml * @param DOMDocument|string $actualXml * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws \PHPUnit\Util\Xml\Exception - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertXmlStringNotEqualsXmlString + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = '') : void + public static function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = '') : void { - \PHPUnit\Framework\Assert::assertXmlStringNotEqualsXmlString(...\func_get_args()); + if (!is_string($expectedXml)) { + self::createWarning('Passing an argument of type DOMDocument for the $expectedXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + $expected = $expectedXml; + } else { + $expected = (new XmlLoader())->load($expectedXml); + } + if (!is_string($actualXml)) { + self::createWarning('Passing an argument of type DOMDocument for the $actualXml parameter is deprecated. Support for this will be removed in PHPUnit 10.'); + $actual = $actualXml; + } else { + $actual = (new XmlLoader())->load($actualXml); + } + static::assertNotEquals($expected, $actual, $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertEqualXMLStructure')) { /** * Asserts that a hierarchy of DOMElements matches. * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws AssertionFailedError * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * * @codeCoverageIgnore * * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4091 - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertEqualXMLStructure */ - function assertEqualXMLStructure(\DOMElement $expectedElement, \DOMElement $actualElement, bool $checkAttributes = \false, string $message = '') : void + public static function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = \false, string $message = '') : void { - \PHPUnit\Framework\Assert::assertEqualXMLStructure(...\func_get_args()); + self::createWarning('assertEqualXMLStructure() is deprecated and will be removed in PHPUnit 10.'); + $expectedElement = Xml::import($expectedElement); + $actualElement = Xml::import($actualElement); + static::assertSame($expectedElement->tagName, $actualElement->tagName, $message); + if ($checkAttributes) { + static::assertSame($expectedElement->attributes->length, $actualElement->attributes->length, sprintf('%s%sNumber of attributes on node "%s" does not match', $message, !empty($message) ? "\n" : '', $expectedElement->tagName)); + for ($i = 0; $i < $expectedElement->attributes->length; $i++) { + $expectedAttribute = $expectedElement->attributes->item($i); + $actualAttribute = $actualElement->attributes->getNamedItem($expectedAttribute->name); + assert($expectedAttribute instanceof DOMAttr); + if (!$actualAttribute) { + static::fail(sprintf('%s%sCould not find attribute "%s" on node "%s"', $message, !empty($message) ? "\n" : '', $expectedAttribute->name, $expectedElement->tagName)); + } + } + } + Xml::removeCharacterDataNodes($expectedElement); + Xml::removeCharacterDataNodes($actualElement); + static::assertSame($expectedElement->childNodes->length, $actualElement->childNodes->length, sprintf('%s%sNumber of child nodes of "%s" differs', $message, !empty($message) ? "\n" : '', $expectedElement->tagName)); + for ($i = 0; $i < $expectedElement->childNodes->length; $i++) { + static::assertEqualXMLStructure($expectedElement->childNodes->item($i), $actualElement->childNodes->item($i), $checkAttributes, $message); + } } -} -if (!\function_exists('PHPUnit\\Framework\\assertThat')) { /** * Evaluates a PHPUnit\Framework\Constraint matcher object. * - * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertThat + * @throws ExpectationFailedException */ - function assertThat($value, \PHPUnit\Framework\Constraint\Constraint $constraint, string $message = '') : void + public static function assertThat($value, Constraint $constraint, string $message = '') : void { - \PHPUnit\Framework\Assert::assertThat(...\func_get_args()); + self::$count += count($constraint); + $constraint->evaluate($value, $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertJson')) { /** * Asserts that a string is a valid JSON string. * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + */ + public static function assertJson(string $actualJson, string $message = '') : void + { + static::assertThat($actualJson, static::isJson(), $message); + } + /** + * Asserts that two given JSON encoded objects or arrays are equal. + * * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + */ + public static function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = '') : void + { + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + static::assertThat($actualJson, new JsonMatches($expectedJson), $message); + } + /** + * Asserts that two given JSON encoded objects or arrays are not equal. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @param string $expectedJson + * @param string $actualJson * - * @see Assert::assertJson + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - function assertJson(string $actualJson, string $message = '') : void + public static function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = '') : void { - \PHPUnit\Framework\Assert::assertJson(...\func_get_args()); + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + static::assertThat($actualJson, new LogicalNot(new JsonMatches($expectedJson)), $message); } -} -if (!\function_exists('PHPUnit\\Framework\\assertJsonStringEqualsJsonString')) { /** - * Asserts that two given JSON encoded objects or arrays are equal. + * Asserts that the generated JSON encoded object and the content of the given file are equal. * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws ExpectationFailedException + */ + public static function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void + { + static::assertFileExists($expectedFile, $message); + $expectedJson = file_get_contents($expectedFile); + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + static::assertThat($actualJson, new JsonMatches($expectedJson), $message); + } + /** + * Asserts that the generated JSON encoded object and the content of the given file are not equal. + * * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + */ + public static function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void + { + static::assertFileExists($expectedFile, $message); + $expectedJson = file_get_contents($expectedFile); + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + static::assertThat($actualJson, new LogicalNot(new JsonMatches($expectedJson)), $message); + } + /** + * Asserts that two JSON files are equal. * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + */ + public static function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void + { + static::assertFileExists($expectedFile, $message); + static::assertFileExists($actualFile, $message); + $actualJson = file_get_contents($actualFile); + $expectedJson = file_get_contents($expectedFile); + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + $constraintExpected = new JsonMatches($expectedJson); + $constraintActual = new JsonMatches($actualJson); + static::assertThat($expectedJson, $constraintActual, $message); + static::assertThat($actualJson, $constraintExpected, $message); + } + /** + * Asserts that two JSON files are not equal. * - * @see Assert::assertJsonStringEqualsJsonString + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = '') : void + public static function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void + { + static::assertFileExists($expectedFile, $message); + static::assertFileExists($actualFile, $message); + $actualJson = file_get_contents($actualFile); + $expectedJson = file_get_contents($expectedFile); + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + $constraintExpected = new JsonMatches($expectedJson); + $constraintActual = new JsonMatches($actualJson); + static::assertThat($expectedJson, new LogicalNot($constraintActual), $message); + static::assertThat($actualJson, new LogicalNot($constraintExpected), $message); + } + /** + * @throws Exception + */ + public static function logicalAnd() : LogicalAnd + { + $constraints = func_get_args(); + $constraint = new LogicalAnd(); + $constraint->setConstraints($constraints); + return $constraint; + } + public static function logicalOr() : LogicalOr + { + $constraints = func_get_args(); + $constraint = new LogicalOr(); + $constraint->setConstraints($constraints); + return $constraint; + } + public static function logicalNot(Constraint $constraint) : LogicalNot + { + return new LogicalNot($constraint); + } + public static function logicalXor() : LogicalXor + { + $constraints = func_get_args(); + $constraint = new LogicalXor(); + $constraint->setConstraints($constraints); + return $constraint; + } + public static function anything() : IsAnything + { + return new IsAnything(); + } + public static function isTrue() : IsTrue + { + return new IsTrue(); + } + /** + * @psalm-template CallbackInput of mixed + * + * @psalm-param callable(CallbackInput $callback): bool $callback + * + * @psalm-return Callback + */ + public static function callback(callable $callback) : Callback + { + return new Callback($callback); + } + public static function isFalse() : IsFalse + { + return new IsFalse(); + } + public static function isJson() : IsJson + { + return new IsJson(); + } + public static function isNull() : IsNull + { + return new IsNull(); + } + public static function isFinite() : IsFinite + { + return new IsFinite(); + } + public static function isInfinite() : IsInfinite + { + return new IsInfinite(); + } + public static function isNan() : IsNan + { + return new IsNan(); + } + public static function containsEqual($value) : TraversableContainsEqual + { + return new TraversableContainsEqual($value); + } + public static function containsIdentical($value) : TraversableContainsIdentical + { + return new TraversableContainsIdentical($value); + } + public static function containsOnly(string $type) : TraversableContainsOnly + { + return new TraversableContainsOnly($type); + } + public static function containsOnlyInstancesOf(string $className) : TraversableContainsOnly + { + return new TraversableContainsOnly($className, \false); + } + /** + * @param int|string $key + */ + public static function arrayHasKey($key) : ArrayHasKey + { + return new ArrayHasKey($key); + } + public static function equalTo($value) : IsEqual + { + return new IsEqual($value, 0.0, \false, \false); + } + public static function equalToCanonicalizing($value) : IsEqualCanonicalizing + { + return new IsEqualCanonicalizing($value); + } + public static function equalToIgnoringCase($value) : IsEqualIgnoringCase + { + return new IsEqualIgnoringCase($value); + } + public static function equalToWithDelta($value, float $delta) : IsEqualWithDelta + { + return new IsEqualWithDelta($value, $delta); + } + public static function isEmpty() : IsEmpty + { + return new IsEmpty(); + } + public static function isWritable() : IsWritable + { + return new IsWritable(); + } + public static function isReadable() : IsReadable + { + return new IsReadable(); + } + public static function directoryExists() : DirectoryExists + { + return new DirectoryExists(); + } + public static function fileExists() : FileExists + { + return new FileExists(); + } + public static function greaterThan($value) : GreaterThan { - \PHPUnit\Framework\Assert::assertJsonStringEqualsJsonString(...\func_get_args()); + return new GreaterThan($value); } -} -if (!\function_exists('PHPUnit\\Framework\\assertJsonStringNotEqualsJsonString')) { - /** - * Asserts that two given JSON encoded objects or arrays are not equal. - * - * @param string $expectedJson - * @param string $actualJson - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertJsonStringNotEqualsJsonString - */ - function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = '') : void + public static function greaterThanOrEqual($value) : LogicalOr { - \PHPUnit\Framework\Assert::assertJsonStringNotEqualsJsonString(...\func_get_args()); + return static::logicalOr(new IsEqual($value), new GreaterThan($value)); } -} -if (!\function_exists('PHPUnit\\Framework\\assertJsonStringEqualsJsonFile')) { - /** - * Asserts that the generated JSON encoded object and the content of the given file are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertJsonStringEqualsJsonFile - */ - function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void + public static function classHasAttribute(string $attributeName) : ClassHasAttribute { - \PHPUnit\Framework\Assert::assertJsonStringEqualsJsonFile(...\func_get_args()); + return new ClassHasAttribute($attributeName); } -} -if (!\function_exists('PHPUnit\\Framework\\assertJsonStringNotEqualsJsonFile')) { - /** - * Asserts that the generated JSON encoded object and the content of the given file are not equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertJsonStringNotEqualsJsonFile - */ - function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') : void + public static function classHasStaticAttribute(string $attributeName) : ClassHasStaticAttribute { - \PHPUnit\Framework\Assert::assertJsonStringNotEqualsJsonFile(...\func_get_args()); + return new ClassHasStaticAttribute($attributeName); } -} -if (!\function_exists('PHPUnit\\Framework\\assertJsonFileEqualsJsonFile')) { - /** - * Asserts that two JSON files are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertJsonFileEqualsJsonFile - */ - function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void + public static function objectHasAttribute($attributeName) : ObjectHasAttribute { - \PHPUnit\Framework\Assert::assertJsonFileEqualsJsonFile(...\func_get_args()); + return new ObjectHasAttribute($attributeName); } -} -if (!\function_exists('PHPUnit\\Framework\\assertJsonFileNotEqualsJsonFile')) { - /** - * Asserts that two JSON files are not equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertJsonFileNotEqualsJsonFile - */ - function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') : void + public static function identicalTo($value) : IsIdentical { - \PHPUnit\Framework\Assert::assertJsonFileNotEqualsJsonFile(...\func_get_args()); + return new IsIdentical($value); } -} -if (!\function_exists('PHPUnit\\Framework\\logicalAnd')) { - function logicalAnd() : \PHPUnit\Framework\Constraint\LogicalAnd + public static function isInstanceOf(string $className) : IsInstanceOf { - return \PHPUnit\Framework\Assert::logicalAnd(...\func_get_args()); + return new IsInstanceOf($className); } -} -if (!\function_exists('PHPUnit\\Framework\\logicalOr')) { - function logicalOr() : \PHPUnit\Framework\Constraint\LogicalOr + public static function isType(string $type) : IsType { - return \PHPUnit\Framework\Assert::logicalOr(...\func_get_args()); + return new IsType($type); } -} -if (!\function_exists('PHPUnit\\Framework\\logicalNot')) { - function logicalNot(\PHPUnit\Framework\Constraint\Constraint $constraint) : \PHPUnit\Framework\Constraint\LogicalNot + public static function lessThan($value) : LessThan { - return \PHPUnit\Framework\Assert::logicalNot(...\func_get_args()); + return new LessThan($value); } -} -if (!\function_exists('PHPUnit\\Framework\\logicalXor')) { - function logicalXor() : \PHPUnit\Framework\Constraint\LogicalXor + public static function lessThanOrEqual($value) : LogicalOr { - return \PHPUnit\Framework\Assert::logicalXor(...\func_get_args()); + return static::logicalOr(new IsEqual($value), new LessThan($value)); } -} -if (!\function_exists('PHPUnit\\Framework\\anything')) { - function anything() : \PHPUnit\Framework\Constraint\IsAnything + public static function matchesRegularExpression(string $pattern) : RegularExpression { - return \PHPUnit\Framework\Assert::anything(...\func_get_args()); + return new RegularExpression($pattern); } -} -if (!\function_exists('PHPUnit\\Framework\\isTrue')) { - function isTrue() : \PHPUnit\Framework\Constraint\IsTrue + public static function matches(string $string) : StringMatchesFormatDescription { - return \PHPUnit\Framework\Assert::isTrue(...\func_get_args()); + return new StringMatchesFormatDescription($string); } -} -if (!\function_exists('PHPUnit\\Framework\\callback')) { - function callback(callable $callback) : \PHPUnit\Framework\Constraint\Callback + public static function stringStartsWith($prefix) : StringStartsWith { - return \PHPUnit\Framework\Assert::callback(...\func_get_args()); + return new StringStartsWith($prefix); } -} -if (!\function_exists('PHPUnit\\Framework\\isFalse')) { - function isFalse() : \PHPUnit\Framework\Constraint\IsFalse + public static function stringContains(string $string, bool $case = \true) : StringContains { - return \PHPUnit\Framework\Assert::isFalse(...\func_get_args()); + return new StringContains($string, $case); } -} -if (!\function_exists('PHPUnit\\Framework\\isJson')) { - function isJson() : \PHPUnit\Framework\Constraint\IsJson + public static function stringEndsWith(string $suffix) : StringEndsWith { - return \PHPUnit\Framework\Assert::isJson(...\func_get_args()); + return new StringEndsWith($suffix); } -} -if (!\function_exists('PHPUnit\\Framework\\isNull')) { - function isNull() : \PHPUnit\Framework\Constraint\IsNull + public static function countOf(int $count) : Count { - return \PHPUnit\Framework\Assert::isNull(...\func_get_args()); + return new Count($count); } -} -if (!\function_exists('PHPUnit\\Framework\\isFinite')) { - function isFinite() : \PHPUnit\Framework\Constraint\IsFinite + public static function objectEquals(object $object, string $method = 'equals') : ObjectEquals { - return \PHPUnit\Framework\Assert::isFinite(...\func_get_args()); + return new ObjectEquals($object, $method); } -} -if (!\function_exists('PHPUnit\\Framework\\isInfinite')) { - function isInfinite() : \PHPUnit\Framework\Constraint\IsInfinite + /** + * Fails a test with the given message. + * + * @throws AssertionFailedError + * + * @psalm-return never-return + */ + public static function fail(string $message = '') : void { - return \PHPUnit\Framework\Assert::isInfinite(...\func_get_args()); + self::$count++; + throw new \PHPUnit\Framework\AssertionFailedError($message); } -} -if (!\function_exists('PHPUnit\\Framework\\isNan')) { - function isNan() : \PHPUnit\Framework\Constraint\IsNan + /** + * Mark the test as incomplete. + * + * @throws IncompleteTestError + * + * @psalm-return never-return + */ + public static function markTestIncomplete(string $message = '') : void { - return \PHPUnit\Framework\Assert::isNan(...\func_get_args()); + throw new \PHPUnit\Framework\IncompleteTestError($message); } -} -if (!\function_exists('PHPUnit\\Framework\\containsEqual')) { - function containsEqual($value) : \PHPUnit\Framework\Constraint\TraversableContainsEqual + /** + * Mark the test as skipped. + * + * @throws SkippedTestError + * @throws SyntheticSkippedError + * + * @psalm-return never-return + */ + public static function markTestSkipped(string $message = '') : void { - return \PHPUnit\Framework\Assert::containsEqual(...\func_get_args()); + if ($hint = self::detectLocationHint($message)) { + $trace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS); + array_unshift($trace, $hint); + throw new \PHPUnit\Framework\SyntheticSkippedError($hint['message'], 0, $hint['file'], (int) $hint['line'], $trace); + } + throw new \PHPUnit\Framework\SkippedTestError($message); } -} -if (!\function_exists('PHPUnit\\Framework\\containsIdentical')) { - function containsIdentical($value) : \PHPUnit\Framework\Constraint\TraversableContainsIdentical + /** + * Return the current assertion count. + */ + public static function getCount() : int { - return \PHPUnit\Framework\Assert::containsIdentical(...\func_get_args()); + return self::$count; } -} -if (!\function_exists('PHPUnit\\Framework\\containsOnly')) { - function containsOnly(string $type) : \PHPUnit\Framework\Constraint\TraversableContainsOnly + /** + * Reset the assertion counter. + */ + public static function resetCount() : void { - return \PHPUnit\Framework\Assert::containsOnly(...\func_get_args()); + self::$count = 0; } -} -if (!\function_exists('PHPUnit\\Framework\\containsOnlyInstancesOf')) { - function containsOnlyInstancesOf(string $className) : \PHPUnit\Framework\Constraint\TraversableContainsOnly + private static function detectLocationHint(string $message) : ?array { - return \PHPUnit\Framework\Assert::containsOnlyInstancesOf(...\func_get_args()); + $hint = null; + $lines = preg_split('/\\r\\n|\\r|\\n/', $message); + while (strpos($lines[0], '__OFFSET') !== \false) { + $offset = explode('=', array_shift($lines)); + if ($offset[0] === '__OFFSET_FILE') { + $hint['file'] = $offset[1]; + } + if ($offset[0] === '__OFFSET_LINE') { + $hint['line'] = $offset[1]; + } + } + if ($hint) { + $hint['message'] = implode(\PHP_EOL, $lines); + } + return $hint; } -} -if (!\function_exists('PHPUnit\\Framework\\arrayHasKey')) { - function arrayHasKey($key) : \PHPUnit\Framework\Constraint\ArrayHasKey + private static function isValidObjectAttributeName(string $attributeName) : bool { - return \PHPUnit\Framework\Assert::arrayHasKey(...\func_get_args()); + return (bool) preg_match('/[^\\x00-\\x1f\\x7f-\\x9f]+/', $attributeName); } -} -if (!\function_exists('PHPUnit\\Framework\\equalTo')) { - function equalTo($value) : \PHPUnit\Framework\Constraint\IsEqual + private static function isValidClassAttributeName(string $attributeName) : bool { - return \PHPUnit\Framework\Assert::equalTo(...\func_get_args()); + return (bool) preg_match('/[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*/', $attributeName); } -} -if (!\function_exists('PHPUnit\\Framework\\equalToCanonicalizing')) { - function equalToCanonicalizing($value) : \PHPUnit\Framework\Constraint\IsEqualCanonicalizing + /** + * @codeCoverageIgnore + */ + private static function createWarning(string $warning) : void { - return \PHPUnit\Framework\Assert::equalToCanonicalizing(...\func_get_args()); + foreach (debug_backtrace() as $step) { + if (isset($step['object']) && $step['object'] instanceof \PHPUnit\Framework\TestCase) { + assert($step['object'] instanceof \PHPUnit\Framework\TestCase); + $step['object']->addWarning($warning); + break; + } + } } } -if (!\function_exists('PHPUnit\\Framework\\equalToIgnoringCase')) { - function equalToIgnoringCase($value) : \PHPUnit\Framework\Constraint\IsEqualIgnoringCase + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function assert; +use function count; +use RecursiveIterator; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestSuiteIterator implements RecursiveIterator +{ + /** + * @var int + */ + private $position = 0; + /** + * @var Test[] + */ + private $tests; + public function __construct(\PHPUnit\Framework\TestSuite $testSuite) { - return \PHPUnit\Framework\Assert::equalToIgnoringCase(...\func_get_args()); + $this->tests = $testSuite->tests(); } -} -if (!\function_exists('PHPUnit\\Framework\\equalToWithDelta')) { - function equalToWithDelta($value, float $delta) : \PHPUnit\Framework\Constraint\IsEqualWithDelta + public function rewind() : void { - return \PHPUnit\Framework\Assert::equalToWithDelta(...\func_get_args()); + $this->position = 0; } -} -if (!\function_exists('PHPUnit\\Framework\\isEmpty')) { - function isEmpty() : \PHPUnit\Framework\Constraint\IsEmpty + public function valid() : bool { - return \PHPUnit\Framework\Assert::isEmpty(...\func_get_args()); + return $this->position < count($this->tests); } -} -if (!\function_exists('PHPUnit\\Framework\\isWritable')) { - function isWritable() : \PHPUnit\Framework\Constraint\IsWritable + public function key() : int { - return \PHPUnit\Framework\Assert::isWritable(...\func_get_args()); + return $this->position; } -} -if (!\function_exists('PHPUnit\\Framework\\isReadable')) { - function isReadable() : \PHPUnit\Framework\Constraint\IsReadable + public function current() : \PHPUnit\Framework\Test { - return \PHPUnit\Framework\Assert::isReadable(...\func_get_args()); + return $this->tests[$this->position]; } -} -if (!\function_exists('PHPUnit\\Framework\\directoryExists')) { - function directoryExists() : \PHPUnit\Framework\Constraint\DirectoryExists + public function next() : void { - return \PHPUnit\Framework\Assert::directoryExists(...\func_get_args()); + $this->position++; } -} -if (!\function_exists('PHPUnit\\Framework\\fileExists')) { - function fileExists() : \PHPUnit\Framework\Constraint\FileExists + /** + * @throws NoChildTestSuiteException + */ + public function getChildren() : self { - return \PHPUnit\Framework\Assert::fileExists(...\func_get_args()); + if (!$this->hasChildren()) { + throw new \PHPUnit\Framework\NoChildTestSuiteException('The current item is not a TestSuite instance and therefore does not have any children.'); + } + $current = $this->current(); + assert($current instanceof \PHPUnit\Framework\TestSuite); + return new self($current); } -} -if (!\function_exists('PHPUnit\\Framework\\greaterThan')) { - function greaterThan($value) : \PHPUnit\Framework\Constraint\GreaterThan + public function hasChildren() : bool { - return \PHPUnit\Framework\Assert::greaterThan(...\func_get_args()); + return $this->valid() && $this->current() instanceof \PHPUnit\Framework\TestSuite; } } -if (!\function_exists('PHPUnit\\Framework\\greaterThanOrEqual')) { - function greaterThanOrEqual($value) : \PHPUnit\Framework\Constraint\LogicalOr + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use PHPUnit\Framework\MockObject\Builder\InvocationStubber; +/** + * @method InvocationStubber method($constraint) + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface Stub +{ + public function __phpunit_getInvocationHandler() : \PHPUnit\Framework\MockObject\InvocationHandler; + public function __phpunit_hasMatchers() : bool; + public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration) : void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function call_user_func; +use function class_exists; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MockClass implements \PHPUnit\Framework\MockObject\MockType +{ + /** + * @var string + */ + private $classCode; + /** + * @var class-string + */ + private $mockName; + /** + * @var ConfigurableMethod[] + */ + private $configurableMethods; + /** + * @psalm-param class-string $mockName + */ + public function __construct(string $classCode, string $mockName, array $configurableMethods) + { + $this->classCode = $classCode; + $this->mockName = $mockName; + $this->configurableMethods = $configurableMethods; + } + /** + * @psalm-return class-string + */ + public function generate() : string + { + if (!class_exists($this->mockName, \false)) { + eval($this->classCode); + call_user_func([$this->mockName, '__phpunit_initConfigurableMethods'], ...$this->configurableMethods); + } + return $this->mockName; + } + public function getClassCode() : string { - return \PHPUnit\Framework\Assert::greaterThanOrEqual(...\func_get_args()); + return $this->classCode; } } -if (!\function_exists('PHPUnit\\Framework\\classHasAttribute')) { - function classHasAttribute(string $attributeName) : \PHPUnit\Framework\Constraint\ClassHasAttribute + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface MockType +{ + /** + * @psalm-return class-string + */ + public function generate() : string; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface MethodNameMatch extends \PHPUnit\Framework\MockObject\Builder\ParametersMatch +{ + /** + * Adds a new method name match and returns the parameter match object for + * further matching possibilities. + * + * @param \PHPUnit\Framework\Constraint\Constraint $constraint Constraint for matching method, if a string is passed it will use the PHPUnit_Framework_Constraint_IsEqual + * + * @return ParametersMatch + */ + public function method($constraint); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; + +use PHPUnit\Framework\MockObject\Stub\Stub as BaseStub; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface Stub extends \PHPUnit\Framework\MockObject\Builder\Identity +{ + /** + * Stubs the matching method with the stub object $stub. Any invocations of + * the matched method will now be handled by the stub instead. + */ + public function will(BaseStub $stub) : \PHPUnit\Framework\MockObject\Builder\Identity; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; + +use PHPUnit\Framework\MockObject\Stub\Stub; +use Throwable; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface InvocationStubber +{ + public function will(Stub $stub) : \PHPUnit\Framework\MockObject\Builder\Identity; + /** @return self */ + public function willReturn($value, ...$nextValues); + /** + * @param mixed $reference + * + * @return self + */ + public function willReturnReference(&$reference); + /** + * @param array> $valueMap + * + * @return self + */ + public function willReturnMap(array $valueMap); + /** + * @param int $argumentIndex + * + * @return self + */ + public function willReturnArgument($argumentIndex); + /** + * @param callable $callback + * + * @return self + */ + public function willReturnCallback($callback); + /** @return self */ + public function willReturnSelf(); + /** + * @param mixed $values + * + * @return self + */ + public function willReturnOnConsecutiveCalls(...$values); + /** @return self */ + public function willThrowException(Throwable $exception); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface Identity +{ + /** + * Sets the identification of the expectation to $id. + * + * @note The identifier is unique per mock object. + * + * @param string $id unique identification of expectation + */ + public function id($id); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface ParametersMatch extends \PHPUnit\Framework\MockObject\Builder\Stub +{ + /** + * Defines the expectation which must occur before the current is valid. + * + * @param string $id the identification of the expectation that should + * occur before this one + * + * @return Stub + */ + public function after($id); + /** + * Sets the parameters to match for, each parameter to this function will + * be part of match. To perform specific matches or constraints create a + * new PHPUnit\Framework\Constraint\Constraint and use it for the parameter. + * If the parameter value is not a constraint it will use the + * PHPUnit\Framework\Constraint\IsEqual for the value. + * + * Some examples: + * + * // match first parameter with value 2 + * $b->with(2); + * // match first parameter with value 'smock' and second identical to 42 + * $b->with('smock', new PHPUnit\Framework\Constraint\IsEqual(42)); + * + * + * @return ParametersMatch + */ + public function with(...$arguments); + /** + * Sets a rule which allows any kind of parameters. + * + * Some examples: + * + * // match any number of parameters + * $b->withAnyParameters(); + * + * + * @return ParametersMatch + */ + public function withAnyParameters(); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; + +use function array_map; +use function array_merge; +use function count; +use function in_array; +use function is_string; +use function strtolower; +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\MockObject\ConfigurableMethod; +use PHPUnit\Framework\MockObject\IncompatibleReturnValueException; +use PHPUnit\Framework\MockObject\InvocationHandler; +use PHPUnit\Framework\MockObject\Matcher; +use PHPUnit\Framework\MockObject\MatcherAlreadyRegisteredException; +use PHPUnit\Framework\MockObject\MethodCannotBeConfiguredException; +use PHPUnit\Framework\MockObject\MethodNameAlreadyConfiguredException; +use PHPUnit\Framework\MockObject\MethodNameNotConfiguredException; +use PHPUnit\Framework\MockObject\MethodParametersAlreadyConfiguredException; +use PHPUnit\Framework\MockObject\Rule; +use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls; +use PHPUnit\Framework\MockObject\Stub\Exception; +use PHPUnit\Framework\MockObject\Stub\ReturnArgument; +use PHPUnit\Framework\MockObject\Stub\ReturnCallback; +use PHPUnit\Framework\MockObject\Stub\ReturnReference; +use PHPUnit\Framework\MockObject\Stub\ReturnSelf; +use PHPUnit\Framework\MockObject\Stub\ReturnStub; +use PHPUnit\Framework\MockObject\Stub\ReturnValueMap; +use PHPUnit\Framework\MockObject\Stub\Stub; +use Throwable; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class InvocationMocker implements \PHPUnit\Framework\MockObject\Builder\InvocationStubber, \PHPUnit\Framework\MockObject\Builder\MethodNameMatch +{ + /** + * @var InvocationHandler + */ + private $invocationHandler; + /** + * @var Matcher + */ + private $matcher; + /** + * @var ConfigurableMethod[] + */ + private $configurableMethods; + public function __construct(InvocationHandler $handler, Matcher $matcher, ConfigurableMethod ...$configurableMethods) { - return \PHPUnit\Framework\Assert::classHasAttribute(...\func_get_args()); + $this->invocationHandler = $handler; + $this->matcher = $matcher; + $this->configurableMethods = $configurableMethods; } -} -if (!\function_exists('PHPUnit\\Framework\\classHasStaticAttribute')) { - function classHasStaticAttribute(string $attributeName) : \PHPUnit\Framework\Constraint\ClassHasStaticAttribute + /** + * @throws MatcherAlreadyRegisteredException + * + * @return $this + */ + public function id($id) : self { - return \PHPUnit\Framework\Assert::classHasStaticAttribute(...\func_get_args()); + $this->invocationHandler->registerMatcher($id, $this->matcher); + return $this; } -} -if (!\function_exists('PHPUnit\\Framework\\objectHasAttribute')) { - function objectHasAttribute($attributeName) : \PHPUnit\Framework\Constraint\ObjectHasAttribute + /** + * @return $this + */ + public function will(Stub $stub) : \PHPUnit\Framework\MockObject\Builder\Identity { - return \PHPUnit\Framework\Assert::objectHasAttribute(...\func_get_args()); + $this->matcher->setStub($stub); + return $this; } -} -if (!\function_exists('PHPUnit\\Framework\\identicalTo')) { - function identicalTo($value) : \PHPUnit\Framework\Constraint\IsIdentical + /** + * @param mixed $value + * @param mixed[] $nextValues + * + * @throws IncompatibleReturnValueException + */ + public function willReturn($value, ...$nextValues) : self { - return \PHPUnit\Framework\Assert::identicalTo(...\func_get_args()); + if (count($nextValues) === 0) { + $this->ensureTypeOfReturnValues([$value]); + $stub = $value instanceof Stub ? $value : new ReturnStub($value); + } else { + $values = array_merge([$value], $nextValues); + $this->ensureTypeOfReturnValues($values); + $stub = new ConsecutiveCalls($values); + } + return $this->will($stub); } -} -if (!\function_exists('PHPUnit\\Framework\\isInstanceOf')) { - function isInstanceOf(string $className) : \PHPUnit\Framework\Constraint\IsInstanceOf + public function willReturnReference(&$reference) : self { - return \PHPUnit\Framework\Assert::isInstanceOf(...\func_get_args()); + $stub = new ReturnReference($reference); + return $this->will($stub); } -} -if (!\function_exists('PHPUnit\\Framework\\isType')) { - function isType(string $type) : \PHPUnit\Framework\Constraint\IsType + public function willReturnMap(array $valueMap) : self { - return \PHPUnit\Framework\Assert::isType(...\func_get_args()); + $stub = new ReturnValueMap($valueMap); + return $this->will($stub); } -} -if (!\function_exists('PHPUnit\\Framework\\lessThan')) { - function lessThan($value) : \PHPUnit\Framework\Constraint\LessThan + public function willReturnArgument($argumentIndex) : self { - return \PHPUnit\Framework\Assert::lessThan(...\func_get_args()); + $stub = new ReturnArgument($argumentIndex); + return $this->will($stub); } -} -if (!\function_exists('PHPUnit\\Framework\\lessThanOrEqual')) { - function lessThanOrEqual($value) : \PHPUnit\Framework\Constraint\LogicalOr + public function willReturnCallback($callback) : self { - return \PHPUnit\Framework\Assert::lessThanOrEqual(...\func_get_args()); + $stub = new ReturnCallback($callback); + return $this->will($stub); } -} -if (!\function_exists('PHPUnit\\Framework\\matchesRegularExpression')) { - function matchesRegularExpression(string $pattern) : \PHPUnit\Framework\Constraint\RegularExpression + public function willReturnSelf() : self { - return \PHPUnit\Framework\Assert::matchesRegularExpression(...\func_get_args()); + $stub = new ReturnSelf(); + return $this->will($stub); } -} -if (!\function_exists('PHPUnit\\Framework\\matches')) { - function matches(string $string) : \PHPUnit\Framework\Constraint\StringMatchesFormatDescription + public function willReturnOnConsecutiveCalls(...$values) : self { - return \PHPUnit\Framework\Assert::matches(...\func_get_args()); + $stub = new ConsecutiveCalls($values); + return $this->will($stub); } -} -if (!\function_exists('PHPUnit\\Framework\\stringStartsWith')) { - function stringStartsWith($prefix) : \PHPUnit\Framework\Constraint\StringStartsWith + public function willThrowException(Throwable $exception) : self { - return \PHPUnit\Framework\Assert::stringStartsWith(...\func_get_args()); + $stub = new Exception($exception); + return $this->will($stub); } -} -if (!\function_exists('PHPUnit\\Framework\\stringContains')) { - function stringContains(string $string, bool $case = \true) : \PHPUnit\Framework\Constraint\StringContains + /** + * @return $this + */ + public function after($id) : self { - return \PHPUnit\Framework\Assert::stringContains(...\func_get_args()); + $this->matcher->setAfterMatchBuilderId($id); + return $this; } -} -if (!\function_exists('PHPUnit\\Framework\\stringEndsWith')) { - function stringEndsWith(string $suffix) : \PHPUnit\Framework\Constraint\StringEndsWith + /** + * @param mixed[] $arguments + * + * @throws \PHPUnit\Framework\Exception + * @throws MethodNameNotConfiguredException + * @throws MethodParametersAlreadyConfiguredException + * + * @return $this + */ + public function with(...$arguments) : self { - return \PHPUnit\Framework\Assert::stringEndsWith(...\func_get_args()); + $this->ensureParametersCanBeConfigured(); + $this->matcher->setParametersRule(new Rule\Parameters($arguments)); + return $this; } -} -if (!\function_exists('PHPUnit\\Framework\\countOf')) { - function countOf(int $count) : \PHPUnit\Framework\Constraint\Count + /** + * @param array ...$arguments + * + * @throws \PHPUnit\Framework\Exception + * @throws MethodNameNotConfiguredException + * @throws MethodParametersAlreadyConfiguredException + * + * @return $this + */ + public function withConsecutive(...$arguments) : self { - return \PHPUnit\Framework\Assert::countOf(...\func_get_args()); + $this->ensureParametersCanBeConfigured(); + $this->matcher->setParametersRule(new Rule\ConsecutiveParameters($arguments)); + return $this; } -} -if (!\function_exists('PHPUnit\\Framework\\objectEquals')) { - function objectEquals(object $object, string $method = 'equals') : \PHPUnit\Framework\Constraint\ObjectEquals + /** + * @throws MethodNameNotConfiguredException + * @throws MethodParametersAlreadyConfiguredException + * + * @return $this + */ + public function withAnyParameters() : self { - return \PHPUnit\Framework\Assert::objectEquals(...\func_get_args()); + $this->ensureParametersCanBeConfigured(); + $this->matcher->setParametersRule(new Rule\AnyParameters()); + return $this; } -} -if (!\function_exists('PHPUnit\\Framework\\any')) { /** - * Returns a matcher that matches when the method is executed - * zero or more times. + * @param Constraint|string $constraint + * + * @throws \PHPUnit\Framework\InvalidArgumentException + * @throws MethodCannotBeConfiguredException + * @throws MethodNameAlreadyConfiguredException + * + * @return $this */ - function any() : \PHPUnit\Framework\MockObject\Rule\AnyInvokedCount + public function method($constraint) : self { - return new \PHPUnit\Framework\MockObject\Rule\AnyInvokedCount(); + if ($this->matcher->hasMethodNameRule()) { + throw new MethodNameAlreadyConfiguredException(); + } + $configurableMethodNames = array_map(static function (ConfigurableMethod $configurable) { + return strtolower($configurable->getName()); + }, $this->configurableMethods); + if (is_string($constraint) && !in_array(strtolower($constraint), $configurableMethodNames, \true)) { + throw new MethodCannotBeConfiguredException($constraint); + } + $this->matcher->setMethodNameRule(new Rule\MethodName($constraint)); + return $this; } -} -if (!\function_exists('PHPUnit\\Framework\\never')) { /** - * Returns a matcher that matches when the method is never executed. + * @throws MethodNameNotConfiguredException + * @throws MethodParametersAlreadyConfiguredException */ - function never() : \PHPUnit\Framework\MockObject\Rule\InvokedCount + private function ensureParametersCanBeConfigured() : void { - return new \PHPUnit\Framework\MockObject\Rule\InvokedCount(0); + if (!$this->matcher->hasMethodNameRule()) { + throw new MethodNameNotConfiguredException(); + } + if ($this->matcher->hasParametersRule()) { + throw new MethodParametersAlreadyConfiguredException(); + } + } + private function getConfiguredMethod() : ?ConfigurableMethod + { + $configuredMethod = null; + foreach ($this->configurableMethods as $configurableMethod) { + if ($this->matcher->getMethodNameRule()->matchesName($configurableMethod->getName())) { + if ($configuredMethod !== null) { + return null; + } + $configuredMethod = $configurableMethod; + } + } + return $configuredMethod; } -} -if (!\function_exists('PHPUnit\\Framework\\atLeast')) { /** - * Returns a matcher that matches when the method is executed - * at least N times. + * @throws IncompatibleReturnValueException */ - function atLeast(int $requiredInvocations) : \PHPUnit\Framework\MockObject\Rule\InvokedAtLeastCount + private function ensureTypeOfReturnValues(array $values) : void { - return new \PHPUnit\Framework\MockObject\Rule\InvokedAtLeastCount($requiredInvocations); + $configuredMethod = $this->getConfiguredMethod(); + if ($configuredMethod === null) { + return; + } + foreach ($values as $value) { + if (!$configuredMethod->mayReturn($value)) { + throw new IncompatibleReturnValueException($configuredMethod, $value); + } + } } } -if (!\function_exists('PHPUnit\\Framework\\atLeastOnce')) { - /** - * Returns a matcher that matches when the method is executed at least once. - */ - function atLeastOnce() : \PHPUnit\Framework\MockObject\Rule\InvokedAtLeastOnce + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MatcherAlreadyRegisteredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $id) { - return new \PHPUnit\Framework\MockObject\Rule\InvokedAtLeastOnce(); + parent::__construct(sprintf('Matcher with id <%s> is already registered', $id)); } } -if (!\function_exists('PHPUnit\\Framework\\once')) { - /** - * Returns a matcher that matches when the method is executed exactly once. - */ - function once() : \PHPUnit\Framework\MockObject\Rule\InvokedCount + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ConfigurableMethodsAlreadyInitializedException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ClassAlreadyExistsException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $className) { - return new \PHPUnit\Framework\MockObject\Rule\InvokedCount(1); + parent::__construct(sprintf('Class "%s" already exists', $className)); } } -if (!\function_exists('PHPUnit\\Framework\\exactly')) { - /** - * Returns a matcher that matches when the method is executed - * exactly $count times. - */ - function exactly(int $count) : \PHPUnit\Framework\MockObject\Rule\InvokedCount + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReturnValueNotConfiguredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(\PHPUnit\Framework\MockObject\Invocation $invocation) { - return new \PHPUnit\Framework\MockObject\Rule\InvokedCount($count); + parent::__construct(\sprintf('Return value inference disabled and no expectation set up for %s::%s()', $invocation->getClassName(), $invocation->getMethodName())); } } -if (!\function_exists('PHPUnit\\Framework\\atMost')) { + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class IncompatibleReturnValueException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ /** - * Returns a matcher that matches when the method is executed - * at most N times. + * @param mixed $value */ - function atMost(int $allowedInvocations) : \PHPUnit\Framework\MockObject\Rule\InvokedAtMostCount + public function __construct(\PHPUnit\Framework\MockObject\ConfigurableMethod $method, $value) { - return new \PHPUnit\Framework\MockObject\Rule\InvokedAtMostCount($allowedInvocations); + parent::__construct(sprintf('Method %s may not return value of type %s, its declared return type is "%s"', $method->getName(), \is_object($value) ? \get_class($value) : \gettype($value), $method->getReturnTypeDeclaration())); } } -if (!\function_exists('PHPUnit\\Framework\\at')) { - /** - * Returns a matcher that matches when the method is executed - * at the given index. - */ - function at(int $index) : \PHPUnit\Framework\MockObject\Rule\InvokedAtIndex + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class SoapExtensionNotAvailableException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct() { - return new \PHPUnit\Framework\MockObject\Rule\InvokedAtIndex($index); + parent::__construct('The SOAP extension is required to generate a test double from WSDL'); } } -if (!\function_exists('PHPUnit\\Framework\\returnValue')) { - function returnValue($value) : \PHPUnit\Framework\MockObject\Stub\ReturnStub + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CannotUseAddMethodsException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $type, string $methodName) { - return new \PHPUnit\Framework\MockObject\Stub\ReturnStub($value); + parent::__construct(sprintf('Trying to configure method "%s" with addMethods(), but it exists in class "%s". Use onlyMethods() for methods that exist in the class', $methodName, $type)); } } -if (!\function_exists('PHPUnit\\Framework\\returnValueMap')) { - function returnValueMap(array $valueMap) : \PHPUnit\Framework\MockObject\Stub\ReturnValueMap + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MethodParametersAlreadyConfiguredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct() { - return new \PHPUnit\Framework\MockObject\Stub\ReturnValueMap($valueMap); + parent::__construct('Method parameters already configured'); } } -if (!\function_exists('PHPUnit\\Framework\\returnArgument')) { - function returnArgument(int $argumentIndex) : \PHPUnit\Framework\MockObject\Stub\ReturnArgument + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MatchBuilderNotFoundException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $id) { - return new \PHPUnit\Framework\MockObject\Stub\ReturnArgument($argumentIndex); + parent::__construct(sprintf('No builder found for match builder identification <%s>', $id)); } } -if (!\function_exists('PHPUnit\\Framework\\returnCallback')) { - function returnCallback($callback) : \PHPUnit\Framework\MockObject\Stub\ReturnCallback + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class OriginalConstructorInvocationRequiredException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct() { - return new \PHPUnit\Framework\MockObject\Stub\ReturnCallback($callback); + parent::__construct('Proxying to original methods requires invoking the original constructor'); } } -if (!\function_exists('PHPUnit\\Framework\\returnSelf')) { - /** - * Returns the current object. - * - * This method is useful when mocking a fluent interface. - */ - function returnSelf() : \PHPUnit\Framework\MockObject\Stub\ReturnSelf + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class UnknownTypeException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $type) { - return new \PHPUnit\Framework\MockObject\Stub\ReturnSelf(); + parent::__construct(sprintf('Class or interface "%s" does not exist', $type)); } } -if (!\function_exists('PHPUnit\\Framework\\throwException')) { - function throwException(\Throwable $exception) : \PHPUnit\Framework\MockObject\Stub\Exception + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CannotUseOnlyMethodsException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $type, string $methodName) { - return new \PHPUnit\Framework\MockObject\Stub\Exception($exception); + parent::__construct(sprintf('Trying to configure method "%s" with onlyMethods(), but it does not exist in class "%s". Use addMethods() for methods that do not exist in the class', $methodName, $type)); } } -if (!\function_exists('PHPUnit\\Framework\\onConsecutiveCalls')) { - function onConsecutiveCalls() : \PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReflectionException extends RuntimeException implements \PHPUnit\Framework\MockObject\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class UnknownClassException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $className) { - $args = \func_get_args(); - return new \PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls($args); + parent::__construct(sprintf('Class "%s" does not exist', $className)); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; -use PHPUnit\Framework\Exception; /** - * @internal + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class Error extends \PHPUnit\Framework\Exception +final class BadMethodCallException extends \BadMethodCallException implements \PHPUnit\Framework\MockObject\Exception { - public function __construct(string $message, int $code, string $file, int $line, \Exception $previous = null) - { - parent::__construct($message, $code, $previous); - $this->file = $file; - $this->line = $line; - } } message = $message; - parent::__construct('Warning'); - } - public function getMessage() : string - { - return $this->message; - } - /** - * Returns a string representation of the test case. - */ - public function toString() : string - { - return 'Warning'; - } - /** - * @throws Exception - * - * @psalm-return never-return - */ - protected function runTest() : void + public function __construct(string $method) { - throw new \PHPUnit\Framework\Warning($this->message); + parent::__construct(sprintf('Trying to configure method "%s" which cannot be configured because it does not exist, has not been specified, is final, or is static', $method)); } } + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\Symfony\Polyfill\Ctype; +namespace PHPUnit\Framework\MockObject; +use function sprintf; /** - * Ctype implementation through regex. - * - * @internal - * - * @author Gert de Pagter + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Ctype +final class DuplicateMethodException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception { /** - * Returns TRUE if every character in text is either a letter or a digit, FALSE otherwise. - * - * @see https://php.net/ctype-alnum - * - * @param string|int $text - * - * @return bool - */ - public static function ctype_alnum($text) - { - $text = self::convert_int_to_char_for_ctype($text); - return \is_string($text) && '' !== $text && !\preg_match('/[^A-Za-z0-9]/', $text); - } - /** - * Returns TRUE if every character in text is a letter, FALSE otherwise. - * - * @see https://php.net/ctype-alpha - * - * @param string|int $text - * - * @return bool - */ - public static function ctype_alpha($text) - { - $text = self::convert_int_to_char_for_ctype($text); - return \is_string($text) && '' !== $text && !\preg_match('/[^A-Za-z]/', $text); - } - /** - * Returns TRUE if every character in text is a control character from the current locale, FALSE otherwise. - * - * @see https://php.net/ctype-cntrl - * - * @param string|int $text - * - * @return bool - */ - public static function ctype_cntrl($text) - { - $text = self::convert_int_to_char_for_ctype($text); - return \is_string($text) && '' !== $text && !\preg_match('/[^\\x00-\\x1f\\x7f]/', $text); - } - /** - * Returns TRUE if every character in the string text is a decimal digit, FALSE otherwise. - * - * @see https://php.net/ctype-digit - * - * @param string|int $text - * - * @return bool - */ - public static function ctype_digit($text) - { - $text = self::convert_int_to_char_for_ctype($text); - return \is_string($text) && '' !== $text && !\preg_match('/[^0-9]/', $text); - } - /** - * Returns TRUE if every character in text is printable and actually creates visible output (no white space), FALSE otherwise. - * - * @see https://php.net/ctype-graph - * - * @param string|int $text - * - * @return bool - */ - public static function ctype_graph($text) - { - $text = self::convert_int_to_char_for_ctype($text); - return \is_string($text) && '' !== $text && !\preg_match('/[^!-~]/', $text); - } - /** - * Returns TRUE if every character in text is a lowercase letter. - * - * @see https://php.net/ctype-lower - * - * @param string|int $text - * - * @return bool - */ - public static function ctype_lower($text) - { - $text = self::convert_int_to_char_for_ctype($text); - return \is_string($text) && '' !== $text && !\preg_match('/[^a-z]/', $text); - } - /** - * Returns TRUE if every character in text will actually create output (including blanks). Returns FALSE if text contains control characters or characters that do not have any output or control function at all. - * - * @see https://php.net/ctype-print - * - * @param string|int $text - * - * @return bool - */ - public static function ctype_print($text) - { - $text = self::convert_int_to_char_for_ctype($text); - return \is_string($text) && '' !== $text && !\preg_match('/[^ -~]/', $text); - } - /** - * Returns TRUE if every character in text is printable, but neither letter, digit or blank, FALSE otherwise. - * - * @see https://php.net/ctype-punct - * - * @param string|int $text - * - * @return bool - */ - public static function ctype_punct($text) - { - $text = self::convert_int_to_char_for_ctype($text); - return \is_string($text) && '' !== $text && !\preg_match('/[^!-\\/\\:-@\\[-`\\{-~]/', $text); - } - /** - * Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. Besides the blank character this also includes tab, vertical tab, line feed, carriage return and form feed characters. - * - * @see https://php.net/ctype-space - * - * @param string|int $text - * - * @return bool - */ - public static function ctype_space($text) - { - $text = self::convert_int_to_char_for_ctype($text); - return \is_string($text) && '' !== $text && !\preg_match('/[^\\s]/', $text); - } - /** - * Returns TRUE if every character in text is an uppercase letter. - * - * @see https://php.net/ctype-upper - * - * @param string|int $text - * - * @return bool - */ - public static function ctype_upper($text) - { - $text = self::convert_int_to_char_for_ctype($text); - return \is_string($text) && '' !== $text && !\preg_match('/[^A-Z]/', $text); - } - /** - * Returns TRUE if every character in text is a hexadecimal 'digit', that is a decimal digit or a character from [A-Fa-f] , FALSE otherwise. - * - * @see https://php.net/ctype-xdigit - * - * @param string|int $text - * - * @return bool - */ - public static function ctype_xdigit($text) - { - $text = self::convert_int_to_char_for_ctype($text); - return \is_string($text) && '' !== $text && !\preg_match('/[^A-Fa-f0-9]/', $text); - } - /** - * Converts integers to their char versions according to normal ctype behaviour, if needed. - * - * If an integer between -128 and 255 inclusive is provided, - * it is interpreted as the ASCII value of a single character - * (negative values have 256 added in order to allow characters in the Extended ASCII range). - * Any other integer is interpreted as a string containing the decimal digits of the integer. - * - * @param string|int $int - * - * @return mixed + * @psalm-param list $methods */ - private static function convert_int_to_char_for_ctype($int) + public function __construct(array $methods) { - if (!\is_int($int)) { - return $int; - } - if ($int < -128 || $int > 255) { - return (string) $int; - } - if ($int < 0) { - $int += 256; - } - return \chr($int); + parent::__construct(sprintf('Cannot double using a method list that contains duplicates: "%s" (duplicate: "%s")', \implode(', ', $methods), \implode(', ', \array_unique(\array_diff_assoc($methods, \array_unique($methods)))))); } } + * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -use PHPUnit\Symfony\Polyfill\Ctype as p; -if (!\function_exists('ctype_alnum')) { - function ctype_alnum($input) - { - return \PHPUnit\Symfony\Polyfill\Ctype\Ctype::ctype_alnum($input); - } -} -if (!\function_exists('ctype_alpha')) { - function ctype_alpha($input) - { - return \PHPUnit\Symfony\Polyfill\Ctype\Ctype::ctype_alpha($input); - } -} -if (!\function_exists('ctype_cntrl')) { - function ctype_cntrl($input) - { - return \PHPUnit\Symfony\Polyfill\Ctype\Ctype::ctype_cntrl($input); - } -} -if (!\function_exists('ctype_digit')) { - function ctype_digit($input) - { - return \PHPUnit\Symfony\Polyfill\Ctype\Ctype::ctype_digit($input); - } -} -if (!\function_exists('ctype_graph')) { - function ctype_graph($input) - { - return \PHPUnit\Symfony\Polyfill\Ctype\Ctype::ctype_graph($input); - } -} -if (!\function_exists('ctype_lower')) { - function ctype_lower($input) - { - return \PHPUnit\Symfony\Polyfill\Ctype\Ctype::ctype_lower($input); - } -} -if (!\function_exists('ctype_print')) { - function ctype_print($input) +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvalidMethodNameException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $method) { - return \PHPUnit\Symfony\Polyfill\Ctype\Ctype::ctype_print($input); + parent::__construct(sprintf('Cannot double method with invalid name "%s"', $method)); } } -if (!\function_exists('ctype_punct')) { - function ctype_punct($input) + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ClassIsFinalException extends \PHPUnit\Framework\Exception implements \PHPUnit\Framework\MockObject\Exception +{ + public function __construct(string $className) { - return \PHPUnit\Symfony\Polyfill\Ctype\Ctype::ctype_punct($input); + parent::__construct(sprintf('Class "%s" is declared "final" and cannot be doubled', $className)); } } -if (!\function_exists('ctype_space')) { - function ctype_space($input) + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Stub; + +use function sprintf; +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnit\SebastianBergmann\Exporter\Exporter; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReturnReference implements \PHPUnit\Framework\MockObject\Stub\Stub +{ + /** + * @var mixed + */ + private $reference; + public function __construct(&$reference) { - return \PHPUnit\Symfony\Polyfill\Ctype\Ctype::ctype_space($input); + $this->reference =& $reference; } -} -if (!\function_exists('ctype_upper')) { - function ctype_upper($input) + public function invoke(Invocation $invocation) { - return \PHPUnit\Symfony\Polyfill\Ctype\Ctype::ctype_upper($input); + return $this->reference; } -} -if (!\function_exists('ctype_xdigit')) { - function ctype_xdigit($input) + public function toString() : string { - return \PHPUnit\Symfony\Polyfill\Ctype\Ctype::ctype_xdigit($input); + $exporter = new Exporter(); + return sprintf('return user-specified reference %s', $exporter->export($this->reference)); } } -Copyright (c) 2018-2019 Fabien Potencier - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -Object Enumerator - -Copyright (c) 2016-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Type; +namespace PHPUnit\Framework\MockObject\Stub; -use function is_subclass_of; -use function strcasecmp; -final class ObjectType extends \PHPUnit\SebastianBergmann\Type\Type +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnit\Framework\SelfDescribing; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface Stub extends SelfDescribing { /** - * @var TypeName - */ - private $className; - /** - * @var bool + * Fakes the processing of the invocation $invocation by returning a + * specific value. + * + * @param Invocation $invocation The invocation which was mocked and matched by the current method and argument matchers */ - private $allowsNull; - public function __construct(\PHPUnit\SebastianBergmann\Type\TypeName $className, bool $allowsNull) - { - $this->className = $className; - $this->allowsNull = $allowsNull; - } - public function isAssignable(\PHPUnit\SebastianBergmann\Type\Type $other) : bool - { - if ($this->allowsNull && $other instanceof \PHPUnit\SebastianBergmann\Type\NullType) { - return \true; - } - if ($other instanceof self) { - if (0 === \strcasecmp($this->className->qualifiedName(), $other->className->qualifiedName())) { - return \true; - } - if (\is_subclass_of($other->className->qualifiedName(), $this->className->qualifiedName(), \true)) { - return \true; - } - } - return \false; - } - public function name() : string - { - return $this->className->qualifiedName(); - } - public function allowsNull() : bool - { - return $this->allowsNull; - } - public function className() : \PHPUnit\SebastianBergmann\Type\TypeName - { - return $this->className; - } + public function invoke(Invocation $invocation); } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Type; +namespace PHPUnit\Framework\MockObject\Stub; -final class GenericObjectType extends \PHPUnit\SebastianBergmann\Type\Type +use function array_pop; +use function count; +use function is_array; +use PHPUnit\Framework\MockObject\Invocation; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReturnValueMap implements \PHPUnit\Framework\MockObject\Stub\Stub { /** - * @var bool + * @var array */ - private $allowsNull; - public function __construct(bool $nullable) + private $valueMap; + public function __construct(array $valueMap) { - $this->allowsNull = $nullable; + $this->valueMap = $valueMap; } - public function isAssignable(\PHPUnit\SebastianBergmann\Type\Type $other) : bool + public function invoke(Invocation $invocation) { - if ($this->allowsNull && $other instanceof \PHPUnit\SebastianBergmann\Type\NullType) { - return \true; - } - if (!$other instanceof \PHPUnit\SebastianBergmann\Type\ObjectType) { - return \false; + $parameterCount = count($invocation->getParameters()); + foreach ($this->valueMap as $map) { + if (!is_array($map) || $parameterCount !== count($map) - 1) { + continue; + } + $return = array_pop($map); + if ($invocation->getParameters() === $map) { + return $return; + } } - return \true; - } - public function name() : string - { - return 'object'; } - public function allowsNull() : bool + public function toString() : string { - return $this->allowsNull; + return 'return value from a map'; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Type; +namespace PHPUnit\Framework\MockObject\Stub; -final class UnknownType extends \PHPUnit\SebastianBergmann\Type\Type +use function call_user_func_array; +use function get_class; +use function is_array; +use function is_object; +use function sprintf; +use PHPUnit\Framework\MockObject\Invocation; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReturnCallback implements \PHPUnit\Framework\MockObject\Stub\Stub { - public function isAssignable(\PHPUnit\SebastianBergmann\Type\Type $other) : bool - { - return \true; - } - public function name() : string - { - return 'unknown type'; - } - public function asString() : string + private $callback; + public function __construct($callback) { - return ''; + $this->callback = $callback; } - /** - * @deprecated - * - * @codeCoverageIgnore - */ - public function getReturnTypeDeclaration() : string + public function invoke(Invocation $invocation) { - return ''; + return call_user_func_array($this->callback, $invocation->getParameters()); } - public function allowsNull() : bool + public function toString() : string { - return \true; + if (is_array($this->callback)) { + if (is_object($this->callback[0])) { + $class = get_class($this->callback[0]); + $type = '->'; + } else { + $class = $this->callback[0]; + $type = '::'; + } + return sprintf('return result of user defined callback %s%s%s() with the ' . 'passed arguments', $class, $type, $this->callback[1]); + } + return 'return result of user defined callback ' . $this->callback . ' with the passed arguments'; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Type; +namespace PHPUnit\Framework\MockObject\Stub; -use Throwable; -interface Exception extends \Throwable +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnit\Framework\MockObject\RuntimeException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReturnSelf implements \PHPUnit\Framework\MockObject\Stub\Stub { + /** + * @throws RuntimeException + */ + public function invoke(Invocation $invocation) + { + return $invocation->getObject(); + } + public function toString() : string + { + return 'return the current object'; + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Type; +namespace PHPUnit\Framework\MockObject\Stub; -final class RuntimeException extends \RuntimeException implements \PHPUnit\SebastianBergmann\Type\Exception +use function sprintf; +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnit\SebastianBergmann\Exporter\Exporter; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Exception implements \PHPUnit\Framework\MockObject\Stub\Stub { + private $exception; + public function __construct(Throwable $exception) + { + $this->exception = $exception; + } + /** + * @throws Throwable + */ + public function invoke(Invocation $invocation) : void + { + throw $this->exception; + } + public function toString() : string + { + $exporter = new Exporter(); + return sprintf('raise user-specified exception %s', $exporter->export($this->exception)); + } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Type; +namespace PHPUnit\Framework\MockObject\Stub; -final class MixedType extends \PHPUnit\SebastianBergmann\Type\Type +use function sprintf; +use PHPUnit\Framework\MockObject\Invocation; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReturnArgument implements \PHPUnit\Framework\MockObject\Stub\Stub { - public function isAssignable(\PHPUnit\SebastianBergmann\Type\Type $other) : bool - { - return !$other instanceof \PHPUnit\SebastianBergmann\Type\VoidType; - } - public function asString() : string + /** + * @var int + */ + private $argumentIndex; + public function __construct($argumentIndex) { - return 'mixed'; + $this->argumentIndex = $argumentIndex; } - public function name() : string + public function invoke(Invocation $invocation) { - return 'mixed'; + if (isset($invocation->getParameters()[$this->argumentIndex])) { + return $invocation->getParameters()[$this->argumentIndex]; + } } - public function allowsNull() : bool + public function toString() : string { - return \true; + return sprintf('return argument #%d', $this->argumentIndex); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Type; +namespace PHPUnit\Framework\MockObject\Stub; -use function get_class; -use function gettype; -use function strtolower; -abstract class Type +use function sprintf; +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnit\SebastianBergmann\Exporter\Exporter; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ReturnStub implements \PHPUnit\Framework\MockObject\Stub\Stub { - public static function fromValue($value, bool $allowsNull) : self - { - $typeName = \gettype($value); - if ($typeName === 'object') { - return new \PHPUnit\SebastianBergmann\Type\ObjectType(\PHPUnit\SebastianBergmann\Type\TypeName::fromQualifiedName(\get_class($value)), $allowsNull); - } - $type = self::fromName($typeName, $allowsNull); - if ($type instanceof \PHPUnit\SebastianBergmann\Type\SimpleType) { - $type = new \PHPUnit\SebastianBergmann\Type\SimpleType($typeName, $allowsNull, $value); - } - return $type; - } - public static function fromName(string $typeName, bool $allowsNull) : self + /** + * @var mixed + */ + private $value; + public function __construct($value) { - switch (\strtolower($typeName)) { - case 'callable': - return new \PHPUnit\SebastianBergmann\Type\CallableType($allowsNull); - case 'iterable': - return new \PHPUnit\SebastianBergmann\Type\IterableType($allowsNull); - case 'null': - return new \PHPUnit\SebastianBergmann\Type\NullType(); - case 'object': - return new \PHPUnit\SebastianBergmann\Type\GenericObjectType($allowsNull); - case 'unknown type': - return new \PHPUnit\SebastianBergmann\Type\UnknownType(); - case 'void': - return new \PHPUnit\SebastianBergmann\Type\VoidType(); - case 'array': - case 'bool': - case 'boolean': - case 'double': - case 'float': - case 'int': - case 'integer': - case 'real': - case 'resource': - case 'resource (closed)': - case 'string': - return new \PHPUnit\SebastianBergmann\Type\SimpleType($typeName, $allowsNull); - default: - return new \PHPUnit\SebastianBergmann\Type\ObjectType(\PHPUnit\SebastianBergmann\Type\TypeName::fromQualifiedName($typeName), $allowsNull); - } + $this->value = $value; } - public function asString() : string + public function invoke(Invocation $invocation) { - return ($this->allowsNull() ? '?' : '') . $this->name(); + return $this->value; } - /** - * @deprecated - * - * @codeCoverageIgnore - */ - public function getReturnTypeDeclaration() : string + public function toString() : string { - return ': ' . $this->asString(); + $exporter = new Exporter(); + return sprintf('return user-specified value %s', $exporter->export($this->value)); } - public abstract function isAssignable(\PHPUnit\SebastianBergmann\Type\Type $other) : bool; - public abstract function name() : string; - public abstract function allowsNull() : bool; } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Type; +namespace PHPUnit\Framework\MockObject\Stub; -final class NullType extends \PHPUnit\SebastianBergmann\Type\Type +use function array_shift; +use function sprintf; +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnit\SebastianBergmann\Exporter\Exporter; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ConsecutiveCalls implements \PHPUnit\Framework\MockObject\Stub\Stub { - public function isAssignable(\PHPUnit\SebastianBergmann\Type\Type $other) : bool - { - return !$other instanceof \PHPUnit\SebastianBergmann\Type\VoidType; - } - public function name() : string - { - return 'null'; - } - public function asString() : string - { - return 'null'; - } /** - * @deprecated - * - * @codeCoverageIgnore + * @var array */ - public function getReturnTypeDeclaration() : string + private $stack; + /** + * @var mixed + */ + private $value; + public function __construct(array $stack) { - return ''; + $this->stack = $stack; } - public function allowsNull() : bool + public function invoke(Invocation $invocation) { - return \true; + $this->value = array_shift($this->stack); + if ($this->value instanceof \PHPUnit\Framework\MockObject\Stub\Stub) { + $this->value = $this->value->invoke($invocation); + } + return $this->value; + } + public function toString() : string + { + $exporter = new Exporter(); + return sprintf('return user-specified value %s', $exporter->export($this->value)); } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Type; +namespace PHPUnit\Framework\MockObject; +use const DIRECTORY_SEPARATOR; +use const PHP_EOL; +use const PHP_MAJOR_VERSION; +use const PREG_OFFSET_CAPTURE; +use const WSDL_CACHE_NONE; +use function array_merge; +use function array_pop; +use function array_unique; +use function class_exists; use function count; +use function explode; +use function extension_loaded; use function implode; +use function in_array; +use function interface_exists; +use function is_array; +use function is_object; +use function md5; +use function mt_rand; +use function preg_match; +use function preg_match_all; +use function range; +use function serialize; use function sort; -final class UnionType extends \PHPUnit\SebastianBergmann\Type\Type +use function sprintf; +use function str_replace; +use function strlen; +use function strpos; +use function strtolower; +use function substr; +use function trait_exists; +use PHPUnit\Doctrine\Instantiator\Exception\ExceptionInterface as InstantiatorException; +use PHPUnit\Doctrine\Instantiator\Instantiator; +use Exception; +use Iterator; +use IteratorAggregate; +use PHPUnit\Framework\InvalidArgumentException; +use ReflectionClass; +use ReflectionMethod; +use PHPUnit\SebastianBergmann\Template\Exception as TemplateException; +use PHPUnit\SebastianBergmann\Template\Template; +use SoapClient; +use SoapFault; +use Throwable; +use Traversable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Generator { /** - * @psalm-var list + * @var array */ - private $types; + private const EXCLUDED_METHOD_NAMES = ['__CLASS__' => \true, '__DIR__' => \true, '__FILE__' => \true, '__FUNCTION__' => \true, '__LINE__' => \true, '__METHOD__' => \true, '__NAMESPACE__' => \true, '__TRAIT__' => \true, '__clone' => \true, '__halt_compiler' => \true]; + /** + * @var array + */ + private static $cache = []; + /** + * @var Template[] + */ + private static $templates = []; /** + * Returns a mock object for the specified class. + * + * @param null|array $methods + * + * @throws \PHPUnit\Framework\InvalidArgumentException + * @throws ClassAlreadyExistsException + * @throws ClassIsFinalException + * @throws DuplicateMethodException + * @throws InvalidMethodNameException + * @throws OriginalConstructorInvocationRequiredException + * @throws ReflectionException * @throws RuntimeException + * @throws UnknownTypeException */ - public function __construct(\PHPUnit\SebastianBergmann\Type\Type ...$types) - { - $this->ensureMinimumOfTwoTypes(...$types); - $this->ensureOnlyValidTypes(...$types); - $this->types = $types; - } - public function isAssignable(\PHPUnit\SebastianBergmann\Type\Type $other) : bool + public function getMock(string $type, $methods = [], array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, bool $cloneArguments = \true, bool $callOriginalMethods = \false, object $proxyTarget = null, bool $allowMockingUnknownTypes = \true, bool $returnValueGeneration = \true) : \PHPUnit\Framework\MockObject\MockObject { - foreach ($this->types as $type) { - if ($type->isAssignable($other)) { - return \true; + if (!is_array($methods) && null !== $methods) { + throw InvalidArgumentException::create(2, 'array'); + } + if ($type === 'Traversable' || $type === '\\Traversable') { + $type = 'Iterator'; + } + if (!$allowMockingUnknownTypes && !class_exists($type, $callAutoload) && !interface_exists($type, $callAutoload)) { + throw new \PHPUnit\Framework\MockObject\UnknownTypeException($type); + } + if (null !== $methods) { + foreach ($methods as $method) { + if (!preg_match('~[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*~', (string) $method)) { + throw new \PHPUnit\Framework\MockObject\InvalidMethodNameException((string) $method); + } + } + if ($methods !== array_unique($methods)) { + throw new \PHPUnit\Framework\MockObject\DuplicateMethodException($methods); } } - return \false; - } - public function asString() : string - { - return $this->name(); + if ($mockClassName !== '' && class_exists($mockClassName, \false)) { + try { + $reflector = new ReflectionClass($mockClassName); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if (!$reflector->implementsInterface(\PHPUnit\Framework\MockObject\MockObject::class)) { + throw new \PHPUnit\Framework\MockObject\ClassAlreadyExistsException($mockClassName); + } + } + if (!$callOriginalConstructor && $callOriginalMethods) { + throw new \PHPUnit\Framework\MockObject\OriginalConstructorInvocationRequiredException(); + } + $mock = $this->generate($type, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods); + return $this->getObject($mock, $type, $callOriginalConstructor, $callAutoload, $arguments, $callOriginalMethods, $proxyTarget, $returnValueGeneration); } /** - * @deprecated + * Returns a mock object for the specified abstract class with all abstract + * methods of the class mocked. * - * @codeCoverageIgnore + * Concrete methods to mock can be specified with the $mockedMethods parameter. + * + * @psalm-template RealInstanceType of object + * @psalm-param class-string $originalClassName + * @psalm-return MockObject&RealInstanceType + * + * @throws \PHPUnit\Framework\InvalidArgumentException + * @throws ClassAlreadyExistsException + * @throws ClassIsFinalException + * @throws DuplicateMethodException + * @throws InvalidMethodNameException + * @throws OriginalConstructorInvocationRequiredException + * @throws ReflectionException + * @throws RuntimeException + * @throws UnknownClassException + * @throws UnknownTypeException */ - public function getReturnTypeDeclaration() : string - { - return ': ' . $this->name(); - } - public function name() : string - { - $types = []; - foreach ($this->types as $type) { - $types[] = $type->name(); - } - \sort($types); - return \implode('|', $types); - } - public function allowsNull() : bool + public function getMockForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = null, bool $cloneArguments = \true) : \PHPUnit\Framework\MockObject\MockObject { - foreach ($this->types as $type) { - if ($type instanceof \PHPUnit\SebastianBergmann\Type\NullType) { - return \true; + if (class_exists($originalClassName, $callAutoload) || interface_exists($originalClassName, $callAutoload)) { + try { + $reflector = new ReflectionClass($originalClassName); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $methods = $mockedMethods; + foreach ($reflector->getMethods() as $method) { + if ($method->isAbstract() && !in_array($method->getName(), $methods ?? [], \true)) { + $methods[] = $method->getName(); + } + } + if (empty($methods)) { + $methods = null; } + return $this->getMock($originalClassName, $methods, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $cloneArguments); } - return \false; + throw new \PHPUnit\Framework\MockObject\UnknownClassException($originalClassName); } /** + * Returns a mock object for the specified trait with all abstract methods + * of the trait mocked. Concrete methods to mock can be specified with the + * `$mockedMethods` parameter. + * + * @psalm-param trait-string $traitName + * + * @throws \PHPUnit\Framework\InvalidArgumentException + * @throws ClassAlreadyExistsException + * @throws ClassIsFinalException + * @throws DuplicateMethodException + * @throws InvalidMethodNameException + * @throws OriginalConstructorInvocationRequiredException + * @throws ReflectionException * @throws RuntimeException + * @throws UnknownClassException + * @throws UnknownTraitException + * @throws UnknownTypeException */ - private function ensureMinimumOfTwoTypes(\PHPUnit\SebastianBergmann\Type\Type ...$types) : void + public function getMockForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = null, bool $cloneArguments = \true) : \PHPUnit\Framework\MockObject\MockObject { - if (\count($types) < 2) { - throw new \PHPUnit\SebastianBergmann\Type\RuntimeException('A union type must be composed of at least two types'); + if (!trait_exists($traitName, $callAutoload)) { + throw new \PHPUnit\Framework\MockObject\UnknownTraitException($traitName); } + $className = $this->generateClassName($traitName, '', 'Trait_'); + $classTemplate = $this->getTemplate('trait_class.tpl'); + $classTemplate->setVar(['prologue' => 'abstract ', 'class_name' => $className['className'], 'trait_name' => $traitName]); + $mockTrait = new \PHPUnit\Framework\MockObject\MockTrait($classTemplate->render(), $className['className']); + $mockTrait->generate(); + return $this->getMockForAbstractClass($className['className'], $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); } /** + * Returns an object for the specified trait. + * + * @psalm-param trait-string $traitName + * + * @throws ReflectionException * @throws RuntimeException + * @throws UnknownTraitException */ - private function ensureOnlyValidTypes(\PHPUnit\SebastianBergmann\Type\Type ...$types) : void + public function getObjectForTrait(string $traitName, string $traitClassName = '', bool $callAutoload = \true, bool $callOriginalConstructor = \false, array $arguments = []) : object { - foreach ($types as $type) { - if ($type instanceof \PHPUnit\SebastianBergmann\Type\UnknownType) { - throw new \PHPUnit\SebastianBergmann\Type\RuntimeException('A union type must not be composed of an unknown type'); - } - if ($type instanceof \PHPUnit\SebastianBergmann\Type\VoidType) { - throw new \PHPUnit\SebastianBergmann\Type\RuntimeException('A union type must not be composed of a void type'); - } + if (!trait_exists($traitName, $callAutoload)) { + throw new \PHPUnit\Framework\MockObject\UnknownTraitException($traitName); } + $className = $this->generateClassName($traitName, $traitClassName, 'Trait_'); + $classTemplate = $this->getTemplate('trait_class.tpl'); + $classTemplate->setVar(['prologue' => '', 'class_name' => $className['className'], 'trait_name' => $traitName]); + return $this->getObject(new \PHPUnit\Framework\MockObject\MockTrait($classTemplate->render(), $className['className']), '', $callOriginalConstructor, $callAutoload, $arguments); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Type; - -use function assert; -use function sprintf; -use ReflectionMethod; -use ReflectionNamedType; -use ReflectionUnionType; -final class ReflectionMapper -{ - public function fromMethodReturnType(\ReflectionMethod $method) : \PHPUnit\SebastianBergmann\Type\Type + /** + * @throws ClassIsFinalException + * @throws ReflectionException + * @throws RuntimeException + */ + public function generate(string $type, array $methods = null, string $mockClassName = '', bool $callOriginalClone = \true, bool $callAutoload = \true, bool $cloneArguments = \true, bool $callOriginalMethods = \false) : \PHPUnit\Framework\MockObject\MockClass { - if (!$method->hasReturnType()) { - return new \PHPUnit\SebastianBergmann\Type\UnknownType(); - } - $returnType = $method->getReturnType(); - \assert($returnType instanceof \ReflectionNamedType || $returnType instanceof \ReflectionUnionType); - if ($returnType instanceof \ReflectionNamedType) { - if ($returnType->getName() === 'self') { - return \PHPUnit\SebastianBergmann\Type\ObjectType::fromName($method->getDeclaringClass()->getName(), $returnType->allowsNull()); - } - if ($returnType->getName() === 'static') { - return new \PHPUnit\SebastianBergmann\Type\StaticType(\PHPUnit\SebastianBergmann\Type\TypeName::fromReflection($method->getDeclaringClass()), $returnType->allowsNull()); - } - if ($returnType->getName() === 'mixed') { - return new \PHPUnit\SebastianBergmann\Type\MixedType(); - } - if ($returnType->getName() === 'parent') { - $parentClass = $method->getDeclaringClass()->getParentClass(); - // @codeCoverageIgnoreStart - if ($parentClass === \false) { - throw new \PHPUnit\SebastianBergmann\Type\RuntimeException(\sprintf('%s::%s() has a "parent" return type declaration but %s does not have a parent class', $method->getDeclaringClass()->getName(), $method->getName(), $method->getDeclaringClass()->getName())); - } - // @codeCoverageIgnoreEnd - return \PHPUnit\SebastianBergmann\Type\ObjectType::fromName($parentClass->getName(), $returnType->allowsNull()); - } - return \PHPUnit\SebastianBergmann\Type\Type::fromName($returnType->getName(), $returnType->allowsNull()); + if ($mockClassName !== '') { + return $this->generateMock($type, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods); } - \assert($returnType instanceof \ReflectionUnionType); - $types = []; - foreach ($returnType->getTypes() as $type) { - \assert($type instanceof \ReflectionNamedType); - if ($type->getName() === 'self') { - $types[] = \PHPUnit\SebastianBergmann\Type\ObjectType::fromName($method->getDeclaringClass()->getName(), \false); - } else { - $types[] = \PHPUnit\SebastianBergmann\Type\Type::fromName($type->getName(), \false); - } + $key = md5($type . serialize($methods) . serialize($callOriginalClone) . serialize($cloneArguments) . serialize($callOriginalMethods)); + if (!isset(self::$cache[$key])) { + self::$cache[$key] = $this->generateMock($type, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods); } - return new \PHPUnit\SebastianBergmann\Type\UnionType(...$types); + return self::$cache[$key]; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Type; - -use function array_pop; -use function explode; -use function implode; -use function substr; -use ReflectionClass; -final class TypeName -{ - /** - * @var ?string - */ - private $namespaceName; /** - * @var string + * @throws RuntimeException + * @throws SoapExtensionNotAvailableException */ - private $simpleName; - public static function fromQualifiedName(string $fullClassName) : self + public function generateClassFromWsdl(string $wsdlFile, string $className, array $methods = [], array $options = []) : string { - if ($fullClassName[0] === '\\') { - $fullClassName = \substr($fullClassName, 1); + if (!extension_loaded('soap')) { + throw new \PHPUnit\Framework\MockObject\SoapExtensionNotAvailableException(); } - $classNameParts = \explode('\\', $fullClassName); - $simpleName = \array_pop($classNameParts); - $namespaceName = \implode('\\', $classNameParts); - return new self($namespaceName, $simpleName); - } - public static function fromReflection(\ReflectionClass $type) : self - { - return new self($type->getNamespaceName(), $type->getShortName()); - } - public function __construct(?string $namespaceName, string $simpleName) - { - if ($namespaceName === '') { - $namespaceName = null; + $options = array_merge($options, ['cache_wsdl' => \WSDL_CACHE_NONE]); + try { + $client = new SoapClient($wsdlFile, $options); + $_methods = array_unique($client->__getFunctions()); + unset($client); + } catch (SoapFault $e) { + throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); } - $this->namespaceName = $namespaceName; - $this->simpleName = $simpleName; - } - public function namespaceName() : ?string - { - return $this->namespaceName; - } - public function simpleName() : string - { - return $this->simpleName; - } - public function qualifiedName() : string - { - return $this->namespaceName === null ? $this->simpleName : $this->namespaceName . '\\' . $this->simpleName; + sort($_methods); + $methodTemplate = $this->getTemplate('wsdl_method.tpl'); + $methodsBuffer = ''; + foreach ($_methods as $method) { + preg_match_all('/[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*\\(/', $method, $matches, \PREG_OFFSET_CAPTURE); + $lastFunction = array_pop($matches[0]); + $nameStart = $lastFunction[1]; + $nameEnd = $nameStart + strlen($lastFunction[0]) - 1; + $name = str_replace('(', '', $lastFunction[0]); + if (empty($methods) || in_array($name, $methods, \true)) { + $args = explode(',', str_replace(')', '', substr($method, $nameEnd + 1))); + foreach (range(0, count($args) - 1) as $i) { + $parameterStart = strpos($args[$i], '$'); + if (!$parameterStart) { + continue; + } + $args[$i] = substr($args[$i], $parameterStart); + } + $methodTemplate->setVar(['method_name' => $name, 'arguments' => implode(', ', $args)]); + $methodsBuffer .= $methodTemplate->render(); + } + } + $optionsBuffer = '['; + foreach ($options as $key => $value) { + $optionsBuffer .= $key . ' => ' . $value; + } + $optionsBuffer .= ']'; + $classTemplate = $this->getTemplate('wsdl_class.tpl'); + $namespace = ''; + if (strpos($className, '\\') !== \false) { + $parts = explode('\\', $className); + $className = array_pop($parts); + $namespace = 'namespace ' . implode('\\', $parts) . ';' . "\n\n"; + } + $classTemplate->setVar(['namespace' => $namespace, 'class_name' => $className, 'wsdl' => $wsdlFile, 'options' => $optionsBuffer, 'methods' => $methodsBuffer]); + return $classTemplate->render(); } /** - * @deprecated Use namespaceName() instead + * @throws ReflectionException * - * @codeCoverageIgnore + * @return string[] */ - public function getNamespaceName() : ?string + public function getClassMethods(string $className) : array { - return $this->namespaceName(); + try { + $class = new ReflectionClass($className); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $methods = []; + foreach ($class->getMethods() as $method) { + if ($method->isPublic() || $method->isAbstract()) { + $methods[] = $method->getName(); + } + } + return $methods; } /** - * @deprecated Use simpleName() instead + * @throws ReflectionException * - * @codeCoverageIgnore + * @return MockMethod[] */ - public function getSimpleName() : string + public function mockClassMethods(string $className, bool $callOriginalMethods, bool $cloneArguments) : array { - return $this->simpleName(); + try { + $class = new ReflectionClass($className); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $methods = []; + foreach ($class->getMethods() as $method) { + if (($method->isPublic() || $method->isAbstract()) && $this->canMockMethod($method)) { + $methods[] = \PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments); + } + } + return $methods; } /** - * @deprecated Use qualifiedName() instead + * @throws ReflectionException * - * @codeCoverageIgnore + * @return MockMethod[] */ - public function getQualifiedName() : string - { - return $this->qualifiedName(); - } - public function isNamespaced() : bool + public function mockInterfaceMethods(string $interfaceName, bool $cloneArguments) : array { - return $this->namespaceName !== null; + try { + $class = new ReflectionClass($interfaceName); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $methods = []; + foreach ($class->getMethods() as $method) { + $methods[] = \PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, \false, $cloneArguments); + } + return $methods; } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Type; - -use function assert; -use function class_exists; -use function is_iterable; -use ReflectionClass; -use ReflectionException; -final class IterableType extends \PHPUnit\SebastianBergmann\Type\Type -{ /** - * @var bool + * @psalm-param class-string $interfaceName + * + * @throws ReflectionException + * + * @return ReflectionMethod[] */ - private $allowsNull; - public function __construct(bool $nullable) + private function userDefinedInterfaceMethods(string $interfaceName) : array { - $this->allowsNull = $nullable; + try { + // @codeCoverageIgnoreStart + $interface = new ReflectionClass($interfaceName); + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $methods = []; + foreach ($interface->getMethods() as $method) { + if (!$method->isUserDefined()) { + continue; + } + $methods[] = $method; + } + return $methods; } /** + * @throws ReflectionException * @throws RuntimeException */ - public function isAssignable(\PHPUnit\SebastianBergmann\Type\Type $other) : bool + private function getObject(\PHPUnit\Framework\MockObject\MockType $mockClass, $type = '', bool $callOriginalConstructor = \false, bool $callAutoload = \false, array $arguments = [], bool $callOriginalMethods = \false, object $proxyTarget = null, bool $returnValueGeneration = \true) { - if ($this->allowsNull && $other instanceof \PHPUnit\SebastianBergmann\Type\NullType) { - return \true; + $className = $mockClass->generate(); + if ($callOriginalConstructor) { + if (count($arguments) === 0) { + $object = new $className(); + } else { + try { + $class = new ReflectionClass($className); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $object = $class->newInstanceArgs($arguments); + } + } else { + try { + $object = (new Instantiator())->instantiate($className); + } catch (InstantiatorException $e) { + throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage()); + } } - if ($other instanceof self) { - return \true; + if ($callOriginalMethods) { + if (!is_object($proxyTarget)) { + if (count($arguments) === 0) { + $proxyTarget = new $type(); + } else { + try { + $class = new ReflectionClass($type); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $proxyTarget = $class->newInstanceArgs($arguments); + } + } + $object->__phpunit_setOriginalObject($proxyTarget); } - if ($other instanceof \PHPUnit\SebastianBergmann\Type\SimpleType) { - return \is_iterable($other->value()); + if ($object instanceof \PHPUnit\Framework\MockObject\MockObject) { + $object->__phpunit_setReturnValueGeneration($returnValueGeneration); } - if ($other instanceof \PHPUnit\SebastianBergmann\Type\ObjectType) { - $className = $other->className()->qualifiedName(); - \assert(\class_exists($className)); + return $object; + } + /** + * @throws ClassIsFinalException + * @throws ReflectionException + * @throws RuntimeException + */ + private function generateMock(string $type, ?array $explicitMethods, string $mockClassName, bool $callOriginalClone, bool $callAutoload, bool $cloneArguments, bool $callOriginalMethods) : \PHPUnit\Framework\MockObject\MockClass + { + $classTemplate = $this->getTemplate('mocked_class.tpl'); + $additionalInterfaces = []; + $mockedCloneMethod = \false; + $unmockedCloneMethod = \false; + $isClass = \false; + $isInterface = \false; + $class = null; + $mockMethods = new \PHPUnit\Framework\MockObject\MockMethodSet(); + $_mockClassName = $this->generateClassName($type, $mockClassName, 'Mock_'); + if (class_exists($_mockClassName['fullClassName'], $callAutoload)) { + $isClass = \true; + } elseif (interface_exists($_mockClassName['fullClassName'], $callAutoload)) { + $isInterface = \true; + } + if (!$isClass && !$isInterface) { + $prologue = 'class ' . $_mockClassName['originalClassName'] . "\n{\n}\n\n"; + if (!empty($_mockClassName['namespaceName'])) { + $prologue = 'namespace ' . $_mockClassName['namespaceName'] . " {\n\n" . $prologue . "}\n\n" . "namespace {\n\n"; + $epilogue = "\n\n}"; + } + $mockedCloneMethod = \true; + } else { try { - return (new \ReflectionClass($className))->isIterable(); + $class = new ReflectionClass($_mockClassName['fullClassName']); // @codeCoverageIgnoreStart } catch (\ReflectionException $e) { - throw new \PHPUnit\SebastianBergmann\Type\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if ($class->isFinal()) { + throw new \PHPUnit\Framework\MockObject\ClassIsFinalException($_mockClassName['fullClassName']); + } + // @see https://github.com/sebastianbergmann/phpunit/issues/2995 + if ($isInterface && $class->implementsInterface(Throwable::class)) { + $actualClassName = Exception::class; + $additionalInterfaces[] = $class->getName(); + $isInterface = \false; + try { + $class = new ReflectionClass($actualClassName); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + foreach ($this->userDefinedInterfaceMethods($_mockClassName['fullClassName']) as $method) { + $methodName = $method->getName(); + if ($class->hasMethod($methodName)) { + try { + $classMethod = $class->getMethod($methodName); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if (!$this->canMockMethod($classMethod)) { + continue; + } + } + $mockMethods->addMethods(\PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments)); + } + $_mockClassName = $this->generateClassName($actualClassName, $_mockClassName['className'], 'Mock_'); + } + // @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/103 + if ($isInterface && $class->implementsInterface(Traversable::class) && !$class->implementsInterface(Iterator::class) && !$class->implementsInterface(IteratorAggregate::class)) { + $additionalInterfaces[] = Iterator::class; + $mockMethods->addMethods(...$this->mockClassMethods(Iterator::class, $callOriginalMethods, $cloneArguments)); + } + if ($class->hasMethod('__clone')) { + try { + $cloneMethod = $class->getMethod('__clone'); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } // @codeCoverageIgnoreEnd + if (!$cloneMethod->isFinal()) { + if ($callOriginalClone && !$isInterface) { + $unmockedCloneMethod = \true; + } else { + $mockedCloneMethod = \true; + } + } + } else { + $mockedCloneMethod = \true; } } - return \false; - } - public function name() : string - { - return 'iterable'; - } - public function allowsNull() : bool - { - return $this->allowsNull; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Type; - -final class StaticType extends \PHPUnit\SebastianBergmann\Type\Type -{ - /** - * @var TypeName - */ - private $className; - /** - * @var bool - */ - private $allowsNull; - public function __construct(\PHPUnit\SebastianBergmann\Type\TypeName $className, bool $allowsNull) - { - $this->className = $className; - $this->allowsNull = $allowsNull; - } - public function isAssignable(\PHPUnit\SebastianBergmann\Type\Type $other) : bool - { - if ($this->allowsNull && $other instanceof \PHPUnit\SebastianBergmann\Type\NullType) { - return \true; + if ($isClass && $explicitMethods === []) { + $mockMethods->addMethods(...$this->mockClassMethods($_mockClassName['fullClassName'], $callOriginalMethods, $cloneArguments)); } - if (!$other instanceof \PHPUnit\SebastianBergmann\Type\ObjectType) { - return \false; + if ($isInterface && ($explicitMethods === [] || $explicitMethods === null)) { + $mockMethods->addMethods(...$this->mockInterfaceMethods($_mockClassName['fullClassName'], $cloneArguments)); } - if (0 === \strcasecmp($this->className->qualifiedName(), $other->className()->qualifiedName())) { - return \true; + if (is_array($explicitMethods)) { + foreach ($explicitMethods as $methodName) { + if ($class !== null && $class->hasMethod($methodName)) { + try { + $method = $class->getMethod($methodName); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if ($this->canMockMethod($method)) { + $mockMethods->addMethods(\PHPUnit\Framework\MockObject\MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments)); + } + } else { + $mockMethods->addMethods(\PHPUnit\Framework\MockObject\MockMethod::fromName($_mockClassName['fullClassName'], $methodName, $cloneArguments)); + } + } } - if (\is_subclass_of($other->className()->qualifiedName(), $this->className->qualifiedName(), \true)) { - return \true; + $mockedMethods = ''; + $configurable = []; + foreach ($mockMethods->asArray() as $mockMethod) { + $mockedMethods .= $mockMethod->generateCode(); + $configurable[] = new \PHPUnit\Framework\MockObject\ConfigurableMethod($mockMethod->getName(), $mockMethod->getReturnType()); } - return \false; - } - public function name() : string - { - return 'static'; - } - public function allowsNull() : bool - { - return $this->allowsNull; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Type; - -use function strtolower; -final class SimpleType extends \PHPUnit\SebastianBergmann\Type\Type -{ - /** - * @var string - */ - private $name; - /** - * @var bool - */ - private $allowsNull; - /** - * @var mixed - */ - private $value; - public function __construct(string $name, bool $nullable, $value = null) - { - $this->name = $this->normalize($name); - $this->allowsNull = $nullable; - $this->value = $value; - } - public function isAssignable(\PHPUnit\SebastianBergmann\Type\Type $other) : bool - { - if ($this->allowsNull && $other instanceof \PHPUnit\SebastianBergmann\Type\NullType) { - return \true; + $method = ''; + if (!$mockMethods->hasMethod('method') && (!isset($class) || !$class->hasMethod('method'))) { + $method = \PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\Method;'; } - if ($other instanceof self) { - return $this->name === $other->name; + $cloneTrait = ''; + if ($mockedCloneMethod) { + $cloneTrait = \PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\MockedCloneMethod;'; } - return \false; + if ($unmockedCloneMethod) { + $cloneTrait = \PHP_EOL . ' use \\PHPUnit\\Framework\\MockObject\\UnmockedCloneMethod;'; + } + $classTemplate->setVar(['prologue' => $prologue ?? '', 'epilogue' => $epilogue ?? '', 'class_declaration' => $this->generateMockClassDeclaration($_mockClassName, $isInterface, $additionalInterfaces), 'clone' => $cloneTrait, 'mock_class_name' => $_mockClassName['className'], 'mocked_methods' => $mockedMethods, 'method' => $method]); + return new \PHPUnit\Framework\MockObject\MockClass($classTemplate->render(), $_mockClassName['className'], $configurable); } - public function name() : string + private function generateClassName(string $type, string $className, string $prefix) : array { - return $this->name; + if ($type[0] === '\\') { + $type = substr($type, 1); + } + $classNameParts = explode('\\', $type); + if (count($classNameParts) > 1) { + $type = array_pop($classNameParts); + $namespaceName = implode('\\', $classNameParts); + $fullClassName = $namespaceName . '\\' . $type; + } else { + $namespaceName = ''; + $fullClassName = $type; + } + if ($className === '') { + do { + $className = $prefix . $type . '_' . substr(md5((string) mt_rand()), 0, 8); + } while (class_exists($className, \false)); + } + return ['className' => $className, 'originalClassName' => $type, 'fullClassName' => $fullClassName, 'namespaceName' => $namespaceName]; } - public function allowsNull() : bool + private function generateMockClassDeclaration(array $mockClassName, bool $isInterface, array $additionalInterfaces = []) : string { - return $this->allowsNull; + $buffer = 'class '; + $additionalInterfaces[] = \PHPUnit\Framework\MockObject\MockObject::class; + $interfaces = implode(', ', $additionalInterfaces); + if ($isInterface) { + $buffer .= sprintf('%s implements %s', $mockClassName['className'], $interfaces); + if (!in_array($mockClassName['originalClassName'], $additionalInterfaces, \true)) { + $buffer .= ', '; + if (!empty($mockClassName['namespaceName'])) { + $buffer .= $mockClassName['namespaceName'] . '\\'; + } + $buffer .= $mockClassName['originalClassName']; + } + } else { + $buffer .= sprintf('%s extends %s%s implements %s', $mockClassName['className'], !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '', $mockClassName['originalClassName'], $interfaces); + } + return $buffer; } - public function value() + private function canMockMethod(ReflectionMethod $method) : bool { - return $this->value; + return !($this->isConstructor($method) || $method->isFinal() || $method->isPrivate() || $this->isMethodNameExcluded($method->getName())); } - private function normalize(string $name) : string + private function isMethodNameExcluded(string $name) : bool { - $name = \strtolower($name); - switch ($name) { - case 'boolean': - return 'bool'; - case 'real': - case 'double': - return 'float'; - case 'integer': - return 'int'; - case '[]': - return 'array'; - default: - return $name; - } + return isset(self::EXCLUDED_METHOD_NAMES[$name]); } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\Type; - -use function assert; -use function class_exists; -use function count; -use function explode; -use function function_exists; -use function is_array; -use function is_object; -use function is_string; -use function strpos; -use Closure; -use ReflectionClass; -use ReflectionException; -use ReflectionObject; -final class CallableType extends \PHPUnit\SebastianBergmann\Type\Type -{ /** - * @var bool + * @throws RuntimeException */ - private $allowsNull; - public function __construct(bool $nullable) + private function getTemplate(string $template) : Template { - $this->allowsNull = $nullable; + $filename = __DIR__ . \DIRECTORY_SEPARATOR . 'Generator' . \DIRECTORY_SEPARATOR . $template; + if (!isset(self::$templates[$filename])) { + try { + self::$templates[$filename] = new Template($filename); + } catch (TemplateException $e) { + throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); + } + } + return self::$templates[$filename]; } /** - * @throws RuntimeException + * @see https://github.com/sebastianbergmann/phpunit/issues/4139#issuecomment-605409765 */ - public function isAssignable(\PHPUnit\SebastianBergmann\Type\Type $other) : bool + private function isConstructor(ReflectionMethod $method) : bool { - if ($this->allowsNull && $other instanceof \PHPUnit\SebastianBergmann\Type\NullType) { + $methodName = strtolower($method->getName()); + if ($methodName === '__construct') { return \true; } - if ($other instanceof self) { - return \true; + if (\PHP_MAJOR_VERSION >= 8) { + return \false; } - if ($other instanceof \PHPUnit\SebastianBergmann\Type\ObjectType) { - if ($this->isClosure($other)) { - return \true; - } - if ($this->hasInvokeMethod($other)) { - return \true; + $className = strtolower($method->getDeclaringClass()->getName()); + return $methodName === $className; + } +} + + {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} + {{deprecation} + $__phpunit_arguments = [{arguments_call}]; + $__phpunit_count = func_num_args(); + + if ($__phpunit_count > {arguments_count}) { + $__phpunit_arguments_tmp = func_get_args(); + + for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { + $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; } } - if ($other instanceof \PHPUnit\SebastianBergmann\Type\SimpleType) { - if ($this->isFunction($other)) { - return \true; - } - if ($this->isClassCallback($other)) { - return \true; - } - if ($this->isObjectCallback($other)) { - return \true; + + $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} + ) + ); + } +declare(strict_types=1); + +{prologue}{class_declaration} +{ + use \PHPUnit\Framework\MockObject\Api;{method}{clone} +{mocked_methods}}{epilogue} + + {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} + {{deprecation} + $__phpunit_arguments = [{arguments_call}]; + $__phpunit_count = func_num_args(); + + if ($__phpunit_count > {arguments_count}) { + $__phpunit_arguments_tmp = func_get_args(); + + for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { + $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; } } - return \false; - } - public function name() : string - { - return 'callable'; - } - public function allowsNull() : bool - { - return $this->allowsNull; + + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} + ) + ); + + return $__phpunit_result; } - private function isClosure(\PHPUnit\SebastianBergmann\Type\ObjectType $type) : bool + + public function {method_name}({arguments}) { - return !$type->className()->isNamespaced() && $type->className()->simpleName() === \Closure::class; } - /** - * @throws RuntimeException - */ - private function hasInvokeMethod(\PHPUnit\SebastianBergmann\Type\ObjectType $type) : bool +declare(strict_types=1); + +{namespace}class {class_name} extends \SoapClient +{ + public function __construct($wsdl, array $options) { - $className = $type->className()->qualifiedName(); - \assert(\class_exists($className)); - try { - $class = new \ReflectionClass($className); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\SebastianBergmann\Type\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); - // @codeCoverageIgnoreEnd - } - if ($class->hasMethod('__invoke')) { - return \true; - } - return \false; + parent::__construct('{wsdl}', $options); } - private function isFunction(\PHPUnit\SebastianBergmann\Type\SimpleType $type) : bool +{methods}} + + {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} { - if (!\is_string($type->value())) { - return \false; + $__phpunit_arguments = [{arguments_call}]; + $__phpunit_count = func_num_args(); + + if ($__phpunit_count > {arguments_count}) { + $__phpunit_arguments_tmp = func_get_args(); + + for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { + $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; + } } - return \function_exists($type->value()); + + $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}, true + ) + ); + + return call_user_func_array(array($this->__phpunit_originalObject, "{method_name}"), $__phpunit_arguments); } - private function isObjectCallback(\PHPUnit\SebastianBergmann\Type\SimpleType $type) : bool + + {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} { - if (!\is_array($type->value())) { - return \false; - } - if (\count($type->value()) !== 2) { - return \false; - } - if (!\is_object($type->value()[0]) || !\is_string($type->value()[1])) { - return \false; + $__phpunit_arguments = [{arguments_call}]; + $__phpunit_count = func_num_args(); + + if ($__phpunit_count > {arguments_count}) { + $__phpunit_arguments_tmp = func_get_args(); + + for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { + $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; + } } - [$object, $methodName] = $type->value(); - return (new \ReflectionObject($object))->hasMethod($methodName); + + $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}, true + ) + ); + + call_user_func_array(array($this->__phpunit_originalObject, "{method_name}"), $__phpunit_arguments); } - private function isClassCallback(\PHPUnit\SebastianBergmann\Type\SimpleType $type) : bool + + {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} { - if (!\is_string($type->value()) && !\is_array($type->value())) { - return \false; - } - if (\is_string($type->value())) { - if (\strpos($type->value(), '::') === \false) { - return \false; - } - [$className, $methodName] = \explode('::', $type->value()); - } - if (\is_array($type->value())) { - if (\count($type->value()) !== 2) { - return \false; - } - if (!\is_string($type->value()[0]) || !\is_string($type->value()[1])) { - return \false; - } - [$className, $methodName] = $type->value(); - } - \assert(isset($className) && \is_string($className) && \class_exists($className)); - \assert(isset($methodName) && \is_string($methodName)); - try { - $class = new \ReflectionClass($className); - if ($class->hasMethod($methodName)) { - $method = $class->getMethod($methodName); - return $method->isPublic() && $method->isStatic(); - } - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new \PHPUnit\SebastianBergmann\Type\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); - // @codeCoverageIgnoreEnd - } - return \false; + throw new \PHPUnit\Framework\MockObject\BadMethodCallException('Static method "{method_name}" cannot be invoked on mock object'); } +declare(strict_types=1); + +{prologue}class {class_name} +{ + use {trait_name}; } + + @trigger_error({deprecation}, E_USER_DEPRECATED); * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\Type; - -final class VoidType extends \PHPUnit\SebastianBergmann\Type\Type -{ - public function isAssignable(\PHPUnit\SebastianBergmann\Type\Type $other) : bool - { - return $other instanceof self; - } - public function name() : string - { - return 'void'; - } - public function allowsNull() : bool - { - return \false; - } -} -sebastian/type - -Copyright (c) 2019-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -|string|string[] $type */ - public function function(string $name) : \PHPUnit\PhpParser\Builder\Function_ + public function __construct(TestCase $testCase, $type) { - return new \PHPUnit\PhpParser\Builder\Function_($name); + $this->testCase = $testCase; + $this->type = $type; + $this->generator = new \PHPUnit\Framework\MockObject\Generator(); } /** - * Creates a namespace/class use builder. + * Creates a mock object using a fluent interface. * - * @param Node\Name|string $name Name of the entity (namespace or class) to alias + * @throws \PHPUnit\Framework\InvalidArgumentException + * @throws ClassAlreadyExistsException + * @throws ClassIsFinalException + * @throws DuplicateMethodException + * @throws InvalidMethodNameException + * @throws OriginalConstructorInvocationRequiredException + * @throws ReflectionException + * @throws RuntimeException + * @throws UnknownTypeException * - * @return Builder\Use_ The created use builder + * @psalm-return MockObject&MockedType */ - public function use($name) : \PHPUnit\PhpParser\Builder\Use_ + public function getMock() : \PHPUnit\Framework\MockObject\MockObject { - return new \PHPUnit\PhpParser\Builder\Use_($name, \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_NORMAL); + $object = $this->generator->getMock($this->type, !$this->emptyMethodsArray ? $this->methods : null, $this->constructorArgs, $this->mockClassName, $this->originalConstructor, $this->originalClone, $this->autoload, $this->cloneArguments, $this->callOriginalMethods, $this->proxyTarget, $this->allowMockingUnknownTypes, $this->returnValueGeneration); + $this->testCase->registerMockObject($object); + return $object; } /** - * Creates a function use builder. + * Creates a mock object for an abstract class using a fluent interface. * - * @param Node\Name|string $name Name of the function to alias + * @psalm-return MockObject&MockedType * - * @return Builder\Use_ The created use function builder + * @throws \PHPUnit\Framework\Exception + * @throws ReflectionException + * @throws RuntimeException */ - public function useFunction($name) : \PHPUnit\PhpParser\Builder\Use_ + public function getMockForAbstractClass() : \PHPUnit\Framework\MockObject\MockObject { - return new \PHPUnit\PhpParser\Builder\Use_($name, \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_FUNCTION); + $object = $this->generator->getMockForAbstractClass($this->type, $this->constructorArgs, $this->mockClassName, $this->originalConstructor, $this->originalClone, $this->autoload, $this->methods, $this->cloneArguments); + $this->testCase->registerMockObject($object); + return $object; } /** - * Creates a constant use builder. + * Creates a mock object for a trait using a fluent interface. * - * @param Node\Name|string $name Name of the const to alias + * @psalm-return MockObject&MockedType * - * @return Builder\Use_ The created use const builder + * @throws \PHPUnit\Framework\Exception + * @throws ReflectionException + * @throws RuntimeException */ - public function useConst($name) : \PHPUnit\PhpParser\Builder\Use_ + public function getMockForTrait() : \PHPUnit\Framework\MockObject\MockObject { - return new \PHPUnit\PhpParser\Builder\Use_($name, \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_CONSTANT); + $object = $this->generator->getMockForTrait($this->type, $this->constructorArgs, $this->mockClassName, $this->originalConstructor, $this->originalClone, $this->autoload, $this->methods, $this->cloneArguments); + $this->testCase->registerMockObject($object); + return $object; } /** - * Creates node a for a literal value. + * Specifies the subset of methods to mock. Default is to mock none of them. * - * @param Expr|bool|null|int|float|string|array $value $value + * @deprecated https://github.com/sebastianbergmann/phpunit/pull/3687 * - * @return Expr + * @return $this */ - public function val($value) : \PHPUnit\PhpParser\Node\Expr + public function setMethods(?array $methods = null) : self { - return \PHPUnit\PhpParser\BuilderHelpers::normalizeValue($value); + if ($methods === null) { + $this->methods = $methods; + } else { + $this->methods = array_merge($this->methods ?? [], $methods); + } + return $this; } /** - * Creates variable node. + * Specifies the subset of methods to mock, requiring each to exist in the class. * - * @param string|Expr $name Name + * @param string[] $methods * - * @return Expr\Variable + * @throws CannotUseOnlyMethodsException + * @throws ReflectionException + * + * @return $this */ - public function var($name) : \PHPUnit\PhpParser\Node\Expr\Variable + public function onlyMethods(array $methods) : self { - if (!\is_string($name) && !$name instanceof \PHPUnit\PhpParser\Node\Expr) { - throw new \LogicException('Variable name must be string or Expr'); + if (empty($methods)) { + $this->emptyMethodsArray = \true; + return $this; + } + try { + $reflector = new ReflectionClass($this->type); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + foreach ($methods as $method) { + if (!$reflector->hasMethod($method)) { + throw new \PHPUnit\Framework\MockObject\CannotUseOnlyMethodsException($this->type, $method); + } } - return new \PHPUnit\PhpParser\Node\Expr\Variable($name); + $this->methods = array_merge($this->methods ?? [], $methods); + return $this; } /** - * Normalizes an argument list. + * Specifies methods that don't exist in the class which you want to mock. * - * Creates Arg nodes for all arguments and converts literal values to expressions. + * @param string[] $methods * - * @param array $args List of arguments to normalize + * @throws CannotUseAddMethodsException + * @throws ReflectionException + * @throws RuntimeException * - * @return Arg[] + * @return $this */ - public function args(array $args) : array + public function addMethods(array $methods) : self { - $normalizedArgs = []; - foreach ($args as $arg) { - if ($arg instanceof \PHPUnit\PhpParser\Node\Arg) { - $normalizedArgs[] = $arg; - } else { - $normalizedArgs[] = new \PHPUnit\PhpParser\Node\Arg(\PHPUnit\PhpParser\BuilderHelpers::normalizeValue($arg)); + if (empty($methods)) { + $this->emptyMethodsArray = \true; + return $this; + } + try { + $reflector = new ReflectionClass($this->type); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + foreach ($methods as $method) { + if ($reflector->hasMethod($method)) { + throw new \PHPUnit\Framework\MockObject\CannotUseAddMethodsException($this->type, $method); } } - return $normalizedArgs; + $this->methods = array_merge($this->methods ?? [], $methods); + return $this; } /** - * Creates a function call node. + * Specifies the subset of methods to not mock. Default is to mock all of them. * - * @param string|Name|Expr $name Function name - * @param array $args Function arguments + * @deprecated https://github.com/sebastianbergmann/phpunit/pull/3687 * - * @return Expr\FuncCall + * @throws ReflectionException */ - public function funcCall($name, array $args = []) : \PHPUnit\PhpParser\Node\Expr\FuncCall + public function setMethodsExcept(array $methods = []) : self { - return new \PHPUnit\PhpParser\Node\Expr\FuncCall(\PHPUnit\PhpParser\BuilderHelpers::normalizeNameOrExpr($name), $this->args($args)); + return $this->setMethods(array_diff($this->generator->getClassMethods($this->type), $methods)); } /** - * Creates a method call node. - * - * @param Expr $var Variable the method is called on - * @param string|Identifier|Expr $name Method name - * @param array $args Method arguments + * Specifies the arguments for the constructor. * - * @return Expr\MethodCall + * @return $this */ - public function methodCall(\PHPUnit\PhpParser\Node\Expr $var, $name, array $args = []) : \PHPUnit\PhpParser\Node\Expr\MethodCall + public function setConstructorArgs(array $args) : self { - return new \PHPUnit\PhpParser\Node\Expr\MethodCall($var, \PHPUnit\PhpParser\BuilderHelpers::normalizeIdentifierOrExpr($name), $this->args($args)); + $this->constructorArgs = $args; + return $this; } /** - * Creates a static method call node. - * - * @param string|Name|Expr $class Class name - * @param string|Identifier|Expr $name Method name - * @param array $args Method arguments + * Specifies the name for the mock class. * - * @return Expr\StaticCall + * @return $this */ - public function staticCall($class, $name, array $args = []) : \PHPUnit\PhpParser\Node\Expr\StaticCall + public function setMockClassName(string $name) : self { - return new \PHPUnit\PhpParser\Node\Expr\StaticCall(\PHPUnit\PhpParser\BuilderHelpers::normalizeNameOrExpr($class), \PHPUnit\PhpParser\BuilderHelpers::normalizeIdentifierOrExpr($name), $this->args($args)); + $this->mockClassName = $name; + return $this; } /** - * Creates an object creation node. - * - * @param string|Name|Expr $class Class name - * @param array $args Constructor arguments + * Disables the invocation of the original constructor. * - * @return Expr\New_ + * @return $this */ - public function new($class, array $args = []) : \PHPUnit\PhpParser\Node\Expr\New_ + public function disableOriginalConstructor() : self { - return new \PHPUnit\PhpParser\Node\Expr\New_(\PHPUnit\PhpParser\BuilderHelpers::normalizeNameOrExpr($class), $this->args($args)); + $this->originalConstructor = \false; + return $this; } /** - * Creates a constant fetch node. - * - * @param string|Name $name Constant name + * Enables the invocation of the original constructor. * - * @return Expr\ConstFetch + * @return $this */ - public function constFetch($name) : \PHPUnit\PhpParser\Node\Expr\ConstFetch + public function enableOriginalConstructor() : self { - return new \PHPUnit\PhpParser\Node\Expr\ConstFetch(\PHPUnit\PhpParser\BuilderHelpers::normalizeName($name)); + $this->originalConstructor = \true; + return $this; } /** - * Creates a property fetch node. - * - * @param Expr $var Variable holding object - * @param string|Identifier|Expr $name Property name + * Disables the invocation of the original clone constructor. * - * @return Expr\PropertyFetch + * @return $this */ - public function propertyFetch(\PHPUnit\PhpParser\Node\Expr $var, $name) : \PHPUnit\PhpParser\Node\Expr\PropertyFetch + public function disableOriginalClone() : self { - return new \PHPUnit\PhpParser\Node\Expr\PropertyFetch($var, \PHPUnit\PhpParser\BuilderHelpers::normalizeIdentifierOrExpr($name)); + $this->originalClone = \false; + return $this; } /** - * Creates a class constant fetch node. - * - * @param string|Name|Expr $class Class name - * @param string|Identifier $name Constant name + * Enables the invocation of the original clone constructor. * - * @return Expr\ClassConstFetch + * @return $this */ - public function classConstFetch($class, $name) : \PHPUnit\PhpParser\Node\Expr\ClassConstFetch + public function enableOriginalClone() : self { - return new \PHPUnit\PhpParser\Node\Expr\ClassConstFetch(\PHPUnit\PhpParser\BuilderHelpers::normalizeNameOrExpr($class), \PHPUnit\PhpParser\BuilderHelpers::normalizeIdentifier($name)); + $this->originalClone = \true; + return $this; } /** - * Creates nested Concat nodes from a list of expressions. - * - * @param Expr|string ...$exprs Expressions or literal strings + * Disables the use of class autoloading while creating the mock object. * - * @return Concat + * @return $this */ - public function concat(...$exprs) : \PHPUnit\PhpParser\Node\Expr\BinaryOp\Concat + public function disableAutoload() : self { - $numExprs = \count($exprs); - if ($numExprs < 2) { - throw new \LogicException('Expected at least two expressions'); - } - $lastConcat = $this->normalizeStringExpr($exprs[0]); - for ($i = 1; $i < $numExprs; $i++) { - $lastConcat = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Concat($lastConcat, $this->normalizeStringExpr($exprs[$i])); - } - return $lastConcat; + $this->autoload = \false; + return $this; } /** - * @param string|Expr $expr - * @return Expr + * Enables the use of class autoloading while creating the mock object. + * + * @return $this */ - private function normalizeStringExpr($expr) : \PHPUnit\PhpParser\Node\Expr - { - if ($expr instanceof \PHPUnit\PhpParser\Node\Expr) { - return $expr; - } - if (\is_string($expr)) { - return new \PHPUnit\PhpParser\Node\Scalar\String_($expr); - } - throw new \LogicException('Expected string or Expr'); - } -} -pAttrGroups($node->attrGroups, \true) . $this->pModifiers($node->flags) . ($node->type ? $this->p($node->type) . ' ' : '') . ($node->byRef ? '&' : '') . ($node->variadic ? '...' : '') . $this->p($node->var) . ($node->default ? ' = ' . $this->p($node->default) : ''); - } - protected function pArg(\PHPUnit\PhpParser\Node\Arg $node) - { - return ($node->name ? $node->name->toString() . ': ' : '') . ($node->byRef ? '&' : '') . ($node->unpack ? '...' : '') . $this->p($node->value); - } - protected function pConst(\PHPUnit\PhpParser\Node\Const_ $node) - { - return $node->name . ' = ' . $this->p($node->value); - } - protected function pNullableType(\PHPUnit\PhpParser\Node\NullableType $node) + public function enableAutoload() : self { - return '?' . $this->p($node->type); + $this->autoload = \true; + return $this; } - protected function pUnionType(\PHPUnit\PhpParser\Node\UnionType $node) + /** + * Disables the cloning of arguments passed to mocked methods. + * + * @return $this + */ + public function disableArgumentCloning() : self { - return $this->pImplode($node->types, '|'); + $this->cloneArguments = \false; + return $this; } - protected function pIdentifier(\PHPUnit\PhpParser\Node\Identifier $node) + /** + * Enables the cloning of arguments passed to mocked methods. + * + * @return $this + */ + public function enableArgumentCloning() : self { - return $node->name; + $this->cloneArguments = \true; + return $this; } - protected function pVarLikeIdentifier(\PHPUnit\PhpParser\Node\VarLikeIdentifier $node) + /** + * Enables the invocation of the original methods. + * + * @return $this + */ + public function enableProxyingToOriginalMethods() : self { - return '$' . $node->name; + $this->callOriginalMethods = \true; + return $this; } - protected function pAttribute(\PHPUnit\PhpParser\Node\Attribute $node) + /** + * Disables the invocation of the original methods. + * + * @return $this + */ + public function disableProxyingToOriginalMethods() : self { - return $this->p($node->name) . ($node->args ? '(' . $this->pCommaSeparated($node->args) . ')' : ''); + $this->callOriginalMethods = \false; + $this->proxyTarget = null; + return $this; } - protected function pAttributeGroup(\PHPUnit\PhpParser\Node\AttributeGroup $node) + /** + * Sets the proxy target. + * + * @return $this + */ + public function setProxyTarget(object $object) : self { - return '#[' . $this->pCommaSeparated($node->attrs) . ']'; + $this->proxyTarget = $object; + return $this; } - // Names - protected function pName(\PHPUnit\PhpParser\Node\Name $node) + /** + * @return $this + */ + public function allowMockingUnknownTypes() : self { - return \implode('\\', $node->parts); + $this->allowMockingUnknownTypes = \true; + return $this; } - protected function pName_FullyQualified(\PHPUnit\PhpParser\Node\Name\FullyQualified $node) + /** + * @return $this + */ + public function disallowMockingUnknownTypes() : self { - return '\\' . \implode('\\', $node->parts); + $this->allowMockingUnknownTypes = \false; + return $this; } - protected function pName_Relative(\PHPUnit\PhpParser\Node\Name\Relative $node) + /** + * @return $this + */ + public function enableAutoReturnValueGeneration() : self { - return 'namespace\\' . \implode('\\', $node->parts); + $this->returnValueGeneration = \true; + return $this; } - // Magic Constants - protected function pScalar_MagicConst_Class(\PHPUnit\PhpParser\Node\Scalar\MagicConst\Class_ $node) + /** + * @return $this + */ + public function disableAutoReturnValueGeneration() : self { - return '__CLASS__'; + $this->returnValueGeneration = \false; + return $this; } - protected function pScalar_MagicConst_Dir(\PHPUnit\PhpParser\Node\Scalar\MagicConst\Dir $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function array_key_exists; +use function array_values; +use function strtolower; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MockMethodSet +{ + /** + * @var MockMethod[] + */ + private $methods = []; + public function addMethods(\PHPUnit\Framework\MockObject\MockMethod ...$methods) : void { - return '__DIR__'; + foreach ($methods as $method) { + $this->methods[strtolower($method->getName())] = $method; + } } - protected function pScalar_MagicConst_File(\PHPUnit\PhpParser\Node\Scalar\MagicConst\File $node) + /** + * @return MockMethod[] + */ + public function asArray() : array { - return '__FILE__'; + return array_values($this->methods); } - protected function pScalar_MagicConst_Function(\PHPUnit\PhpParser\Node\Scalar\MagicConst\Function_ $node) + public function hasMethod(string $methodName) : bool { - return '__FUNCTION__'; + return array_key_exists(strtolower($methodName), $this->methods); } - protected function pScalar_MagicConst_Line(\PHPUnit\PhpParser\Node\Scalar\MagicConst\Line $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function array_map; +use function explode; +use function get_class; +use function implode; +use function is_object; +use function sprintf; +use function strpos; +use function strtolower; +use function substr; +use PHPUnit\Doctrine\Instantiator\Instantiator; +use PHPUnit\Framework\SelfDescribing; +use PHPUnit\Util\Type; +use PHPUnit\SebastianBergmann\Exporter\Exporter; +use stdClass; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Invocation implements SelfDescribing +{ + /** + * @var string + */ + private $className; + /** + * @var string + */ + private $methodName; + /** + * @var array + */ + private $parameters; + /** + * @var string + */ + private $returnType; + /** + * @var bool + */ + private $isReturnTypeNullable = \false; + /** + * @var bool + */ + private $proxiedCall; + /** + * @var object + */ + private $object; + public function __construct(string $className, string $methodName, array $parameters, string $returnType, object $object, bool $cloneObjects = \false, bool $proxiedCall = \false) { - return '__LINE__'; + $this->className = $className; + $this->methodName = $methodName; + $this->parameters = $parameters; + $this->object = $object; + $this->proxiedCall = $proxiedCall; + if (strtolower($methodName) === '__tostring') { + $returnType = 'string'; + } + if (strpos($returnType, '?') === 0) { + $returnType = substr($returnType, 1); + $this->isReturnTypeNullable = \true; + } + $this->returnType = $returnType; + if (!$cloneObjects) { + return; + } + foreach ($this->parameters as $key => $value) { + if (is_object($value)) { + $this->parameters[$key] = $this->cloneObject($value); + } + } } - protected function pScalar_MagicConst_Method(\PHPUnit\PhpParser\Node\Scalar\MagicConst\Method $node) + public function getClassName() : string { - return '__METHOD__'; + return $this->className; } - protected function pScalar_MagicConst_Namespace(\PHPUnit\PhpParser\Node\Scalar\MagicConst\Namespace_ $node) + public function getMethodName() : string { - return '__NAMESPACE__'; + return $this->methodName; } - protected function pScalar_MagicConst_Trait(\PHPUnit\PhpParser\Node\Scalar\MagicConst\Trait_ $node) + public function getParameters() : array { - return '__TRAIT__'; + return $this->parameters; } - // Scalars - protected function pScalar_String(\PHPUnit\PhpParser\Node\Scalar\String_ $node) + /** + * @throws RuntimeException + * + * @return mixed Mocked return value + */ + public function generateReturnValue() { - $kind = $node->getAttribute('kind', \PHPUnit\PhpParser\Node\Scalar\String_::KIND_SINGLE_QUOTED); - switch ($kind) { - case \PHPUnit\PhpParser\Node\Scalar\String_::KIND_NOWDOC: - $label = $node->getAttribute('docLabel'); - if ($label && !$this->containsEndLabel($node->value, $label)) { - if ($node->value === '') { - return "<<<'{$label}'\n{$label}" . $this->docStringEndToken; - } - return "<<<'{$label}'\n{$node->value}\n{$label}" . $this->docStringEndToken; - } - /* break missing intentionally */ - case \PHPUnit\PhpParser\Node\Scalar\String_::KIND_SINGLE_QUOTED: - return $this->pSingleQuotedString($node->value); - case \PHPUnit\PhpParser\Node\Scalar\String_::KIND_HEREDOC: - $label = $node->getAttribute('docLabel'); - if ($label && !$this->containsEndLabel($node->value, $label)) { - if ($node->value === '') { - return "<<<{$label}\n{$label}" . $this->docStringEndToken; - } - $escaped = $this->escapeString($node->value, null); - return "<<<{$label}\n" . $escaped . "\n{$label}" . $this->docStringEndToken; - } - /* break missing intentionally */ - case \PHPUnit\PhpParser\Node\Scalar\String_::KIND_DOUBLE_QUOTED: - return '"' . $this->escapeString($node->value, '"') . '"'; + if ($this->isReturnTypeNullable || $this->proxiedCall) { + return null; } - throw new \Exception('Invalid string kind'); - } - protected function pScalar_Encapsed(\PHPUnit\PhpParser\Node\Scalar\Encapsed $node) - { - if ($node->getAttribute('kind') === \PHPUnit\PhpParser\Node\Scalar\String_::KIND_HEREDOC) { - $label = $node->getAttribute('docLabel'); - if ($label && !$this->encapsedContainsEndLabel($node->parts, $label)) { - if (\count($node->parts) === 1 && $node->parts[0] instanceof \PHPUnit\PhpParser\Node\Scalar\EncapsedStringPart && $node->parts[0]->value === '') { - return "<<<{$label}\n{$label}" . $this->docStringEndToken; - } - return "<<<{$label}\n" . $this->pEncapsList($node->parts, null) . "\n{$label}" . $this->docStringEndToken; - } + $union = \false; + if (strpos($this->returnType, '|') !== \false) { + $types = explode('|', $this->returnType); + $union = \true; + } else { + $types = [$this->returnType]; } - return '"' . $this->pEncapsList($node->parts, '"') . '"'; - } - protected function pScalar_LNumber(\PHPUnit\PhpParser\Node\Scalar\LNumber $node) - { - if ($node->value === -\PHP_INT_MAX - 1) { - // PHP_INT_MIN cannot be represented as a literal, - // because the sign is not part of the literal - return '(-' . \PHP_INT_MAX . '-1)'; + $types = array_map('strtolower', $types); + if (\in_array('', $types, \true) || \in_array('null', $types, \true) || \in_array('mixed', $types, \true) || \in_array('void', $types, \true)) { + return null; } - $kind = $node->getAttribute('kind', \PHPUnit\PhpParser\Node\Scalar\LNumber::KIND_DEC); - if (\PHPUnit\PhpParser\Node\Scalar\LNumber::KIND_DEC === $kind) { - return (string) $node->value; + if (\in_array('false', $types, \true) || \in_array('bool', $types, \true)) { + return \false; } - if ($node->value < 0) { - $sign = '-'; - $str = (string) -$node->value; - } else { - $sign = ''; - $str = (string) $node->value; + if (\in_array('float', $types, \true)) { + return 0.0; } - switch ($kind) { - case \PHPUnit\PhpParser\Node\Scalar\LNumber::KIND_BIN: - return $sign . '0b' . \base_convert($str, 10, 2); - case \PHPUnit\PhpParser\Node\Scalar\LNumber::KIND_OCT: - return $sign . '0' . \base_convert($str, 10, 8); - case \PHPUnit\PhpParser\Node\Scalar\LNumber::KIND_HEX: - return $sign . '0x' . \base_convert($str, 10, 16); + if (\in_array('int', $types, \true)) { + return 0; } - throw new \Exception('Invalid number kind'); - } - protected function pScalar_DNumber(\PHPUnit\PhpParser\Node\Scalar\DNumber $node) - { - if (!\is_finite($node->value)) { - if ($node->value === \INF) { - return '\\INF'; - } elseif ($node->value === -\INF) { - return '-\\INF'; - } else { - return '\\NAN'; + if (\in_array('string', $types, \true)) { + return ''; + } + if (\in_array('array', $types, \true)) { + return []; + } + if (\in_array('static', $types, \true)) { + try { + return (new Instantiator())->instantiate(get_class($this->object)); + } catch (Throwable $t) { + throw new \PHPUnit\Framework\MockObject\RuntimeException($t->getMessage(), (int) $t->getCode(), $t); } } - // Try to find a short full-precision representation - $stringValue = \sprintf('%.16G', $node->value); - if ($node->value !== (double) $stringValue) { - $stringValue = \sprintf('%.17G', $node->value); + if (\in_array('object', $types, \true)) { + return new stdClass(); } - // %G is locale dependent and there exists no locale-independent alternative. We don't want - // mess with switching locales here, so let's assume that a comma is the only non-standard - // decimal separator we may encounter... - $stringValue = \str_replace(',', '.', $stringValue); - // ensure that number is really printed as float - return \preg_match('/^-?[0-9]+$/', $stringValue) ? $stringValue . '.0' : $stringValue; - } - protected function pScalar_EncapsedStringPart(\PHPUnit\PhpParser\Node\Scalar\EncapsedStringPart $node) - { - throw new \LogicException('Cannot directly print EncapsedStringPart'); - } - // Assignments - protected function pExpr_Assign(\PHPUnit\PhpParser\Node\Expr\Assign $node) - { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\Assign::class, $node->var, ' = ', $node->expr); - } - protected function pExpr_AssignRef(\PHPUnit\PhpParser\Node\Expr\AssignRef $node) - { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\AssignRef::class, $node->var, ' =& ', $node->expr); - } - protected function pExpr_AssignOp_Plus(\PHPUnit\PhpParser\Node\Expr\AssignOp\Plus $node) - { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\AssignOp\Plus::class, $node->var, ' += ', $node->expr); + if (\in_array('callable', $types, \true) || \in_array('closure', $types, \true)) { + return static function () : void { + }; + } + if (\in_array('traversable', $types, \true) || \in_array('generator', $types, \true) || \in_array('iterable', $types, \true)) { + $generator = static function () : \Generator { + yield from []; + }; + return $generator(); + } + if (!$union) { + try { + return (new \PHPUnit\Framework\MockObject\Generator())->getMock($this->returnType, [], [], '', \false); + } catch (Throwable $t) { + throw new \PHPUnit\Framework\MockObject\RuntimeException(sprintf('Return value for %s::%s() cannot be generated: %s', $this->className, $this->methodName, $t->getMessage()), (int) $t->getCode()); + } + } + throw new \PHPUnit\Framework\MockObject\RuntimeException(sprintf('Return value for %s::%s() cannot be generated because the declared return type is a union, please configure a return value for this method', $this->className, $this->methodName)); } - protected function pExpr_AssignOp_Minus(\PHPUnit\PhpParser\Node\Expr\AssignOp\Minus $node) + public function toString() : string { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\AssignOp\Minus::class, $node->var, ' -= ', $node->expr); + $exporter = new Exporter(); + return sprintf('%s::%s(%s)%s', $this->className, $this->methodName, implode(', ', array_map([$exporter, 'shortenedExport'], $this->parameters)), $this->returnType ? sprintf(': %s', $this->returnType) : ''); } - protected function pExpr_AssignOp_Mul(\PHPUnit\PhpParser\Node\Expr\AssignOp\Mul $node) + public function getObject() : object { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\AssignOp\Mul::class, $node->var, ' *= ', $node->expr); + return $this->object; } - protected function pExpr_AssignOp_Div(\PHPUnit\PhpParser\Node\Expr\AssignOp\Div $node) + private function cloneObject(object $original) : object { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\AssignOp\Div::class, $node->var, ' /= ', $node->expr); + if (Type::isCloneable($original)) { + return clone $original; + } + return $original; } - protected function pExpr_AssignOp_Concat(\PHPUnit\PhpParser\Node\Expr\AssignOp\Concat $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use const DIRECTORY_SEPARATOR; +use function implode; +use function is_string; +use function preg_match; +use function preg_replace; +use function sprintf; +use function substr_count; +use function trim; +use function var_export; +use ReflectionMethod; +use ReflectionNamedType; +use ReflectionParameter; +use ReflectionUnionType; +use PHPUnit\SebastianBergmann\Template\Exception as TemplateException; +use PHPUnit\SebastianBergmann\Template\Template; +use PHPUnit\SebastianBergmann\Type\ReflectionMapper; +use PHPUnit\SebastianBergmann\Type\Type; +use PHPUnit\SebastianBergmann\Type\UnknownType; +use PHPUnit\SebastianBergmann\Type\VoidType; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MockMethod +{ + /** + * @var Template[] + */ + private static $templates = []; + /** + * @var string + */ + private $className; + /** + * @var string + */ + private $methodName; + /** + * @var bool + */ + private $cloneArguments; + /** + * @var string string + */ + private $modifier; + /** + * @var string + */ + private $argumentsForDeclaration; + /** + * @var string + */ + private $argumentsForCall; + /** + * @var Type + */ + private $returnType; + /** + * @var string + */ + private $reference; + /** + * @var bool + */ + private $callOriginalMethod; + /** + * @var bool + */ + private $static; + /** + * @var ?string + */ + private $deprecation; + /** + * @throws ReflectionException + * @throws RuntimeException + */ + public static function fromReflection(ReflectionMethod $method, bool $callOriginalMethod, bool $cloneArguments) : self { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\AssignOp\Concat::class, $node->var, ' .= ', $node->expr); + if ($method->isPrivate()) { + $modifier = 'private'; + } elseif ($method->isProtected()) { + $modifier = 'protected'; + } else { + $modifier = 'public'; + } + if ($method->isStatic()) { + $modifier .= ' static'; + } + if ($method->returnsReference()) { + $reference = '&'; + } else { + $reference = ''; + } + $docComment = $method->getDocComment(); + if (is_string($docComment) && preg_match('#\\*[ \\t]*+@deprecated[ \\t]*+(.*?)\\r?+\\n[ \\t]*+\\*(?:[ \\t]*+@|/$)#s', $docComment, $deprecation)) { + $deprecation = trim(preg_replace('#[ \\t]*\\r?\\n[ \\t]*+\\*[ \\t]*+#', ' ', $deprecation[1])); + } else { + $deprecation = null; + } + return new self($method->getDeclaringClass()->getName(), $method->getName(), $cloneArguments, $modifier, self::getMethodParametersForDeclaration($method), self::getMethodParametersForCall($method), (new ReflectionMapper())->fromMethodReturnType($method), $reference, $callOriginalMethod, $method->isStatic(), $deprecation); } - protected function pExpr_AssignOp_Mod(\PHPUnit\PhpParser\Node\Expr\AssignOp\Mod $node) + public static function fromName(string $fullClassName, string $methodName, bool $cloneArguments) : self { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\AssignOp\Mod::class, $node->var, ' %= ', $node->expr); + return new self($fullClassName, $methodName, $cloneArguments, 'public', '', '', new UnknownType(), '', \false, \false, null); } - protected function pExpr_AssignOp_BitwiseAnd(\PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseAnd $node) + public function __construct(string $className, string $methodName, bool $cloneArguments, string $modifier, string $argumentsForDeclaration, string $argumentsForCall, Type $returnType, string $reference, bool $callOriginalMethod, bool $static, ?string $deprecation) { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseAnd::class, $node->var, ' &= ', $node->expr); + $this->className = $className; + $this->methodName = $methodName; + $this->cloneArguments = $cloneArguments; + $this->modifier = $modifier; + $this->argumentsForDeclaration = $argumentsForDeclaration; + $this->argumentsForCall = $argumentsForCall; + $this->returnType = $returnType; + $this->reference = $reference; + $this->callOriginalMethod = $callOriginalMethod; + $this->static = $static; + $this->deprecation = $deprecation; } - protected function pExpr_AssignOp_BitwiseOr(\PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseOr $node) + public function getName() : string { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseOr::class, $node->var, ' |= ', $node->expr); + return $this->methodName; } - protected function pExpr_AssignOp_BitwiseXor(\PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseXor $node) + /** + * @throws RuntimeException + */ + public function generateCode() : string { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseXor::class, $node->var, ' ^= ', $node->expr); + if ($this->static) { + $templateFile = 'mocked_static_method.tpl'; + } elseif ($this->returnType instanceof VoidType) { + $templateFile = sprintf('%s_method_void.tpl', $this->callOriginalMethod ? 'proxied' : 'mocked'); + } else { + $templateFile = sprintf('%s_method.tpl', $this->callOriginalMethod ? 'proxied' : 'mocked'); + } + $deprecation = $this->deprecation; + if (null !== $this->deprecation) { + $deprecation = "The {$this->className}::{$this->methodName} method is deprecated ({$this->deprecation})."; + $deprecationTemplate = $this->getTemplate('deprecation.tpl'); + $deprecationTemplate->setVar(['deprecation' => var_export($deprecation, \true)]); + $deprecation = $deprecationTemplate->render(); + } + $template = $this->getTemplate($templateFile); + $template->setVar(['arguments_decl' => $this->argumentsForDeclaration, 'arguments_call' => $this->argumentsForCall, 'return_declaration' => !empty($this->returnType->asString()) ? ': ' . $this->returnType->asString() : '', 'return_type' => $this->returnType->asString(), 'arguments_count' => !empty($this->argumentsForCall) ? substr_count($this->argumentsForCall, ',') + 1 : 0, 'class_name' => $this->className, 'method_name' => $this->methodName, 'modifier' => $this->modifier, 'reference' => $this->reference, 'clone_arguments' => $this->cloneArguments ? 'true' : 'false', 'deprecation' => $deprecation]); + return $template->render(); } - protected function pExpr_AssignOp_ShiftLeft(\PHPUnit\PhpParser\Node\Expr\AssignOp\ShiftLeft $node) + public function getReturnType() : Type { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\AssignOp\ShiftLeft::class, $node->var, ' <<= ', $node->expr); + return $this->returnType; } - protected function pExpr_AssignOp_ShiftRight(\PHPUnit\PhpParser\Node\Expr\AssignOp\ShiftRight $node) + /** + * @throws RuntimeException + */ + private function getTemplate(string $template) : Template { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\AssignOp\ShiftRight::class, $node->var, ' >>= ', $node->expr); + $filename = __DIR__ . \DIRECTORY_SEPARATOR . 'Generator' . \DIRECTORY_SEPARATOR . $template; + if (!isset(self::$templates[$filename])) { + try { + self::$templates[$filename] = new Template($filename); + } catch (TemplateException $e) { + throw new \PHPUnit\Framework\MockObject\RuntimeException($e->getMessage(), (int) $e->getCode(), $e); + } + } + return self::$templates[$filename]; } - protected function pExpr_AssignOp_Pow(\PHPUnit\PhpParser\Node\Expr\AssignOp\Pow $node) + /** + * Returns the parameters of a function or method. + * + * @throws RuntimeException + */ + private static function getMethodParametersForDeclaration(ReflectionMethod $method) : string { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\AssignOp\Pow::class, $node->var, ' **= ', $node->expr); + $parameters = []; + foreach ($method->getParameters() as $i => $parameter) { + $name = '$' . $parameter->getName(); + /* Note: PHP extensions may use empty names for reference arguments + * or "..." for methods taking a variable number of arguments. + */ + if ($name === '$' || $name === '$...') { + $name = '$arg' . $i; + } + $nullable = ''; + $default = ''; + $reference = ''; + $typeDeclaration = ''; + $type = null; + $typeName = null; + if ($parameter->hasType()) { + $type = $parameter->getType(); + if ($type instanceof ReflectionNamedType) { + $typeName = $type->getName(); + } + } + if ($parameter->isVariadic()) { + $name = '...' . $name; + } elseif ($parameter->isDefaultValueAvailable()) { + $default = ' = ' . self::exportDefaultValue($parameter); + } elseif ($parameter->isOptional()) { + $default = ' = null'; + } + if ($type !== null) { + if ($typeName !== 'mixed' && $parameter->allowsNull() && !$type instanceof ReflectionUnionType) { + $nullable = '?'; + } + if ($typeName === 'self') { + $typeDeclaration = $method->getDeclaringClass()->getName() . ' '; + } elseif ($typeName !== null) { + $typeDeclaration = $typeName . ' '; + } elseif ($type instanceof ReflectionUnionType) { + $typeDeclaration = self::unionTypeAsString($type, $method->getDeclaringClass()->getName()); + } + } + if ($parameter->isPassedByReference()) { + $reference = '&'; + } + $parameters[] = $nullable . $typeDeclaration . $reference . $name . $default; + } + return implode(', ', $parameters); } - protected function pExpr_AssignOp_Coalesce(\PHPUnit\PhpParser\Node\Expr\AssignOp\Coalesce $node) + /** + * Returns the parameters of a function or method. + * + * @throws ReflectionException + */ + private static function getMethodParametersForCall(ReflectionMethod $method) : string { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\AssignOp\Coalesce::class, $node->var, ' ??= ', $node->expr); + $parameters = []; + foreach ($method->getParameters() as $i => $parameter) { + $name = '$' . $parameter->getName(); + /* Note: PHP extensions may use empty names for reference arguments + * or "..." for methods taking a variable number of arguments. + */ + if ($name === '$' || $name === '$...') { + $name = '$arg' . $i; + } + if ($parameter->isVariadic()) { + continue; + } + if ($parameter->isPassedByReference()) { + $parameters[] = '&' . $name; + } else { + $parameters[] = $name; + } + } + return implode(', ', $parameters); } - // Binary expressions - protected function pExpr_BinaryOp_Plus(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Plus $node) + /** + * @throws ReflectionException + */ + private static function exportDefaultValue(ReflectionParameter $parameter) : string { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Plus::class, $node->left, ' + ', $node->right); + try { + return (string) var_export($parameter->getDefaultValue(), \true); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new \PHPUnit\Framework\MockObject\ReflectionException($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd } - protected function pExpr_BinaryOp_Minus(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Minus $node) + private static function unionTypeAsString(ReflectionUnionType $union, string $self) : string { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Minus::class, $node->left, ' - ', $node->right); + $types = []; + foreach ($union->getTypes() as $type) { + if ((string) $type === 'self') { + $types[] = $self; + } else { + $types[] = $type; + } + } + return implode('|', $types) . ' '; } - protected function pExpr_BinaryOp_Mul(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Mul $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function is_string; +use function sprintf; +use function strtolower; +use PHPUnit\Framework\Constraint\Constraint; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MethodNameConstraint extends Constraint +{ + /** + * @var string + */ + private $methodName; + public function __construct(string $methodName) { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Mul::class, $node->left, ' * ', $node->right); + $this->methodName = $methodName; } - protected function pExpr_BinaryOp_Div(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Div $node) + public function toString() : string { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Div::class, $node->left, ' / ', $node->right); + return sprintf('is "%s"', $this->methodName); } - protected function pExpr_BinaryOp_Concat(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Concat $node) + protected function matches($other) : bool { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Concat::class, $node->left, ' . ', $node->right); + if (!is_string($other)) { + return \false; + } + return strtolower($this->methodName) === strtolower($other); } - protected function pExpr_BinaryOp_Mod(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Mod $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Rule; + +use function count; +use function gettype; +use function is_iterable; +use function sprintf; +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\Constraint\IsEqual; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\InvalidParameterGroupException; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ConsecutiveParameters implements \PHPUnit\Framework\MockObject\Rule\ParametersRule +{ + /** + * @var array + */ + private $parameterGroups = []; + /** + * @var array + */ + private $invocations = []; + /** + * @throws \PHPUnit\Framework\Exception + */ + public function __construct(array $parameterGroups) { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Mod::class, $node->left, ' % ', $node->right); + foreach ($parameterGroups as $index => $parameters) { + if (!is_iterable($parameters)) { + throw new InvalidParameterGroupException(sprintf('Parameter group #%d must be an array or Traversable, got %s', $index, gettype($parameters))); + } + foreach ($parameters as $parameter) { + if (!$parameter instanceof Constraint) { + $parameter = new IsEqual($parameter); + } + $this->parameterGroups[$index][] = $parameter; + } + } } - protected function pExpr_BinaryOp_BooleanAnd(\PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanAnd $node) + public function toString() : string { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanAnd::class, $node->left, ' && ', $node->right); + return 'with consecutive parameters'; } - protected function pExpr_BinaryOp_BooleanOr(\PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanOr $node) + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + */ + public function apply(BaseInvocation $invocation) : void { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanOr::class, $node->left, ' || ', $node->right); + $this->invocations[] = $invocation; + $callIndex = count($this->invocations) - 1; + $this->verifyInvocation($invocation, $callIndex); } - protected function pExpr_BinaryOp_BitwiseAnd(\PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseAnd $node) + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function verify() : void { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseAnd::class, $node->left, ' & ', $node->right); + foreach ($this->invocations as $callIndex => $invocation) { + $this->verifyInvocation($invocation, $callIndex); + } } - protected function pExpr_BinaryOp_BitwiseOr(\PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseOr $node) + /** + * Verify a single invocation. + * + * @param int $callIndex + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + */ + private function verifyInvocation(BaseInvocation $invocation, $callIndex) : void { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseOr::class, $node->left, ' | ', $node->right); + if (!isset($this->parameterGroups[$callIndex])) { + // no parameter assertion for this call index + return; + } + $parameters = $this->parameterGroups[$callIndex]; + if (count($invocation->getParameters()) < count($parameters)) { + throw new ExpectationFailedException(sprintf('Parameter count for invocation %s is too low.', $invocation->toString())); + } + foreach ($parameters as $i => $parameter) { + $parameter->evaluate($invocation->getParameters()[$i], sprintf('Parameter %s for invocation #%d %s does not match expected ' . 'value.', $i, $callIndex, $invocation->toString())); + } } - protected function pExpr_BinaryOp_BitwiseXor(\PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseXor $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Rule; + +use function is_string; +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\InvalidArgumentException; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +use PHPUnit\Framework\MockObject\MethodNameConstraint; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MethodName +{ + /** + * @var Constraint + */ + private $constraint; + /** + * @param Constraint|string $constraint + * + * @throws InvalidArgumentException + */ + public function __construct($constraint) { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseXor::class, $node->left, ' ^ ', $node->right); + if (is_string($constraint)) { + $constraint = new MethodNameConstraint($constraint); + } + if (!$constraint instanceof Constraint) { + throw InvalidArgumentException::create(1, 'PHPUnit\\Framework\\Constraint\\Constraint object or string'); + } + $this->constraint = $constraint; } - protected function pExpr_BinaryOp_ShiftLeft(\PHPUnit\PhpParser\Node\Expr\BinaryOp\ShiftLeft $node) + public function toString() : string { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\ShiftLeft::class, $node->left, ' << ', $node->right); + return 'method name ' . $this->constraint->toString(); } - protected function pExpr_BinaryOp_ShiftRight(\PHPUnit\PhpParser\Node\Expr\BinaryOp\ShiftRight $node) + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function matches(BaseInvocation $invocation) : bool { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\ShiftRight::class, $node->left, ' >> ', $node->right); + return $this->matchesName($invocation->getMethodName()); } - protected function pExpr_BinaryOp_Pow(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Pow $node) + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function matchesName(string $methodName) : bool { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Pow::class, $node->left, ' ** ', $node->right); + return (bool) $this->constraint->evaluate($methodName, '', \true); } - protected function pExpr_BinaryOp_LogicalAnd(\PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalAnd $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Rule; + +use function sprintf; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4297 + * @codeCoverageIgnore + */ +final class InvokedAtIndex extends \PHPUnit\Framework\MockObject\Rule\InvocationOrder +{ + /** + * @var int + */ + private $sequenceIndex; + /** + * @var int + */ + private $currentIndex = -1; + /** + * @param int $sequenceIndex + */ + public function __construct($sequenceIndex) { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalAnd::class, $node->left, ' and ', $node->right); + $this->sequenceIndex = $sequenceIndex; } - protected function pExpr_BinaryOp_LogicalOr(\PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalOr $node) + public function toString() : string { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalOr::class, $node->left, ' or ', $node->right); + return 'invoked at sequence index ' . $this->sequenceIndex; } - protected function pExpr_BinaryOp_LogicalXor(\PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalXor $node) + public function matches(BaseInvocation $invocation) : bool { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalXor::class, $node->left, ' xor ', $node->right); + $this->currentIndex++; + return $this->currentIndex == $this->sequenceIndex; } - protected function pExpr_BinaryOp_Equal(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Equal $node) + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws ExpectationFailedException + */ + public function verify() : void { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Equal::class, $node->left, ' == ', $node->right); + if ($this->currentIndex < $this->sequenceIndex) { + throw new ExpectationFailedException(sprintf('The expected invocation at index %s was never reached.', $this->sequenceIndex)); + } } - protected function pExpr_BinaryOp_NotEqual(\PHPUnit\PhpParser\Node\Expr\BinaryOp\NotEqual $node) + protected function invokedDo(BaseInvocation $invocation) : void { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\NotEqual::class, $node->left, ' != ', $node->right); } - protected function pExpr_BinaryOp_Identical(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Identical $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Rule; + +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class AnyParameters implements \PHPUnit\Framework\MockObject\Rule\ParametersRule +{ + public function toString() : string { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Identical::class, $node->left, ' === ', $node->right); + return 'with any parameters'; } - protected function pExpr_BinaryOp_NotIdentical(\PHPUnit\PhpParser\Node\Expr\BinaryOp\NotIdentical $node) + public function apply(BaseInvocation $invocation) : void { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\NotIdentical::class, $node->left, ' !== ', $node->right); } - protected function pExpr_BinaryOp_Spaceship(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Spaceship $node) + public function verify() : void { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Spaceship::class, $node->left, ' <=> ', $node->right); } - protected function pExpr_BinaryOp_Greater(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Greater $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Rule; + +use function count; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +use PHPUnit\Framework\MockObject\Verifiable; +use PHPUnit\Framework\SelfDescribing; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +abstract class InvocationOrder implements SelfDescribing, Verifiable +{ + /** + * @var BaseInvocation[] + */ + private $invocations = []; + public function getInvocationCount() : int { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Greater::class, $node->left, ' > ', $node->right); + return count($this->invocations); } - protected function pExpr_BinaryOp_GreaterOrEqual(\PHPUnit\PhpParser\Node\Expr\BinaryOp\GreaterOrEqual $node) + public function hasBeenInvoked() : bool { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\GreaterOrEqual::class, $node->left, ' >= ', $node->right); + return count($this->invocations) > 0; } - protected function pExpr_BinaryOp_Smaller(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Smaller $node) + public final function invoked(BaseInvocation $invocation) { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Smaller::class, $node->left, ' < ', $node->right); + $this->invocations[] = $invocation; + return $this->invokedDo($invocation); } - protected function pExpr_BinaryOp_SmallerOrEqual(\PHPUnit\PhpParser\Node\Expr\BinaryOp\SmallerOrEqual $node) + public abstract function matches(BaseInvocation $invocation) : bool; + protected abstract function invokedDo(BaseInvocation $invocation); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Rule; + +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvokedAtLeastOnce extends \PHPUnit\Framework\MockObject\Rule\InvocationOrder +{ + public function toString() : string { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\SmallerOrEqual::class, $node->left, ' <= ', $node->right); + return 'invoked at least once'; } - protected function pExpr_BinaryOp_Coalesce(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Coalesce $node) + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws ExpectationFailedException + */ + public function verify() : void { - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp\Coalesce::class, $node->left, ' ?? ', $node->right); + $count = $this->getInvocationCount(); + if ($count < 1) { + throw new ExpectationFailedException('Expected invocation at least once but it never occurred.'); + } } - protected function pExpr_Instanceof(\PHPUnit\PhpParser\Node\Expr\Instanceof_ $node) + public function matches(BaseInvocation $invocation) : bool { - list($precedence, $associativity) = $this->precedenceMap[\PHPUnit\PhpParser\Node\Expr\Instanceof_::class]; - return $this->pPrec($node->expr, $precedence, $associativity, -1) . ' instanceof ' . $this->pNewVariable($node->class); + return \true; } - // Unary expressions - protected function pExpr_BooleanNot(\PHPUnit\PhpParser\Node\Expr\BooleanNot $node) + protected function invokedDo(BaseInvocation $invocation) : void { - return $this->pPrefixOp(\PHPUnit\PhpParser\Node\Expr\BooleanNot::class, '!', $node->expr); } - protected function pExpr_BitwiseNot(\PHPUnit\PhpParser\Node\Expr\BitwiseNot $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Rule; + +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvokedAtMostCount extends \PHPUnit\Framework\MockObject\Rule\InvocationOrder +{ + /** + * @var int + */ + private $allowedInvocations; + /** + * @param int $allowedInvocations + */ + public function __construct($allowedInvocations) { - return $this->pPrefixOp(\PHPUnit\PhpParser\Node\Expr\BitwiseNot::class, '~', $node->expr); + $this->allowedInvocations = $allowedInvocations; } - protected function pExpr_UnaryMinus(\PHPUnit\PhpParser\Node\Expr\UnaryMinus $node) + public function toString() : string { - if ($node->expr instanceof \PHPUnit\PhpParser\Node\Expr\UnaryMinus || $node->expr instanceof \PHPUnit\PhpParser\Node\Expr\PreDec) { - // Enforce -(-$expr) instead of --$expr - return '-(' . $this->p($node->expr) . ')'; - } - return $this->pPrefixOp(\PHPUnit\PhpParser\Node\Expr\UnaryMinus::class, '-', $node->expr); + return 'invoked at most ' . $this->allowedInvocations . ' times'; } - protected function pExpr_UnaryPlus(\PHPUnit\PhpParser\Node\Expr\UnaryPlus $node) + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws ExpectationFailedException + */ + public function verify() : void { - if ($node->expr instanceof \PHPUnit\PhpParser\Node\Expr\UnaryPlus || $node->expr instanceof \PHPUnit\PhpParser\Node\Expr\PreInc) { - // Enforce +(+$expr) instead of ++$expr - return '+(' . $this->p($node->expr) . ')'; + $count = $this->getInvocationCount(); + if ($count > $this->allowedInvocations) { + throw new ExpectationFailedException('Expected invocation at most ' . $this->allowedInvocations . ' times but it occurred ' . $count . ' time(s).'); } - return $this->pPrefixOp(\PHPUnit\PhpParser\Node\Expr\UnaryPlus::class, '+', $node->expr); - } - protected function pExpr_PreInc(\PHPUnit\PhpParser\Node\Expr\PreInc $node) - { - return $this->pPrefixOp(\PHPUnit\PhpParser\Node\Expr\PreInc::class, '++', $node->var); } - protected function pExpr_PreDec(\PHPUnit\PhpParser\Node\Expr\PreDec $node) + public function matches(BaseInvocation $invocation) : bool { - return $this->pPrefixOp(\PHPUnit\PhpParser\Node\Expr\PreDec::class, '--', $node->var); + return \true; } - protected function pExpr_PostInc(\PHPUnit\PhpParser\Node\Expr\PostInc $node) + protected function invokedDo(BaseInvocation $invocation) : void { - return $this->pPostfixOp(\PHPUnit\PhpParser\Node\Expr\PostInc::class, $node->var, '++'); } - protected function pExpr_PostDec(\PHPUnit\PhpParser\Node\Expr\PostDec $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Rule; + +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvokedAtLeastCount extends \PHPUnit\Framework\MockObject\Rule\InvocationOrder +{ + /** + * @var int + */ + private $requiredInvocations; + /** + * @param int $requiredInvocations + */ + public function __construct($requiredInvocations) { - return $this->pPostfixOp(\PHPUnit\PhpParser\Node\Expr\PostDec::class, $node->var, '--'); + $this->requiredInvocations = $requiredInvocations; } - protected function pExpr_ErrorSuppress(\PHPUnit\PhpParser\Node\Expr\ErrorSuppress $node) + public function toString() : string { - return $this->pPrefixOp(\PHPUnit\PhpParser\Node\Expr\ErrorSuppress::class, '@', $node->expr); + return 'invoked at least ' . $this->requiredInvocations . ' times'; } - protected function pExpr_YieldFrom(\PHPUnit\PhpParser\Node\Expr\YieldFrom $node) + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws ExpectationFailedException + */ + public function verify() : void { - return $this->pPrefixOp(\PHPUnit\PhpParser\Node\Expr\YieldFrom::class, 'yield from ', $node->expr); + $count = $this->getInvocationCount(); + if ($count < $this->requiredInvocations) { + throw new ExpectationFailedException('Expected invocation at least ' . $this->requiredInvocations . ' times but it occurred ' . $count . ' time(s).'); + } } - protected function pExpr_Print(\PHPUnit\PhpParser\Node\Expr\Print_ $node) + public function matches(BaseInvocation $invocation) : bool { - return $this->pPrefixOp(\PHPUnit\PhpParser\Node\Expr\Print_::class, 'print ', $node->expr); + return \true; } - // Casts - protected function pExpr_Cast_Int(\PHPUnit\PhpParser\Node\Expr\Cast\Int_ $node) + protected function invokedDo(BaseInvocation $invocation) : void { - return $this->pPrefixOp(\PHPUnit\PhpParser\Node\Expr\Cast\Int_::class, '(int) ', $node->expr); } - protected function pExpr_Cast_Double(\PHPUnit\PhpParser\Node\Expr\Cast\Double $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Rule; + +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class AnyInvokedCount extends \PHPUnit\Framework\MockObject\Rule\InvocationOrder +{ + public function toString() : string { - $kind = $node->getAttribute('kind', \PHPUnit\PhpParser\Node\Expr\Cast\Double::KIND_DOUBLE); - if ($kind === \PHPUnit\PhpParser\Node\Expr\Cast\Double::KIND_DOUBLE) { - $cast = '(double)'; - } elseif ($kind === \PHPUnit\PhpParser\Node\Expr\Cast\Double::KIND_FLOAT) { - $cast = '(float)'; - } elseif ($kind === \PHPUnit\PhpParser\Node\Expr\Cast\Double::KIND_REAL) { - $cast = '(real)'; - } - return $this->pPrefixOp(\PHPUnit\PhpParser\Node\Expr\Cast\Double::class, $cast . ' ', $node->expr); + return 'invoked zero or more times'; } - protected function pExpr_Cast_String(\PHPUnit\PhpParser\Node\Expr\Cast\String_ $node) + public function verify() : void { - return $this->pPrefixOp(\PHPUnit\PhpParser\Node\Expr\Cast\String_::class, '(string) ', $node->expr); } - protected function pExpr_Cast_Array(\PHPUnit\PhpParser\Node\Expr\Cast\Array_ $node) + public function matches(BaseInvocation $invocation) : bool { - return $this->pPrefixOp(\PHPUnit\PhpParser\Node\Expr\Cast\Array_::class, '(array) ', $node->expr); + return \true; } - protected function pExpr_Cast_Object(\PHPUnit\PhpParser\Node\Expr\Cast\Object_ $node) + protected function invokedDo(BaseInvocation $invocation) : void { - return $this->pPrefixOp(\PHPUnit\PhpParser\Node\Expr\Cast\Object_::class, '(object) ', $node->expr); } - protected function pExpr_Cast_Bool(\PHPUnit\PhpParser\Node\Expr\Cast\Bool_ $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Rule; + +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +use PHPUnit\Framework\MockObject\Verifiable; +use PHPUnit\Framework\SelfDescribing; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface ParametersRule extends SelfDescribing, Verifiable +{ + /** + * @throws ExpectationFailedException if the invocation violates the rule + */ + public function apply(BaseInvocation $invocation) : void; + public function verify() : void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Rule; + +use function sprintf; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvokedCount extends \PHPUnit\Framework\MockObject\Rule\InvocationOrder +{ + /** + * @var int + */ + private $expectedCount; + /** + * @param int $expectedCount + */ + public function __construct($expectedCount) { - return $this->pPrefixOp(\PHPUnit\PhpParser\Node\Expr\Cast\Bool_::class, '(bool) ', $node->expr); + $this->expectedCount = $expectedCount; } - protected function pExpr_Cast_Unset(\PHPUnit\PhpParser\Node\Expr\Cast\Unset_ $node) + public function isNever() : bool { - return $this->pPrefixOp(\PHPUnit\PhpParser\Node\Expr\Cast\Unset_::class, '(unset) ', $node->expr); + return $this->expectedCount === 0; } - // Function calls and similar constructs - protected function pExpr_FuncCall(\PHPUnit\PhpParser\Node\Expr\FuncCall $node) + public function toString() : string { - return $this->pCallLhs($node->name) . '(' . $this->pMaybeMultiline($node->args) . ')'; + return 'invoked ' . $this->expectedCount . ' time(s)'; } - protected function pExpr_MethodCall(\PHPUnit\PhpParser\Node\Expr\MethodCall $node) + public function matches(BaseInvocation $invocation) : bool { - return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name) . '(' . $this->pMaybeMultiline($node->args) . ')'; + return \true; } - protected function pExpr_NullsafeMethodCall(\PHPUnit\PhpParser\Node\Expr\NullsafeMethodCall $node) + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws ExpectationFailedException + */ + public function verify() : void { - return $this->pDereferenceLhs($node->var) . '?->' . $this->pObjectProperty($node->name) . '(' . $this->pMaybeMultiline($node->args) . ')'; + $count = $this->getInvocationCount(); + if ($count !== $this->expectedCount) { + throw new ExpectationFailedException(sprintf('Method was expected to be called %d times, ' . 'actually called %d times.', $this->expectedCount, $count)); + } } - protected function pExpr_StaticCall(\PHPUnit\PhpParser\Node\Expr\StaticCall $node) + /** + * @throws ExpectationFailedException + */ + protected function invokedDo(BaseInvocation $invocation) : void { - return $this->pDereferenceLhs($node->class) . '::' . ($node->name instanceof \PHPUnit\PhpParser\Node\Expr ? $node->name instanceof \PHPUnit\PhpParser\Node\Expr\Variable ? $this->p($node->name) : '{' . $this->p($node->name) . '}' : $node->name) . '(' . $this->pMaybeMultiline($node->args) . ')'; + $count = $this->getInvocationCount(); + if ($count > $this->expectedCount) { + $message = $invocation->toString() . ' '; + switch ($this->expectedCount) { + case 0: + $message .= 'was not expected to be called.'; + break; + case 1: + $message .= 'was not expected to be called more than once.'; + break; + default: + $message .= sprintf('was not expected to be called more than %d times.', $this->expectedCount); + } + throw new ExpectationFailedException($message); + } } - protected function pExpr_Empty(\PHPUnit\PhpParser\Node\Expr\Empty_ $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Rule; + +use function count; +use function get_class; +use function sprintf; +use Exception; +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\Constraint\IsAnything; +use PHPUnit\Framework\Constraint\IsEqual; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Parameters implements \PHPUnit\Framework\MockObject\Rule\ParametersRule +{ + /** + * @var Constraint[] + */ + private $parameters = []; + /** + * @var BaseInvocation + */ + private $invocation; + /** + * @var bool|ExpectationFailedException + */ + private $parameterVerificationResult; + /** + * @throws \PHPUnit\Framework\Exception + */ + public function __construct(array $parameters) { - return 'empty(' . $this->p($node->expr) . ')'; + foreach ($parameters as $parameter) { + if (!$parameter instanceof Constraint) { + $parameter = new IsEqual($parameter); + } + $this->parameters[] = $parameter; + } } - protected function pExpr_Isset(\PHPUnit\PhpParser\Node\Expr\Isset_ $node) + public function toString() : string { - return 'isset(' . $this->pCommaSeparated($node->vars) . ')'; + $text = 'with parameter'; + foreach ($this->parameters as $index => $parameter) { + if ($index > 0) { + $text .= ' and'; + } + $text .= ' ' . $index . ' ' . $parameter->toString(); + } + return $text; } - protected function pExpr_Eval(\PHPUnit\PhpParser\Node\Expr\Eval_ $node) + /** + * @throws Exception + */ + public function apply(BaseInvocation $invocation) : void { - return 'eval(' . $this->p($node->expr) . ')'; + $this->invocation = $invocation; + $this->parameterVerificationResult = null; + try { + $this->parameterVerificationResult = $this->doVerify(); + } catch (ExpectationFailedException $e) { + $this->parameterVerificationResult = $e; + throw $this->parameterVerificationResult; + } } - protected function pExpr_Include(\PHPUnit\PhpParser\Node\Expr\Include_ $node) + /** + * Checks if the invocation $invocation matches the current rules. If it + * does the rule will get the invoked() method called which should check + * if an expectation is met. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + */ + public function verify() : void { - static $map = [\PHPUnit\PhpParser\Node\Expr\Include_::TYPE_INCLUDE => 'include', \PHPUnit\PhpParser\Node\Expr\Include_::TYPE_INCLUDE_ONCE => 'include_once', \PHPUnit\PhpParser\Node\Expr\Include_::TYPE_REQUIRE => 'require', \PHPUnit\PhpParser\Node\Expr\Include_::TYPE_REQUIRE_ONCE => 'require_once']; - return $map[$node->type] . ' ' . $this->p($node->expr); + $this->doVerify(); } - protected function pExpr_List(\PHPUnit\PhpParser\Node\Expr\List_ $node) + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + */ + private function doVerify() : bool { - return 'list(' . $this->pCommaSeparated($node->items) . ')'; + if (isset($this->parameterVerificationResult)) { + return $this->guardAgainstDuplicateEvaluationOfParameterConstraints(); + } + if ($this->invocation === null) { + throw new ExpectationFailedException('Mocked method does not exist.'); + } + if (count($this->invocation->getParameters()) < count($this->parameters)) { + $message = 'Parameter count for invocation %s is too low.'; + // The user called `->with($this->anything())`, but may have meant + // `->withAnyParameters()`. + // + // @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/199 + if (count($this->parameters) === 1 && get_class($this->parameters[0]) === IsAnything::class) { + $message .= "\nTo allow 0 or more parameters with any value, omit ->with() or use ->withAnyParameters() instead."; + } + throw new ExpectationFailedException(sprintf($message, $this->invocation->toString())); + } + foreach ($this->parameters as $i => $parameter) { + $parameter->evaluate($this->invocation->getParameters()[$i], sprintf('Parameter %s for invocation %s does not match expected ' . 'value.', $i, $this->invocation->toString())); + } + return \true; } - // Other - protected function pExpr_Error(\PHPUnit\PhpParser\Node\Expr\Error $node) + /** + * @throws ExpectationFailedException + */ + private function guardAgainstDuplicateEvaluationOfParameterConstraints() : bool { - throw new \LogicException('Cannot pretty-print AST with Error nodes'); + if ($this->parameterVerificationResult instanceof ExpectationFailedException) { + throw $this->parameterVerificationResult; + } + return (bool) $this->parameterVerificationResult; } - protected function pExpr_Variable(\PHPUnit\PhpParser\Node\Expr\Variable $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function class_exists; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MockTrait implements \PHPUnit\Framework\MockObject\MockType +{ + /** + * @var string + */ + private $classCode; + /** + * @var class-string + */ + private $mockName; + /** + * @psalm-param class-string $mockName + */ + public function __construct(string $classCode, string $mockName) { - if ($node->name instanceof \PHPUnit\PhpParser\Node\Expr) { - return '${' . $this->p($node->name) . '}'; - } else { - return '$' . $node->name; - } + $this->classCode = $classCode; + $this->mockName = $mockName; } - protected function pExpr_Array(\PHPUnit\PhpParser\Node\Expr\Array_ $node) + /** + * @psalm-return class-string + */ + public function generate() : string { - $syntax = $node->getAttribute('kind', $this->options['shortArraySyntax'] ? \PHPUnit\PhpParser\Node\Expr\Array_::KIND_SHORT : \PHPUnit\PhpParser\Node\Expr\Array_::KIND_LONG); - if ($syntax === \PHPUnit\PhpParser\Node\Expr\Array_::KIND_SHORT) { - return '[' . $this->pMaybeMultiline($node->items, \true) . ']'; - } else { - return 'array(' . $this->pMaybeMultiline($node->items, \true) . ')'; + if (!class_exists($this->mockName, \false)) { + eval($this->classCode); } + return $this->mockName; } - protected function pExpr_ArrayItem(\PHPUnit\PhpParser\Node\Expr\ArrayItem $node) + public function getClassCode() : string { - return (null !== $node->key ? $this->p($node->key) . ' => ' : '') . ($node->byRef ? '&' : '') . ($node->unpack ? '...' : '') . $this->p($node->value); + return $this->classCode; } - protected function pExpr_ArrayDimFetch(\PHPUnit\PhpParser\Node\Expr\ArrayDimFetch $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use PHPUnit\SebastianBergmann\Type\Type; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ConfigurableMethod +{ + /** + * @var string + */ + private $name; + /** + * @var Type + */ + private $returnType; + public function __construct(string $name, Type $returnType) { - return $this->pDereferenceLhs($node->var) . '[' . (null !== $node->dim ? $this->p($node->dim) : '') . ']'; + $this->name = $name; + $this->returnType = $returnType; } - protected function pExpr_ConstFetch(\PHPUnit\PhpParser\Node\Expr\ConstFetch $node) + public function getName() : string { - return $this->p($node->name); + return $this->name; } - protected function pExpr_ClassConstFetch(\PHPUnit\PhpParser\Node\Expr\ClassConstFetch $node) + public function mayReturn($value) : bool { - return $this->pDereferenceLhs($node->class) . '::' . $this->p($node->name); + if ($value === null && $this->returnType->allowsNull()) { + return \true; + } + return $this->returnType->isAssignable(Type::fromValue($value, \false)); } - protected function pExpr_PropertyFetch(\PHPUnit\PhpParser\Node\Expr\PropertyFetch $node) + public function getReturnTypeDeclaration() : string { - return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name); + return $this->returnType->asString(); } - protected function pExpr_NullsafePropertyFetch(\PHPUnit\PhpParser\Node\Expr\NullsafePropertyFetch $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use PHPUnit\Framework\ExpectationFailedException; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface Verifiable +{ + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws ExpectationFailedException + */ + public function verify() : void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function call_user_func_array; +use function func_get_args; +use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount; +/** + * @internal This trait is not covered by the backward compatibility promise for PHPUnit + */ +trait Method +{ + public function method() { - return $this->pDereferenceLhs($node->var) . '?->' . $this->pObjectProperty($node->name); + $expects = $this->expects(new AnyInvokedCount()); + return call_user_func_array([$expects, 'method'], func_get_args()); } - protected function pExpr_StaticPropertyFetch(\PHPUnit\PhpParser\Node\Expr\StaticPropertyFetch $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use PHPUnit\Framework\MockObject\Builder\InvocationMocker as InvocationMockerBuilder; +use PHPUnit\Framework\MockObject\Rule\InvocationOrder; +/** + * @internal This trait is not covered by the backward compatibility promise for PHPUnit + */ +trait Api +{ + /** + * @var ConfigurableMethod[] + */ + private static $__phpunit_configurableMethods; + /** + * @var object + */ + private $__phpunit_originalObject; + /** + * @var bool + */ + private $__phpunit_returnValueGeneration = \true; + /** + * @var InvocationHandler + */ + private $__phpunit_invocationMocker; + /** @noinspection MagicMethodsValidityInspection */ + public static function __phpunit_initConfigurableMethods(\PHPUnit\Framework\MockObject\ConfigurableMethod ...$configurableMethods) : void { - return $this->pDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name); + if (isset(static::$__phpunit_configurableMethods)) { + throw new \PHPUnit\Framework\MockObject\ConfigurableMethodsAlreadyInitializedException('Configurable methods is already initialized and can not be reinitialized'); + } + static::$__phpunit_configurableMethods = $configurableMethods; } - protected function pExpr_ShellExec(\PHPUnit\PhpParser\Node\Expr\ShellExec $node) + /** @noinspection MagicMethodsValidityInspection */ + public function __phpunit_setOriginalObject($originalObject) : void { - return '`' . $this->pEncapsList($node->parts, '`') . '`'; + $this->__phpunit_originalObject = $originalObject; } - protected function pExpr_Closure(\PHPUnit\PhpParser\Node\Expr\Closure $node) + /** @noinspection MagicMethodsValidityInspection */ + public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration) : void { - return $this->pAttrGroups($node->attrGroups, \true) . ($node->static ? 'static ' : '') . 'function ' . ($node->byRef ? '&' : '') . '(' . $this->pCommaSeparated($node->params) . ')' . (!empty($node->uses) ? ' use(' . $this->pCommaSeparated($node->uses) . ')' : '') . (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '') . ' {' . $this->pStmts($node->stmts) . $this->nl . '}'; + $this->__phpunit_returnValueGeneration = $returnValueGeneration; } - protected function pExpr_Match(\PHPUnit\PhpParser\Node\Expr\Match_ $node) + /** @noinspection MagicMethodsValidityInspection */ + public function __phpunit_getInvocationHandler() : \PHPUnit\Framework\MockObject\InvocationHandler { - return 'match (' . $this->p($node->cond) . ') {' . $this->pCommaSeparatedMultiline($node->arms, \true) . $this->nl . '}'; + if ($this->__phpunit_invocationMocker === null) { + $this->__phpunit_invocationMocker = new \PHPUnit\Framework\MockObject\InvocationHandler(static::$__phpunit_configurableMethods, $this->__phpunit_returnValueGeneration); + } + return $this->__phpunit_invocationMocker; } - protected function pMatchArm(\PHPUnit\PhpParser\Node\MatchArm $node) + /** @noinspection MagicMethodsValidityInspection */ + public function __phpunit_hasMatchers() : bool { - return ($node->conds ? $this->pCommaSeparated($node->conds) : 'default') . ' => ' . $this->p($node->body); + return $this->__phpunit_getInvocationHandler()->hasMatchers(); } - protected function pExpr_ArrowFunction(\PHPUnit\PhpParser\Node\Expr\ArrowFunction $node) + /** @noinspection MagicMethodsValidityInspection */ + public function __phpunit_verify(bool $unsetInvocationMocker = \true) : void { - return $this->pAttrGroups($node->attrGroups, \true) . ($node->static ? 'static ' : '') . 'fn' . ($node->byRef ? '&' : '') . '(' . $this->pCommaSeparated($node->params) . ')' . (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '') . ' => ' . $this->p($node->expr); + $this->__phpunit_getInvocationHandler()->verify(); + if ($unsetInvocationMocker) { + $this->__phpunit_invocationMocker = null; + } } - protected function pExpr_ClosureUse(\PHPUnit\PhpParser\Node\Expr\ClosureUse $node) + public function expects(InvocationOrder $matcher) : InvocationMockerBuilder { - return ($node->byRef ? '&' : '') . $this->p($node->var); + return $this->__phpunit_getInvocationHandler()->expects($matcher); } - protected function pExpr_New(\PHPUnit\PhpParser\Node\Expr\New_ $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +/** + * @internal This trait is not covered by the backward compatibility promise for PHPUnit + */ +trait MockedCloneMethod +{ + public function __clone() { - if ($node->class instanceof \PHPUnit\PhpParser\Node\Stmt\Class_) { - $args = $node->args ? '(' . $this->pMaybeMultiline($node->args) . ')' : ''; - return 'new ' . $this->pClassCommon($node->class, $args); - } - return 'new ' . $this->pNewVariable($node->class) . '(' . $this->pMaybeMultiline($node->args) . ')'; + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); } - protected function pExpr_Clone(\PHPUnit\PhpParser\Node\Expr\Clone_ $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +/** + * @internal This trait is not covered by the backward compatibility promise for PHPUnit + */ +trait UnmockedCloneMethod +{ + public function __clone() { - return 'clone ' . $this->p($node->expr); + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); + parent::__clone(); } - protected function pExpr_Ternary(\PHPUnit\PhpParser\Node\Expr\Ternary $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function assert; +use function implode; +use function sprintf; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount; +use PHPUnit\Framework\MockObject\Rule\AnyParameters; +use PHPUnit\Framework\MockObject\Rule\InvocationOrder; +use PHPUnit\Framework\MockObject\Rule\InvokedCount; +use PHPUnit\Framework\MockObject\Rule\MethodName; +use PHPUnit\Framework\MockObject\Rule\ParametersRule; +use PHPUnit\Framework\MockObject\Stub\Stub; +use PHPUnit\Framework\TestFailure; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Matcher +{ + /** + * @var InvocationOrder + */ + private $invocationRule; + /** + * @var mixed + */ + private $afterMatchBuilderId; + /** + * @var bool + */ + private $afterMatchBuilderIsInvoked = \false; + /** + * @var MethodName + */ + private $methodNameRule; + /** + * @var ParametersRule + */ + private $parametersRule; + /** + * @var Stub + */ + private $stub; + public function __construct(InvocationOrder $rule) { - // a bit of cheating: we treat the ternary as a binary op where the ?...: part is the operator. - // this is okay because the part between ? and : never needs parentheses. - return $this->pInfixOp(\PHPUnit\PhpParser\Node\Expr\Ternary::class, $node->cond, ' ?' . (null !== $node->if ? ' ' . $this->p($node->if) . ' ' : '') . ': ', $node->else); + $this->invocationRule = $rule; } - protected function pExpr_Exit(\PHPUnit\PhpParser\Node\Expr\Exit_ $node) + public function hasMatchers() : bool { - $kind = $node->getAttribute('kind', \PHPUnit\PhpParser\Node\Expr\Exit_::KIND_DIE); - return ($kind === \PHPUnit\PhpParser\Node\Expr\Exit_::KIND_EXIT ? 'exit' : 'die') . (null !== $node->expr ? '(' . $this->p($node->expr) . ')' : ''); + return !$this->invocationRule instanceof AnyInvokedCount; } - protected function pExpr_Throw(\PHPUnit\PhpParser\Node\Expr\Throw_ $node) + public function hasMethodNameRule() : bool { - return 'throw ' . $this->p($node->expr); + return $this->methodNameRule !== null; } - protected function pExpr_Yield(\PHPUnit\PhpParser\Node\Expr\Yield_ $node) + public function getMethodNameRule() : MethodName { - if ($node->value === null) { - return 'yield'; - } else { - // this is a bit ugly, but currently there is no way to detect whether the parentheses are necessary - return '(yield ' . ($node->key !== null ? $this->p($node->key) . ' => ' : '') . $this->p($node->value) . ')'; - } + return $this->methodNameRule; } - // Declarations - protected function pStmt_Namespace(\PHPUnit\PhpParser\Node\Stmt\Namespace_ $node) + public function setMethodNameRule(MethodName $rule) : void { - if ($this->canUseSemicolonNamespaces) { - return 'namespace ' . $this->p($node->name) . ';' . $this->nl . $this->pStmts($node->stmts, \false); - } else { - return 'namespace' . (null !== $node->name ? ' ' . $this->p($node->name) : '') . ' {' . $this->pStmts($node->stmts) . $this->nl . '}'; - } + $this->methodNameRule = $rule; } - protected function pStmt_Use(\PHPUnit\PhpParser\Node\Stmt\Use_ $node) + public function hasParametersRule() : bool { - return 'use ' . $this->pUseType($node->type) . $this->pCommaSeparated($node->uses) . ';'; + return $this->parametersRule !== null; } - protected function pStmt_GroupUse(\PHPUnit\PhpParser\Node\Stmt\GroupUse $node) + public function setParametersRule(ParametersRule $rule) : void { - return 'use ' . $this->pUseType($node->type) . $this->pName($node->prefix) . '\\{' . $this->pCommaSeparated($node->uses) . '};'; + $this->parametersRule = $rule; } - protected function pStmt_UseUse(\PHPUnit\PhpParser\Node\Stmt\UseUse $node) + public function setStub(Stub $stub) : void { - return $this->pUseType($node->type) . $this->p($node->name) . (null !== $node->alias ? ' as ' . $node->alias : ''); + $this->stub = $stub; } - protected function pUseType($type) + public function setAfterMatchBuilderId(string $id) : void { - return $type === \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_FUNCTION ? 'function ' : ($type === \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_CONSTANT ? 'const ' : ''); + $this->afterMatchBuilderId = $id; } - protected function pStmt_Interface(\PHPUnit\PhpParser\Node\Stmt\Interface_ $node) + /** + * @throws ExpectationFailedException + * @throws MatchBuilderNotFoundException + * @throws MethodNameNotConfiguredException + * @throws RuntimeException + */ + public function invoked(\PHPUnit\Framework\MockObject\Invocation $invocation) { - return $this->pAttrGroups($node->attrGroups) . 'interface ' . $node->name . (!empty($node->extends) ? ' extends ' . $this->pCommaSeparated($node->extends) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + if ($this->methodNameRule === null) { + throw new \PHPUnit\Framework\MockObject\MethodNameNotConfiguredException(); + } + if ($this->afterMatchBuilderId !== null) { + $matcher = $invocation->getObject()->__phpunit_getInvocationHandler()->lookupMatcher($this->afterMatchBuilderId); + if (!$matcher) { + throw new \PHPUnit\Framework\MockObject\MatchBuilderNotFoundException($this->afterMatchBuilderId); + } + assert($matcher instanceof self); + if ($matcher->invocationRule->hasBeenInvoked()) { + $this->afterMatchBuilderIsInvoked = \true; + } + } + $this->invocationRule->invoked($invocation); + try { + if ($this->parametersRule !== null) { + $this->parametersRule->apply($invocation); + } + } catch (ExpectationFailedException $e) { + throw new ExpectationFailedException(sprintf("Expectation failed for %s when %s\n%s", $this->methodNameRule->toString(), $this->invocationRule->toString(), $e->getMessage()), $e->getComparisonFailure()); + } + if ($this->stub) { + return $this->stub->invoke($invocation); + } + return $invocation->generateReturnValue(); } - protected function pStmt_Class(\PHPUnit\PhpParser\Node\Stmt\Class_ $node) + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * @throws MatchBuilderNotFoundException + * @throws MethodNameNotConfiguredException + * @throws RuntimeException + */ + public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool { - return $this->pClassCommon($node, ' ' . $node->name); + if ($this->afterMatchBuilderId !== null) { + $matcher = $invocation->getObject()->__phpunit_getInvocationHandler()->lookupMatcher($this->afterMatchBuilderId); + if (!$matcher) { + throw new \PHPUnit\Framework\MockObject\MatchBuilderNotFoundException($this->afterMatchBuilderId); + } + assert($matcher instanceof self); + if (!$matcher->invocationRule->hasBeenInvoked()) { + return \false; + } + } + if ($this->methodNameRule === null) { + throw new \PHPUnit\Framework\MockObject\MethodNameNotConfiguredException(); + } + if (!$this->invocationRule->matches($invocation)) { + return \false; + } + try { + if (!$this->methodNameRule->matches($invocation)) { + return \false; + } + } catch (ExpectationFailedException $e) { + throw new ExpectationFailedException(sprintf("Expectation failed for %s when %s\n%s", $this->methodNameRule->toString(), $this->invocationRule->toString(), $e->getMessage()), $e->getComparisonFailure()); + } + return \true; } - protected function pStmt_Trait(\PHPUnit\PhpParser\Node\Stmt\Trait_ $node) + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * @throws MethodNameNotConfiguredException + */ + public function verify() : void { - return $this->pAttrGroups($node->attrGroups) . 'trait ' . $node->name . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + if ($this->methodNameRule === null) { + throw new \PHPUnit\Framework\MockObject\MethodNameNotConfiguredException(); + } + try { + $this->invocationRule->verify(); + if ($this->parametersRule === null) { + $this->parametersRule = new AnyParameters(); + } + $invocationIsAny = $this->invocationRule instanceof AnyInvokedCount; + $invocationIsNever = $this->invocationRule instanceof InvokedCount && $this->invocationRule->isNever(); + if (!$invocationIsAny && !$invocationIsNever) { + $this->parametersRule->verify(); + } + } catch (ExpectationFailedException $e) { + throw new ExpectationFailedException(sprintf("Expectation failed for %s when %s.\n%s", $this->methodNameRule->toString(), $this->invocationRule->toString(), TestFailure::exceptionToString($e))); + } } - protected function pStmt_TraitUse(\PHPUnit\PhpParser\Node\Stmt\TraitUse $node) + public function toString() : string { - return 'use ' . $this->pCommaSeparated($node->traits) . (empty($node->adaptations) ? ';' : ' {' . $this->pStmts($node->adaptations) . $this->nl . '}'); + $list = []; + if ($this->invocationRule !== null) { + $list[] = $this->invocationRule->toString(); + } + if ($this->methodNameRule !== null) { + $list[] = 'where ' . $this->methodNameRule->toString(); + } + if ($this->parametersRule !== null) { + $list[] = 'and ' . $this->parametersRule->toString(); + } + if ($this->afterMatchBuilderId !== null) { + $list[] = 'after ' . $this->afterMatchBuilderId; + } + if ($this->stub !== null) { + $list[] = 'will ' . $this->stub->toString(); + } + return implode(' ', $list); } - protected function pStmt_TraitUseAdaptation_Precedence(\PHPUnit\PhpParser\Node\Stmt\TraitUseAdaptation\Precedence $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function strtolower; +use Exception; +use PHPUnit\Framework\MockObject\Builder\InvocationMocker; +use PHPUnit\Framework\MockObject\Rule\InvocationOrder; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvocationHandler +{ + /** + * @var Matcher[] + */ + private $matchers = []; + /** + * @var Matcher[] + */ + private $matcherMap = []; + /** + * @var ConfigurableMethod[] + */ + private $configurableMethods; + /** + * @var bool + */ + private $returnValueGeneration; + /** + * @var Throwable + */ + private $deferredError; + public function __construct(array $configurableMethods, bool $returnValueGeneration) { - return $this->p($node->trait) . '::' . $node->method . ' insteadof ' . $this->pCommaSeparated($node->insteadof) . ';'; + $this->configurableMethods = $configurableMethods; + $this->returnValueGeneration = $returnValueGeneration; } - protected function pStmt_TraitUseAdaptation_Alias(\PHPUnit\PhpParser\Node\Stmt\TraitUseAdaptation\Alias $node) + public function hasMatchers() : bool { - return (null !== $node->trait ? $this->p($node->trait) . '::' : '') . $node->method . ' as' . (null !== $node->newModifier ? ' ' . \rtrim($this->pModifiers($node->newModifier), ' ') : '') . (null !== $node->newName ? ' ' . $node->newName : '') . ';'; + foreach ($this->matchers as $matcher) { + if ($matcher->hasMatchers()) { + return \true; + } + } + return \false; } - protected function pStmt_Property(\PHPUnit\PhpParser\Node\Stmt\Property $node) + /** + * Looks up the match builder with identification $id and returns it. + * + * @param string $id The identification of the match builder + */ + public function lookupMatcher(string $id) : ?\PHPUnit\Framework\MockObject\Matcher { - return $this->pAttrGroups($node->attrGroups) . (0 === $node->flags ? 'var ' : $this->pModifiers($node->flags)) . ($node->type ? $this->p($node->type) . ' ' : '') . $this->pCommaSeparated($node->props) . ';'; + if (isset($this->matcherMap[$id])) { + return $this->matcherMap[$id]; + } + return null; } - protected function pStmt_PropertyProperty(\PHPUnit\PhpParser\Node\Stmt\PropertyProperty $node) + /** + * Registers a matcher with the identification $id. The matcher can later be + * looked up using lookupMatcher() to figure out if it has been invoked. + * + * @param string $id The identification of the matcher + * @param Matcher $matcher The builder which is being registered + * + * @throws MatcherAlreadyRegisteredException + */ + public function registerMatcher(string $id, \PHPUnit\Framework\MockObject\Matcher $matcher) : void { - return '$' . $node->name . (null !== $node->default ? ' = ' . $this->p($node->default) : ''); + if (isset($this->matcherMap[$id])) { + throw new \PHPUnit\Framework\MockObject\MatcherAlreadyRegisteredException($id); + } + $this->matcherMap[$id] = $matcher; } - protected function pStmt_ClassMethod(\PHPUnit\PhpParser\Node\Stmt\ClassMethod $node) + public function expects(InvocationOrder $rule) : InvocationMocker { - return $this->pAttrGroups($node->attrGroups) . $this->pModifiers($node->flags) . 'function ' . ($node->byRef ? '&' : '') . $node->name . '(' . $this->pMaybeMultiline($node->params) . ')' . (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '') . (null !== $node->stmts ? $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}' : ';'); + $matcher = new \PHPUnit\Framework\MockObject\Matcher($rule); + $this->addMatcher($matcher); + return new InvocationMocker($this, $matcher, ...$this->configurableMethods); } - protected function pStmt_ClassConst(\PHPUnit\PhpParser\Node\Stmt\ClassConst $node) + /** + * @throws Exception + * @throws RuntimeException + */ + public function invoke(\PHPUnit\Framework\MockObject\Invocation $invocation) { - return $this->pAttrGroups($node->attrGroups) . $this->pModifiers($node->flags) . 'const ' . $this->pCommaSeparated($node->consts) . ';'; + $exception = null; + $hasReturnValue = \false; + $returnValue = null; + foreach ($this->matchers as $match) { + try { + if ($match->matches($invocation)) { + $value = $match->invoked($invocation); + if (!$hasReturnValue) { + $returnValue = $value; + $hasReturnValue = \true; + } + } + } catch (Exception $e) { + $exception = $e; + } + } + if ($exception !== null) { + throw $exception; + } + if ($hasReturnValue) { + return $returnValue; + } + if (!$this->returnValueGeneration) { + $exception = new \PHPUnit\Framework\MockObject\ReturnValueNotConfiguredException($invocation); + if (strtolower($invocation->getMethodName()) === '__tostring') { + $this->deferredError = $exception; + return ''; + } + throw $exception; + } + return $invocation->generateReturnValue(); } - protected function pStmt_Function(\PHPUnit\PhpParser\Node\Stmt\Function_ $node) + public function matches(\PHPUnit\Framework\MockObject\Invocation $invocation) : bool { - return $this->pAttrGroups($node->attrGroups) . 'function ' . ($node->byRef ? '&' : '') . $node->name . '(' . $this->pCommaSeparated($node->params) . ')' . (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + foreach ($this->matchers as $matcher) { + if (!$matcher->matches($invocation)) { + return \false; + } + } + return \true; } - protected function pStmt_Const(\PHPUnit\PhpParser\Node\Stmt\Const_ $node) + /** + * @throws Throwable + */ + public function verify() : void { - return 'const ' . $this->pCommaSeparated($node->consts) . ';'; + foreach ($this->matchers as $matcher) { + $matcher->verify(); + } + if ($this->deferredError) { + throw $this->deferredError; + } } - protected function pStmt_Declare(\PHPUnit\PhpParser\Node\Stmt\Declare_ $node) + private function addMatcher(\PHPUnit\Framework\MockObject\Matcher $matcher) : void { - return 'declare (' . $this->pCommaSeparated($node->declares) . ')' . (null !== $node->stmts ? ' {' . $this->pStmts($node->stmts) . $this->nl . '}' : ';'); + $this->matchers[] = $matcher; } - protected function pStmt_DeclareDeclare(\PHPUnit\PhpParser\Node\Stmt\DeclareDeclare $node) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use PHPUnit\Framework\MockObject\Builder\InvocationMocker as BuilderInvocationMocker; +use PHPUnit\Framework\MockObject\Rule\InvocationOrder; +/** + * @method BuilderInvocationMocker method($constraint) + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface MockObject extends \PHPUnit\Framework\MockObject\Stub +{ + public function __phpunit_setOriginalObject($originalObject) : void; + public function __phpunit_verify(bool $unsetInvocationMocker = \true) : void; + public function expects(InvocationOrder $invocationRule) : BuilderInvocationMocker; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface SelfDescribing +{ + /** + * Returns a string representation of the object. + */ + public function toString() : string; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const LC_ALL; +use const LC_COLLATE; +use const LC_CTYPE; +use const LC_MONETARY; +use const LC_NUMERIC; +use const LC_TIME; +use const PATHINFO_FILENAME; +use const PHP_EOL; +use const PHP_URL_PATH; +use function array_filter; +use function array_flip; +use function array_keys; +use function array_merge; +use function array_pop; +use function array_search; +use function array_unique; +use function array_values; +use function basename; +use function call_user_func; +use function chdir; +use function class_exists; +use function clearstatcache; +use function count; +use function debug_backtrace; +use function defined; +use function explode; +use function get_class; +use function get_include_path; +use function getcwd; +use function implode; +use function in_array; +use function ini_set; +use function is_array; +use function is_callable; +use function is_int; +use function is_object; +use function is_string; +use function libxml_clear_errors; +use function method_exists; +use function ob_end_clean; +use function ob_get_contents; +use function ob_get_level; +use function ob_start; +use function parse_url; +use function pathinfo; +use function preg_replace; +use function serialize; +use function setlocale; +use function sprintf; +use function strpos; +use function substr; +use function trim; +use function var_export; +use PHPUnit\DeepCopy\DeepCopy; +use PHPUnit\Framework\Constraint\Exception as ExceptionConstraint; +use PHPUnit\Framework\Constraint\ExceptionCode; +use PHPUnit\Framework\Constraint\ExceptionMessage; +use PHPUnit\Framework\Constraint\ExceptionMessageRegularExpression; +use PHPUnit\Framework\Constraint\LogicalOr; +use PHPUnit\Framework\Error\Deprecated; +use PHPUnit\Framework\Error\Error; +use PHPUnit\Framework\Error\Notice; +use PHPUnit\Framework\Error\Warning as WarningError; +use PHPUnit\Framework\MockObject\Generator as MockGenerator; +use PHPUnit\Framework\MockObject\MockBuilder; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount as AnyInvokedCountMatcher; +use PHPUnit\Framework\MockObject\Rule\InvokedAtIndex as InvokedAtIndexMatcher; +use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastCount as InvokedAtLeastCountMatcher; +use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastOnce as InvokedAtLeastOnceMatcher; +use PHPUnit\Framework\MockObject\Rule\InvokedAtMostCount as InvokedAtMostCountMatcher; +use PHPUnit\Framework\MockObject\Rule\InvokedCount as InvokedCountMatcher; +use PHPUnit\Framework\MockObject\Stub; +use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls as ConsecutiveCallsStub; +use PHPUnit\Framework\MockObject\Stub\Exception as ExceptionStub; +use PHPUnit\Framework\MockObject\Stub\ReturnArgument as ReturnArgumentStub; +use PHPUnit\Framework\MockObject\Stub\ReturnCallback as ReturnCallbackStub; +use PHPUnit\Framework\MockObject\Stub\ReturnSelf as ReturnSelfStub; +use PHPUnit\Framework\MockObject\Stub\ReturnStub; +use PHPUnit\Framework\MockObject\Stub\ReturnValueMap as ReturnValueMapStub; +use PHPUnit\Runner\BaseTestRunner; +use PHPUnit\Runner\PhptTestCase; +use PHPUnit\Util\Exception as UtilException; +use PHPUnit\Util\GlobalState; +use PHPUnit\Util\PHP\AbstractPhpProcess; +use PHPUnit\Util\Test as TestUtil; +use PHPUnit\Util\Type; +use Prophecy\Exception\Prediction\PredictionException; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophet; +use ReflectionClass; +use ReflectionException; +use PHPUnit\SebastianBergmann\Comparator\Comparator; +use PHPUnit\SebastianBergmann\Comparator\Factory as ComparatorFactory; +use PHPUnit\SebastianBergmann\Diff\Differ; +use PHPUnit\SebastianBergmann\Exporter\Exporter; +use PHPUnit\SebastianBergmann\GlobalState\ExcludeList; +use PHPUnit\SebastianBergmann\GlobalState\Restorer; +use PHPUnit\SebastianBergmann\GlobalState\Snapshot; +use PHPUnit\SebastianBergmann\ObjectEnumerator\Enumerator; +use PHPUnit\SebastianBergmann\Template\Template; +use SoapClient; +use Throwable; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +abstract class TestCase extends \PHPUnit\Framework\Assert implements \PHPUnit\Framework\Reorderable, \PHPUnit\Framework\SelfDescribing, \PHPUnit\Framework\Test +{ + private const LOCALE_CATEGORIES = [\LC_ALL, \LC_COLLATE, \LC_CTYPE, \LC_MONETARY, \LC_NUMERIC, \LC_TIME]; + /** + * @var ?bool + */ + protected $backupGlobals; + /** + * @var string[] + */ + protected $backupGlobalsExcludeList = []; + /** + * @var string[] + * + * @deprecated Use $backupGlobalsExcludeList instead + */ + protected $backupGlobalsBlacklist = []; + /** + * @var bool + */ + protected $backupStaticAttributes; + /** + * @var array> + */ + protected $backupStaticAttributesExcludeList = []; + /** + * @var array> + * + * @deprecated Use $backupStaticAttributesExcludeList instead + */ + protected $backupStaticAttributesBlacklist = []; + /** + * @var bool + */ + protected $runTestInSeparateProcess; + /** + * @var bool + */ + protected $preserveGlobalState = \true; + /** + * @var list + */ + protected $providedTests = []; + /** + * @var bool + */ + private $runClassInSeparateProcess; + /** + * @var bool + */ + private $inIsolation = \false; + /** + * @var array + */ + private $data; + /** + * @var int|string + */ + private $dataName; + /** + * @var null|string + */ + private $expectedException; + /** + * @var null|string + */ + private $expectedExceptionMessage; + /** + * @var null|string + */ + private $expectedExceptionMessageRegExp; + /** + * @var null|int|string + */ + private $expectedExceptionCode; + /** + * @var string + */ + private $name = ''; + /** + * @var list + */ + private $dependencies = []; + /** + * @var array + */ + private $dependencyInput = []; + /** + * @var array + */ + private $iniSettings = []; + /** + * @var array + */ + private $locale = []; + /** + * @var MockObject[] + */ + private $mockObjects = []; + /** + * @var MockGenerator + */ + private $mockObjectGenerator; + /** + * @var int + */ + private $status = BaseTestRunner::STATUS_UNKNOWN; + /** + * @var string + */ + private $statusMessage = ''; + /** + * @var int + */ + private $numAssertions = 0; + /** + * @var TestResult + */ + private $result; + /** + * @var mixed + */ + private $testResult; + /** + * @var string + */ + private $output = ''; + /** + * @var ?string + */ + private $outputExpectedRegex; + /** + * @var ?string + */ + private $outputExpectedString; + /** + * @var mixed + */ + private $outputCallback = \false; + /** + * @var bool + */ + private $outputBufferingActive = \false; + /** + * @var int + */ + private $outputBufferingLevel; + /** + * @var bool + */ + private $outputRetrievedForAssertion = \false; + /** + * @var ?Snapshot + */ + private $snapshot; + /** + * @var \Prophecy\Prophet + */ + private $prophet; + /** + * @var bool + */ + private $beStrictAboutChangesToGlobalState = \false; + /** + * @var bool + */ + private $registerMockObjectsFromTestArgumentsRecursively = \false; + /** + * @var string[] + */ + private $warnings = []; + /** + * @var string[] + */ + private $groups = []; + /** + * @var bool + */ + private $doesNotPerformAssertions = \false; + /** + * @var Comparator[] + */ + private $customComparators = []; + /** + * @var string[] + */ + private $doubledTypes = []; + /** + * Returns a matcher that matches when the method is executed + * zero or more times. + */ + public static function any() : AnyInvokedCountMatcher { - return $node->key . '=' . $this->p($node->value); + return new AnyInvokedCountMatcher(); } - // Control flow - protected function pStmt_If(\PHPUnit\PhpParser\Node\Stmt\If_ $node) + /** + * Returns a matcher that matches when the method is never executed. + */ + public static function never() : InvokedCountMatcher { - return 'if (' . $this->p($node->cond) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}' . ($node->elseifs ? ' ' . $this->pImplode($node->elseifs, ' ') : '') . (null !== $node->else ? ' ' . $this->p($node->else) : ''); + return new InvokedCountMatcher(0); } - protected function pStmt_ElseIf(\PHPUnit\PhpParser\Node\Stmt\ElseIf_ $node) + /** + * Returns a matcher that matches when the method is executed + * at least N times. + */ + public static function atLeast(int $requiredInvocations) : InvokedAtLeastCountMatcher { - return 'elseif (' . $this->p($node->cond) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; + return new InvokedAtLeastCountMatcher($requiredInvocations); } - protected function pStmt_Else(\PHPUnit\PhpParser\Node\Stmt\Else_ $node) + /** + * Returns a matcher that matches when the method is executed at least once. + */ + public static function atLeastOnce() : InvokedAtLeastOnceMatcher { - return 'else {' . $this->pStmts($node->stmts) . $this->nl . '}'; + return new InvokedAtLeastOnceMatcher(); } - protected function pStmt_For(\PHPUnit\PhpParser\Node\Stmt\For_ $node) + /** + * Returns a matcher that matches when the method is executed exactly once. + */ + public static function once() : InvokedCountMatcher { - return 'for (' . $this->pCommaSeparated($node->init) . ';' . (!empty($node->cond) ? ' ' : '') . $this->pCommaSeparated($node->cond) . ';' . (!empty($node->loop) ? ' ' : '') . $this->pCommaSeparated($node->loop) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; + return new InvokedCountMatcher(1); } - protected function pStmt_Foreach(\PHPUnit\PhpParser\Node\Stmt\Foreach_ $node) + /** + * Returns a matcher that matches when the method is executed + * exactly $count times. + */ + public static function exactly(int $count) : InvokedCountMatcher { - return 'foreach (' . $this->p($node->expr) . ' as ' . (null !== $node->keyVar ? $this->p($node->keyVar) . ' => ' : '') . ($node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; + return new InvokedCountMatcher($count); } - protected function pStmt_While(\PHPUnit\PhpParser\Node\Stmt\While_ $node) + /** + * Returns a matcher that matches when the method is executed + * at most N times. + */ + public static function atMost(int $allowedInvocations) : InvokedAtMostCountMatcher { - return 'while (' . $this->p($node->cond) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; + return new InvokedAtMostCountMatcher($allowedInvocations); } - protected function pStmt_Do(\PHPUnit\PhpParser\Node\Stmt\Do_ $node) + /** + * Returns a matcher that matches when the method is executed + * at the given index. + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4297 + * @codeCoverageIgnore + */ + public static function at(int $index) : InvokedAtIndexMatcher { - return 'do {' . $this->pStmts($node->stmts) . $this->nl . '} while (' . $this->p($node->cond) . ');'; + $stack = debug_backtrace(); + while (!empty($stack)) { + $frame = array_pop($stack); + if (isset($frame['object']) && $frame['object'] instanceof self) { + $frame['object']->addWarning('The at() matcher has been deprecated. It will be removed in PHPUnit 10. Please refactor your test to not rely on the order in which methods are invoked.'); + break; + } + } + return new InvokedAtIndexMatcher($index); } - protected function pStmt_Switch(\PHPUnit\PhpParser\Node\Stmt\Switch_ $node) + public static function returnValue($value) : ReturnStub { - return 'switch (' . $this->p($node->cond) . ') {' . $this->pStmts($node->cases) . $this->nl . '}'; + return new ReturnStub($value); } - protected function pStmt_Case(\PHPUnit\PhpParser\Node\Stmt\Case_ $node) + public static function returnValueMap(array $valueMap) : ReturnValueMapStub { - return (null !== $node->cond ? 'case ' . $this->p($node->cond) : 'default') . ':' . $this->pStmts($node->stmts); + return new ReturnValueMapStub($valueMap); } - protected function pStmt_TryCatch(\PHPUnit\PhpParser\Node\Stmt\TryCatch $node) + public static function returnArgument(int $argumentIndex) : ReturnArgumentStub { - return 'try {' . $this->pStmts($node->stmts) . $this->nl . '}' . ($node->catches ? ' ' . $this->pImplode($node->catches, ' ') : '') . ($node->finally !== null ? ' ' . $this->p($node->finally) : ''); + return new ReturnArgumentStub($argumentIndex); } - protected function pStmt_Catch(\PHPUnit\PhpParser\Node\Stmt\Catch_ $node) + public static function returnCallback($callback) : ReturnCallbackStub { - return 'catch (' . $this->pImplode($node->types, '|') . ($node->var !== null ? ' ' . $this->p($node->var) : '') . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; + return new ReturnCallbackStub($callback); } - protected function pStmt_Finally(\PHPUnit\PhpParser\Node\Stmt\Finally_ $node) + /** + * Returns the current object. + * + * This method is useful when mocking a fluent interface. + */ + public static function returnSelf() : ReturnSelfStub { - return 'finally {' . $this->pStmts($node->stmts) . $this->nl . '}'; + return new ReturnSelfStub(); } - protected function pStmt_Break(\PHPUnit\PhpParser\Node\Stmt\Break_ $node) + public static function throwException(Throwable $exception) : ExceptionStub { - return 'break' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';'; + return new ExceptionStub($exception); } - protected function pStmt_Continue(\PHPUnit\PhpParser\Node\Stmt\Continue_ $node) + public static function onConsecutiveCalls(...$args) : ConsecutiveCallsStub { - return 'continue' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';'; + return new ConsecutiveCallsStub($args); } - protected function pStmt_Return(\PHPUnit\PhpParser\Node\Stmt\Return_ $node) + /** + * @param int|string $dataName + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function __construct(?string $name = null, array $data = [], $dataName = '') { - return 'return' . (null !== $node->expr ? ' ' . $this->p($node->expr) : '') . ';'; + if ($name !== null) { + $this->setName($name); + } + $this->data = $data; + $this->dataName = $dataName; } - protected function pStmt_Throw(\PHPUnit\PhpParser\Node\Stmt\Throw_ $node) + /** + * This method is called before the first test of this test class is run. + */ + public static function setUpBeforeClass() : void { - return 'throw ' . $this->p($node->expr) . ';'; } - protected function pStmt_Label(\PHPUnit\PhpParser\Node\Stmt\Label $node) + /** + * This method is called after the last test of this test class is run. + */ + public static function tearDownAfterClass() : void { - return $node->name . ':'; } - protected function pStmt_Goto(\PHPUnit\PhpParser\Node\Stmt\Goto_ $node) + /** + * This method is called before each test. + */ + protected function setUp() : void { - return 'goto ' . $node->name . ';'; } - // Other - protected function pStmt_Expression(\PHPUnit\PhpParser\Node\Stmt\Expression $node) + /** + * This method is called after each test. + */ + protected function tearDown() : void { - return $this->p($node->expr) . ';'; } - protected function pStmt_Echo(\PHPUnit\PhpParser\Node\Stmt\Echo_ $node) + /** + * Returns a string representation of the test case. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws Exception + */ + public function toString() : string { - return 'echo ' . $this->pCommaSeparated($node->exprs) . ';'; + try { + $class = new ReflectionClass($this); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $buffer = sprintf('%s::%s', $class->name, $this->getName(\false)); + return $buffer . $this->getDataSetAsString(); } - protected function pStmt_Static(\PHPUnit\PhpParser\Node\Stmt\Static_ $node) + public function count() : int { - return 'static ' . $this->pCommaSeparated($node->vars) . ';'; + return 1; } - protected function pStmt_Global(\PHPUnit\PhpParser\Node\Stmt\Global_ $node) + public function getActualOutputForAssertion() : string { - return 'global ' . $this->pCommaSeparated($node->vars) . ';'; + $this->outputRetrievedForAssertion = \true; + return $this->getActualOutput(); } - protected function pStmt_StaticVar(\PHPUnit\PhpParser\Node\Stmt\StaticVar $node) + public function expectOutputRegex(string $expectedRegex) : void { - return $this->p($node->var) . (null !== $node->default ? ' = ' . $this->p($node->default) : ''); + $this->outputExpectedRegex = $expectedRegex; } - protected function pStmt_Unset(\PHPUnit\PhpParser\Node\Stmt\Unset_ $node) + public function expectOutputString(string $expectedString) : void { - return 'unset(' . $this->pCommaSeparated($node->vars) . ');'; + $this->outputExpectedString = $expectedString; } - protected function pStmt_InlineHTML(\PHPUnit\PhpParser\Node\Stmt\InlineHTML $node) + /** + * @psalm-param class-string<\Throwable> $exception + */ + public function expectException(string $exception) : void { - $newline = $node->getAttribute('hasLeadingNewline', \true) ? "\n" : ''; - return '?>' . $newline . $node->value . 'addWarning('Support for using expectException() with PHPUnit\\Framework\\Error\\Deprecated is deprecated and will be removed in PHPUnit 10. Use expectDeprecation() instead.'); + break; + case Error::class: + $this->addWarning('Support for using expectException() with PHPUnit\\Framework\\Error\\Error is deprecated and will be removed in PHPUnit 10. Use expectError() instead.'); + break; + case Notice::class: + $this->addWarning('Support for using expectException() with PHPUnit\\Framework\\Error\\Notice is deprecated and will be removed in PHPUnit 10. Use expectNotice() instead.'); + break; + case WarningError::class: + $this->addWarning('Support for using expectException() with PHPUnit\\Framework\\Error\\Warning is deprecated and will be removed in PHPUnit 10. Use expectWarning() instead.'); + break; + } + // @codeCoverageIgnoreEnd + $this->expectedException = $exception; } - protected function pStmt_HaltCompiler(\PHPUnit\PhpParser\Node\Stmt\HaltCompiler $node) + /** + * @param int|string $code + */ + public function expectExceptionCode($code) : void { - return '__halt_compiler();' . $node->remaining; + $this->expectedExceptionCode = $code; } - protected function pStmt_Nop(\PHPUnit\PhpParser\Node\Stmt\Nop $node) + public function expectExceptionMessage(string $message) : void { - return ''; + $this->expectedExceptionMessage = $message; } - // Helpers - protected function pClassCommon(\PHPUnit\PhpParser\Node\Stmt\Class_ $node, $afterClassToken) + public function expectExceptionMessageMatches(string $regularExpression) : void { - return $this->pAttrGroups($node->attrGroups, $node->name === null) . $this->pModifiers($node->flags) . 'class' . $afterClassToken . (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '') . (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + $this->expectedExceptionMessageRegExp = $regularExpression; } - protected function pObjectProperty($node) + /** + * Sets up an expectation for an exception to be raised by the code under test. + * Information for expected exception class, expected exception message, and + * expected exception code are retrieved from a given Exception object. + */ + public function expectExceptionObject(\Exception $exception) : void { - if ($node instanceof \PHPUnit\PhpParser\Node\Expr) { - return '{' . $this->p($node) . '}'; - } else { - return $node; - } + $this->expectException(get_class($exception)); + $this->expectExceptionMessage($exception->getMessage()); + $this->expectExceptionCode($exception->getCode()); } - protected function pEncapsList(array $encapsList, $quote) + public function expectNotToPerformAssertions() : void { - $return = ''; - foreach ($encapsList as $element) { - if ($element instanceof \PHPUnit\PhpParser\Node\Scalar\EncapsedStringPart) { - $return .= $this->escapeString($element->value, $quote); - } else { - $return .= '{' . $this->p($element) . '}'; - } - } - return $return; + $this->doesNotPerformAssertions = \true; } - protected function pSingleQuotedString(string $string) + public function expectDeprecation() : void { - return '\'' . \addcslashes($string, '\'\\') . '\''; + $this->expectedException = Deprecated::class; } - protected function escapeString($string, $quote) + public function expectDeprecationMessage(string $message) : void { - if (null === $quote) { - // For doc strings, don't escape newlines - $escaped = \addcslashes($string, "\t\f\v\$\\"); - } else { - $escaped = \addcslashes($string, "\n\r\t\f\v\$" . $quote . "\\"); - } - // Escape other control characters - return \preg_replace_callback('/([\\0-\\10\\16-\\37])(?=([0-7]?))/', function ($matches) { - $oct = \decoct(\ord($matches[1])); - if ($matches[2] !== '') { - // If there is a trailing digit, use the full three character form - return '\\' . \str_pad($oct, 3, '0', \STR_PAD_LEFT); - } - return '\\' . $oct; - }, $escaped); + $this->expectExceptionMessage($message); } - protected function containsEndLabel($string, $label, $atStart = \true, $atEnd = \true) + public function expectDeprecationMessageMatches(string $regularExpression) : void { - $start = $atStart ? '(?:^|[\\r\\n])' : '[\\r\\n]'; - $end = $atEnd ? '(?:$|[;\\r\\n])' : '[;\\r\\n]'; - return \false !== \strpos($string, $label) && \preg_match('/' . $start . $label . $end . '/', $string); + $this->expectExceptionMessageMatches($regularExpression); } - protected function encapsedContainsEndLabel(array $parts, $label) + public function expectNotice() : void { - foreach ($parts as $i => $part) { - $atStart = $i === 0; - $atEnd = $i === \count($parts) - 1; - if ($part instanceof \PHPUnit\PhpParser\Node\Scalar\EncapsedStringPart && $this->containsEndLabel($part->value, $label, $atStart, $atEnd)) { - return \true; - } - } - return \false; + $this->expectedException = Notice::class; } - protected function pDereferenceLhs(\PHPUnit\PhpParser\Node $node) + public function expectNoticeMessage(string $message) : void { - if (!$this->dereferenceLhsRequiresParens($node)) { - return $this->p($node); - } else { - return '(' . $this->p($node) . ')'; - } + $this->expectExceptionMessage($message); } - protected function pCallLhs(\PHPUnit\PhpParser\Node $node) + public function expectNoticeMessageMatches(string $regularExpression) : void { - if (!$this->callLhsRequiresParens($node)) { - return $this->p($node); - } else { - return '(' . $this->p($node) . ')'; - } + $this->expectExceptionMessageMatches($regularExpression); } - protected function pNewVariable(\PHPUnit\PhpParser\Node $node) + public function expectWarning() : void { - // TODO: This is not fully accurate. - return $this->pDereferenceLhs($node); + $this->expectedException = WarningError::class; } - /** - * @param Node[] $nodes - * @return bool - */ - private function hasNodeWithComments(array $nodes) + public function expectWarningMessage(string $message) : void { - foreach ($nodes as $node) { - if ($node && $node->getComments()) { - return \true; - } - } - return \false; + $this->expectExceptionMessage($message); } - private function pMaybeMultiline(array $nodes, bool $trailingComma = \false) + public function expectWarningMessageMatches(string $regularExpression) : void { - if (!$this->hasNodeWithComments($nodes)) { - return $this->pCommaSeparated($nodes); - } else { - return $this->pCommaSeparatedMultiline($nodes, $trailingComma) . $this->nl; - } + $this->expectExceptionMessageMatches($regularExpression); } - private function pAttrGroups(array $nodes, bool $inline = \false) : string + public function expectError() : void { - $result = ''; - $sep = $inline ? ' ' : $this->nl; - foreach ($nodes as $node) { - $result .= $this->p($node) . $sep; - } - return $result; + $this->expectedException = Error::class; } -} -defineCompatibilityTokens(); - $this->tokenMap = $this->createTokenMap(); - $this->identifierTokens = $this->createIdentifierTokenMap(); - // map of tokens to drop while lexing (the map is only used for isset lookup, - // that's why the value is simply set to 1; the value is never actually used.) - $this->dropTokens = \array_fill_keys([\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT, \T_BAD_CHARACTER], 1); - $defaultAttributes = ['comments', 'startLine', 'endLine']; - $usedAttributes = \array_fill_keys($options['usedAttributes'] ?? $defaultAttributes, \true); - // Create individual boolean properties to make these checks faster. - $this->attributeStartLineUsed = isset($usedAttributes['startLine']); - $this->attributeEndLineUsed = isset($usedAttributes['endLine']); - $this->attributeStartTokenPosUsed = isset($usedAttributes['startTokenPos']); - $this->attributeEndTokenPosUsed = isset($usedAttributes['endTokenPos']); - $this->attributeStartFilePosUsed = isset($usedAttributes['startFilePos']); - $this->attributeEndFilePosUsed = isset($usedAttributes['endFilePos']); - $this->attributeCommentsUsed = isset($usedAttributes['comments']); + $this->expectExceptionMessage($message); } - /** - * Initializes the lexer for lexing the provided source code. - * - * This function does not throw if lexing errors occur. Instead, errors may be retrieved using - * the getErrors() method. - * - * @param string $code The source code to lex - * @param ErrorHandler|null $errorHandler Error handler to use for lexing errors. Defaults to - * ErrorHandler\Throwing - */ - public function startLexing(string $code, \PHPUnit\PhpParser\ErrorHandler $errorHandler = null) + public function expectErrorMessageMatches(string $regularExpression) : void { - if (null === $errorHandler) { - $errorHandler = new \PHPUnit\PhpParser\ErrorHandler\Throwing(); - } - $this->code = $code; - // keep the code around for __halt_compiler() handling - $this->pos = -1; - $this->line = 1; - $this->filePos = 0; - // If inline HTML occurs without preceding code, treat it as if it had a leading newline. - // This ensures proper composability, because having a newline is the "safe" assumption. - $this->prevCloseTagHasNewline = \true; - $scream = \ini_set('xdebug.scream', '0'); - \error_clear_last(); - $this->tokens = @\token_get_all($code); - $this->postprocessTokens($errorHandler); - if (\false !== $scream) { - \ini_set('xdebug.scream', $scream); - } + $this->expectExceptionMessageMatches($regularExpression); } - private function handleInvalidCharacterRange($start, $end, $line, \PHPUnit\PhpParser\ErrorHandler $errorHandler) + public function getStatus() : int { - $tokens = []; - for ($i = $start; $i < $end; $i++) { - $chr = $this->code[$i]; - if ($chr === "\0") { - // PHP cuts error message after null byte, so need special case - $errorMsg = 'Unexpected null byte'; - } else { - $errorMsg = \sprintf('Unexpected character "%s" (ASCII %d)', $chr, \ord($chr)); - } - $tokens[] = [\T_BAD_CHARACTER, $chr, $line]; - $errorHandler->handleError(new \PHPUnit\PhpParser\Error($errorMsg, ['startLine' => $line, 'endLine' => $line, 'startFilePos' => $i, 'endFilePos' => $i])); - } - return $tokens; + return $this->status; } - /** - * Check whether comment token is unterminated. - * - * @return bool - */ - private function isUnterminatedComment($token) : bool + public function markAsRisky() : void { - return ($token[0] === \T_COMMENT || $token[0] === \T_DOC_COMMENT) && \substr($token[1], 0, 2) === '/*' && \substr($token[1], -2) !== '*/'; + $this->status = BaseTestRunner::STATUS_RISKY; } - protected function postprocessTokens(\PHPUnit\PhpParser\ErrorHandler $errorHandler) - { - // PHP's error handling for token_get_all() is rather bad, so if we want detailed - // error information we need to compute it ourselves. Invalid character errors are - // detected by finding "gaps" in the token array. Unterminated comments are detected - // by checking if a trailing comment has a "*/" at the end. - // - // Additionally, we canonicalize to the PHP 8 comment format here, which does not include - // the trailing whitespace anymore. - // - // We also canonicalize to the PHP 8 T_NAME_* tokens. - $filePos = 0; - $line = 1; - $numTokens = \count($this->tokens); - for ($i = 0; $i < $numTokens; $i++) { - $token = $this->tokens[$i]; - // Since PHP 7.4 invalid characters are represented by a T_BAD_CHARACTER token. - // In this case we only need to emit an error. - if ($token[0] === \T_BAD_CHARACTER) { - $this->handleInvalidCharacterRange($filePos, $filePos + 1, $line, $errorHandler); - } - if ($token[0] === \T_COMMENT && \substr($token[1], 0, 2) !== '/*' && \preg_match('/(\\r\\n|\\n|\\r)$/D', $token[1], $matches)) { - $trailingNewline = $matches[0]; - $token[1] = \substr($token[1], 0, -\strlen($trailingNewline)); - $this->tokens[$i] = $token; - if (isset($this->tokens[$i + 1]) && $this->tokens[$i + 1][0] === \T_WHITESPACE) { - // Move trailing newline into following T_WHITESPACE token, if it already exists. - $this->tokens[$i + 1][1] = $trailingNewline . $this->tokens[$i + 1][1]; - $this->tokens[$i + 1][2]--; - } else { - // Otherwise, we need to create a new T_WHITESPACE token. - \array_splice($this->tokens, $i + 1, 0, [[\T_WHITESPACE, $trailingNewline, $line]]); - $numTokens++; - } - } - // Emulate PHP 8 T_NAME_* tokens, by combining sequences of T_NS_SEPARATOR and T_STRING - // into a single token. - if (\is_array($token) && ($token[0] === \T_NS_SEPARATOR || isset($this->identifierTokens[$token[0]]))) { - $lastWasSeparator = $token[0] === \T_NS_SEPARATOR; - $text = $token[1]; - for ($j = $i + 1; isset($this->tokens[$j]); $j++) { - if ($lastWasSeparator) { - if (!isset($this->identifierTokens[$this->tokens[$j][0]])) { - break; - } - $lastWasSeparator = \false; - } else { - if ($this->tokens[$j][0] !== \T_NS_SEPARATOR) { - break; - } - $lastWasSeparator = \true; - } - $text .= $this->tokens[$j][1]; - } - if ($lastWasSeparator) { - // Trailing separator is not part of the name. - $j--; - $text = \substr($text, 0, -1); - } - if ($j > $i + 1) { - if ($token[0] === \T_NS_SEPARATOR) { - $type = \T_NAME_FULLY_QUALIFIED; - } else { - if ($token[0] === \T_NAMESPACE) { - $type = \T_NAME_RELATIVE; - } else { - $type = \T_NAME_QUALIFIED; - } - } - $token = [$type, $text, $line]; - \array_splice($this->tokens, $i, $j - $i, [$token]); - $numTokens -= $j - $i - 1; - } - } - $tokenValue = \is_string($token) ? $token : $token[1]; - $tokenLen = \strlen($tokenValue); - if (\substr($this->code, $filePos, $tokenLen) !== $tokenValue) { - // Something is missing, must be an invalid character - $nextFilePos = \strpos($this->code, $tokenValue, $filePos); - $badCharTokens = $this->handleInvalidCharacterRange($filePos, $nextFilePos, $line, $errorHandler); - $filePos = (int) $nextFilePos; - \array_splice($this->tokens, $i, 0, $badCharTokens); - $numTokens += \count($badCharTokens); - $i += \count($badCharTokens); - } - $filePos += $tokenLen; - $line += \substr_count($tokenValue, "\n"); - } - if ($filePos !== \strlen($this->code)) { - if (\substr($this->code, $filePos, 2) === '/*') { - // Unlike PHP, HHVM will drop unterminated comments entirely - $comment = \substr($this->code, $filePos); - $errorHandler->handleError(new \PHPUnit\PhpParser\Error('Unterminated comment', ['startLine' => $line, 'endLine' => $line + \substr_count($comment, "\n"), 'startFilePos' => $filePos, 'endFilePos' => $filePos + \strlen($comment)])); - // Emulate the PHP behavior - $isDocComment = isset($comment[3]) && $comment[3] === '*'; - $this->tokens[] = [$isDocComment ? \T_DOC_COMMENT : \T_COMMENT, $comment, $line]; - } else { - // Invalid characters at the end of the input - $badCharTokens = $this->handleInvalidCharacterRange($filePos, \strlen($this->code), $line, $errorHandler); - $this->tokens = \array_merge($this->tokens, $badCharTokens); - } - return; - } - if (\count($this->tokens) > 0) { - // Check for unterminated comment - $lastToken = $this->tokens[\count($this->tokens) - 1]; - if ($this->isUnterminatedComment($lastToken)) { - $errorHandler->handleError(new \PHPUnit\PhpParser\Error('Unterminated comment', ['startLine' => $line - \substr_count($lastToken[1], "\n"), 'endLine' => $line, 'startFilePos' => $filePos - \strlen($lastToken[1]), 'endFilePos' => $filePos])); - } - } + public function getStatusMessage() : string + { + return $this->statusMessage; + } + public function hasFailed() : bool + { + $status = $this->getStatus(); + return $status === BaseTestRunner::STATUS_FAILURE || $status === BaseTestRunner::STATUS_ERROR; } /** - * Fetches the next token. - * - * The available attributes are determined by the 'usedAttributes' option, which can - * be specified in the constructor. The following attributes are supported: - * - * * 'comments' => Array of PhpParser\Comment or PhpParser\Comment\Doc instances, - * representing all comments that occurred between the previous - * non-discarded token and the current one. - * * 'startLine' => Line in which the node starts. - * * 'endLine' => Line in which the node ends. - * * 'startTokenPos' => Offset into the token array of the first token in the node. - * * 'endTokenPos' => Offset into the token array of the last token in the node. - * * 'startFilePos' => Offset into the code string of the first character that is part of the node. - * * 'endFilePos' => Offset into the code string of the last character that is part of the node. - * - * @param mixed $value Variable to store token content in - * @param mixed $startAttributes Variable to store start attributes in - * @param mixed $endAttributes Variable to store end attributes in + * Runs the test case and collects the results in a TestResult object. + * If no TestResult object is passed a new one will be created. * - * @return int Token id + * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException + * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws CodeCoverageException + * @throws UtilException */ - public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) : int + public function run(\PHPUnit\Framework\TestResult $result = null) : \PHPUnit\Framework\TestResult { - $startAttributes = []; - $endAttributes = []; - while (1) { - if (isset($this->tokens[++$this->pos])) { - $token = $this->tokens[$this->pos]; - } else { - // EOF token with ID 0 - $token = "\0"; - } - if ($this->attributeStartLineUsed) { - $startAttributes['startLine'] = $this->line; - } - if ($this->attributeStartTokenPosUsed) { - $startAttributes['startTokenPos'] = $this->pos; + if ($result === null) { + $result = $this->createResult(); + } + if (!$this instanceof \PHPUnit\Framework\ErrorTestCase && !$this instanceof \PHPUnit\Framework\WarningTestCase) { + $this->setTestResultObject($result); + } + if (!$this instanceof \PHPUnit\Framework\ErrorTestCase && !$this instanceof \PHPUnit\Framework\WarningTestCase && !$this instanceof \PHPUnit\Framework\SkippedTestCase && !$this->handleDependencies()) { + return $result; + } + if ($this->runInSeparateProcess()) { + $runEntireClass = $this->runClassInSeparateProcess && !$this->runTestInSeparateProcess; + try { + $class = new ReflectionClass($this); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); } - if ($this->attributeStartFilePosUsed) { - $startAttributes['startFilePos'] = $this->filePos; + // @codeCoverageIgnoreEnd + if ($runEntireClass) { + $template = new Template(__DIR__ . '/../Util/PHP/Template/TestCaseClass.tpl'); + } else { + $template = new Template(__DIR__ . '/../Util/PHP/Template/TestCaseMethod.tpl'); } - if (\is_string($token)) { - $value = $token; - if (isset($token[1])) { - // bug in token_get_all - $this->filePos += 2; - $id = \ord('"'); + if ($this->preserveGlobalState) { + $constants = GlobalState::getConstantsAsString(); + $globals = GlobalState::getGlobalsAsString(); + $includedFiles = GlobalState::getIncludedFilesAsString(); + $iniSettings = GlobalState::getIniSettingsAsString(); + } else { + $constants = ''; + if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + $globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . var_export($GLOBALS['__PHPUNIT_BOOTSTRAP'], \true) . ";\n"; } else { - $this->filePos += 1; - $id = \ord($token); - } - } elseif (!isset($this->dropTokens[$token[0]])) { - $value = $token[1]; - $id = $this->tokenMap[$token[0]]; - if (\T_CLOSE_TAG === $token[0]) { - $this->prevCloseTagHasNewline = \false !== \strpos($token[1], "\n"); - } elseif (\T_INLINE_HTML === $token[0]) { - $startAttributes['hasLeadingNewline'] = $this->prevCloseTagHasNewline; + $globals = ''; } - $this->line += \substr_count($value, "\n"); - $this->filePos += \strlen($value); + $includedFiles = ''; + $iniSettings = ''; + } + $coverage = $result->getCollectCodeCoverageInformation() ? 'true' : 'false'; + $isStrictAboutTestsThatDoNotTestAnything = $result->isStrictAboutTestsThatDoNotTestAnything() ? 'true' : 'false'; + $isStrictAboutOutputDuringTests = $result->isStrictAboutOutputDuringTests() ? 'true' : 'false'; + $enforcesTimeLimit = $result->enforcesTimeLimit() ? 'true' : 'false'; + $isStrictAboutTodoAnnotatedTests = $result->isStrictAboutTodoAnnotatedTests() ? 'true' : 'false'; + $isStrictAboutResourceUsageDuringSmallTests = $result->isStrictAboutResourceUsageDuringSmallTests() ? 'true' : 'false'; + if (defined('PHPUNIT_COMPOSER_INSTALL')) { + $composerAutoload = var_export(PHPUNIT_COMPOSER_INSTALL, \true); } else { - $origLine = $this->line; - $origFilePos = $this->filePos; - $this->line += \substr_count($token[1], "\n"); - $this->filePos += \strlen($token[1]); - if (\T_COMMENT === $token[0] || \T_DOC_COMMENT === $token[0]) { - if ($this->attributeCommentsUsed) { - $comment = \T_DOC_COMMENT === $token[0] ? new \PHPUnit\PhpParser\Comment\Doc($token[1], $origLine, $origFilePos, $this->pos, $this->line, $this->filePos - 1, $this->pos) : new \PHPUnit\PhpParser\Comment($token[1], $origLine, $origFilePos, $this->pos, $this->line, $this->filePos - 1, $this->pos); - $startAttributes['comments'][] = $comment; - } - } - continue; + $composerAutoload = '\'\''; } - if ($this->attributeEndLineUsed) { - $endAttributes['endLine'] = $this->line; + if (defined('__PHPUNIT_PHAR__')) { + $phar = var_export(__PHPUNIT_PHAR__, \true); + } else { + $phar = '\'\''; } - if ($this->attributeEndTokenPosUsed) { - $endAttributes['endTokenPos'] = $this->pos; + $codeCoverage = $result->getCodeCoverage(); + $codeCoverageFilter = null; + $cachesStaticAnalysis = 'false'; + $codeCoverageCacheDirectory = null; + $driverMethod = 'forLineCoverage'; + if ($codeCoverage) { + $codeCoverageFilter = $codeCoverage->filter(); + if ($codeCoverage->collectsBranchAndPathCoverage()) { + $driverMethod = 'forLineAndPathCoverage'; + } + if ($codeCoverage->cachesStaticAnalysis()) { + $cachesStaticAnalysis = 'true'; + $codeCoverageCacheDirectory = $codeCoverage->cacheDirectory(); + } } - if ($this->attributeEndFilePosUsed) { - $endAttributes['endFilePos'] = $this->filePos - 1; + $data = var_export(serialize($this->data), \true); + $dataName = var_export($this->dataName, \true); + $dependencyInput = var_export(serialize($this->dependencyInput), \true); + $includePath = var_export(get_include_path(), \true); + $codeCoverageFilter = var_export(serialize($codeCoverageFilter), \true); + $codeCoverageCacheDirectory = var_export(serialize($codeCoverageCacheDirectory), \true); + // must do these fixes because TestCaseMethod.tpl has unserialize('{data}') in it, and we can't break BC + // the lines above used to use addcslashes() rather than var_export(), which breaks null byte escape sequences + $data = "'." . $data . ".'"; + $dataName = "'.(" . $dataName . ").'"; + $dependencyInput = "'." . $dependencyInput . ".'"; + $includePath = "'." . $includePath . ".'"; + $codeCoverageFilter = "'." . $codeCoverageFilter . ".'"; + $codeCoverageCacheDirectory = "'." . $codeCoverageCacheDirectory . ".'"; + $configurationFilePath = $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] ?? ''; + $var = ['composerAutoload' => $composerAutoload, 'phar' => $phar, 'filename' => $class->getFileName(), 'className' => $class->getName(), 'collectCodeCoverageInformation' => $coverage, 'cachesStaticAnalysis' => $cachesStaticAnalysis, 'codeCoverageCacheDirectory' => $codeCoverageCacheDirectory, 'driverMethod' => $driverMethod, 'data' => $data, 'dataName' => $dataName, 'dependencyInput' => $dependencyInput, 'constants' => $constants, 'globals' => $globals, 'include_path' => $includePath, 'included_files' => $includedFiles, 'iniSettings' => $iniSettings, 'isStrictAboutTestsThatDoNotTestAnything' => $isStrictAboutTestsThatDoNotTestAnything, 'isStrictAboutOutputDuringTests' => $isStrictAboutOutputDuringTests, 'enforcesTimeLimit' => $enforcesTimeLimit, 'isStrictAboutTodoAnnotatedTests' => $isStrictAboutTodoAnnotatedTests, 'isStrictAboutResourceUsageDuringSmallTests' => $isStrictAboutResourceUsageDuringSmallTests, 'codeCoverageFilter' => $codeCoverageFilter, 'configurationFilePath' => $configurationFilePath, 'name' => $this->getName(\false)]; + if (!$runEntireClass) { + $var['methodName'] = $this->name; } - return $id; + $template->setVar($var); + $php = AbstractPhpProcess::factory(); + $php->runTestJob($template->render(), $this, $result); + } else { + $result->run($this); } - throw new \RuntimeException('Reached end of lexer loop'); + $this->result = null; + return $result; } /** - * Returns the token array for current code. - * - * The token array is in the same format as provided by the - * token_get_all() function and does not discard tokens (i.e. - * whitespace and comments are included). The token position - * attributes are against this token array. + * Returns a builder object to create mock objects using a fluent interface. * - * @return array Array of tokens in token_get_all() format + * @psalm-template RealInstanceType of object + * @psalm-param class-string $className + * @psalm-return MockBuilder */ - public function getTokens() : array + public function getMockBuilder(string $className) : MockBuilder { - return $this->tokens; + $this->recordDoubledType($className); + return new MockBuilder($this, $className); + } + public function registerComparator(Comparator $comparator) : void + { + ComparatorFactory::getInstance()->register($comparator); + $this->customComparators[] = $comparator; } /** - * Handles __halt_compiler() by returning the text after it. + * @return string[] * - * @return string Remaining text + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function handleHaltCompiler() : string + public function doubledTypes() : array { - // text after T_HALT_COMPILER, still including (); - $textAfter = \substr($this->code, $this->filePos); - // ensure that it is followed by (); - // this simplifies the situation, by not allowing any comments - // in between of the tokens. - if (!\preg_match('~^\\s*\\(\\s*\\)\\s*(?:;|\\?>\\r?\\n?)~', $textAfter, $matches)) { - throw new \PHPUnit\PhpParser\Error('__HALT_COMPILER must be followed by "();"'); - } - // prevent the lexer from returning any further tokens - $this->pos = \count($this->tokens); - // return with (); removed - return \substr($textAfter, \strlen($matches[0])); + return array_unique($this->doubledTypes); } - private function defineCompatibilityTokens() + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function getGroups() : array { - static $compatTokensDefined = \false; - if ($compatTokensDefined) { - return; - } - $compatTokens = [ - // PHP 7.4 - 'T_BAD_CHARACTER', - 'T_FN', - 'T_COALESCE_EQUAL', - // PHP 8.0 - 'T_NAME_QUALIFIED', - 'T_NAME_FULLY_QUALIFIED', - 'T_NAME_RELATIVE', - 'T_MATCH', - 'T_NULLSAFE_OBJECT_OPERATOR', - 'T_ATTRIBUTE', - ]; - // PHP-Parser might be used together with another library that also emulates some or all - // of these tokens. Perform a sanity-check that all already defined tokens have been - // assigned a unique ID. - $usedTokenIds = []; - foreach ($compatTokens as $token) { - if (\defined($token)) { - $tokenId = \constant($token); - $clashingToken = $usedTokenIds[$tokenId] ?? null; - if ($clashingToken !== null) { - throw new \Error(\sprintf('Token %s has same ID as token %s, ' . 'you may be using a library with broken token emulation', $token, $clashingToken)); - } - $usedTokenIds[$tokenId] = $token; - } - } - // Now define any tokens that have not yet been emulated. Try to assign IDs from -1 - // downwards, but skip any IDs that may already be in use. - $newTokenId = -1; - foreach ($compatTokens as $token) { - if (!\defined($token)) { - while (isset($usedTokenIds[$newTokenId])) { - $newTokenId--; - } - \define($token, $newTokenId); - $newTokenId--; - } - } - $compatTokensDefined = \true; + return $this->groups; } /** - * Creates the token map. - * - * The token map maps the PHP internal token identifiers - * to the identifiers used by the Parser. Additionally it - * maps T_OPEN_TAG_WITH_ECHO to T_ECHO and T_CLOSE_TAG to ';'. + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function setGroups(array $groups) : void + { + $this->groups = $groups; + } + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @return array The token map + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - protected function createTokenMap() : array + public function getName(bool $withDataSet = \true) : string { - $tokenMap = []; - // 256 is the minimum possible token number, as everything below - // it is an ASCII value - for ($i = 256; $i < 1000; ++$i) { - if (\T_DOUBLE_COLON === $i) { - // T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM - $tokenMap[$i] = \PHPUnit\PhpParser\Parser\Tokens::T_PAAMAYIM_NEKUDOTAYIM; - } elseif (\T_OPEN_TAG_WITH_ECHO === $i) { - // T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO - $tokenMap[$i] = \PHPUnit\PhpParser\Parser\Tokens::T_ECHO; - } elseif (\T_CLOSE_TAG === $i) { - // T_CLOSE_TAG is equivalent to ';' - $tokenMap[$i] = \ord(';'); - } elseif ('UNKNOWN' !== ($name = \token_name($i))) { - if ('T_HASHBANG' === $name) { - // HHVM uses a special token for #! hashbang lines - $tokenMap[$i] = \PHPUnit\PhpParser\Parser\Tokens::T_INLINE_HTML; - } elseif (\defined($name = \PHPUnit\PhpParser\Parser\Tokens::class . '::' . $name)) { - // Other tokens can be mapped directly - $tokenMap[$i] = \constant($name); - } - } - } - // HHVM uses a special token for numbers that overflow to double - if (\defined('T_ONUMBER')) { - $tokenMap[\T_ONUMBER] = \PHPUnit\PhpParser\Parser\Tokens::T_DNUMBER; - } - // HHVM also has a separate token for the __COMPILER_HALT_OFFSET__ constant - if (\defined('T_COMPILER_HALT_OFFSET')) { - $tokenMap[\T_COMPILER_HALT_OFFSET] = \PHPUnit\PhpParser\Parser\Tokens::T_STRING; + if ($withDataSet) { + return $this->name . $this->getDataSetAsString(\false); } - // Assign tokens for which we define compatibility constants, as token_name() does not know them. - $tokenMap[\T_FN] = \PHPUnit\PhpParser\Parser\Tokens::T_FN; - $tokenMap[\T_COALESCE_EQUAL] = \PHPUnit\PhpParser\Parser\Tokens::T_COALESCE_EQUAL; - $tokenMap[\T_NAME_QUALIFIED] = \PHPUnit\PhpParser\Parser\Tokens::T_NAME_QUALIFIED; - $tokenMap[\T_NAME_FULLY_QUALIFIED] = \PHPUnit\PhpParser\Parser\Tokens::T_NAME_FULLY_QUALIFIED; - $tokenMap[\T_NAME_RELATIVE] = \PHPUnit\PhpParser\Parser\Tokens::T_NAME_RELATIVE; - $tokenMap[\T_MATCH] = \PHPUnit\PhpParser\Parser\Tokens::T_MATCH; - $tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = \PHPUnit\PhpParser\Parser\Tokens::T_NULLSAFE_OBJECT_OPERATOR; - $tokenMap[\T_ATTRIBUTE] = \PHPUnit\PhpParser\Parser\Tokens::T_ATTRIBUTE; - return $tokenMap; + return $this->name; } - private function createIdentifierTokenMap() : array + /** + * Returns the size of the test. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function getSize() : int { - // Based on semi_reserved production. - return \array_fill_keys([\T_STRING, \T_STATIC, \T_ABSTRACT, \T_FINAL, \T_PRIVATE, \T_PROTECTED, \T_PUBLIC, \T_INCLUDE, \T_INCLUDE_ONCE, \T_EVAL, \T_REQUIRE, \T_REQUIRE_ONCE, \T_LOGICAL_OR, \T_LOGICAL_XOR, \T_LOGICAL_AND, \T_INSTANCEOF, \T_NEW, \T_CLONE, \T_EXIT, \T_IF, \T_ELSEIF, \T_ELSE, \T_ENDIF, \T_ECHO, \T_DO, \T_WHILE, \T_ENDWHILE, \T_FOR, \T_ENDFOR, \T_FOREACH, \T_ENDFOREACH, \T_DECLARE, \T_ENDDECLARE, \T_AS, \T_TRY, \T_CATCH, \T_FINALLY, \T_THROW, \T_USE, \T_INSTEADOF, \T_GLOBAL, \T_VAR, \T_UNSET, \T_ISSET, \T_EMPTY, \T_CONTINUE, \T_GOTO, \T_FUNCTION, \T_CONST, \T_RETURN, \T_PRINT, \T_YIELD, \T_LIST, \T_SWITCH, \T_ENDSWITCH, \T_CASE, \T_DEFAULT, \T_BREAK, \T_ARRAY, \T_CALLABLE, \T_EXTENDS, \T_IMPLEMENTS, \T_NAMESPACE, \T_TRAIT, \T_INTERFACE, \T_CLASS, \T_CLASS_C, \T_TRAIT_C, \T_FUNC_C, \T_METHOD_C, \T_LINE, \T_FILE, \T_DIR, \T_NS_C, \T_HALT_COMPILER, \T_FN, \T_MATCH], \true); + return TestUtil::getSize(static::class, $this->getName(\false)); } -} -rawMessage = $message; - if (\is_array($attributes)) { - $this->attributes = $attributes; - } else { - $this->attributes = ['startLine' => $attributes]; - } - $this->updateMessage(); + return $this->getSize() !== TestUtil::UNKNOWN; } /** - * Gets the error message + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @return string Error message + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function getRawMessage() : string + public function isSmall() : bool { - return $this->rawMessage; + return $this->getSize() === TestUtil::SMALL; } /** - * Gets the line the error starts in. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @return int Error start line + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function getStartLine() : int + public function isMedium() : bool { - return $this->attributes['startLine'] ?? -1; + return $this->getSize() === TestUtil::MEDIUM; } /** - * Gets the line the error ends in. + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @return int Error end line + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function getEndLine() : int + public function isLarge() : bool { - return $this->attributes['endLine'] ?? -1; + return $this->getSize() === TestUtil::LARGE; } /** - * Gets the attributes of the node/token the error occurred at. - * - * @return array + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function getAttributes() : array + public function getActualOutput() : string { - return $this->attributes; + if (!$this->outputBufferingActive) { + return $this->output; + } + return (string) ob_get_contents(); } /** - * Sets the attributes of the node/token the error occurred at. - * - * @param array $attributes + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function setAttributes(array $attributes) + public function hasOutput() : bool { - $this->attributes = $attributes; - $this->updateMessage(); + if ($this->output === '') { + return \false; + } + if ($this->hasExpectationOnOutput()) { + return \false; + } + return \true; } /** - * Sets the line of the PHP file the error occurred in. - * - * @param string $message Error message + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function setRawMessage(string $message) + public function doesNotPerformAssertions() : bool { - $this->rawMessage = $message; - $this->updateMessage(); + return $this->doesNotPerformAssertions; } /** - * Sets the line the error starts in. - * - * @param int $line Error start line + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function setStartLine(int $line) + public function hasExpectationOnOutput() : bool { - $this->attributes['startLine'] = $line; - $this->updateMessage(); + return is_string($this->outputExpectedString) || is_string($this->outputExpectedRegex) || $this->outputRetrievedForAssertion; } /** - * Returns whether the error has start and end column information. - * - * For column information enable the startFilePos and endFilePos in the lexer options. - * - * @return bool + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function hasColumnInfo() : bool + public function getExpectedException() : ?string { - return isset($this->attributes['startFilePos'], $this->attributes['endFilePos']); + return $this->expectedException; } /** - * Gets the start column (1-based) into the line where the error started. + * @return null|int|string * - * @param string $code Source code of the file - * @return int + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function getStartColumn(string $code) : int + public function getExpectedExceptionCode() { - if (!$this->hasColumnInfo()) { - throw new \RuntimeException('Error does not have column information'); - } - return $this->toColumn($code, $this->attributes['startFilePos']); + return $this->expectedExceptionCode; } /** - * Gets the end column (1-based) into the line where the error ended. + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function getExpectedExceptionMessage() : ?string + { + return $this->expectedExceptionMessage; + } + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function getExpectedExceptionMessageRegExp() : ?string + { + return $this->expectedExceptionMessageRegExp; + } + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag) : void + { + $this->registerMockObjectsFromTestArgumentsRecursively = $flag; + } + /** + * @throws Throwable * - * @param string $code Source code of the file - * @return int + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function getEndColumn(string $code) : int + public function runBare() : void { - if (!$this->hasColumnInfo()) { - throw new \RuntimeException('Error does not have column information'); + $this->numAssertions = 0; + $this->snapshotGlobalState(); + $this->startOutputBuffering(); + clearstatcache(); + $currentWorkingDirectory = getcwd(); + $hookMethods = TestUtil::getHookMethods(static::class); + $hasMetRequirements = \false; + try { + $this->checkRequirements(); + $hasMetRequirements = \true; + if ($this->inIsolation) { + foreach ($hookMethods['beforeClass'] as $method) { + $this->{$method}(); + } + } + $this->setDoesNotPerformAssertionsFromAnnotation(); + foreach ($hookMethods['before'] as $method) { + $this->{$method}(); + } + foreach ($hookMethods['preCondition'] as $method) { + $this->{$method}(); + } + $this->testResult = $this->runTest(); + $this->verifyMockObjects(); + foreach ($hookMethods['postCondition'] as $method) { + $this->{$method}(); + } + if (!empty($this->warnings)) { + throw new \PHPUnit\Framework\Warning(implode("\n", array_unique($this->warnings))); + } + $this->status = BaseTestRunner::STATUS_PASSED; + } catch (\PHPUnit\Framework\IncompleteTest $e) { + $this->status = BaseTestRunner::STATUS_INCOMPLETE; + $this->statusMessage = $e->getMessage(); + } catch (\PHPUnit\Framework\SkippedTest $e) { + $this->status = BaseTestRunner::STATUS_SKIPPED; + $this->statusMessage = $e->getMessage(); + } catch (\PHPUnit\Framework\Warning $e) { + $this->status = BaseTestRunner::STATUS_WARNING; + $this->statusMessage = $e->getMessage(); + } catch (\PHPUnit\Framework\AssertionFailedError $e) { + $this->status = BaseTestRunner::STATUS_FAILURE; + $this->statusMessage = $e->getMessage(); + } catch (PredictionException $e) { + $this->status = BaseTestRunner::STATUS_FAILURE; + $this->statusMessage = $e->getMessage(); + } catch (Throwable $_e) { + $e = $_e; + $this->status = BaseTestRunner::STATUS_ERROR; + $this->statusMessage = $_e->getMessage(); + } + $this->mockObjects = []; + $this->prophet = null; + // Tear down the fixture. An exception raised in tearDown() will be + // caught and passed on when no exception was raised before. + try { + if ($hasMetRequirements) { + foreach ($hookMethods['after'] as $method) { + $this->{$method}(); + } + if ($this->inIsolation) { + foreach ($hookMethods['afterClass'] as $method) { + $this->{$method}(); + } + } + } + } catch (Throwable $_e) { + $e = $e ?? $_e; + } + try { + $this->stopOutputBuffering(); + } catch (\PHPUnit\Framework\RiskyTestError $_e) { + $e = $e ?? $_e; + } + if (isset($_e)) { + $this->status = BaseTestRunner::STATUS_ERROR; + $this->statusMessage = $_e->getMessage(); + } + clearstatcache(); + if ($currentWorkingDirectory !== getcwd()) { + chdir($currentWorkingDirectory); + } + $this->restoreGlobalState(); + $this->unregisterCustomComparators(); + $this->cleanupIniSettings(); + $this->cleanupLocaleSettings(); + libxml_clear_errors(); + // Perform assertion on output. + if (!isset($e)) { + try { + if ($this->outputExpectedRegex !== null) { + $this->assertMatchesRegularExpression($this->outputExpectedRegex, $this->output); + } elseif ($this->outputExpectedString !== null) { + $this->assertEquals($this->outputExpectedString, $this->output); + } + } catch (Throwable $_e) { + $e = $_e; + } + } + // Workaround for missing "finally". + if (isset($e)) { + if ($e instanceof PredictionException) { + $e = new \PHPUnit\Framework\AssertionFailedError($e->getMessage()); + } + $this->onNotSuccessfulTest($e); } - return $this->toColumn($code, $this->attributes['endFilePos']); } /** - * Formats message including line and column information. - * - * @param string $code Source code associated with the error, for calculation of the columns - * - * @return string Formatted message + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function getMessageWithColumnInfo(string $code) : string + public function setName(string $name) : void { - return \sprintf('%s from %d:%d to %d:%d', $this->getRawMessage(), $this->getStartLine(), $this->getStartColumn($code), $this->getEndLine(), $this->getEndColumn($code)); + $this->name = $name; + if (is_callable($this->sortId(), \true)) { + $this->providedTests = [new \PHPUnit\Framework\ExecutionOrderDependency($this->sortId())]; + } } /** - * Converts a file offset into a column. - * - * @param string $code Source code that $pos indexes into - * @param int $pos 0-based position in $code + * @param list $dependencies * - * @return int 1-based column (relative to start of line) + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - private function toColumn(string $code, int $pos) : int + public function setDependencies(array $dependencies) : void { - if ($pos > \strlen($code)) { - throw new \RuntimeException('Invalid position information'); - } - $lineStartPos = \strrpos($code, "\n", $pos - \strlen($code)); - if (\false === $lineStartPos) { - $lineStartPos = -1; - } - return $pos - $lineStartPos; + $this->dependencies = $dependencies; } /** - * Updates the exception message after a change to rawMessage or rawLine. + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - protected function updateMessage() + public function setDependencyInput(array $dependencyInput) : void { - $this->message = $this->rawMessage; - if (-1 === $this->getStartLine()) { - $this->message .= ' on unknown line'; - } else { - $this->message .= ' on line ' . $this->getStartLine(); - } + $this->dependencyInput = $dependencyInput; } -} -dumpComments = !empty($options['dumpComments']); - $this->dumpPositions = !empty($options['dumpPositions']); + $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; } /** - * Dumps a node or array. - * - * @param array|Node $node Node or array to dump - * @param string|null $code Code corresponding to dumped AST. This only needs to be passed if - * the dumpPositions option is enabled and the dumping of node offsets - * is desired. - * - * @return string Dumped value + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function dump($node, string $code = null) : string - { - $this->code = $code; - return $this->dumpRecursive($node); - } - protected function dumpRecursive($node) + public function setBackupGlobals(?bool $backupGlobals) : void { - if ($node instanceof \PHPUnit\PhpParser\Node) { - $r = $node->getType(); - if ($this->dumpPositions && null !== ($p = $this->dumpPosition($node))) { - $r .= $p; - } - $r .= '('; - foreach ($node->getSubNodeNames() as $key) { - $r .= "\n " . $key . ': '; - $value = $node->{$key}; - if (null === $value) { - $r .= 'null'; - } elseif (\false === $value) { - $r .= 'false'; - } elseif (\true === $value) { - $r .= 'true'; - } elseif (\is_scalar($value)) { - if ('flags' === $key || 'newModifier' === $key) { - $r .= $this->dumpFlags($value); - } elseif ('type' === $key && $node instanceof \PHPUnit\PhpParser\Node\Expr\Include_) { - $r .= $this->dumpIncludeType($value); - } elseif ('type' === $key && ($node instanceof \PHPUnit\PhpParser\Node\Stmt\Use_ || $node instanceof \PHPUnit\PhpParser\Node\Stmt\UseUse || $node instanceof \PHPUnit\PhpParser\Node\Stmt\GroupUse)) { - $r .= $this->dumpUseType($value); - } else { - $r .= $value; - } - } else { - $r .= \str_replace("\n", "\n ", $this->dumpRecursive($value)); - } - } - if ($this->dumpComments && ($comments = $node->getComments())) { - $r .= "\n comments: " . \str_replace("\n", "\n ", $this->dumpRecursive($comments)); - } - } elseif (\is_array($node)) { - $r = 'array('; - foreach ($node as $key => $value) { - $r .= "\n " . $key . ': '; - if (null === $value) { - $r .= 'null'; - } elseif (\false === $value) { - $r .= 'false'; - } elseif (\true === $value) { - $r .= 'true'; - } elseif (\is_scalar($value)) { - $r .= $value; - } else { - $r .= \str_replace("\n", "\n ", $this->dumpRecursive($value)); - } - } - } elseif ($node instanceof \PHPUnit\PhpParser\Comment) { - return $node->getReformattedText(); - } else { - throw new \InvalidArgumentException('Can only dump nodes and arrays.'); + if ($this->backupGlobals === null && $backupGlobals !== null) { + $this->backupGlobals = $backupGlobals; } - return $r . "\n)"; } - protected function dumpFlags($flags) + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function setBackupStaticAttributes(?bool $backupStaticAttributes) : void { - $strs = []; - if ($flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC) { - $strs[] = 'MODIFIER_PUBLIC'; - } - if ($flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED) { - $strs[] = 'MODIFIER_PROTECTED'; - } - if ($flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE) { - $strs[] = 'MODIFIER_PRIVATE'; - } - if ($flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_ABSTRACT) { - $strs[] = 'MODIFIER_ABSTRACT'; - } - if ($flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_STATIC) { - $strs[] = 'MODIFIER_STATIC'; - } - if ($flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_FINAL) { - $strs[] = 'MODIFIER_FINAL'; - } - if ($strs) { - return \implode(' | ', $strs) . ' (' . $flags . ')'; - } else { - return $flags; + if ($this->backupStaticAttributes === null && $backupStaticAttributes !== null) { + $this->backupStaticAttributes = $backupStaticAttributes; } } - protected function dumpIncludeType($type) + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess) : void { - $map = [\PHPUnit\PhpParser\Node\Expr\Include_::TYPE_INCLUDE => 'TYPE_INCLUDE', \PHPUnit\PhpParser\Node\Expr\Include_::TYPE_INCLUDE_ONCE => 'TYPE_INCLUDE_ONCE', \PHPUnit\PhpParser\Node\Expr\Include_::TYPE_REQUIRE => 'TYPE_REQUIRE', \PHPUnit\PhpParser\Node\Expr\Include_::TYPE_REQUIRE_ONCE => 'TYPE_REQUIRE_ONCE']; - if (!isset($map[$type])) { - return $type; + if ($this->runTestInSeparateProcess === null) { + $this->runTestInSeparateProcess = $runTestInSeparateProcess; } - return $map[$type] . ' (' . $type . ')'; } - protected function dumpUseType($type) + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function setRunClassInSeparateProcess(bool $runClassInSeparateProcess) : void { - $map = [\PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN => 'TYPE_UNKNOWN', \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_NORMAL => 'TYPE_NORMAL', \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_FUNCTION => 'TYPE_FUNCTION', \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_CONSTANT => 'TYPE_CONSTANT']; - if (!isset($map[$type])) { - return $type; + if ($this->runClassInSeparateProcess === null) { + $this->runClassInSeparateProcess = $runClassInSeparateProcess; } - return $map[$type] . ' (' . $type . ')'; } /** - * Dump node position, if possible. - * - * @param Node $node Node for which to dump position - * - * @return string|null Dump of position, or null if position information not available + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - protected function dumpPosition(\PHPUnit\PhpParser\Node $node) + public function setPreserveGlobalState(bool $preserveGlobalState) : void { - if (!$node->hasAttribute('startLine') || !$node->hasAttribute('endLine')) { - return null; - } - $start = $node->getStartLine(); - $end = $node->getEndLine(); - if ($node->hasAttribute('startFilePos') && $node->hasAttribute('endFilePos') && null !== $this->code) { - $start .= ':' . $this->toColumn($this->code, $node->getStartFilePos()); - $end .= ':' . $this->toColumn($this->code, $node->getEndFilePos()); - } - return "[{$start} - {$end}]"; + $this->preserveGlobalState = $preserveGlobalState; } - // Copied from Error class - private function toColumn($code, $pos) + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function setInIsolation(bool $inIsolation) : void { - if ($pos > \strlen($code)) { - throw new \RuntimeException('Invalid position information'); - } - $lineStartPos = \strrpos($code, "\n", $pos - \strlen($code)); - if (\false === $lineStartPos) { - $lineStartPos = -1; - } - return $pos - $lineStartPos; + $this->inIsolation = $inIsolation; } -} -fallbackEvaluator = $fallbackEvaluator ?? function (\PHPUnit\PhpParser\Node\Expr $expr) { - throw new \PHPUnit\PhpParser\ConstExprEvaluationException("Expression of type {$expr->getType()} cannot be evaluated"); - }; + return $this->inIsolation; } /** - * Silently evaluates a constant expression into a PHP value. - * - * Thrown Errors, warnings or notices will be converted into a ConstExprEvaluationException. - * The original source of the exception is available through getPrevious(). - * - * If some part of the expression cannot be evaluated, the fallback evaluator passed to the - * constructor will be invoked. By default, if no fallback is provided, an exception of type - * ConstExprEvaluationException is thrown. - * - * See class doc comment for caveats and limitations. - * - * @param Expr $expr Constant expression to evaluate - * @return mixed Result of evaluation - * - * @throws ConstExprEvaluationException if the expression cannot be evaluated or an error occurred + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function evaluateSilently(\PHPUnit\PhpParser\Node\Expr $expr) + public function getResult() { - \set_error_handler(function ($num, $str, $file, $line) { - throw new \ErrorException($str, 0, $num, $file, $line); - }); - try { - return $this->evaluate($expr); - } catch (\Throwable $e) { - if (!$e instanceof \PHPUnit\PhpParser\ConstExprEvaluationException) { - $e = new \PHPUnit\PhpParser\ConstExprEvaluationException("An error occurred during constant expression evaluation", 0, $e); - } - throw $e; - } finally { - \restore_error_handler(); - } + return $this->testResult; } /** - * Directly evaluates a constant expression into a PHP value. - * - * May generate Error exceptions, warnings or notices. Use evaluateSilently() to convert these - * into a ConstExprEvaluationException. - * - * If some part of the expression cannot be evaluated, the fallback evaluator passed to the - * constructor will be invoked. By default, if no fallback is provided, an exception of type - * ConstExprEvaluationException is thrown. - * - * See class doc comment for caveats and limitations. - * - * @param Expr $expr Constant expression to evaluate - * @return mixed Result of evaluation - * - * @throws ConstExprEvaluationException if the expression cannot be evaluated + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function evaluateDirectly(\PHPUnit\PhpParser\Node\Expr $expr) + public function setResult($result) : void { - return $this->evaluate($expr); + $this->testResult = $result; } - private function evaluate(\PHPUnit\PhpParser\Node\Expr $expr) + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function setOutputCallback(callable $callback) : void { - if ($expr instanceof \PHPUnit\PhpParser\Node\Scalar\LNumber || $expr instanceof \PHPUnit\PhpParser\Node\Scalar\DNumber || $expr instanceof \PHPUnit\PhpParser\Node\Scalar\String_) { - return $expr->value; - } - if ($expr instanceof \PHPUnit\PhpParser\Node\Expr\Array_) { - return $this->evaluateArray($expr); - } - // Unary operators - if ($expr instanceof \PHPUnit\PhpParser\Node\Expr\UnaryPlus) { - return +$this->evaluate($expr->expr); - } - if ($expr instanceof \PHPUnit\PhpParser\Node\Expr\UnaryMinus) { - return -$this->evaluate($expr->expr); - } - if ($expr instanceof \PHPUnit\PhpParser\Node\Expr\BooleanNot) { - return !$this->evaluate($expr->expr); - } - if ($expr instanceof \PHPUnit\PhpParser\Node\Expr\BitwiseNot) { - return ~$this->evaluate($expr->expr); - } - if ($expr instanceof \PHPUnit\PhpParser\Node\Expr\BinaryOp) { - return $this->evaluateBinaryOp($expr); - } - if ($expr instanceof \PHPUnit\PhpParser\Node\Expr\Ternary) { - return $this->evaluateTernary($expr); - } - if ($expr instanceof \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch && null !== $expr->dim) { - return $this->evaluate($expr->var)[$this->evaluate($expr->dim)]; - } - if ($expr instanceof \PHPUnit\PhpParser\Node\Expr\ConstFetch) { - return $this->evaluateConstFetch($expr); - } - return ($this->fallbackEvaluator)($expr); + $this->outputCallback = $callback; } - private function evaluateArray(\PHPUnit\PhpParser\Node\Expr\Array_ $expr) + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function getTestResultObject() : ?\PHPUnit\Framework\TestResult { - $array = []; - foreach ($expr->items as $item) { - if (null !== $item->key) { - $array[$this->evaluate($item->key)] = $this->evaluate($item->value); - } else { - $array[] = $this->evaluate($item->value); - } - } - return $array; + return $this->result; } - private function evaluateTernary(\PHPUnit\PhpParser\Node\Expr\Ternary $expr) + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function setTestResultObject(\PHPUnit\Framework\TestResult $result) : void { - if (null === $expr->if) { - return $this->evaluate($expr->cond) ?: $this->evaluate($expr->else); - } - return $this->evaluate($expr->cond) ? $this->evaluate($expr->if) : $this->evaluate($expr->else); + $this->result = $result; } - private function evaluateBinaryOp(\PHPUnit\PhpParser\Node\Expr\BinaryOp $expr) + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function registerMockObject(MockObject $mockObject) : void { - if ($expr instanceof \PHPUnit\PhpParser\Node\Expr\BinaryOp\Coalesce && $expr->left instanceof \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch) { - // This needs to be special cased to respect BP_VAR_IS fetch semantics - return $this->evaluate($expr->left->var)[$this->evaluate($expr->left->dim)] ?? $this->evaluate($expr->right); - } - // The evaluate() calls are repeated in each branch, because some of the operators are - // short-circuiting and evaluating the RHS in advance may be illegal in that case - $l = $expr->left; - $r = $expr->right; - switch ($expr->getOperatorSigil()) { - case '&': - return $this->evaluate($l) & $this->evaluate($r); - case '|': - return $this->evaluate($l) | $this->evaluate($r); - case '^': - return $this->evaluate($l) ^ $this->evaluate($r); - case '&&': - return $this->evaluate($l) && $this->evaluate($r); - case '||': - return $this->evaluate($l) || $this->evaluate($r); - case '??': - return $this->evaluate($l) ?? $this->evaluate($r); - case '.': - return $this->evaluate($l) . $this->evaluate($r); - case '/': - return $this->evaluate($l) / $this->evaluate($r); - case '==': - return $this->evaluate($l) == $this->evaluate($r); - case '>': - return $this->evaluate($l) > $this->evaluate($r); - case '>=': - return $this->evaluate($l) >= $this->evaluate($r); - case '===': - return $this->evaluate($l) === $this->evaluate($r); - case 'and': - return $this->evaluate($l) and $this->evaluate($r); - case 'or': - return $this->evaluate($l) or $this->evaluate($r); - case 'xor': - return $this->evaluate($l) xor $this->evaluate($r); - case '-': - return $this->evaluate($l) - $this->evaluate($r); - case '%': - return $this->evaluate($l) % $this->evaluate($r); - case '*': - return $this->evaluate($l) * $this->evaluate($r); - case '!=': - return $this->evaluate($l) != $this->evaluate($r); - case '!==': - return $this->evaluate($l) !== $this->evaluate($r); - case '+': - return $this->evaluate($l) + $this->evaluate($r); - case '**': - return $this->evaluate($l) ** $this->evaluate($r); - case '<<': - return $this->evaluate($l) << $this->evaluate($r); - case '>>': - return $this->evaluate($l) >> $this->evaluate($r); - case '<': - return $this->evaluate($l) < $this->evaluate($r); - case '<=': - return $this->evaluate($l) <= $this->evaluate($r); - case '<=>': - return $this->evaluate($l) <=> $this->evaluate($r); - } - throw new \Exception('Should not happen'); + $this->mockObjects[] = $mockObject; } - private function evaluateConstFetch(\PHPUnit\PhpParser\Node\Expr\ConstFetch $expr) + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function addToAssertionCount(int $count) : void { - $name = $expr->name->toLowerString(); - switch ($name) { - case 'null': - return null; - case 'false': - return \false; - case 'true': - return \true; - } - return ($this->fallbackEvaluator)($expr); + $this->numAssertions += $count; } -} -attributes = $attributes; - $this->type = \is_string($type) ? new \PHPUnit\PhpParser\Node\Identifier($type) : $type; + return $this->numAssertions; } - public function getSubNodeNames() : array + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function usesDataProvider() : bool { - return ['type']; + return !empty($this->data); } - public function getType() : string + /** + * @return int|string + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function dataName() { - return 'NullableType'; + return $this->dataName; } -} -data)) { + if (is_int($this->dataName)) { + $buffer .= sprintf(' with data set #%d', $this->dataName); + } else { + $buffer .= sprintf(' with data set "%s"', $this->dataName); + } + if ($includeData) { + $exporter = new Exporter(); + $buffer .= sprintf(' (%s)', $exporter->shortenedRecursiveExport($this->data)); + } + } + return $buffer; + } + /** + * Gets the data set of a TestCase. * - * @param Expr[] $parts Encaps list - * @param array $attributes Additional attributes + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function __construct(array $parts, array $attributes = []) + public function getProvidedData() : array { - $this->attributes = $attributes; - $this->parts = $parts; + return $this->data; } - public function getSubNodeNames() : array + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function addWarning(string $warning) : void { - return ['parts']; + $this->warnings[] = $warning; } - public function getType() : string + public function sortId() : string { - return 'Scalar_Encapsed'; + $id = $this->name; + if (strpos($id, '::') === \false) { + $id = static::class . '::' . $id; + } + if ($this->usesDataProvider()) { + $id .= $this->getDataSetAsString(\false); + } + return $id; } -} - '\\', '$' => '$', 'n' => "\n", 'r' => "\r", 't' => "\t", 'f' => "\f", 'v' => "\v", 'e' => "\33"]; /** - * Constructs a string scalar node. + * Returns the normalized test name as class::method. * - * @param string $value Value of the string - * @param array $attributes Additional attributes + * @return list */ - public function __construct(string $value, array $attributes = []) - { - $this->attributes = $attributes; - $this->value = $value; - } - public function getSubNodeNames() : array + public function provides() : array { - return ['value']; + return $this->providedTests; } /** - * @internal + * Returns a list of normalized dependency names, class::method. * - * Parses a string token. + * This list can differ from the raw dependencies as the resolver has + * no need for the [!][shallow]clone prefix that is filtered out + * during normalization. * - * @param string $str String token content - * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes + * @return list + */ + public function requires() : array + { + return $this->dependencies; + } + /** + * Override to run the test and assert its state. * - * @return string The parsed string + * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException + * @throws AssertionFailedError + * @throws Exception + * @throws ExpectationFailedException + * @throws Throwable */ - public static function parse(string $str, bool $parseUnicodeEscape = \true) : string + protected function runTest() { - $bLength = 0; - if ('b' === $str[0] || 'B' === $str[0]) { - $bLength = 1; + if (trim($this->name) === '') { + throw new \PHPUnit\Framework\Exception('PHPUnit\\Framework\\TestCase::$name must be a non-blank string.'); } - if ('\'' === $str[$bLength]) { - return \str_replace(['\\\\', '\\\''], ['\\', '\''], \substr($str, $bLength + 1, -1)); - } else { - return self::parseEscapeSequences(\substr($str, $bLength + 1, -1), '"', $parseUnicodeEscape); + $testArguments = array_merge($this->data, $this->dependencyInput); + $this->registerMockObjectsFromTestArguments($testArguments); + try { + $testResult = $this->{$this->name}(...array_values($testArguments)); + } catch (Throwable $exception) { + if (!$this->checkExceptionExpectations($exception)) { + throw $exception; + } + if ($this->expectedException !== null) { + if ($this->expectedException === Error::class) { + $this->assertThat($exception, LogicalOr::fromConstraints(new ExceptionConstraint(Error::class), new ExceptionConstraint(\Error::class))); + } else { + $this->assertThat($exception, new ExceptionConstraint($this->expectedException)); + } + } + if ($this->expectedExceptionMessage !== null) { + $this->assertThat($exception, new ExceptionMessage($this->expectedExceptionMessage)); + } + if ($this->expectedExceptionMessageRegExp !== null) { + $this->assertThat($exception, new ExceptionMessageRegularExpression($this->expectedExceptionMessageRegExp)); + } + if ($this->expectedExceptionCode !== null) { + $this->assertThat($exception, new ExceptionCode($this->expectedExceptionCode)); + } + return; + } + if ($this->expectedException !== null) { + $this->assertThat(null, new ExceptionConstraint($this->expectedException)); + } elseif ($this->expectedExceptionMessage !== null) { + $this->numAssertions++; + throw new \PHPUnit\Framework\AssertionFailedError(sprintf('Failed asserting that exception with message "%s" is thrown', $this->expectedExceptionMessage)); + } elseif ($this->expectedExceptionMessageRegExp !== null) { + $this->numAssertions++; + throw new \PHPUnit\Framework\AssertionFailedError(sprintf('Failed asserting that exception with message matching "%s" is thrown', $this->expectedExceptionMessageRegExp)); + } elseif ($this->expectedExceptionCode !== null) { + $this->numAssertions++; + throw new \PHPUnit\Framework\AssertionFailedError(sprintf('Failed asserting that exception with code "%s" is thrown', $this->expectedExceptionCode)); } + return $testResult; } /** - * @internal - * - * Parses escape sequences in strings (all string types apart from single quoted). - * - * @param string $str String without quotes - * @param null|string $quote Quote type - * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes + * This method is a wrapper for the ini_set() function that automatically + * resets the modified php.ini setting to its original value after the + * test is run. * - * @return string String with escape sequences parsed + * @throws Exception */ - public static function parseEscapeSequences(string $str, $quote, bool $parseUnicodeEscape = \true) : string + protected function iniSet(string $varName, string $newValue) : void { - if (null !== $quote) { - $str = \str_replace('\\' . $quote, $quote, $str); - } - $extra = ''; - if ($parseUnicodeEscape) { - $extra = '|u\\{([0-9a-fA-F]+)\\}'; + $currentValue = ini_set($varName, $newValue); + if ($currentValue !== \false) { + $this->iniSettings[$varName] = $currentValue; + } else { + throw new \PHPUnit\Framework\Exception(sprintf('INI setting "%s" could not be set to "%s".', $varName, $newValue)); } - return \preg_replace_callback('~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}' . $extra . ')~', function ($matches) { - $str = $matches[1]; - if (isset(self::$replacements[$str])) { - return self::$replacements[$str]; - } elseif ('x' === $str[0] || 'X' === $str[0]) { - return \chr(\hexdec(\substr($str, 1))); - } elseif ('u' === $str[0]) { - return self::codePointToUtf8(\hexdec($matches[2])); - } else { - return \chr(\octdec($str)); - } - }, $str); } /** - * Converts a Unicode code point to its UTF-8 encoded representation. - * - * @param int $num Code point + * This method is a wrapper for the setlocale() function that automatically + * resets the locale to its original value after the test is run. * - * @return string UTF-8 representation of code point + * @throws Exception */ - private static function codePointToUtf8(int $num) : string + protected function setLocale(...$args) : void { - if ($num <= 0x7f) { - return \chr($num); + if (count($args) < 2) { + throw new \PHPUnit\Framework\Exception(); } - if ($num <= 0x7ff) { - return \chr(($num >> 6) + 0xc0) . \chr(($num & 0x3f) + 0x80); + [$category, $locale] = $args; + if (!in_array($category, self::LOCALE_CATEGORIES, \true)) { + throw new \PHPUnit\Framework\Exception(); } - if ($num <= 0xffff) { - return \chr(($num >> 12) + 0xe0) . \chr(($num >> 6 & 0x3f) + 0x80) . \chr(($num & 0x3f) + 0x80); + if (!is_array($locale) && !is_string($locale)) { + throw new \PHPUnit\Framework\Exception(); } - if ($num <= 0x1fffff) { - return \chr(($num >> 18) + 0xf0) . \chr(($num >> 12 & 0x3f) + 0x80) . \chr(($num >> 6 & 0x3f) + 0x80) . \chr(($num & 0x3f) + 0x80); + $this->locale[$category] = setlocale($category, 0); + $result = setlocale(...$args); + if ($result === \false) { + throw new \PHPUnit\Framework\Exception('The locale functionality is not implemented on your platform, ' . 'the specified locale does not exist or the category name is ' . 'invalid.'); } - throw new \PHPUnit\PhpParser\Error('Invalid UTF-8 codepoint escape sequence: Codepoint too large'); } - public function getType() : string + /** + * Makes configurable stub for the specified class. + * + * @psalm-template RealInstanceType of object + * @psalm-param class-string $originalClassName + * @psalm-return Stub&RealInstanceType + */ + protected function createStub(string $originalClassName) : Stub { - return 'Scalar_String'; + return $this->createMockObject($originalClassName); } -} - $originalClassName + * @psalm-return MockObject&RealInstanceType + */ + protected function createMock(string $originalClassName) : MockObject { - return '__DIR__'; + return $this->createMockObject($originalClassName); } - public function getType() : string + /** + * Returns a configured mock object for the specified class. + * + * @psalm-template RealInstanceType of object + * @psalm-param class-string $originalClassName + * @psalm-return MockObject&RealInstanceType + */ + protected function createConfiguredMock(string $originalClassName, array $configuration) : MockObject { - return 'Scalar_MagicConst_Dir'; + $o = $this->createMockObject($originalClassName); + foreach ($configuration as $method => $return) { + $o->method($method)->willReturn($return); + } + return $o; } -} - $originalClassName + * @psalm-return MockObject&RealInstanceType + */ + protected function createPartialMock(string $originalClassName, array $methods) : MockObject { - return '__LINE__'; + try { + $reflector = new ReflectionClass($originalClassName); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $mockedMethodsThatDontExist = array_filter($methods, static function (string $method) use($reflector) { + return !$reflector->hasMethod($method); + }); + if ($mockedMethodsThatDontExist) { + $this->addWarning(sprintf('createPartialMock() called with method(s) %s that do not exist in %s. This will not be allowed in future versions of PHPUnit.', implode(', ', $mockedMethodsThatDontExist), $originalClassName)); + } + return $this->getMockBuilder($originalClassName)->disableOriginalConstructor()->disableOriginalClone()->disableArgumentCloning()->disallowMockingUnknownTypes()->setMethods(empty($methods) ? null : $methods)->getMock(); } - public function getType() : string + /** + * Returns a test proxy for the specified class. + * + * @psalm-template RealInstanceType of object + * @psalm-param class-string $originalClassName + * @psalm-return MockObject&RealInstanceType + */ + protected function createTestProxy(string $originalClassName, array $constructorArguments = []) : MockObject { - return 'Scalar_MagicConst_Line'; + return $this->getMockBuilder($originalClassName)->setConstructorArgs($constructorArguments)->enableProxyingToOriginalMethods()->getMock(); } -} -|string $originalClassName + * @psalm-return class-string + */ + protected function getMockClass(string $originalClassName, $methods = [], array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \false, bool $callOriginalClone = \true, bool $callAutoload = \true, bool $cloneArguments = \false) : string { - return '__NAMESPACE__'; + $this->recordDoubledType($originalClassName); + $mock = $this->getMockObjectGenerator()->getMock($originalClassName, $methods, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $cloneArguments); + return get_class($mock); } - public function getType() : string + /** + * Returns a mock object for the specified abstract class with all abstract + * methods of the class mocked. Concrete methods are not mocked by default. + * To mock concrete methods, use the 7th parameter ($mockedMethods). + * + * @psalm-template RealInstanceType of object + * @psalm-param class-string $originalClassName + * @psalm-return MockObject&RealInstanceType + */ + protected function getMockForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = [], bool $cloneArguments = \false) : MockObject { - return 'Scalar_MagicConst_Namespace'; + $this->recordDoubledType($originalClassName); + $mockObject = $this->getMockObjectGenerator()->getMockForAbstractClass($originalClassName, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); + $this->registerMockObject($mockObject); + return $mockObject; } -} -|string $originalClassName + * @psalm-return MockObject&RealInstanceType + */ + protected function getMockFromWsdl(string $wsdlFile, string $originalClassName = '', string $mockClassName = '', array $methods = [], bool $callOriginalConstructor = \true, array $options = []) : MockObject { - return '__CLASS__'; + $this->recordDoubledType(SoapClient::class); + if ($originalClassName === '') { + $fileName = pathinfo(basename(parse_url($wsdlFile, \PHP_URL_PATH)), \PATHINFO_FILENAME); + $originalClassName = preg_replace('/\\W/', '', $fileName); + } + if (!class_exists($originalClassName)) { + eval($this->getMockObjectGenerator()->generateClassFromWsdl($wsdlFile, $originalClassName, $methods, $options)); + } + $mockObject = $this->getMockObjectGenerator()->getMock($originalClassName, $methods, ['', $options], $mockClassName, $callOriginalConstructor, \false, \false); + $this->registerMockObject($mockObject); + return $mockObject; } - public function getType() : string + /** + * Returns a mock object for the specified trait with all abstract methods + * of the trait mocked. Concrete methods to mock can be specified with the + * `$mockedMethods` parameter. + * + * @psalm-param trait-string $traitName + */ + protected function getMockForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = \true, bool $callOriginalClone = \true, bool $callAutoload = \true, array $mockedMethods = [], bool $cloneArguments = \false) : MockObject { - return 'Scalar_MagicConst_Class'; + $this->recordDoubledType($traitName); + $mockObject = $this->getMockObjectGenerator()->getMockForTrait($traitName, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); + $this->registerMockObject($mockObject); + return $mockObject; } -} -recordDoubledType($traitName); + return $this->getMockObjectGenerator()->getObjectForTrait($traitName, $traitClassName, $callAutoload, $callOriginalConstructor, $arguments); } - public function getType() : string + /** + * @throws \Prophecy\Exception\Doubler\ClassNotFoundException + * @throws \Prophecy\Exception\Doubler\DoubleException + * @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException + * + * @psalm-param class-string|null $classOrInterface + */ + protected function prophesize(?string $classOrInterface = null) : ObjectProphecy { - return 'Scalar_MagicConst_Method'; + $this->addWarning('PHPUnit\\Framework\\TestCase::prophesize() is deprecated and will be removed in PHPUnit 10. Please use the trait provided by phpspec/prophecy-phpunit.'); + if (is_string($classOrInterface)) { + $this->recordDoubledType($classOrInterface); + } + return $this->getProphet()->prophesize($classOrInterface); } -} -doubledTypes[] = $originalClassName; } - public function getType() : string + /** + * @throws Throwable + */ + private function verifyMockObjects() : void { - return 'Scalar_MagicConst_File'; + foreach ($this->mockObjects as $mockObject) { + if ($mockObject->__phpunit_hasMatchers()) { + $this->numAssertions++; + } + $mockObject->__phpunit_verify($this->shouldInvocationMockerBeReset($mockObject)); + } + if ($this->prophet !== null) { + try { + $this->prophet->checkPredictions(); + } finally { + foreach ($this->prophet->getProphecies() as $objectProphecy) { + foreach ($objectProphecy->getMethodProphecies() as $methodProphecies) { + foreach ($methodProphecies as $methodProphecy) { + /* @var MethodProphecy $methodProphecy */ + $this->numAssertions += count($methodProphecy->getCheckedPredictions()); + } + } + } + } + } } -} -name || !method_exists($this, $this->name)) { + return; + } + $missingRequirements = TestUtil::getMissingRequirements(static::class, $this->name); + if (!empty($missingRequirements)) { + $this->markTestSkipped(implode(\PHP_EOL, $missingRequirements)); + } + } + private function handleDependencies() : bool + { + if ([] === $this->dependencies || $this->inIsolation) { + return \true; + } + $passed = $this->result->passed(); + $passedKeys = array_keys($passed); + $numKeys = count($passedKeys); + for ($i = 0; $i < $numKeys; $i++) { + $pos = strpos($passedKeys[$i], ' with data set'); + if ($pos !== \false) { + $passedKeys[$i] = substr($passedKeys[$i], 0, $pos); + } + } + $passedKeys = array_flip(array_unique($passedKeys)); + foreach ($this->dependencies as $dependency) { + if (!$dependency->isValid()) { + $this->markSkippedForNotSpecifyingDependency(); + return \false; + } + if ($dependency->targetIsClass()) { + $dependencyClassName = $dependency->getTargetClassName(); + if (array_search($dependencyClassName, $this->result->passedClasses(), \true) === \false) { + $this->markSkippedForMissingDependency($dependency); + return \false; + } + continue; + } + $dependencyTarget = $dependency->getTarget(); + if (!isset($passedKeys[$dependencyTarget])) { + if (!$this->isCallableTestMethod($dependencyTarget)) { + $this->markWarningForUncallableDependency($dependency); + } else { + $this->markSkippedForMissingDependency($dependency); + } + return \false; + } + if (isset($passed[$dependencyTarget])) { + if ($passed[$dependencyTarget]['size'] != \PHPUnit\Util\Test::UNKNOWN && $this->getSize() != \PHPUnit\Util\Test::UNKNOWN && $passed[$dependencyTarget]['size'] > $this->getSize()) { + $this->result->addError($this, new \PHPUnit\Framework\SkippedTestError('This test depends on a test that is larger than itself.'), 0); + return \false; + } + if ($dependency->useDeepClone()) { + $deepCopy = new DeepCopy(); + $deepCopy->skipUncloneable(\false); + $this->dependencyInput[$dependencyTarget] = $deepCopy->copy($passed[$dependencyTarget]['result']); + } elseif ($dependency->useShallowClone()) { + $this->dependencyInput[$dependencyTarget] = clone $passed[$dependencyTarget]['result']; + } else { + $this->dependencyInput[$dependencyTarget] = $passed[$dependencyTarget]['result']; + } + } else { + $this->dependencyInput[$dependencyTarget] = null; + } + } + return \true; + } + private function markSkippedForNotSpecifyingDependency() : void { - $this->attributes = $attributes; - $this->value = $value; + $this->status = BaseTestRunner::STATUS_SKIPPED; + $this->result->startTest($this); + $this->result->addError($this, new \PHPUnit\Framework\SkippedTestError('This method has an invalid @depends annotation.'), 0); + $this->result->endTest($this, 0); } - public function getSubNodeNames() : array + private function markSkippedForMissingDependency(\PHPUnit\Framework\ExecutionOrderDependency $dependency) : void { - return ['value']; + $this->status = BaseTestRunner::STATUS_SKIPPED; + $this->result->startTest($this); + $this->result->addError($this, new \PHPUnit\Framework\SkippedTestError(sprintf('This test depends on "%s" to pass.', $dependency->getTarget())), 0); + $this->result->endTest($this, 0); } - public function getType() : string + private function markWarningForUncallableDependency(\PHPUnit\Framework\ExecutionOrderDependency $dependency) : void { - return 'Scalar_EncapsedStringPart'; + $this->status = BaseTestRunner::STATUS_WARNING; + $this->result->startTest($this); + $this->result->addWarning($this, new \PHPUnit\Framework\Warning(sprintf('This test depends on "%s" which does not exist.', $dependency->getTarget())), 0); + $this->result->endTest($this, 0); } -} -attributes = $attributes; - $this->value = $value; + if ($this->mockObjectGenerator === null) { + $this->mockObjectGenerator = new MockGenerator(); + } + return $this->mockObjectGenerator; } - public function getSubNodeNames() : array + private function startOutputBuffering() : void { - return ['value']; + ob_start(); + $this->outputBufferingActive = \true; + $this->outputBufferingLevel = ob_get_level(); } /** - * @internal - * - * Parses a DNUMBER token like PHP would. - * - * @param string $str A string number - * - * @return float The parsed number + * @throws RiskyTestError */ - public static function parse(string $str) : float + private function stopOutputBuffering() : void { - $str = \str_replace('_', '', $str); - // if string contains any of .eE just cast it to float - if (\false !== \strpbrk($str, '.eE')) { - return (float) $str; - } - // otherwise it's an integer notation that overflowed into a float - // if it starts with 0 it's one of the special integer notations - if ('0' === $str[0]) { - // hex - if ('x' === $str[1] || 'X' === $str[1]) { - return \hexdec($str); - } - // bin - if ('b' === $str[1] || 'B' === $str[1]) { - return \bindec($str); + if (ob_get_level() !== $this->outputBufferingLevel) { + while (ob_get_level() >= $this->outputBufferingLevel) { + ob_end_clean(); } - // oct - // substr($str, 0, strcspn($str, '89')) cuts the string at the first invalid digit (8 or 9) - // so that only the digits before that are used - return \octdec(\substr($str, 0, \strcspn($str, '89'))); + throw new \PHPUnit\Framework\RiskyTestError('Test code or tested code did not (only) close its own output buffers'); } - // dec - return (float) $str; + $this->output = ob_get_contents(); + if ($this->outputCallback !== \false) { + $this->output = (string) call_user_func($this->outputCallback, $this->output); + } + ob_end_clean(); + $this->outputBufferingActive = \false; + $this->outputBufferingLevel = ob_get_level(); } - public function getType() : string + private function snapshotGlobalState() : void { - return 'Scalar_DNumber'; + if ($this->runTestInSeparateProcess || $this->inIsolation || !$this->backupGlobals && !$this->backupStaticAttributes) { + return; + } + $this->snapshot = $this->createGlobalStateSnapshot($this->backupGlobals === \true); } -} -attributes = $attributes; + if (!$this->snapshot instanceof Snapshot) { + return; + } + if ($this->beStrictAboutChangesToGlobalState) { + try { + $this->compareGlobalStateSnapshots($this->snapshot, $this->createGlobalStateSnapshot($this->backupGlobals === \true)); + } catch (\PHPUnit\Framework\RiskyTestError $rte) { + // Intentionally left empty + } + } + $restorer = new Restorer(); + if ($this->backupGlobals) { + $restorer->restoreGlobalVariables($this->snapshot); + } + if ($this->backupStaticAttributes) { + $restorer->restoreStaticAttributes($this->snapshot); + } + $this->snapshot = null; + if (isset($rte)) { + throw $rte; + } } - public function getSubNodeNames() : array + private function createGlobalStateSnapshot(bool $backupGlobals) : Snapshot { - return []; + $excludeList = new ExcludeList(); + foreach ($this->backupGlobalsExcludeList as $globalVariable) { + $excludeList->addGlobalVariable($globalVariable); + } + if (!empty($this->backupGlobalsBlacklist)) { + $this->addWarning('PHPUnit\\Framework\\TestCase::$backupGlobalsBlacklist is deprecated and will be removed in PHPUnit 10. Please use PHPUnit\\Framework\\TestCase::$backupGlobalsExcludeList instead.'); + foreach ($this->backupGlobalsBlacklist as $globalVariable) { + $excludeList->addGlobalVariable($globalVariable); + } + } + if (!defined('PHPUNIT_TESTSUITE')) { + $excludeList->addClassNamePrefix('PHPUnit'); + $excludeList->addClassNamePrefix('PHPUnit\\SebastianBergmann\\CodeCoverage'); + $excludeList->addClassNamePrefix('PHPUnit\\SebastianBergmann\\FileIterator'); + $excludeList->addClassNamePrefix('PHPUnit\\SebastianBergmann\\Invoker'); + $excludeList->addClassNamePrefix('PHPUnit\\SebastianBergmann\\Template'); + $excludeList->addClassNamePrefix('PHPUnit\\SebastianBergmann\\Timer'); + $excludeList->addClassNamePrefix('Symfony'); + $excludeList->addClassNamePrefix('PHPUnit\\Doctrine\\Instantiator'); + $excludeList->addClassNamePrefix('Prophecy'); + $excludeList->addStaticAttribute(ComparatorFactory::class, 'instance'); + foreach ($this->backupStaticAttributesExcludeList as $class => $attributes) { + foreach ($attributes as $attribute) { + $excludeList->addStaticAttribute($class, $attribute); + } + } + if (!empty($this->backupStaticAttributesBlacklist)) { + $this->addWarning('PHPUnit\\Framework\\TestCase::$backupStaticAttributesBlacklist is deprecated and will be removed in PHPUnit 10. Please use PHPUnit\\Framework\\TestCase::$backupStaticAttributesExcludeList instead.'); + foreach ($this->backupStaticAttributesBlacklist as $class => $attributes) { + foreach ($attributes as $attribute) { + $excludeList->addStaticAttribute($class, $attribute); + } + } + } + } + return new Snapshot($excludeList, $backupGlobals, (bool) $this->backupStaticAttributes, \false, \false, \false, \false, \false, \false, \false); } /** - * Get name of magic constant. - * - * @return string Name of magic constant + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws RiskyTestError */ - public abstract function getName() : string; -} -backupGlobals === null || $this->backupGlobals; + if ($backupGlobals) { + $this->compareGlobalStateSnapshotPart($before->globalVariables(), $after->globalVariables(), "--- Global variables before the test\n+++ Global variables after the test\n"); + $this->compareGlobalStateSnapshotPart($before->superGlobalVariables(), $after->superGlobalVariables(), "--- Super-global variables before the test\n+++ Super-global variables after the test\n"); + } + if ($this->backupStaticAttributes) { + $this->compareGlobalStateSnapshotPart($before->staticAttributes(), $after->staticAttributes(), "--- Static attributes before the test\n+++ Static attributes after the test\n"); + } + } /** - * Constructs an integer number scalar node. - * - * @param int $value Value of the number - * @param array $attributes Additional attributes + * @throws RiskyTestError */ - public function __construct(int $value, array $attributes = []) + private function compareGlobalStateSnapshotPart(array $before, array $after, string $header) : void { - $this->attributes = $attributes; - $this->value = $value; + if ($before != $after) { + $differ = new Differ($header); + $exporter = new Exporter(); + $diff = $differ->diff($exporter->export($before), $exporter->export($after)); + throw new \PHPUnit\Framework\RiskyTestError($diff); + } } - public function getSubNodeNames() : array + private function getProphet() : Prophet { - return ['value']; + if ($this->prophet === null) { + $this->prophet = new Prophet(); + } + return $this->prophet; } /** - * Constructs an LNumber node from a string number literal. - * - * @param string $str String number literal (decimal, octal, hex or binary) - * @param array $attributes Additional attributes - * @param bool $allowInvalidOctal Whether to allow invalid octal numbers (PHP 5) - * - * @return LNumber The constructed LNumber, including kind attribute + * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException */ - public static function fromString(string $str, array $attributes = [], bool $allowInvalidOctal = \false) : \PHPUnit\PhpParser\Node\Scalar\LNumber + private function shouldInvocationMockerBeReset(MockObject $mock) : bool { - $str = \str_replace('_', '', $str); - if ('0' !== $str[0] || '0' === $str) { - $attributes['kind'] = \PHPUnit\PhpParser\Node\Scalar\LNumber::KIND_DEC; - return new \PHPUnit\PhpParser\Node\Scalar\LNumber((int) $str, $attributes); - } - if ('x' === $str[1] || 'X' === $str[1]) { - $attributes['kind'] = \PHPUnit\PhpParser\Node\Scalar\LNumber::KIND_HEX; - return new \PHPUnit\PhpParser\Node\Scalar\LNumber(\hexdec($str), $attributes); + $enumerator = new Enumerator(); + foreach ($enumerator->enumerate($this->dependencyInput) as $object) { + if ($mock === $object) { + return \false; + } } - if ('b' === $str[1] || 'B' === $str[1]) { - $attributes['kind'] = \PHPUnit\PhpParser\Node\Scalar\LNumber::KIND_BIN; - return new \PHPUnit\PhpParser\Node\Scalar\LNumber(\bindec($str), $attributes); + if (!is_array($this->testResult) && !is_object($this->testResult)) { + return \true; } - if (!$allowInvalidOctal && \strpbrk($str, '89')) { - throw new \PHPUnit\PhpParser\Error('Invalid numeric literal', $attributes); + return !in_array($mock, $enumerator->enumerate($this->testResult), \true); + } + /** + * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException + * @throws \SebastianBergmann\ObjectReflector\InvalidArgumentException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + private function registerMockObjectsFromTestArguments(array $testArguments, array &$visited = []) : void + { + if ($this->registerMockObjectsFromTestArgumentsRecursively) { + foreach ((new Enumerator())->enumerate($testArguments) as $object) { + if ($object instanceof MockObject) { + $this->registerMockObject($object); + } + } + } else { + foreach ($testArguments as $testArgument) { + if ($testArgument instanceof MockObject) { + if (Type::isCloneable($testArgument)) { + $testArgument = clone $testArgument; + } + $this->registerMockObject($testArgument); + } elseif (is_array($testArgument) && !in_array($testArgument, $visited, \true)) { + $visited[] = $testArgument; + $this->registerMockObjectsFromTestArguments($testArgument, $visited); + } + } } - // use intval instead of octdec to get proper cutting behavior with malformed numbers - $attributes['kind'] = \PHPUnit\PhpParser\Node\Scalar\LNumber::KIND_OCT; - return new \PHPUnit\PhpParser\Node\Scalar\LNumber(\intval($str, 8), $attributes); } - public function getType() : string + private function setDoesNotPerformAssertionsFromAnnotation() : void { - return 'Scalar_LNumber'; + $annotations = TestUtil::parseTestMethodAnnotations(static::class, $this->name); + if (isset($annotations['method']['doesNotPerformAssertions'])) { + $this->doesNotPerformAssertions = \true; + } } -} -customComparators as $comparator) { + $factory->unregister($comparator); + } + $this->customComparators = []; } -} -attributes = $attributes; - $this->left = $left; - $this->right = $right; + foreach ($this->iniSettings as $varName => $oldValue) { + ini_set($varName, $oldValue); + } + $this->iniSettings = []; } - public function getSubNodeNames() : array + private function cleanupLocaleSettings() : void { - return ['left', 'right']; + foreach ($this->locale as $category => $locale) { + setlocale($category, $locale); + } + $this->locale = []; } /** - * Get the operator sigil for this binary operation. - * - * In the case there are multiple possible sigils for an operator, this method does not - * necessarily return the one used in the parsed code. - * - * @return string - */ - public abstract function getOperatorSigil() : string; -} -attributes = $attributes; - $this->class = $class; - $this->name = \is_string($name) ? new \PHPUnit\PhpParser\Node\Identifier($name) : $name; - $this->args = $args; + $result = \false; + if ($this->expectedException !== null || $this->expectedExceptionCode !== null || $this->expectedExceptionMessage !== null || $this->expectedExceptionMessageRegExp !== null) { + $result = \true; + } + if ($throwable instanceof \PHPUnit\Framework\Exception) { + $result = \false; + } + if (is_string($this->expectedException)) { + try { + $reflector = new ReflectionClass($this->expectedException); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + if ($this->expectedException === 'PHPUnit\\Framework\\Exception' || $this->expectedException === '\\PHPUnit\\Framework\\Exception' || $reflector->isSubclassOf(\PHPUnit\Framework\Exception::class)) { + $result = \true; + } + } + return $result; } - public function getSubNodeNames() : array + private function runInSeparateProcess() : bool { - return ['class', 'name', 'args']; + return ($this->runTestInSeparateProcess || $this->runClassInSeparateProcess) && !$this->inIsolation && !$this instanceof PhptTestCase; } - public function getType() : string + private function isCallableTestMethod(string $dependency) : bool { - return 'Expr_StaticCall'; + [$className, $methodName] = explode('::', $dependency); + if (!class_exists($className)) { + return \false; + } + try { + $class = new ReflectionClass($className); + } catch (ReflectionException $e) { + return \false; + } + if (!$class->isSubclassOf(__CLASS__)) { + return \false; + } + if (!$class->hasMethod($methodName)) { + return \false; + } + try { + $method = $class->getMethod($methodName); + } catch (ReflectionException $e) { + return \false; + } + return TestUtil::isTestMethod($method); + } + /** + * @psalm-template RealInstanceType of object + * @psalm-param class-string $originalClassName + * @psalm-return MockObject&RealInstanceType + */ + private function createMockObject(string $originalClassName) : MockObject + { + return $this->getMockBuilder($originalClassName)->disableOriginalConstructor()->disableOriginalClone()->disableArgumentCloning()->disallowMockingUnknownTypes()->getMock(); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; -use PHPUnit\PhpParser\Node\Expr; -class Include_ extends \PHPUnit\PhpParser\Node\Expr +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class WarningTestCase extends \PHPUnit\Framework\TestCase { - const TYPE_INCLUDE = 1; - const TYPE_INCLUDE_ONCE = 2; - const TYPE_REQUIRE = 3; - const TYPE_REQUIRE_ONCE = 4; - /** @var Expr Expression */ - public $expr; - /** @var int Type of include */ - public $type; /** - * Constructs an include node. - * - * @param Expr $expr Expression - * @param int $type Type of include - * @param array $attributes Additional attributes + * @var bool + */ + protected $backupGlobals = \false; + /** + * @var bool + */ + protected $backupStaticAttributes = \false; + /** + * @var bool + */ + protected $runTestInSeparateProcess = \false; + /** + * @var string */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $expr, int $type, array $attributes = []) + private $message; + public function __construct(string $message = '') { - $this->attributes = $attributes; - $this->expr = $expr; - $this->type = $type; + $this->message = $message; + parent::__construct('Warning'); } - public function getSubNodeNames() : array + public function getMessage() : string { - return ['expr', 'type']; + return $this->message; } - public function getType() : string + /** + * Returns a string representation of the test case. + */ + public function toString() : string { - return 'Expr_Include'; + return 'Warning'; } -} -attributes = $attributes; - $this->var = $var; - $this->dim = $dim; - } - public function getSubNodeNames() : array - { - return ['var', 'dim']; - } - public function getType() : string + protected function runTest() : void { - return 'Expr_ArrayDimFetch'; + throw new \PHPUnit\Framework\Warning($this->message); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; -use PHPUnit\PhpParser\Node\Expr; -class Eval_ extends \PHPUnit\PhpParser\Node\Expr -{ - /** @var Expr Expression */ - public $expr; - /** - * Constructs an eval() node. - * - * @param Expr $expr Expression - * @param array $attributes Additional attributes - */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $expr, array $attributes = []) - { - $this->attributes = $attributes; - $this->expr = $expr; - } - public function getSubNodeNames() : array - { - return ['expr']; - } - public function getType() : string - { - return 'Expr_Eval'; - } +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvalidParameterGroupException extends \PHPUnit\Framework\Exception +{ } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Expr; -class FuncCall extends \PHPUnit\PhpParser\Node\Expr +use function sprintf; +use function strpos; +use Throwable; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class ExceptionMessage extends \PHPUnit\Framework\Constraint\Constraint { - /** @var Node\Name|Expr Function name */ - public $name; - /** @var Node\Arg[] Arguments */ - public $args; /** - * Constructs a function call node. - * - * @param Node\Name|Expr $name Function name - * @param Node\Arg[] $args Arguments - * @param array $attributes Additional attributes + * @var string */ - public function __construct($name, array $args = [], array $attributes = []) - { - $this->attributes = $attributes; - $this->name = $name; - $this->args = $args; - } - public function getSubNodeNames() : array + private $expectedMessage; + public function __construct(string $expected) { - return ['name', 'args']; + $this->expectedMessage = $expected; } - public function getType() : string + public function toString() : string { - return 'Expr_FuncCall'; + if ($this->expectedMessage === '') { + return 'exception message is empty'; + } + return 'exception message contains '; } -} -attributes = $attributes; - $this->vars = $vars; - } - public function getSubNodeNames() : array + protected function matches($other) : bool { - return ['vars']; + if ($this->expectedMessage === '') { + return $other->getMessage() === ''; + } + return strpos((string) $other->getMessage(), $this->expectedMessage) !== \false; } - public function getType() : string + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other) : string { - return 'Expr_Isset'; + if ($this->expectedMessage === '') { + return sprintf("exception message is empty but is '%s'", $other->getMessage()); + } + return sprintf("exception message '%s' contains '%s'", $other->getMessage(), $this->expectedMessage); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; +use function sprintf; +use Throwable; /** - * Error node used during parsing with error recovery. - * - * An error node may be placed at a position where an expression is required, but an error occurred. - * Error nodes will not be present if the parser is run in throwOnError mode (the default). + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -class Error extends \PHPUnit\PhpParser\Node\Expr +final class ExceptionCode extends \PHPUnit\Framework\Constraint\Constraint { /** - * Constructs an error node. - * - * @param array $attributes Additional attributes + * @var int|string */ - public function __construct(array $attributes = []) - { - $this->attributes = $attributes; - } - public function getSubNodeNames() : array + private $expectedCode; + /** + * @param int|string $expected + */ + public function __construct($expected) { - return []; + $this->expectedCode = $expected; } - public function getType() : string + public function toString() : string { - return 'Expr_Error'; + return 'exception code is '; } -} -attributes = $attributes; - $this->class = $class; - $this->name = \is_string($name) ? new \PHPUnit\PhpParser\Node\VarLikeIdentifier($name) : $name; - } - public function getSubNodeNames() : array + protected function matches($other) : bool { - return ['class', 'name']; + return (string) $other->getCode() === (string) $this->expectedCode; } - public function getType() : string + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other) : string { - return 'Expr_StaticPropertyFetch'; + return sprintf('%s is equal to expected exception code %s', $this->exporter()->export($other->getCode()), $this->exporter()->export($this->expectedCode)); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node; -class Throw_ extends \PHPUnit\PhpParser\Node\Expr +use function sprintf; +use Exception; +use PHPUnit\Util\RegularExpression as RegularExpressionUtil; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class ExceptionMessageRegularExpression extends \PHPUnit\Framework\Constraint\Constraint { - /** @var Node\Expr Expression */ - public $expr; /** - * Constructs a throw expression node. - * - * @param Node\Expr $expr Expression - * @param array $attributes Additional attributes + * @var string */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $expr, array $attributes = []) - { - $this->attributes = $attributes; - $this->expr = $expr; - } - public function getSubNodeNames() : array + private $expectedMessageRegExp; + public function __construct(string $expected) { - return ['expr']; + $this->expectedMessageRegExp = $expected; } - public function getType() : string + public function toString() : string { - return 'Expr_Throw'; + return 'exception message matches '; } -} -attributes = $attributes; - $this->key = $key; - $this->value = $value; - $this->byRef = $byRef; - $this->unpack = $unpack; - } - public function getSubNodeNames() : array + protected function matches($other) : bool { - return ['key', 'value', 'byRef', 'unpack']; + $match = RegularExpressionUtil::safeMatch($this->expectedMessageRegExp, $other->getMessage()); + if ($match === \false) { + throw new \PHPUnit\Framework\Exception("Invalid expected exception message regex given: '{$this->expectedMessageRegExp}'"); + } + return $match === 1; } - public function getType() : string + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other) : string { - return 'Expr_ArrayItem'; + return sprintf("exception message '%s' matches '%s'", $other->getMessage(), $this->expectedMessageRegExp); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -class BooleanNot extends \PHPUnit\PhpParser\Node\Expr +use function get_class; +use function sprintf; +use PHPUnit\Util\Filter; +use Throwable; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Exception extends \PHPUnit\Framework\Constraint\Constraint { - /** @var Expr Expression */ - public $expr; /** - * Constructs a boolean not node. - * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @var string + */ + private $className; + public function __construct(string $className) + { + $this->className = $className; + } + /** + * Returns a string representation of the constraint. */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $expr, array $attributes = []) + public function toString() : string { - $this->attributes = $attributes; - $this->expr = $expr; + return sprintf('exception of type "%s"', $this->className); } - public function getSubNodeNames() : array + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return ['expr']; + return $other instanceof $this->className; } - public function getType() : string + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other) : string { - return 'Expr_BooleanNot'; + if ($other !== null) { + $message = ''; + if ($other instanceof Throwable) { + $message = '. Message was: "' . $other->getMessage() . '" at' . "\n" . Filter::getFilteredStacktrace($other); + } + return sprintf('exception of type "%s" matches expected exception "%s"%s', get_class($other), $this->className, $message); + } + return sprintf('exception of type "%s" is thrown', $this->className); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -use PHPUnit\PhpParser\Node\Identifier; -class NullsafePropertyFetch extends \PHPUnit\PhpParser\Node\Expr +use function array_map; +use function array_values; +use function count; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +abstract class BinaryOperator extends \PHPUnit\Framework\Constraint\Operator { - /** @var Expr Variable holding object */ - public $var; - /** @var Identifier|Expr Property name */ - public $name; /** - * Constructs a nullsafe property fetch node. - * - * @param Expr $var Variable holding object - * @param string|Identifier|Expr $name Property name - * @param array $attributes Additional attributes + * @var Constraint[] */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $var, $name, array $attributes = []) + private $constraints = []; + public static function fromConstraints(\PHPUnit\Framework\Constraint\Constraint ...$constraints) : self { - $this->attributes = $attributes; - $this->var = $var; - $this->name = \is_string($name) ? new \PHPUnit\PhpParser\Node\Identifier($name) : $name; + $constraint = new static(); + $constraint->constraints = $constraints; + return $constraint; } - public function getSubNodeNames() : array + /** + * @param mixed[] $constraints + */ + public function setConstraints(array $constraints) : void { - return ['var', 'name']; + $this->constraints = array_map(function ($constraint) : Constraint { + return $this->checkConstraint($constraint); + }, array_values($constraints)); } - public function getType() : string + /** + * Returns the number of operands (constraints). + */ + public final function arity() : int { - return 'Expr_NullsafePropertyFetch'; + return count($this->constraints); } -} -attributes = $attributes; - $this->parts = $parts; + $reduced = $this->reduce(); + if ($reduced !== $this) { + return $reduced->toString(); + } + $text = ''; + foreach ($this->constraints as $key => $constraint) { + $constraint = $constraint->reduce(); + $text .= $this->constraintToString($constraint, $key); + } + return $text; } - public function getSubNodeNames() : array + /** + * Counts the number of constraint elements. + */ + public function count() : int { - return ['parts']; + $count = 0; + foreach ($this->constraints as $constraint) { + $count += count($constraint); + } + return $count; } - public function getType() : string + /** + * Returns the nested constraints. + */ + protected final function constraints() : array { - return 'Expr_ShellExec'; + return $this->constraints; } -} -attributes = $attributes; - $this->var = $var; - $this->name = \is_string($name) ? new \PHPUnit\PhpParser\Node\Identifier($name) : $name; + return $this->arity() > 1 && parent::constraintNeedsParentheses($constraint); } - public function getSubNodeNames() : array + /** + * Reduces the sub-expression starting at $this by skipping degenerate + * sub-expression and returns first descendant constraint that starts + * a non-reducible sub-expression. + * + * See Constraint::reduce() for more. + */ + protected function reduce() : \PHPUnit\Framework\Constraint\Constraint { - return ['var', 'name']; + if ($this->arity() === 1 && $this->constraints[0] instanceof \PHPUnit\Framework\Constraint\Operator) { + return $this->constraints[0]->reduce(); + } + return parent::reduce(); } - public function getType() : string + /** + * Returns string representation of given operand in context of this operator. + * + * @param Constraint $constraint operand constraint + * @param int $position position of $constraint in this expression + */ + private function constraintToString(\PHPUnit\Framework\Constraint\Constraint $constraint, int $position) : string { - return 'Expr_PropertyFetch'; + $prefix = ''; + if ($position > 0) { + $prefix = ' ' . $this->operator() . ' '; + } + if ($this->constraintNeedsParentheses($constraint)) { + return $prefix . '( ' . $constraint->toString() . ' )'; + } + $string = $constraint->toStringInContext($this, $position); + if ($string === '') { + $string = $constraint->toString(); + } + return $prefix . $string; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -class YieldFrom extends \PHPUnit\PhpParser\Node\Expr +use function array_reduce; +use function array_shift; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class LogicalXor extends \PHPUnit\Framework\Constraint\BinaryOperator { - /** @var Expr Expression to yield from */ - public $expr; /** - * Constructs an "yield from" node. - * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * Returns the name of this operator. */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $expr, array $attributes = []) + public function operator() : string { - $this->attributes = $attributes; - $this->expr = $expr; + return 'xor'; } - public function getSubNodeNames() : array + /** + * Returns this operator's precedence. + * + * @see https://www.php.net/manual/en/language.operators.precedence.php. + */ + public function precedence() : int { - return ['expr']; + return 23; } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + public function matches($other) : bool { - return 'Expr_YieldFrom'; + $constraints = $this->constraints(); + $initial = array_shift($constraints); + if ($initial === null) { + return \false; + } + return array_reduce($constraints, static function (bool $matches, \PHPUnit\Framework\Constraint\Constraint $constraint) use($other) : bool { + return $matches xor $constraint->evaluate($other, '', \true); + }, $initial->evaluate($other, '', \true)); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Expr; -use PHPUnit\PhpParser\Node\FunctionLike; -class ArrowFunction extends \PHPUnit\PhpParser\Node\Expr implements \PHPUnit\PhpParser\Node\FunctionLike +use function array_map; +use function count; +use function preg_match; +use function preg_quote; +use function preg_replace; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class LogicalNot extends \PHPUnit\Framework\Constraint\UnaryOperator { - /** @var bool */ - public $static; - /** @var bool */ - public $byRef; - /** @var Node\Param[] */ - public $params = []; - /** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType */ - public $returnType; - /** @var Expr */ - public $expr; - /** @var Node\AttributeGroup[] */ - public $attrGroups; - /** - * @param array $subNodes Array of the following optional subnodes: - * 'static' => false : Whether the closure is static - * 'byRef' => false : Whether to return by reference - * 'params' => array() : Parameters - * 'returnType' => null : Return type - * 'expr' => Expr : Expression body - * 'attrGroups' => array() : PHP attribute groups - * @param array $attributes Additional attributes - */ - public function __construct(array $subNodes = [], array $attributes = []) - { - $this->attributes = $attributes; - $this->static = $subNodes['static'] ?? \false; - $this->byRef = $subNodes['byRef'] ?? \false; - $this->params = $subNodes['params'] ?? []; - $returnType = $subNodes['returnType'] ?? null; - $this->returnType = \is_string($returnType) ? new \PHPUnit\PhpParser\Node\Identifier($returnType) : $returnType; - $this->expr = $subNodes['expr'] ?? null; - $this->attrGroups = $subNodes['attrGroups'] ?? []; - } - public function getSubNodeNames() : array - { - return ['attrGroups', 'static', 'byRef', 'params', 'returnType', 'expr']; - } - public function returnsByRef() : bool + public static function negate(string $string) : string { - return $this->byRef; + $positives = ['contains ', 'exists', 'has ', 'is ', 'are ', 'matches ', 'starts with ', 'ends with ', 'reference ', 'not not ']; + $negatives = ['does not contain ', 'does not exist', 'does not have ', 'is not ', 'are not ', 'does not match ', 'starts not with ', 'ends not with ', 'don\'t reference ', 'not ']; + preg_match('/(\'[\\w\\W]*\')([\\w\\W]*)("[\\w\\W]*")/i', $string, $matches); + $positives = array_map(static function (string $s) { + return '/\\b' . preg_quote($s, '/') . '/'; + }, $positives); + if (count($matches) > 0) { + $nonInput = $matches[2]; + $negatedString = preg_replace('/' . preg_quote($nonInput, '/') . '/', preg_replace($positives, $negatives, $nonInput), $string); + } else { + $negatedString = preg_replace($positives, $negatives, $string); + } + return $negatedString; } - public function getParams() : array + /** + * Returns the name of this operator. + */ + public function operator() : string { - return $this->params; + return 'not'; } - public function getReturnType() + /** + * Returns this operator's precedence. + * + * @see https://www.php.net/manual/en/language.operators.precedence.php + */ + public function precedence() : int { - return $this->returnType; + return 5; } - public function getAttrGroups() : array + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return $this->attrGroups; + return !$this->constraint()->evaluate($other, '', \true); } /** - * @return Node\Stmt\Return_[] + * Applies additional transformation to strings returned by toString() or + * failureDescription(). */ - public function getStmts() : array + protected function transformString(string $string) : string { - return [new \PHPUnit\PhpParser\Node\Stmt\Return_($this->expr)]; + return self::negate($string); } - public function getType() : string + /** + * Reduces the sub-expression starting at $this by skipping degenerate + * sub-expression and returns first descendant constraint that starts + * a non-reducible sub-expression. + * + * See Constraint::reduce() for more. + */ + protected function reduce() : \PHPUnit\Framework\Constraint\Constraint { - return 'Expr_ArrowFunction'; + $constraint = $this->constraint(); + if ($constraint instanceof self) { + return $constraint->constraint()->reduce(); + } + return parent::reduce(); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -use PHPUnit\PhpParser\Node\Name; -class ConstFetch extends \PHPUnit\PhpParser\Node\Expr +use function count; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +abstract class UnaryOperator extends \PHPUnit\Framework\Constraint\Operator { - /** @var Name Constant name */ - public $name; /** - * Constructs a const fetch node. - * - * @param Name $name Constant name - * @param array $attributes Additional attributes + * @var Constraint */ - public function __construct(\PHPUnit\PhpParser\Node\Name $name, array $attributes = []) - { - $this->attributes = $attributes; - $this->name = $name; - } - public function getSubNodeNames() : array + private $constraint; + /** + * @param Constraint|mixed $constraint + */ + public function __construct($constraint) { - return ['name']; + $this->constraint = $this->checkConstraint($constraint); } - public function getType() : string + /** + * Returns the number of operands (constraints). + */ + public function arity() : int { - return 'Expr_ConstFetch'; + return 1; } -} -attributes = $attributes; - $this->name = $name; + $reduced = $this->reduce(); + if ($reduced !== $this) { + return $reduced->toString(); + } + $constraint = $this->constraint->reduce(); + if ($this->constraintNeedsParentheses($constraint)) { + return $this->operator() . '( ' . $constraint->toString() . ' )'; + } + $string = $constraint->toStringInContext($this, 0); + if ($string === '') { + return $this->transformString($constraint->toString()); + } + return $string; } - public function getSubNodeNames() : array + /** + * Counts the number of constraint elements. + */ + public function count() : int { - return ['name']; + return count($this->constraint); } - public function getType() : string + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other) : string { - return 'Expr_Variable'; + $reduced = $this->reduce(); + if ($reduced !== $this) { + return $reduced->failureDescription($other); + } + $constraint = $this->constraint->reduce(); + if ($this->constraintNeedsParentheses($constraint)) { + return $this->operator() . '( ' . $constraint->failureDescription($other) . ' )'; + } + $string = $constraint->failureDescriptionInContext($this, 0, $other); + if ($string === '') { + return $this->transformString($constraint->failureDescription($other)); + } + return $string; } -} -attributes = $attributes; - $this->cond = $cond; - $this->if = $if; - $this->else = $else; + return $string; } - public function getSubNodeNames() : array + /** + * Provides access to $this->constraint for subclasses. + */ + protected final function constraint() : \PHPUnit\Framework\Constraint\Constraint { - return ['cond', 'if', 'else']; + return $this->constraint; } - public function getType() : string + /** + * Returns true if the $constraint needs to be wrapped with parentheses. + */ + protected function constraintNeedsParentheses(\PHPUnit\Framework\Constraint\Constraint $constraint) : bool { - return 'Expr_Ternary'; + $constraint = $constraint->reduce(); + return $constraint instanceof self || parent::constraintNeedsParentheses($constraint); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -class Empty_ extends \PHPUnit\PhpParser\Node\Expr +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +abstract class Operator extends \PHPUnit\Framework\Constraint\Constraint { - /** @var Expr Expression */ - public $expr; /** - * Constructs an empty() node. + * Returns the name of this operator. + */ + public abstract function operator() : string; + /** + * Returns this operator's precedence. * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @see https://www.php.net/manual/en/language.operators.precedence.php */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $expr, array $attributes = []) - { - $this->attributes = $attributes; - $this->expr = $expr; - } - public function getSubNodeNames() : array + public abstract function precedence() : int; + /** + * Returns the number of operands. + */ + public abstract function arity() : int; + /** + * Validates $constraint argument. + */ + protected function checkConstraint($constraint) : \PHPUnit\Framework\Constraint\Constraint { - return ['expr']; + if (!$constraint instanceof \PHPUnit\Framework\Constraint\Constraint) { + return new \PHPUnit\Framework\Constraint\IsEqual($constraint); + } + return $constraint; } - public function getType() : string + /** + * Returns true if the $constraint needs to be wrapped with braces. + */ + protected function constraintNeedsParentheses(\PHPUnit\Framework\Constraint\Constraint $constraint) : bool { - return 'Expr_Empty'; + return $constraint instanceof self && $constraint->arity() > 1 && $this->precedence() <= $constraint->precedence(); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -class BitwiseNot extends \PHPUnit\PhpParser\Node\Expr +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class LogicalOr extends \PHPUnit\Framework\Constraint\BinaryOperator { - /** @var Expr Expression */ - public $expr; /** - * Constructs a bitwise not node. - * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * Returns the name of this operator. */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $expr, array $attributes = []) + public function operator() : string { - $this->attributes = $attributes; - $this->expr = $expr; + return 'or'; } - public function getSubNodeNames() : array + /** + * Returns this operator's precedence. + * + * @see https://www.php.net/manual/en/language.operators.precedence.php + */ + public function precedence() : int { - return ['expr']; + return 24; } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + public function matches($other) : bool { - return 'Expr_BitwiseNot'; + foreach ($this->constraints() as $constraint) { + if ($constraint->evaluate($other, '', \true)) { + return \true; + } + } + return \false; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -class ErrorSuppress extends \PHPUnit\PhpParser\Node\Expr +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class LogicalAnd extends \PHPUnit\Framework\Constraint\BinaryOperator { - /** @var Expr Expression */ - public $expr; /** - * Constructs an error suppress node. - * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * Returns the name of this operator. */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $expr, array $attributes = []) + public function operator() : string { - $this->attributes = $attributes; - $this->expr = $expr; + return 'and'; } - public function getSubNodeNames() : array + /** + * Returns this operator's precedence. + * + * @see https://www.php.net/manual/en/language.operators.precedence.php + */ + public function precedence() : int { - return ['expr']; + return 22; } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return 'Expr_ErrorSuppress'; + foreach ($this->constraints() as $constraint) { + if (!$constraint->evaluate($other, '', \true)) { + return \false; + } + } + return [] !== $this->constraints(); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Expr; -use PHPUnit\PhpParser\Node\FunctionLike; -class Closure extends \PHPUnit\PhpParser\Node\Expr implements \PHPUnit\PhpParser\Node\FunctionLike +use function file_exists; +use function sprintf; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class FileExists extends \PHPUnit\Framework\Constraint\Constraint { - /** @var bool Whether the closure is static */ - public $static; - /** @var bool Whether to return by reference */ - public $byRef; - /** @var Node\Param[] Parameters */ - public $params; - /** @var ClosureUse[] use()s */ - public $uses; - /** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType Return type */ - public $returnType; - /** @var Node\Stmt[] Statements */ - public $stmts; - /** @var Node\AttributeGroup[] PHP attribute groups */ - public $attrGroups; /** - * Constructs a lambda function node. - * - * @param array $subNodes Array of the following optional subnodes: - * 'static' => false : Whether the closure is static - * 'byRef' => false : Whether to return by reference - * 'params' => array(): Parameters - * 'uses' => array(): use()s - * 'returnType' => null : Return type - * 'stmts' => array(): Statements - * 'attrGroups' => array(): PHP attributes groups - * @param array $attributes Additional attributes + * Returns a string representation of the constraint. */ - public function __construct(array $subNodes = [], array $attributes = []) - { - $this->attributes = $attributes; - $this->static = $subNodes['static'] ?? \false; - $this->byRef = $subNodes['byRef'] ?? \false; - $this->params = $subNodes['params'] ?? []; - $this->uses = $subNodes['uses'] ?? []; - $returnType = $subNodes['returnType'] ?? null; - $this->returnType = \is_string($returnType) ? new \PHPUnit\PhpParser\Node\Identifier($returnType) : $returnType; - $this->stmts = $subNodes['stmts'] ?? []; - $this->attrGroups = $subNodes['attrGroups'] ?? []; - } - public function getSubNodeNames() : array - { - return ['attrGroups', 'static', 'byRef', 'params', 'uses', 'returnType', 'stmts']; - } - public function returnsByRef() : bool - { - return $this->byRef; - } - public function getParams() : array - { - return $this->params; - } - public function getReturnType() - { - return $this->returnType; - } - /** @return Node\Stmt[] */ - public function getStmts() : array + public function toString() : string { - return $this->stmts; + return 'file exists'; } - public function getAttrGroups() : array + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return $this->attrGroups; + return file_exists($other); } - public function getType() : string + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other) : string { - return 'Expr_Closure'; + return sprintf('file "%s" exists', $other); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -class Array_ extends \PHPUnit\PhpParser\Node\Expr +use function is_writable; +use function sprintf; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsWritable extends \PHPUnit\Framework\Constraint\Constraint { - // For use in "kind" attribute - const KIND_LONG = 1; - // array() syntax - const KIND_SHORT = 2; - // [] syntax - /** @var (ArrayItem|null)[] Items */ - public $items; /** - * Constructs an array node. - * - * @param (ArrayItem|null)[] $items Items of the array - * @param array $attributes Additional attributes + * Returns a string representation of the constraint. */ - public function __construct(array $items = [], array $attributes = []) + public function toString() : string { - $this->attributes = $attributes; - $this->items = $items; + return 'is writable'; } - public function getSubNodeNames() : array + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return ['items']; + return is_writable($other); } - public function getType() : string + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other) : string { - return 'Expr_Array'; + return sprintf('"%s" is writable', $other); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -class UnaryPlus extends \PHPUnit\PhpParser\Node\Expr +use function is_dir; +use function sprintf; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class DirectoryExists extends \PHPUnit\Framework\Constraint\Constraint { - /** @var Expr Expression */ - public $expr; /** - * Constructs a unary plus node. - * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * Returns a string representation of the constraint. */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $expr, array $attributes = []) + public function toString() : string { - $this->attributes = $attributes; - $this->expr = $expr; + return 'directory exists'; } - public function getSubNodeNames() : array + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return ['expr']; + return is_dir($other); } - public function getType() : string + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other) : string { - return 'Expr_UnaryPlus'; + return sprintf('directory "%s" exists', $other); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class LogicalXor extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +use function is_readable; +use function sprintf; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsReadable extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string + /** + * Returns a string representation of the constraint. + */ + public function toString() : string { - return 'xor'; + return 'is readable'; } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return 'Expr_BinaryOp_LogicalXor'; + return is_readable($other); + } + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other) : string + { + return sprintf('"%s" is readable', $other); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class NotEqual extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsTrue extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string + /** + * Returns a string representation of the constraint. + */ + public function toString() : string { - return '!='; + return 'is true'; } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return 'Expr_BinaryOp_NotEqual'; + return $other === \true; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class Equal extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsFalse extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string + /** + * Returns a string representation of the constraint. + */ + public function toString() : string { - return '=='; + return 'is false'; } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return 'Expr_BinaryOp_Equal'; + return $other === \false; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class BooleanAnd extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +use function is_string; +use function sprintf; +use function strpos; +use function trim; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; +use PHPUnit\SebastianBergmann\Comparator\Factory as ComparatorFactory; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsEqualCanonicalizing extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string + /** + * @var mixed + */ + private $value; + public function __construct($value) { - return '&&'; + $this->value = $value; } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @throws ExpectationFailedException + */ + public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool { - return 'Expr_BinaryOp_BooleanAnd'; + // If $this->value and $other are identical, they are also equal. + // This is the most common path and will allow us to skip + // initialization of all the comparators. + if ($this->value === $other) { + return \true; + } + $comparatorFactory = ComparatorFactory::getInstance(); + try { + $comparator = $comparatorFactory->getComparatorFor($this->value, $other); + $comparator->assertEquals($this->value, $other, 0.0, \true, \false); + } catch (ComparisonFailure $f) { + if ($returnResult) { + return \false; + } + throw new ExpectationFailedException(trim($description . "\n" . $f->getMessage()), $f); + } + return \true; + } + /** + * Returns a string representation of the constraint. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function toString() : string + { + if (is_string($this->value)) { + if (strpos($this->value, "\n") !== \false) { + return 'is equal to '; + } + return sprintf("is equal to '%s'", $this->value); + } + return sprintf('is equal to %s', $this->exporter()->export($this->value)); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class BooleanOr extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +use function is_string; +use function sprintf; +use function strpos; +use function trim; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; +use PHPUnit\SebastianBergmann\Comparator\Factory as ComparatorFactory; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsEqualIgnoringCase extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string + /** + * @var mixed + */ + private $value; + public function __construct($value) { - return '||'; + $this->value = $value; } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @throws ExpectationFailedException + */ + public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool { - return 'Expr_BinaryOp_BooleanOr'; + // If $this->value and $other are identical, they are also equal. + // This is the most common path and will allow us to skip + // initialization of all the comparators. + if ($this->value === $other) { + return \true; + } + $comparatorFactory = ComparatorFactory::getInstance(); + try { + $comparator = $comparatorFactory->getComparatorFor($this->value, $other); + $comparator->assertEquals($this->value, $other, 0.0, \false, \true); + } catch (ComparisonFailure $f) { + if ($returnResult) { + return \false; + } + throw new ExpectationFailedException(trim($description . "\n" . $f->getMessage()), $f); + } + return \true; + } + /** + * Returns a string representation of the constraint. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function toString() : string + { + if (is_string($this->value)) { + if (strpos($this->value, "\n") !== \false) { + return 'is equal to '; + } + return sprintf("is equal to '%s'", $this->value); + } + return sprintf('is equal to %s', $this->exporter()->export($this->value)); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class Mul extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +use function sprintf; +use function trim; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; +use PHPUnit\SebastianBergmann\Comparator\Factory as ComparatorFactory; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsEqualWithDelta extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string + /** + * @var mixed + */ + private $value; + /** + * @var float + */ + private $delta; + public function __construct($value, float $delta) { - return '*'; + $this->value = $value; + $this->delta = $delta; } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @throws ExpectationFailedException + */ + public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool { - return 'Expr_BinaryOp_Mul'; + // If $this->value and $other are identical, they are also equal. + // This is the most common path and will allow us to skip + // initialization of all the comparators. + if ($this->value === $other) { + return \true; + } + $comparatorFactory = ComparatorFactory::getInstance(); + try { + $comparator = $comparatorFactory->getComparatorFor($this->value, $other); + $comparator->assertEquals($this->value, $other, $this->delta); + } catch (ComparisonFailure $f) { + if ($returnResult) { + return \false; + } + throw new ExpectationFailedException(trim($description . "\n" . $f->getMessage()), $f); + } + return \true; + } + /** + * Returns a string representation of the constraint. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function toString() : string + { + return sprintf('is equal to %s with delta <%F>>', $this->exporter()->export($this->value), $this->delta); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class SmallerOrEqual extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +use function is_string; +use function sprintf; +use function strpos; +use function trim; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; +use PHPUnit\SebastianBergmann\Comparator\Factory as ComparatorFactory; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsEqual extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string + /** + * @var mixed + */ + private $value; + /** + * @var float + */ + private $delta; + /** + * @var bool + */ + private $canonicalize; + /** + * @var bool + */ + private $ignoreCase; + public function __construct($value, float $delta = 0.0, bool $canonicalize = \false, bool $ignoreCase = \false) { - return '<='; + $this->value = $value; + $this->delta = $delta; + $this->canonicalize = $canonicalize; + $this->ignoreCase = $ignoreCase; + } + /** + * Evaluates the constraint for parameter $other. + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @throws ExpectationFailedException + * + * @return bool + */ + public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool + { + // If $this->value and $other are identical, they are also equal. + // This is the most common path and will allow us to skip + // initialization of all the comparators. + if ($this->value === $other) { + return \true; + } + $comparatorFactory = ComparatorFactory::getInstance(); + try { + $comparator = $comparatorFactory->getComparatorFor($this->value, $other); + $comparator->assertEquals($this->value, $other, $this->delta, $this->canonicalize, $this->ignoreCase); + } catch (ComparisonFailure $f) { + if ($returnResult) { + return \false; + } + throw new ExpectationFailedException(trim($description . "\n" . $f->getMessage()), $f); + } + return \true; } - public function getType() : string + /** + * Returns a string representation of the constraint. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function toString() : string { - return 'Expr_BinaryOp_SmallerOrEqual'; + $delta = ''; + if (is_string($this->value)) { + if (strpos($this->value, "\n") !== \false) { + return 'is equal to '; + } + return sprintf("is equal to '%s'", $this->value); + } + if ($this->delta != 0) { + $delta = sprintf(' with delta <%F>', $this->delta); + } + return sprintf('is equal to %s%s', $this->exporter()->export($this->value), $delta); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class Plus extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +/** + * @psalm-template CallbackInput of mixed + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class Callback extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string - { - return '+'; - } - public function getType() : string + /** + * @var callable + * + * @psalm-var callable(CallbackInput $input): bool + */ + private $callback; + /** @psalm-param callable(CallbackInput $input): bool $callback */ + public function __construct(callable $callback) { - return 'Expr_BinaryOp_Plus'; + $this->callback = $callback; } -} -callback)($other); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class Pow extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +use PHPUnit\Framework\ExpectationFailedException; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsAnything extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string - { - return '**'; - } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @throws ExpectationFailedException + */ + public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool { - return 'Expr_BinaryOp_Pow'; + return $returnResult ? \true : null; } -} - + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class Spaceship extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +use function is_nan; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsNan extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string + /** + * Returns a string representation of the constraint. + */ + public function toString() : string { - return '<=>'; + return 'is nan'; } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return 'Expr_BinaryOp_Spaceship'; + return is_nan($other); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class Coalesce extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +use function is_finite; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsFinite extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string + /** + * Returns a string representation of the constraint. + */ + public function toString() : string { - return '??'; + return 'is finite'; } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return 'Expr_BinaryOp_Coalesce'; + return is_finite($other); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class Identical extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +use function is_infinite; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsInfinite extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string + /** + * Returns a string representation of the constraint. + */ + public function toString() : string { - return '==='; + return 'is infinite'; } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return 'Expr_BinaryOp_Identical'; + return is_infinite($other); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class Mod extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +use function count; +use function is_array; +use function iterator_count; +use function sprintf; +use Countable; +use EmptyIterator; +use Generator; +use Iterator; +use IteratorAggregate; +use PHPUnit\Framework\Exception; +use Traversable; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +class Count extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string + /** + * @var int + */ + private $expectedCount; + public function __construct(int $expected) { - return '%'; + $this->expectedCount = $expected; } - public function getType() : string + public function toString() : string { - return 'Expr_BinaryOp_Mod'; + return sprintf('count matches %d', $this->expectedCount); } -} -expectedCount === $this->getCountOf($other); } - public function getType() : string + /** + * @throws Exception + */ + protected function getCountOf($other) : ?int { - return 'Expr_BinaryOp_BitwiseXor'; + if ($other instanceof Countable || is_array($other)) { + return count($other); + } + if ($other instanceof EmptyIterator) { + return 0; + } + if ($other instanceof Traversable) { + while ($other instanceof IteratorAggregate) { + try { + $other = $other->getIterator(); + } catch (\Exception $e) { + throw new Exception($e->getMessage(), $e->getCode(), $e); + } + } + $iterator = $other; + if ($iterator instanceof Generator) { + return $this->getCountOfGenerator($iterator); + } + if (!$iterator instanceof Iterator) { + return iterator_count($iterator); + } + $key = $iterator->key(); + $count = iterator_count($iterator); + // Manually rewind $iterator to previous key, since iterator_count + // moves pointer. + if ($key !== null) { + $iterator->rewind(); + while ($iterator->valid() && $key !== $iterator->key()) { + $iterator->next(); + } + } + return $count; + } + return null; } -} -valid(); $generator->next()) { + $count++; + } + return $count; } - public function getType() : string + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other) : string { - return 'Expr_BinaryOp_Minus'; + return sprintf('actual size %d matches expected size %d', (int) $this->getCountOf($other), $this->expectedCount); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class Div extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class GreaterThan extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string - { - return '/'; - } - public function getType() : string + /** + * @var float|int + */ + private $value; + /** + * @param float|int $value + */ + public function __construct($value) { - return 'Expr_BinaryOp_Div'; + $this->value = $value; } -} -'; + return 'is greater than ' . $this->exporter()->export($this->value); } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return 'Expr_BinaryOp_Greater'; + return $this->value < $other; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class GreaterOrEqual extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +use function count; +use function gettype; +use function sprintf; +use function strpos; +use Countable; +use EmptyIterator; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsEmpty extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string - { - return '>='; - } - public function getType() : string + /** + * Returns a string representation of the constraint. + */ + public function toString() : string { - return 'Expr_BinaryOp_GreaterOrEqual'; + return 'is empty'; } -} -toString()); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class BitwiseOr extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class LessThan extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string - { - return '|'; - } - public function getType() : string + /** + * @var float|int + */ + private $value; + /** + * @param float|int $value + */ + public function __construct($value) { - return 'Expr_BinaryOp_BitwiseOr'; + $this->value = $value; } -} -exporter()->export($this->value); } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return 'Expr_BinaryOp_LogicalAnd'; + return $this->value > $other; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class BitwiseAnd extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class SameSize extends \PHPUnit\Framework\Constraint\Count { - public function getOperatorSigil() : string - { - return '&'; - } - public function getType() : string + public function __construct(iterable $expected) { - return 'Expr_BinaryOp_BitwiseAnd'; + parent::__construct((int) $this->getCountOf($expected)); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class Smaller extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +use function abs; +use function get_class; +use function is_array; +use function is_float; +use function is_infinite; +use function is_nan; +use function is_object; +use function is_string; +use function sprintf; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsIdentical extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string + /** + * @var float + */ + private const EPSILON = 1.0E-10; + /** + * @var mixed + */ + private $value; + public function __construct($value) { - return '<'; + $this->value = $value; } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + */ + public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool { - return 'Expr_BinaryOp_Smaller'; + if (is_float($this->value) && is_float($other) && !is_infinite($this->value) && !is_infinite($other) && !is_nan($this->value) && !is_nan($other)) { + $success = abs($this->value - $other) < self::EPSILON; + } else { + $success = $this->value === $other; + } + if ($returnResult) { + return $success; + } + if (!$success) { + $f = null; + // if both values are strings, make sure a diff is generated + if (is_string($this->value) && is_string($other)) { + $f = new ComparisonFailure($this->value, $other, sprintf("'%s'", $this->value), sprintf("'%s'", $other)); + } + // if both values are array, make sure a diff is generated + if (is_array($this->value) && is_array($other)) { + $f = new ComparisonFailure($this->value, $other, $this->exporter()->export($this->value), $this->exporter()->export($other)); + } + $this->fail($other, $description, $f); + } + return null; } -} -value)) { + return 'is identical to an object of class "' . get_class($this->value) . '"'; + } + return 'is identical to ' . $this->exporter()->export($this->value); } - public function getType() : string + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other) : string { - return 'Expr_BinaryOp_Concat'; + if (is_object($this->value) && is_object($other)) { + return 'two variables reference the same object'; + } + if (is_string($this->value) && is_string($other)) { + return 'two strings are identical'; + } + if (is_array($this->value) && is_array($other)) { + return 'two arrays are identical'; + } + return parent::failureDescription($other); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -class ShiftRight extends \PHPUnit\PhpParser\Node\Expr\BinaryOp +use function get_class; +use function is_object; +use PHPUnit\Framework\ActualValueIsNotAnObjectException; +use PHPUnit\Framework\ComparisonMethodDoesNotAcceptParameterTypeException; +use PHPUnit\Framework\ComparisonMethodDoesNotDeclareBoolReturnTypeException; +use PHPUnit\Framework\ComparisonMethodDoesNotDeclareExactlyOneParameterException; +use PHPUnit\Framework\ComparisonMethodDoesNotDeclareParameterTypeException; +use PHPUnit\Framework\ComparisonMethodDoesNotExistException; +use ReflectionNamedType; +use ReflectionObject; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class ObjectEquals extends \PHPUnit\Framework\Constraint\Constraint { - public function getOperatorSigil() : string + /** + * @var object + */ + private $expected; + /** + * @var string + */ + private $method; + public function __construct(object $object, string $method = 'equals') { - return '>>'; + $this->expected = $object; + $this->method = $method; } - public function getType() : string + public function toString() : string { - return 'Expr_BinaryOp_ShiftRight'; + return 'two objects are equal'; } -} -hasMethod($this->method)) { + throw new ComparisonMethodDoesNotExistException(get_class($other), $this->method); + } + /** @noinspection PhpUnhandledExceptionInspection */ + $method = $object->getMethod($this->method); + if (!$method->hasReturnType()) { + throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(get_class($other), $this->method); + } + $returnType = $method->getReturnType(); + if (!$returnType instanceof ReflectionNamedType) { + throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(get_class($other), $this->method); + } + if ($returnType->allowsNull()) { + throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(get_class($other), $this->method); + } + if ($returnType->getName() !== 'bool') { + throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(get_class($other), $this->method); + } + if ($method->getNumberOfParameters() !== 1 || $method->getNumberOfRequiredParameters() !== 1) { + throw new ComparisonMethodDoesNotDeclareExactlyOneParameterException(get_class($other), $this->method); + } + $parameter = $method->getParameters()[0]; + if (!$parameter->hasType()) { + throw new ComparisonMethodDoesNotDeclareParameterTypeException(get_class($other), $this->method); + } + $type = $parameter->getType(); + if (!$type instanceof ReflectionNamedType) { + throw new ComparisonMethodDoesNotDeclareParameterTypeException(get_class($other), $this->method); + } + $typeName = $type->getName(); + if ($typeName === 'self') { + $typeName = get_class($other); + } + if (!$this->expected instanceof $typeName) { + throw new ComparisonMethodDoesNotAcceptParameterTypeException(get_class($other), $this->method, get_class($this->expected)); + } + return $other->{$this->method}($this->expected); } -} -toString(); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\AssignOp; -class Pow extends \PHPUnit\PhpParser\Node\Expr\AssignOp +use ReflectionObject; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class ObjectHasAttribute extends \PHPUnit\Framework\Constraint\ClassHasAttribute { - public function getType() : string + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return 'Expr_AssignOp_Pow'; + return (new ReflectionObject($other))->hasProperty($this->attributeName()); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\AssignOp; -class Coalesce extends \PHPUnit\PhpParser\Node\Expr\AssignOp +use function get_class; +use function is_object; +use function sprintf; +use PHPUnit\Framework\Exception; +use ReflectionClass; +use ReflectionException; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +class ClassHasAttribute extends \PHPUnit\Framework\Constraint\Constraint { - public function getType() : string + /** + * @var string + */ + private $attributeName; + public function __construct(string $attributeName) { - return 'Expr_AssignOp_Coalesce'; + $this->attributeName = $attributeName; } -} -attributeName); } -} -hasProperty($this->attributeName); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd } -} -toString()); } -} -attributeName; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\AssignOp; -class ShiftLeft extends \PHPUnit\PhpParser\Node\Expr\AssignOp +use function sprintf; +use PHPUnit\Framework\Exception; +use ReflectionClass; +use ReflectionException; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class ClassHasStaticAttribute extends \PHPUnit\Framework\Constraint\ClassHasAttribute { - public function getType() : string + /** + * Returns a string representation of the constraint. + */ + public function toString() : string { - return 'Expr_AssignOp_ShiftLeft'; + return sprintf('has static attribute "%s"', $this->attributeName()); } -} -hasProperty($this->attributeName())) { + return $class->getProperty($this->attributeName())->isStatic(); + } + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + return \false; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\AssignOp; -class BitwiseAnd extends \PHPUnit\PhpParser\Node\Expr\AssignOp +use function array_key_exists; +use function is_array; +use ArrayAccess; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class ArrayHasKey extends \PHPUnit\Framework\Constraint\Constraint { - public function getType() : string + /** + * @var int|string + */ + private $key; + /** + * @param int|string $key + */ + public function __construct($key) { - return 'Expr_AssignOp_BitwiseAnd'; + $this->key = $key; } -} -exporter()->export($this->key); } -} -key, $other); + } + if ($other instanceof ArrayAccess) { + return $other->offsetExists($this->key); + } + return \false; + } + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other) : string + { + return 'an array ' . $this->toString(); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -class Assign extends \PHPUnit\PhpParser\Node\Expr +use SplObjectStorage; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class TraversableContainsIdentical extends \PHPUnit\Framework\Constraint\TraversableContains { - /** @var Expr Variable */ - public $var; - /** @var Expr Expression */ - public $expr; /** - * Constructs an assignment node. + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. * - * @param Expr $var Variable - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @param mixed $other value or object to evaluate */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $var, \PHPUnit\PhpParser\Node\Expr $expr, array $attributes = []) - { - $this->attributes = $attributes; - $this->var = $var; - $this->expr = $expr; - } - public function getSubNodeNames() : array - { - return ['var', 'expr']; - } - public function getType() : string + protected function matches($other) : bool { - return 'Expr_Assign'; + if ($other instanceof SplObjectStorage) { + return $other->contains($this->value()); + } + foreach ($other as $element) { + if ($this->value() === $element) { + return \true; + } + } + return \false; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\MatchArm; -class Match_ extends \PHPUnit\PhpParser\Node\Expr +use function is_array; +use function sprintf; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +abstract class TraversableContains extends \PHPUnit\Framework\Constraint\Constraint { - /** @var Node\Expr */ - public $cond; - /** @var MatchArm[] */ - public $arms; /** - * @param MatchArm[] $arms + * @var mixed */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $cond, array $arms = [], array $attributes = []) - { - $this->attributes = $attributes; - $this->cond = $cond; - $this->arms = $arms; - } - public function getSubNodeNames() : array - { - return ['cond', 'arms']; - } - public function getType() : string + private $value; + public function __construct($value) { - return 'Expr_Match'; + $this->value = $value; } -} -attributes = $attributes; - $this->var = $var; - $this->name = \is_string($name) ? new \PHPUnit\PhpParser\Node\Identifier($name) : $name; - $this->args = $args; + return 'contains ' . $this->exporter()->export($this->value); } - public function getSubNodeNames() : array + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other) : string { - return ['var', 'name', 'args']; + return sprintf('%s %s', is_array($other) ? 'an array' : 'a traversable', $this->toString()); } - public function getType() : string + protected function value() { - return 'Expr_MethodCall'; + return $this->value; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -class ClosureUse extends \PHPUnit\PhpParser\Node\Expr +use SplObjectStorage; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class TraversableContainsEqual extends \PHPUnit\Framework\Constraint\TraversableContains { - /** @var Expr\Variable Variable to use */ - public $var; - /** @var bool Whether to use by reference */ - public $byRef; /** - * Constructs a closure use node. + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. * - * @param Expr\Variable $var Variable to use - * @param bool $byRef Whether to use by reference - * @param array $attributes Additional attributes + * @param mixed $other value or object to evaluate */ - public function __construct(\PHPUnit\PhpParser\Node\Expr\Variable $var, bool $byRef = \false, array $attributes = []) - { - $this->attributes = $attributes; - $this->var = $var; - $this->byRef = $byRef; - } - public function getSubNodeNames() : array - { - return ['var', 'byRef']; - } - public function getType() : string + protected function matches($other) : bool { - return 'Expr_ClosureUse'; + if ($other instanceof SplObjectStorage) { + return $other->contains($this->value()); + } + foreach ($other as $element) { + /* @noinspection TypeUnsafeComparisonInspection */ + if ($this->value() == $element) { + return \true; + } + } + return \false; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -class PreInc extends \PHPUnit\PhpParser\Node\Expr +use PHPUnit\Framework\ExpectationFailedException; +use Traversable; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class TraversableContainsOnly extends \PHPUnit\Framework\Constraint\Constraint { - /** @var Expr Variable */ - public $var; /** - * Constructs a pre increment node. - * - * @param Expr $var Variable - * @param array $attributes Additional attributes + * @var Constraint */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $var, array $attributes = []) + private $constraint; + /** + * @var string + */ + private $type; + /** + * @throws \PHPUnit\Framework\Exception + */ + public function __construct(string $type, bool $isNativeType = \true) { - $this->attributes = $attributes; - $this->var = $var; + if ($isNativeType) { + $this->constraint = new \PHPUnit\Framework\Constraint\IsType($type); + } else { + $this->constraint = new \PHPUnit\Framework\Constraint\IsInstanceOf($type); + } + $this->type = $type; } - public function getSubNodeNames() : array + /** + * Evaluates the constraint for parameter $other. + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed|Traversable $other + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + */ + public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool { - return ['var']; + $success = \true; + foreach ($other as $item) { + if (!$this->constraint->evaluate($item, '', \true)) { + $success = \false; + break; + } + } + if ($returnResult) { + return $success; + } + if (!$success) { + $this->fail($other, $description); + } + return null; } - public function getType() : string + /** + * Returns a string representation of the constraint. + */ + public function toString() : string { - return 'Expr_PreInc'; + return 'contains only values of type "' . $this->type . '"'; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -class Exit_ extends \PHPUnit\PhpParser\Node\Expr +use function sprintf; +use Countable; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\SelfDescribing; +use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; +use PHPUnit\SebastianBergmann\Exporter\Exporter; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +abstract class Constraint implements Countable, SelfDescribing { - /* For use in "kind" attribute */ - const KIND_EXIT = 1; - const KIND_DIE = 2; - /** @var null|Expr Expression */ - public $expr; /** - * Constructs an exit() node. + * @var ?Exporter + */ + private $exporter; + /** + * Evaluates the constraint for parameter $other. * - * @param null|Expr $expr Expression - * @param array $attributes Additional attributes + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $expr = null, array $attributes = []) + public function evaluate($other, string $description = '', bool $returnResult = \false) : ?bool { - $this->attributes = $attributes; - $this->expr = $expr; + $success = \false; + if ($this->matches($other)) { + $success = \true; + } + if ($returnResult) { + return $success; + } + if (!$success) { + $this->fail($other, $description); + } + return null; } - public function getSubNodeNames() : array + /** + * Counts the number of constraint elements. + */ + public function count() : int { - return ['expr']; + return 1; } - public function getType() : string + protected function exporter() : Exporter { - return 'Expr_Exit'; + if ($this->exporter === null) { + $this->exporter = new Exporter(); + } + return $this->exporter; } -} -attributes = $attributes; - $this->var = $var; - $this->name = \is_string($name) ? new \PHPUnit\PhpParser\Node\Identifier($name) : $name; - $this->args = $args; + return \false; } - public function getSubNodeNames() : array + /** + * Throws an exception for the given compared value and test description. + * + * @param mixed $other evaluated value or object + * @param string $description Additional information about the test + * @param ComparisonFailure $comparisonFailure + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * + * @psalm-return never-return + */ + protected function fail($other, $description, ComparisonFailure $comparisonFailure = null) : void { - return ['var', 'name', 'args']; + $failureDescription = sprintf('Failed asserting that %s.', $this->failureDescription($other)); + $additionalFailureDescription = $this->additionalFailureDescription($other); + if ($additionalFailureDescription) { + $failureDescription .= "\n" . $additionalFailureDescription; + } + if (!empty($description)) { + $failureDescription = $description . "\n" . $failureDescription; + } + throw new ExpectationFailedException($failureDescription, $comparisonFailure); } - public function getType() : string + /** + * Return additional failure description where needed. + * + * The function can be overridden to provide additional failure + * information like a diff + * + * @param mixed $other evaluated value or object + */ + protected function additionalFailureDescription($other) : string { - return 'Expr_NullsafeMethodCall'; + return ''; } -} -attributes = $attributes; - $this->key = $key; - $this->value = $value; + return $this->exporter()->export($other) . ' ' . $this->toString(); } - public function getSubNodeNames() : array + /** + * Returns a custom string representation of the constraint object when it + * appears in context of an $operator expression. + * + * The purpose of this method is to provide meaningful descriptive string + * in context of operators such as LogicalNot. Native PHPUnit constraints + * are supported out of the box by LogicalNot, but externally developed + * ones had no way to provide correct strings in this context. + * + * The method shall return empty string, when it does not handle + * customization by itself. + * + * @param Operator $operator the $operator of the expression + * @param mixed $role role of $this constraint in the $operator expression + */ + protected function toStringInContext(\PHPUnit\Framework\Constraint\Operator $operator, $role) : string { - return ['key', 'value']; + return ''; } - public function getType() : string + /** + * Returns the description of the failure when this constraint appears in + * context of an $operator expression. + * + * The purpose of this method is to provide meaningful failue description + * in context of operators such as LogicalNot. Native PHPUnit constraints + * are supported out of the box by LogicalNot, but externally developed + * ones had no way to provide correct messages in this context. + * + * The method shall return empty string, when it does not handle + * customization by itself. + * + * @param Operator $operator the $operator of the expression + * @param mixed $role role of $this constraint in the $operator expression + * @param mixed $other evaluated value or object + */ + protected function failureDescriptionInContext(\PHPUnit\Framework\Constraint\Operator $operator, $role, $other) : string { - return 'Expr_Yield'; + $string = $this->toStringInContext($operator, $role); + if ($string === '') { + return ''; + } + return $this->exporter()->export($other) . ' ' . $string; } -} -attributes = $attributes; - $this->class = $class; - $this->name = \is_string($name) ? new \PHPUnit\PhpParser\Node\Identifier($name) : $name; - } - public function getSubNodeNames() : array - { - return ['class', 'name']; - } - public function getType() : string + protected function reduce() : self { - return 'Expr_ClassConstFetch'; + return $this; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\Cast; -class Bool_ extends \PHPUnit\PhpParser\Node\Expr\Cast +use const JSON_ERROR_CTRL_CHAR; +use const JSON_ERROR_DEPTH; +use const JSON_ERROR_NONE; +use const JSON_ERROR_STATE_MISMATCH; +use const JSON_ERROR_SYNTAX; +use const JSON_ERROR_UTF8; +use function strtolower; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class JsonMatchesErrorMessageProvider { - public function getType() : string + /** + * Translates JSON error to a human readable string. + */ + public static function determineJsonError(string $error, string $prefix = '') : ?string { - return 'Expr_Cast_Bool'; + switch ($error) { + case \JSON_ERROR_NONE: + return null; + case \JSON_ERROR_DEPTH: + return $prefix . 'Maximum stack depth exceeded'; + case \JSON_ERROR_STATE_MISMATCH: + return $prefix . 'Underflow or the modes mismatch'; + case \JSON_ERROR_CTRL_CHAR: + return $prefix . 'Unexpected control character found'; + case \JSON_ERROR_SYNTAX: + return $prefix . 'Syntax error, malformed JSON'; + case \JSON_ERROR_UTF8: + return $prefix . 'Malformed UTF-8 characters, possibly incorrectly encoded'; + default: + return $prefix . 'Unknown error'; + } } -} - + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\Cast; -class Double extends \PHPUnit\PhpParser\Node\Expr\Cast +use function json_decode; +use function sprintf; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Util\Json; +use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class JsonMatches extends \PHPUnit\Framework\Constraint\Constraint { - // For use in "kind" attribute - const KIND_DOUBLE = 1; - // "double" syntax - const KIND_FLOAT = 2; - // "float" syntax - const KIND_REAL = 3; - // "real" syntax - public function getType() : string + /** + * @var string + */ + private $value; + public function __construct(string $value) { - return 'Expr_Cast_Double'; + $this->value = $value; } -} -value); } -} -value); + if ($error) { + return \false; + } + return $recodedOther == $recodedValue; } -} -value); + if ($error) { + parent::fail($other, $description); + } + $comparisonFailure = new ComparisonFailure(json_decode($this->value), json_decode($other), Json::prettify($recodedValue), Json::prettify($recodedOther), \false, 'Failed asserting that two json values are equal.'); + } + parent::fail($other, $description, $comparisonFailure); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr\Cast; -class Unset_ extends \PHPUnit\PhpParser\Node\Expr\Cast +use const DIRECTORY_SEPARATOR; +use function explode; +use function implode; +use function preg_match; +use function preg_quote; +use function preg_replace; +use function strtr; +use PHPUnit\SebastianBergmann\Diff\Differ; +use PHPUnit\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class StringMatchesFormatDescription extends \PHPUnit\Framework\Constraint\RegularExpression { - public function getType() : string + /** + * @var string + */ + private $string; + public function __construct(string $string) { - return 'Expr_Cast_Unset'; + parent::__construct($this->createPatternFromFormat($this->convertNewlines($string))); + $this->string = $string; } -} -attributes = $attributes; - $this->expr = $expr; - } - public function getSubNodeNames() : array + protected function matches($other) : bool { - return ['expr']; + return parent::matches($this->convertNewlines($other)); } - public function getType() : string + protected function failureDescription($other) : string { - return 'Expr_Clone'; + return 'string matches format description'; } -} -attributes = $attributes; - $this->var = $var; + $from = explode("\n", $this->string); + $to = explode("\n", $this->convertNewlines($other)); + foreach ($from as $index => $line) { + if (isset($to[$index]) && $line !== $to[$index]) { + $line = $this->createPatternFromFormat($line); + if (preg_match($line, $to[$index]) > 0) { + $from[$index] = $to[$index]; + } + } + } + $this->string = implode("\n", $from); + $other = implode("\n", $to); + return (new Differ(new UnifiedDiffOutputBuilder("--- Expected\n+++ Actual\n")))->diff($this->string, $other); } - public function getSubNodeNames() : array + private function createPatternFromFormat(string $string) : string { - return ['var']; + $string = strtr(preg_quote($string, '/'), ['%%' => '%', '%e' => '\\' . \DIRECTORY_SEPARATOR, '%s' => '[^\\r\\n]+', '%S' => '[^\\r\\n]*', '%a' => '.+', '%A' => '.*', '%w' => '\\s*', '%i' => '[+-]?\\d+', '%d' => '\\d+', '%x' => '[0-9a-fA-F]+', '%f' => '[+-]?\\.?\\d+\\.?\\d*(?:[Ee][+-]?\\d+)?', '%c' => '.']); + return '/^' . $string . '$/s'; } - public function getType() : string + private function convertNewlines(string $text) : string { - return 'Expr_PreDec'; + return preg_replace('/\\r\\n/', "\n", $text); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -class Print_ extends \PHPUnit\PhpParser\Node\Expr +use function json_decode; +use function json_last_error; +use function sprintf; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsJson extends \PHPUnit\Framework\Constraint\Constraint { - /** @var Expr Expression */ - public $expr; /** - * Constructs an print() node. - * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * Returns a string representation of the constraint. */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $expr, array $attributes = []) + public function toString() : string { - $this->attributes = $attributes; - $this->expr = $expr; + return 'is valid JSON'; } - public function getSubNodeNames() : array + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return ['expr']; + if ($other === '') { + return \false; + } + json_decode($other); + if (json_last_error()) { + return \false; + } + return \true; } - public function getType() : string + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other) : string { - return 'Expr_Print'; + if ($other === '') { + return 'an empty string is valid JSON'; + } + json_decode($other); + $error = (string) \PHPUnit\Framework\Constraint\JsonMatchesErrorMessageProvider::determineJsonError((string) json_last_error()); + return sprintf('%s is valid JSON (%s)', $this->exporter()->shortenedExport($other), $error); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -class List_ extends \PHPUnit\PhpParser\Node\Expr +use function strlen; +use function strpos; +use PHPUnit\Framework\InvalidArgumentException; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class StringStartsWith extends \PHPUnit\Framework\Constraint\Constraint { - /** @var (ArrayItem|null)[] List of items to assign to */ - public $items; /** - * Constructs a list() destructuring node. - * - * @param (ArrayItem|null)[] $items List of items to assign to - * @param array $attributes Additional attributes + * @var string */ - public function __construct(array $items, array $attributes = []) - { - $this->attributes = $attributes; - $this->items = $items; - } - public function getSubNodeNames() : array + private $prefix; + public function __construct(string $prefix) { - return ['items']; + if (strlen($prefix) === 0) { + throw InvalidArgumentException::create(1, 'non-empty string'); + } + $this->prefix = $prefix; } - public function getType() : string + /** + * Returns a string representation of the constraint. + */ + public function toString() : string { - return 'Expr_List'; + return 'starts with "' . $this->prefix . '"'; } -} -attributes = $attributes; - $this->expr = $expr; - } - public function getSubNodeNames() : array + protected function matches($other) : bool { - return ['expr']; + return strpos((string) $other, $this->prefix) === 0; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -use PHPUnit\PhpParser\Node\Name; -class Instanceof_ extends \PHPUnit\PhpParser\Node\Expr +use function mb_stripos; +use function mb_strtolower; +use function sprintf; +use function strpos; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class StringContains extends \PHPUnit\Framework\Constraint\Constraint { - /** @var Expr Expression */ - public $expr; - /** @var Name|Expr Class name */ - public $class; /** - * Constructs an instanceof check node. - * - * @param Expr $expr Expression - * @param Name|Expr $class Class name - * @param array $attributes Additional attributes + * @var string + */ + private $string; + /** + * @var bool */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $expr, $class, array $attributes = []) + private $ignoreCase; + public function __construct(string $string, bool $ignoreCase = \false) { - $this->attributes = $attributes; - $this->expr = $expr; - $this->class = $class; + $this->string = $string; + $this->ignoreCase = $ignoreCase; } - public function getSubNodeNames() : array + /** + * Returns a string representation of the constraint. + */ + public function toString() : string { - return ['expr', 'class']; + if ($this->ignoreCase) { + $string = mb_strtolower($this->string, 'UTF-8'); + } else { + $string = $this->string; + } + return sprintf('contains "%s"', $string); } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return 'Expr_Instanceof'; + if ('' === $this->string) { + return \true; + } + if ($this->ignoreCase) { + /* + * We must use the multi byte safe version so we can accurately compare non latin upper characters with + * their lowercase equivalents. + */ + return mb_stripos($other, $this->string, 0, 'UTF-8') !== \false; + } + /* + * Use the non multi byte safe functions to see if the string is contained in $other. + * + * This function is very fast and we don't care about the character position in the string. + * + * Additionally, we want this method to be binary safe so we can check if some binary data is in other binary + * data. + */ + return strpos($other, $this->string) !== \false; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -class PostDec extends \PHPUnit\PhpParser\Node\Expr +use function preg_match; +use function sprintf; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +class RegularExpression extends \PHPUnit\Framework\Constraint\Constraint { - /** @var Expr Variable */ - public $var; /** - * Constructs a post decrement node. - * - * @param Expr $var Variable - * @param array $attributes Additional attributes + * @var string */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $var, array $attributes = []) + private $pattern; + public function __construct(string $pattern) { - $this->attributes = $attributes; - $this->var = $var; + $this->pattern = $pattern; } - public function getSubNodeNames() : array + /** + * Returns a string representation of the constraint. + */ + public function toString() : string { - return ['var']; + return sprintf('matches PCRE pattern "%s"', $this->pattern); } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return 'Expr_PostDec'; + return preg_match($this->pattern, $other) > 0; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -class AssignRef extends \PHPUnit\PhpParser\Node\Expr +use function strlen; +use function substr; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class StringEndsWith extends \PHPUnit\Framework\Constraint\Constraint { - /** @var Expr Variable reference is assigned to */ - public $var; - /** @var Expr Variable which is referenced */ - public $expr; /** - * Constructs an assignment node. - * - * @param Expr $var Variable - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @var string */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $var, \PHPUnit\PhpParser\Node\Expr $expr, array $attributes = []) + private $suffix; + public function __construct(string $suffix) { - $this->attributes = $attributes; - $this->var = $var; - $this->expr = $expr; + $this->suffix = $suffix; } - public function getSubNodeNames() : array + /** + * Returns a string representation of the constraint. + */ + public function toString() : string { - return ['var', 'expr']; + return 'ends with "' . $this->suffix . '"'; } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return 'Expr_AssignRef'; + return substr($other, 0 - strlen($this->suffix)) === $this->suffix; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -abstract class AssignOp extends \PHPUnit\PhpParser\Node\Expr +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsNull extends \PHPUnit\Framework\Constraint\Constraint { - /** @var Expr Variable */ - public $var; - /** @var Expr Expression */ - public $expr; /** - * Constructs a compound assignment operation node. - * - * @param Expr $var Variable - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * Returns a string representation of the constraint. */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $var, \PHPUnit\PhpParser\Node\Expr $expr, array $attributes = []) + public function toString() : string { - $this->attributes = $attributes; - $this->var = $var; - $this->expr = $expr; + return 'is null'; } - public function getSubNodeNames() : array + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return ['var', 'expr']; + return $other === null; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -class PostInc extends \PHPUnit\PhpParser\Node\Expr +use function gettype; +use function is_array; +use function is_bool; +use function is_callable; +use function is_float; +use function is_int; +use function is_iterable; +use function is_numeric; +use function is_object; +use function is_scalar; +use function is_string; +use function sprintf; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsType extends \PHPUnit\Framework\Constraint\Constraint { - /** @var Expr Variable */ - public $var; /** - * Constructs a post increment node. - * - * @param Expr $var Variable - * @param array $attributes Additional attributes + * @var string + */ + public const TYPE_ARRAY = 'array'; + /** + * @var string + */ + public const TYPE_BOOL = 'bool'; + /** + * @var string + */ + public const TYPE_FLOAT = 'float'; + /** + * @var string + */ + public const TYPE_INT = 'int'; + /** + * @var string + */ + public const TYPE_NULL = 'null'; + /** + * @var string + */ + public const TYPE_NUMERIC = 'numeric'; + /** + * @var string + */ + public const TYPE_OBJECT = 'object'; + /** + * @var string + */ + public const TYPE_RESOURCE = 'resource'; + /** + * @var string + */ + public const TYPE_CLOSED_RESOURCE = 'resource (closed)'; + /** + * @var string + */ + public const TYPE_STRING = 'string'; + /** + * @var string + */ + public const TYPE_SCALAR = 'scalar'; + /** + * @var string + */ + public const TYPE_CALLABLE = 'callable'; + /** + * @var string + */ + public const TYPE_ITERABLE = 'iterable'; + /** + * @var array + */ + private const KNOWN_TYPES = ['array' => \true, 'boolean' => \true, 'bool' => \true, 'double' => \true, 'float' => \true, 'integer' => \true, 'int' => \true, 'null' => \true, 'numeric' => \true, 'object' => \true, 'real' => \true, 'resource' => \true, 'resource (closed)' => \true, 'string' => \true, 'scalar' => \true, 'callable' => \true, 'iterable' => \true]; + /** + * @var string + */ + private $type; + /** + * @throws \PHPUnit\Framework\Exception */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $var, array $attributes = []) + public function __construct(string $type) { - $this->attributes = $attributes; - $this->var = $var; + if (!isset(self::KNOWN_TYPES[$type])) { + throw new \PHPUnit\Framework\Exception(sprintf('Type specified for PHPUnit\\Framework\\Constraint\\IsType <%s> ' . 'is not a valid type.', $type)); + } + $this->type = $type; } - public function getSubNodeNames() : array + /** + * Returns a string representation of the constraint. + */ + public function toString() : string { - return ['var']; + return sprintf('is of type "%s"', $this->type); } - public function getType() : string + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other) : bool { - return 'Expr_PostInc'; + switch ($this->type) { + case 'numeric': + return is_numeric($other); + case 'integer': + case 'int': + return is_int($other); + case 'double': + case 'float': + case 'real': + return is_float($other); + case 'string': + return is_string($other); + case 'boolean': + case 'bool': + return is_bool($other); + case 'null': + return null === $other; + case 'array': + return is_array($other); + case 'object': + return is_object($other); + case 'resource': + $type = gettype($other); + return $type === 'resource' || $type === 'resource (closed)'; + case 'resource (closed)': + return gettype($other) === 'resource (closed)'; + case 'scalar': + return is_scalar($other); + case 'callable': + return is_callable($other); + case 'iterable': + return is_iterable($other); + default: + return \false; + } } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; -use PHPUnit\PhpParser\Node\Expr; -class UnaryMinus extends \PHPUnit\PhpParser\Node\Expr +use function sprintf; +use ReflectionClass; +use ReflectionException; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IsInstanceOf extends \PHPUnit\Framework\Constraint\Constraint { - /** @var Expr Expression */ - public $expr; /** - * Constructs a unary minus node. - * - * @param Expr $expr Expression - * @param array $attributes Additional attributes + * @var string */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $expr, array $attributes = []) - { - $this->attributes = $attributes; - $this->expr = $expr; - } - public function getSubNodeNames() : array + private $className; + public function __construct(string $className) { - return ['expr']; + $this->className = $className; } - public function getType() : string + /** + * Returns a string representation of the constraint. + */ + public function toString() : string { - return 'Expr_UnaryMinus'; + return sprintf('is instance of %s "%s"', $this->getType(), $this->className); } -} -attributes = $attributes; - $this->class = $class; - $this->args = $args; + return $other instanceof $this->className; } - public function getSubNodeNames() : array + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other) : string { - return ['class', 'args']; + return sprintf('%s is an instance of %s "%s"', $this->exporter()->shortenedExport($other), $this->getType(), $this->className); } - public function getType() : string + private function getType() : string { - return 'Expr_New'; + try { + $reflection = new ReflectionClass($this->className); + if ($reflection->isInterface()) { + return 'interface'; + } + } catch (ReflectionException $e) { + } + return 'class'; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; -use PHPUnit\PhpParser\Node; -interface FunctionLike extends \PHPUnit\PhpParser\Node +use function array_filter; +use function array_map; +use function array_values; +use function count; +use function explode; +use function in_array; +use function strpos; +use function trim; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ExecutionOrderDependency { /** - * Whether to return by reference - * - * @return bool + * @var string */ - public function returnsByRef() : bool; + private $className = ''; /** - * List of parameters - * - * @return Param[] + * @var string */ - public function getParams() : array; + private $methodName = ''; /** - * Get the declared return type or null - * - * @return null|Identifier|Name|NullableType|UnionType + * @var bool */ - public function getReturnType(); + private $useShallowClone = \false; /** - * The function body - * - * @return Stmt[]|null + * @var bool */ - public function getStmts(); + private $useDeepClone = \false; + public static function createFromDependsAnnotation(string $className, string $annotation) : self + { + // Split clone option and target + $parts = explode(' ', trim($annotation), 2); + if (count($parts) === 1) { + $cloneOption = ''; + $target = $parts[0]; + } else { + $cloneOption = $parts[0]; + $target = $parts[1]; + } + // Prefix provided class for targets assumed to be in scope + if ($target !== '' && strpos($target, '::') === \false) { + $target = $className . '::' . $target; + } + return new self($target, null, $cloneOption); + } /** - * Get PHP attribute groups. + * @psalm-param list $dependencies * - * @return AttributeGroup[] + * @psalm-return list */ - public function getAttrGroups() : array; -} -isValid(); + })); + } /** - * Constructs a const node for use in class const and const statements. + * @psalm-param list $existing + * @psalm-param list $additional * - * @param string|Identifier $name Name - * @param Expr $value Value - * @param array $attributes Additional attributes + * @psalm-return list */ - public function __construct($name, \PHPUnit\PhpParser\Node\Expr $value, array $attributes = []) + public static function mergeUnique(array $existing, array $additional) : array { - $this->attributes = $attributes; - $this->name = \is_string($name) ? new \PHPUnit\PhpParser\Node\Identifier($name) : $name; - $this->value = $value; + $existingTargets = array_map(static function ($dependency) { + return $dependency->getTarget(); + }, $existing); + foreach ($additional as $dependency) { + if (in_array($dependency->getTarget(), $existingTargets, \true)) { + continue; + } + $existingTargets[] = $dependency->getTarget(); + $existing[] = $dependency; + } + return $existing; } - public function getSubNodeNames() : array + /** + * @psalm-param list $left + * @psalm-param list $right + * + * @psalm-return list + */ + public static function diff(array $left, array $right) : array { - return ['name', 'value']; + if ($right === []) { + return $left; + } + if ($left === []) { + return []; + } + $diff = []; + $rightTargets = array_map(static function ($dependency) { + return $dependency->getTarget(); + }, $right); + foreach ($left as $dependency) { + if (in_array($dependency->getTarget(), $rightTargets, \true)) { + continue; + } + $diff[] = $dependency; + } + return $diff; } - public function getType() : string + public function __construct(string $classOrCallableName, ?string $methodName = null, ?string $option = null) { - return 'Const'; + if ($classOrCallableName === '') { + return; + } + if (strpos($classOrCallableName, '::') !== \false) { + [$this->className, $this->methodName] = explode('::', $classOrCallableName); + } else { + $this->className = $classOrCallableName; + $this->methodName = !empty($methodName) ? $methodName : 'class'; + } + if ($option === 'clone') { + $this->useDeepClone = \true; + } elseif ($option === 'shallowClone') { + $this->useShallowClone = \true; + } } -} -attributes = $attributes; - $this->type = \is_string($type) ? new \PHPUnit\PhpParser\Node\Identifier($type) : $type; - $this->byRef = $byRef; - $this->variadic = $variadic; - $this->var = $var; - $this->default = $default; - $this->flags = $flags; - $this->attrGroups = $attrGroups; + return $this->getTarget(); + } + public function isValid() : bool + { + // Invalid dependencies can be declared and are skipped by the runner + return $this->className !== '' && $this->methodName !== ''; } - public function getSubNodeNames() : array + public function useShallowClone() : bool { - return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default']; + return $this->useShallowClone; } - public function getType() : string + public function useDeepClone() : bool { - return 'Param'; + return $this->useDeepClone; } -} -conds = $conds; - $this->body = $body; - $this->attributes = $attributes; + return $this->methodName === 'class'; } - public function getSubNodeNames() : array + public function getTarget() : string { - return ['conds', 'body']; + return $this->isValid() ? $this->className . '::' . $this->methodName : ''; } - public function getType() : string + public function getTargetClassName() : string { - return 'MatchArm'; + return $this->className; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; -use PHPUnit\PhpParser\NodeAbstract; -abstract class Stmt extends \PHPUnit\PhpParser\NodeAbstract +use Throwable; +/** + * @deprecated Use the `TestHook` interfaces instead + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface TestListener { + /** + * An error occurred. + * + * @deprecated Use `AfterTestErrorHook::executeAfterTestError` instead + */ + public function addError(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void; + /** + * A warning occurred. + * + * @deprecated Use `AfterTestWarningHook::executeAfterTestWarning` instead + */ + public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time) : void; + /** + * A failure occurred. + * + * @deprecated Use `AfterTestFailureHook::executeAfterTestFailure` instead + */ + public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time) : void; + /** + * Incomplete test. + * + * @deprecated Use `AfterIncompleteTestHook::executeAfterIncompleteTest` instead + */ + public function addIncompleteTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void; + /** + * Risky test. + * + * @deprecated Use `AfterRiskyTestHook::executeAfterRiskyTest` instead + */ + public function addRiskyTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void; + /** + * Skipped test. + * + * @deprecated Use `AfterSkippedTestHook::executeAfterSkippedTest` instead + */ + public function addSkippedTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void; + /** + * A test suite started. + */ + public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) : void; + /** + * A test suite ended. + */ + public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) : void; + /** + * A test started. + * + * @deprecated Use `BeforeTestHook::executeBeforeTest` instead + */ + public function startTest(\PHPUnit\Framework\Test $test) : void; + /** + * A test ended. + * + * @deprecated Use `AfterTestHook::executeAfterTest` instead + */ + public function endTest(\PHPUnit\Framework\Test $test, float $time) : void; } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; -use PHPUnit\PhpParser\NodeAbstract; -class UnionType extends \PHPUnit\PhpParser\NodeAbstract +use function assert; +use function count; +use function get_class; +use function sprintf; +use function trim; +use PHPUnit\Util\Filter; +use PHPUnit\Util\InvalidDataSetException; +use PHPUnit\Util\Test as TestUtil; +use ReflectionClass; +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestBuilder { - /** @var (Identifier|Name)[] Types */ - public $types; - /** - * Constructs a union type. - * - * @param (Identifier|Name)[] $types Types - * @param array $attributes Additional attributes - */ - public function __construct(array $types, array $attributes = []) + public function build(ReflectionClass $theClass, string $methodName) : \PHPUnit\Framework\Test { - $this->attributes = $attributes; - $this->types = $types; - } - public function getSubNodeNames() : array - { - return ['types']; + $className = $theClass->getName(); + if (!$theClass->isInstantiable()) { + return new \PHPUnit\Framework\ErrorTestCase(sprintf('Cannot instantiate class "%s".', $className)); + } + $backupSettings = TestUtil::getBackupSettings($className, $methodName); + $preserveGlobalState = TestUtil::getPreserveGlobalStateSettings($className, $methodName); + $runTestInSeparateProcess = TestUtil::getProcessIsolationSettings($className, $methodName); + $runClassInSeparateProcess = TestUtil::getClassProcessIsolationSettings($className, $methodName); + $constructor = $theClass->getConstructor(); + if ($constructor === null) { + throw new \PHPUnit\Framework\Exception('No valid test provided.'); + } + $parameters = $constructor->getParameters(); + // TestCase() or TestCase($name) + if (count($parameters) < 2) { + $test = $this->buildTestWithoutData($className); + } else { + try { + $data = TestUtil::getProvidedData($className, $methodName); + } catch (\PHPUnit\Framework\IncompleteTestError $e) { + $message = sprintf("Test for %s::%s marked incomplete by data provider\n%s", $className, $methodName, $this->throwableToString($e)); + $data = new \PHPUnit\Framework\IncompleteTestCase($className, $methodName, $message); + } catch (\PHPUnit\Framework\SkippedTestError $e) { + $message = sprintf("Test for %s::%s skipped by data provider\n%s", $className, $methodName, $this->throwableToString($e)); + $data = new \PHPUnit\Framework\SkippedTestCase($className, $methodName, $message); + } catch (Throwable $t) { + $message = sprintf("The data provider specified for %s::%s is invalid.\n%s", $className, $methodName, $this->throwableToString($t)); + $data = new \PHPUnit\Framework\ErrorTestCase($message); + } + // Test method with @dataProvider. + if (isset($data)) { + $test = $this->buildDataProviderTestSuite($methodName, $className, $data, $runTestInSeparateProcess, $preserveGlobalState, $runClassInSeparateProcess, $backupSettings); + } else { + $test = $this->buildTestWithoutData($className); + } + } + if ($test instanceof \PHPUnit\Framework\TestCase) { + $test->setName($methodName); + $this->configureTestCase($test, $runTestInSeparateProcess, $preserveGlobalState, $runClassInSeparateProcess, $backupSettings); + } + return $test; } - public function getType() : string + /** @psalm-param class-string $className */ + private function buildTestWithoutData(string $className) { - return 'UnionType'; + return new $className(); } -} -attributes = $attributes; - $this->name = $name; - $this->args = $args; + $dataProviderTestSuite = new \PHPUnit\Framework\DataProviderTestSuite($className . '::' . $methodName); + $groups = TestUtil::getGroups($className, $methodName); + if ($data instanceof \PHPUnit\Framework\ErrorTestCase || $data instanceof \PHPUnit\Framework\SkippedTestCase || $data instanceof \PHPUnit\Framework\IncompleteTestCase) { + $dataProviderTestSuite->addTest($data, $groups); + } else { + foreach ($data as $_dataName => $_data) { + $_test = new $className($methodName, $_data, $_dataName); + assert($_test instanceof \PHPUnit\Framework\TestCase); + $this->configureTestCase($_test, $runTestInSeparateProcess, $preserveGlobalState, $runClassInSeparateProcess, $backupSettings); + $dataProviderTestSuite->addTest($_test, $groups); + } + } + return $dataProviderTestSuite; } - public function getSubNodeNames() : array + private function configureTestCase(\PHPUnit\Framework\TestCase $test, bool $runTestInSeparateProcess, ?bool $preserveGlobalState, bool $runClassInSeparateProcess, array $backupSettings) : void { - return ['name', 'args']; + if ($runTestInSeparateProcess) { + $test->setRunTestInSeparateProcess(\true); + if ($preserveGlobalState !== null) { + $test->setPreserveGlobalState($preserveGlobalState); + } + } + if ($runClassInSeparateProcess) { + $test->setRunClassInSeparateProcess(\true); + if ($preserveGlobalState !== null) { + $test->setPreserveGlobalState($preserveGlobalState); + } + } + if ($backupSettings['backupGlobals'] !== null) { + $test->setBackupGlobals($backupSettings['backupGlobals']); + } + if ($backupSettings['backupStaticAttributes'] !== null) { + $test->setBackupStaticAttributes($backupSettings['backupStaticAttributes']); + } } - public function getType() : string + private function throwableToString(Throwable $t) : string { - return 'Attribute'; + $message = $t->getMessage(); + if (empty(trim($message))) { + $message = ''; + } + if ($t instanceof InvalidDataSetException) { + return sprintf("%s\n%s", $message, Filter::getFilteredStacktrace($t)); + } + return sprintf("%s: %s\n%s", get_class($t), $message, Filter::getFilteredStacktrace($t)); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; -use PHPUnit\PhpParser\NodeAbstract; +use const PHP_EOL; +use function count; +use function function_exists; +use function get_class; +use function sprintf; +use AssertionError; +use Countable; +use Error; +use PHPUnit\Util\ErrorHandler; +use PHPUnit\Util\ExcludeList; +use PHPUnit\Util\Printer; +use PHPUnit\Util\Test as TestUtil; +use ReflectionClass; +use ReflectionException; +use PHPUnit\SebastianBergmann\CodeCoverage\CodeCoverage; +use PHPUnit\SebastianBergmann\CodeCoverage\Exception as OriginalCodeCoverageException; +use PHPUnit\SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException; +use PHPUnit\SebastianBergmann\Invoker\Invoker; +use PHPUnit\SebastianBergmann\Invoker\TimeoutException; +use PHPUnit\SebastianBergmann\ResourceOperations\ResourceOperations; +use PHPUnit\SebastianBergmann\Timer\Timer; +use Throwable; /** - * Represents a non-namespaced name. Namespaced names are represented using Name nodes. + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class Identifier extends \PHPUnit\PhpParser\NodeAbstract +final class TestResult implements Countable { - /** @var string Identifier as string */ - public $name; - private static $specialClassNames = ['self' => \true, 'parent' => \true, 'static' => \true]; /** - * Constructs an identifier node. - * - * @param string $name Identifier as string - * @param array $attributes Additional attributes + * @var array */ - public function __construct(string $name, array $attributes = []) - { - $this->attributes = $attributes; - $this->name = $name; - } - public function getSubNodeNames() : array - { - return ['name']; - } + private $passed = []; /** - * Get identifier as string. - * - * @return string Identifier as string. + * @var array */ - public function toString() : string - { - return $this->name; - } + private $passedTestClasses = []; /** - * Get lowercased identifier as string. - * - * @return string Lowercased identifier as string + * @var bool */ - public function toLowerString() : string - { - return \strtolower($this->name); - } + private $currentTestSuiteFailed = \false; /** - * Checks whether the identifier is a special class name (self, parent or static). - * - * @return bool Whether identifier is a special class name + * @var TestFailure[] */ - public function isSpecialClassName() : bool - { - return isset(self::$specialClassNames[\strtolower($this->name)]); - } + private $errors = []; /** - * Get identifier as string. - * - * @return string Identifier as string + * @var TestFailure[] */ - public function __toString() : string - { - return $this->name; - } - public function getType() : string - { - return 'Identifier'; - } -} - \true, 'parent' => \true, 'static' => \true]; + private $failures = []; /** - * Constructs a name node. - * - * @param string|string[]|self $name Name as string, part array or Name instance (copy ctor) - * @param array $attributes Additional attributes + * @var TestFailure[] */ - public function __construct($name, array $attributes = []) - { - $this->attributes = $attributes; - $this->parts = self::prepareName($name); - } - public function getSubNodeNames() : array - { - return ['parts']; - } + private $warnings = []; /** - * Gets the first part of the name, i.e. everything before the first namespace separator. - * - * @return string First part of the name + * @var TestFailure[] */ - public function getFirst() : string - { - return $this->parts[0]; - } + private $notImplemented = []; /** - * Gets the last part of the name, i.e. everything after the last namespace separator. - * - * @return string Last part of the name + * @var TestFailure[] */ - public function getLast() : string - { - return $this->parts[\count($this->parts) - 1]; - } + private $risky = []; /** - * Checks whether the name is unqualified. (E.g. Name) - * - * @return bool Whether the name is unqualified + * @var TestFailure[] */ - public function isUnqualified() : bool - { - return 1 === \count($this->parts); - } + private $skipped = []; /** - * Checks whether the name is qualified. (E.g. Name\Name) + * @deprecated Use the `TestHook` interfaces instead * - * @return bool Whether the name is qualified + * @var TestListener[] */ - public function isQualified() : bool - { - return 1 < \count($this->parts); - } + private $listeners = []; /** - * Checks whether the name is fully qualified. (E.g. \Name) - * - * @return bool Whether the name is fully qualified + * @var int */ - public function isFullyQualified() : bool - { - return \false; - } + private $runTests = 0; /** - * Checks whether the name is explicitly relative to the current namespace. (E.g. namespace\Name) - * - * @return bool Whether the name is relative + * @var float */ - public function isRelative() : bool - { - return \false; - } + private $time = 0; /** - * Returns a string representation of the name itself, without taking the name type into - * account (e.g., not including a leading backslash for fully qualified names). + * Code Coverage information. * - * @return string String representation + * @var CodeCoverage */ - public function toString() : string - { - return \implode('\\', $this->parts); - } + private $codeCoverage; /** - * Returns a string representation of the name as it would occur in code (e.g., including - * leading backslash for fully qualified names. - * - * @return string String representation + * @var bool */ - public function toCodeString() : string - { - return $this->toString(); - } + private $convertDeprecationsToExceptions = \false; /** - * Returns lowercased string representation of the name, without taking the name type into - * account (e.g., no leading backslash for fully qualified names). - * - * @return string Lowercased string representation + * @var bool */ - public function toLowerString() : string - { - return \strtolower(\implode('\\', $this->parts)); - } + private $convertErrorsToExceptions = \true; /** - * Checks whether the identifier is a special class name (self, parent or static). - * - * @return bool Whether identifier is a special class name + * @var bool */ - public function isSpecialClassName() : bool - { - return \count($this->parts) === 1 && isset(self::$specialClassNames[\strtolower($this->parts[0])]); - } + private $convertNoticesToExceptions = \true; /** - * Returns a string representation of the name by imploding the namespace parts with the - * namespace separator. - * - * @return string String representation + * @var bool + */ + private $convertWarningsToExceptions = \true; + /** + * @var bool + */ + private $stop = \false; + /** + * @var bool + */ + private $stopOnError = \false; + /** + * @var bool + */ + private $stopOnFailure = \false; + /** + * @var bool + */ + private $stopOnWarning = \false; + /** + * @var bool + */ + private $beStrictAboutTestsThatDoNotTestAnything = \true; + /** + * @var bool + */ + private $beStrictAboutOutputDuringTests = \false; + /** + * @var bool + */ + private $beStrictAboutTodoAnnotatedTests = \false; + /** + * @var bool + */ + private $beStrictAboutResourceUsageDuringSmallTests = \false; + /** + * @var bool + */ + private $enforceTimeLimit = \false; + /** + * @var bool + */ + private $forceCoversAnnotation = \false; + /** + * @var int */ - public function __toString() : string - { - return \implode('\\', $this->parts); - } + private $timeoutForSmallTests = 1; /** - * Gets a slice of a name (similar to array_slice). - * - * This method returns a new instance of the same type as the original and with the same - * attributes. - * - * If the slice is empty, null is returned. The null value will be correctly handled in - * concatenations using concat(). - * - * Offset and length have the same meaning as in array_slice(). - * - * @param int $offset Offset to start the slice at (may be negative) - * @param int|null $length Length of the slice (may be negative) - * - * @return static|null Sliced name + * @var int */ - public function slice(int $offset, int $length = null) - { - $numParts = \count($this->parts); - $realOffset = $offset < 0 ? $offset + $numParts : $offset; - if ($realOffset < 0 || $realOffset > $numParts) { - throw new \OutOfBoundsException(\sprintf('Offset %d is out of bounds', $offset)); - } - if (null === $length) { - $realLength = $numParts - $realOffset; - } else { - $realLength = $length < 0 ? $length + $numParts - $realOffset : $length; - if ($realLength < 0 || $realLength > $numParts) { - throw new \OutOfBoundsException(\sprintf('Length %d is out of bounds', $length)); - } - } - if ($realLength === 0) { - // Empty slice is represented as null - return null; - } - return new static(\array_slice($this->parts, $realOffset, $realLength), $this->attributes); - } + private $timeoutForMediumTests = 10; /** - * Concatenate two names, yielding a new Name instance. - * - * The type of the generated instance depends on which class this method is called on, for - * example Name\FullyQualified::concat() will yield a Name\FullyQualified instance. - * - * If one of the arguments is null, a new instance of the other name will be returned. If both - * arguments are null, null will be returned. As such, writing - * Name::concat($namespace, $shortName) - * where $namespace is a Name node or null will work as expected. - * - * @param string|string[]|self|null $name1 The first name - * @param string|string[]|self|null $name2 The second name - * @param array $attributes Attributes to assign to concatenated name - * - * @return static|null Concatenated name + * @var int */ - public static function concat($name1, $name2, array $attributes = []) - { - if (null === $name1 && null === $name2) { - return null; - } elseif (null === $name1) { - return new static(self::prepareName($name2), $attributes); - } elseif (null === $name2) { - return new static(self::prepareName($name1), $attributes); - } else { - return new static(\array_merge(self::prepareName($name1), self::prepareName($name2)), $attributes); - } - } + private $timeoutForLargeTests = 60; /** - * Prepares a (string, array or Name node) name for use in name changing methods by converting - * it to an array. - * - * @param string|string[]|self $name Name to prepare - * - * @return string[] Prepared name + * @var bool */ - private static function prepareName($name) : array - { - if (\is_string($name)) { - if ('' === $name) { - throw new \InvalidArgumentException('Name cannot be empty'); - } - return \explode('\\', $name); - } elseif (\is_array($name)) { - if (empty($name)) { - throw new \InvalidArgumentException('Name cannot be empty'); - } - return $name; - } elseif ($name instanceof self) { - return $name->parts; - } - throw new \InvalidArgumentException('Expected string, array of parts or Name instance'); - } - public function getType() : string - { - return 'Name'; - } -} -attributes = $attributes; - $this->name = $name; - $this->value = $value; - $this->byRef = $byRef; - $this->unpack = $unpack; - } - public function getSubNodeNames() : array - { - return ['name', 'value', 'byRef', 'unpack']; - } - public function getType() : string - { - return 'Arg'; - } -} -toString(); - } - public function getType() : string - { - return 'Name_Relative'; - } -} -listeners[] = $listener; } /** - * Checks whether the name is fully qualified. (E.g. \Name) + * @deprecated Use the `TestHook` interfaces instead * - * @return bool Whether the name is fully qualified + * @codeCoverageIgnore + * + * Unregisters a TestListener. */ - public function isFullyQualified() : bool + public function removeListener(\PHPUnit\Framework\TestListener $listener) : void { - return \true; + foreach ($this->listeners as $key => $_listener) { + if ($listener === $_listener) { + unset($this->listeners[$key]); + } + } } /** - * Checks whether the name is explicitly relative to the current namespace. (E.g. namespace\Name) + * @deprecated Use the `TestHook` interfaces instead * - * @return bool Whether the name is relative + * @codeCoverageIgnore + * + * Flushes all flushable TestListeners. */ - public function isRelative() : bool - { - return \false; - } - public function toCodeString() : string - { - return '\\' . $this->toString(); - } - public function getType() : string + public function flushListeners() : void { - return 'Name_FullyQualified'; + foreach ($this->listeners as $listener) { + if ($listener instanceof Printer) { + $listener->flush(); + } + } } -} -attributes = $attributes; - $this->attrs = $attrs; - } - public function getSubNodeNames() : array - { - return ['attrs']; - } - public function getType() : string + public function addError(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void { - return 'AttributeGroup'; + if ($t instanceof \PHPUnit\Framework\RiskyTestError) { + $this->recordRisky($test, $t); + $notifyMethod = 'addRiskyTest'; + if ($test instanceof \PHPUnit\Framework\TestCase) { + $test->markAsRisky(); + } + if ($this->stopOnRisky || $this->stopOnDefect) { + $this->stop(); + } + } elseif ($t instanceof \PHPUnit\Framework\IncompleteTest) { + $this->recordNotImplemented($test, $t); + $notifyMethod = 'addIncompleteTest'; + if ($this->stopOnIncomplete) { + $this->stop(); + } + } elseif ($t instanceof \PHPUnit\Framework\SkippedTest) { + $this->recordSkipped($test, $t); + $notifyMethod = 'addSkippedTest'; + if ($this->stopOnSkipped) { + $this->stop(); + } + } else { + $this->recordError($test, $t); + $notifyMethod = 'addError'; + if ($this->stopOnError || $this->stopOnFailure) { + $this->stop(); + } + } + // @see https://github.com/sebastianbergmann/phpunit/issues/1953 + if ($t instanceof Error) { + $t = new \PHPUnit\Framework\ExceptionWrapper($t); + } + foreach ($this->listeners as $listener) { + $listener->{$notifyMethod}($test, $t, $time); + } + $this->lastTestFailed = \true; + $this->time += $time; } -} -attributes = $attributes; - $this->name = \is_string($name) ? new \PHPUnit\PhpParser\Node\Identifier($name) : $name; - } - public function getSubNodeNames() : array - { - return ['name']; - } - public function getType() : string + public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time) : void { - return 'Stmt_Goto'; + if ($this->stopOnWarning || $this->stopOnDefect) { + $this->stop(); + } + $this->recordWarning($test, $e); + foreach ($this->listeners as $listener) { + $listener->addWarning($test, $e, $time); + } + $this->time += $time; } -} -attributes = $attributes; - $this->flags = $flags; - $this->consts = $consts; - $this->attrGroups = $attrGroups; - } - public function getSubNodeNames() : array + public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time) : void { - return ['attrGroups', 'flags', 'consts']; + if ($e instanceof \PHPUnit\Framework\RiskyTestError || $e instanceof \PHPUnit\Framework\OutputError) { + $this->recordRisky($test, $e); + $notifyMethod = 'addRiskyTest'; + if ($test instanceof \PHPUnit\Framework\TestCase) { + $test->markAsRisky(); + } + if ($this->stopOnRisky || $this->stopOnDefect) { + $this->stop(); + } + } elseif ($e instanceof \PHPUnit\Framework\IncompleteTest) { + $this->recordNotImplemented($test, $e); + $notifyMethod = 'addIncompleteTest'; + if ($this->stopOnIncomplete) { + $this->stop(); + } + } elseif ($e instanceof \PHPUnit\Framework\SkippedTest) { + $this->recordSkipped($test, $e); + $notifyMethod = 'addSkippedTest'; + if ($this->stopOnSkipped) { + $this->stop(); + } + } else { + $this->failures[] = new \PHPUnit\Framework\TestFailure($test, $e); + $notifyMethod = 'addFailure'; + if ($this->stopOnFailure || $this->stopOnDefect) { + $this->stop(); + } + } + foreach ($this->listeners as $listener) { + $listener->{$notifyMethod}($test, $e, $time); + } + $this->lastTestFailed = \true; + $this->time += $time; } /** - * Whether constant is explicitly or implicitly public. - * - * @return bool + * Informs the result that a test suite will be started. */ - public function isPublic() : bool + public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) : void { - return ($this->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC) !== 0 || ($this->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::VISIBILITY_MODIFIER_MASK) === 0; + $this->currentTestSuiteFailed = \false; + foreach ($this->listeners as $listener) { + $listener->startTestSuite($suite); + } } /** - * Whether constant is protected. - * - * @return bool + * Informs the result that a test suite was completed. */ - public function isProtected() : bool + public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) : void { - return (bool) ($this->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED); + if (!$this->currentTestSuiteFailed) { + $this->passedTestClasses[] = $suite->getName(); + } + foreach ($this->listeners as $listener) { + $listener->endTestSuite($suite); + } } /** - * Whether constant is private. - * - * @return bool + * Informs the result that a test will be started. */ - public function isPrivate() : bool - { - return (bool) ($this->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE); - } - public function getType() : string + public function startTest(\PHPUnit\Framework\Test $test) : void { - return 'Stmt_ClassConst'; + $this->lastTestFailed = \false; + $this->runTests += count($test); + foreach ($this->listeners as $listener) { + $listener->startTest($test); + } } -} -attributes = $attributes; - $this->expr = $expr; - } - public function getSubNodeNames() : array - { - return ['expr']; - } - public function getType() : string + public function endTest(\PHPUnit\Framework\Test $test, float $time) : void { - return 'Stmt_Return'; + foreach ($this->listeners as $listener) { + $listener->endTest($test, $time); + } + if (!$this->lastTestFailed && $test instanceof \PHPUnit\Framework\TestCase) { + $class = get_class($test); + $key = $class . '::' . $test->getName(); + $this->passed[$key] = ['result' => $test->getResult(), 'size' => \PHPUnit\Util\Test::getSize($class, $test->getName(\false))]; + $this->time += $time; + } + if ($this->lastTestFailed && $test instanceof \PHPUnit\Framework\TestCase) { + $this->currentTestSuiteFailed = \true; + } } -} -attributes = $attributes; - $this->cond = $cond; - $this->stmts = $stmts; - } - public function getSubNodeNames() : array + public function allHarmless() : bool { - return ['stmts', 'cond']; + return $this->riskyCount() === 0; } - public function getType() : string + /** + * Gets the number of risky tests. + */ + public function riskyCount() : int { - return 'Stmt_Do'; + return count($this->risky); } -} -attributes = $attributes; - $this->type = $type; - $this->prefix = $prefix; - $this->uses = $uses; + return $this->notImplementedCount() === 0; } - public function getSubNodeNames() : array + /** + * Gets the number of incomplete tests. + */ + public function notImplementedCount() : int { - return ['type', 'prefix', 'uses']; + return count($this->notImplemented); } - public function getType() : string + /** + * Returns an array of TestFailure objects for the risky tests. + * + * @return TestFailure[] + */ + public function risky() : array { - return 'Stmt_GroupUse'; + return $this->risky; } -} -attributes = $attributes; - $this->expr = $expr; + return $this->notImplemented; } - public function getSubNodeNames() : array + /** + * Returns true if no test has been skipped. + */ + public function noneSkipped() : bool { - return ['expr']; + return $this->skippedCount() === 0; } - public function getType() : string + /** + * Gets the number of skipped tests. + */ + public function skippedCount() : int { - return 'Stmt_Throw'; + return count($this->skipped); } -} -attributes = $attributes; - $this->flags = $flags; - $this->props = $props; - $this->type = \is_string($type) ? new \PHPUnit\PhpParser\Node\Identifier($type) : $type; - $this->attrGroups = $attrGroups; + return $this->skipped; } - public function getSubNodeNames() : array + /** + * Gets the number of detected errors. + */ + public function errorCount() : int { - return ['attrGroups', 'flags', 'type', 'props']; + return count($this->errors); } /** - * Whether the property is explicitly or implicitly public. + * Returns an array of TestFailure objects for the errors. * - * @return bool + * @return TestFailure[] */ - public function isPublic() : bool + public function errors() : array { - return ($this->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC) !== 0 || ($this->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::VISIBILITY_MODIFIER_MASK) === 0; + return $this->errors; } /** - * Whether the property is protected. - * - * @return bool + * Gets the number of detected failures. */ - public function isProtected() : bool + public function failureCount() : int { - return (bool) ($this->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED); + return count($this->failures); } /** - * Whether the property is private. + * Returns an array of TestFailure objects for the failures. * - * @return bool + * @return TestFailure[] */ - public function isPrivate() : bool + public function failures() : array { - return (bool) ($this->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE); + return $this->failures; } /** - * Whether the property is static. + * Gets the number of detected warnings. + */ + public function warningCount() : int + { + return count($this->warnings); + } + /** + * Returns an array of TestFailure objects for the warnings. * - * @return bool + * @return TestFailure[] */ - public function isStatic() : bool + public function warnings() : array { - return (bool) ($this->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_STATIC); + return $this->warnings; } - public function getType() : string + /** + * Returns the names of the tests that have passed. + */ + public function passed() : array { - return 'Stmt_Property'; + return $this->passed; } -} -attributes = $attributes; - $this->stmts = $stmts; + return $this->passedTestClasses; } - public function getSubNodeNames() : array + /** + * Returns whether code coverage information should be collected. + */ + public function getCollectCodeCoverageInformation() : bool { - return ['stmts']; + return $this->codeCoverage !== null; } - public function getType() : string + /** + * Runs a TestCase. + * + * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws CodeCoverageException + * @throws UnintentionallyCoveredCodeException + */ + public function run(\PHPUnit\Framework\Test $test) : void { - return 'Stmt_Finally'; + \PHPUnit\Framework\Assert::resetCount(); + if ($test instanceof \PHPUnit\Framework\TestCase) { + $test->setRegisterMockObjectsFromTestArgumentsRecursively($this->registerMockObjectsFromTestArgumentsRecursively); + $isAnyCoverageRequired = TestUtil::requiresCodeCoverageDataCollection($test); + } + $error = \false; + $failure = \false; + $warning = \false; + $incomplete = \false; + $risky = \false; + $skipped = \false; + $this->startTest($test); + if ($this->convertDeprecationsToExceptions || $this->convertErrorsToExceptions || $this->convertNoticesToExceptions || $this->convertWarningsToExceptions) { + $errorHandler = new ErrorHandler($this->convertDeprecationsToExceptions, $this->convertErrorsToExceptions, $this->convertNoticesToExceptions, $this->convertWarningsToExceptions); + $errorHandler->register(); + } + $collectCodeCoverage = $this->codeCoverage !== null && !$test instanceof \PHPUnit\Framework\ErrorTestCase && !$test instanceof \PHPUnit\Framework\WarningTestCase && $isAnyCoverageRequired; + if ($collectCodeCoverage) { + $this->codeCoverage->start($test); + } + $monitorFunctions = $this->beStrictAboutResourceUsageDuringSmallTests && !$test instanceof \PHPUnit\Framework\ErrorTestCase && !$test instanceof \PHPUnit\Framework\WarningTestCase && $test->getSize() === \PHPUnit\Util\Test::SMALL && function_exists('xdebug_start_function_monitor'); + if ($monitorFunctions) { + /* @noinspection ForgottenDebugOutputInspection */ + \xdebug_start_function_monitor(ResourceOperations::getFunctions()); + } + $timer = new Timer(); + $timer->start(); + try { + $invoker = new Invoker(); + if (!$test instanceof \PHPUnit\Framework\ErrorTestCase && !$test instanceof \PHPUnit\Framework\WarningTestCase && $this->enforceTimeLimit && ($this->defaultTimeLimit || $test->getSize() != \PHPUnit\Util\Test::UNKNOWN) && $invoker->canInvokeWithTimeout()) { + switch ($test->getSize()) { + case \PHPUnit\Util\Test::SMALL: + $_timeout = $this->timeoutForSmallTests; + break; + case \PHPUnit\Util\Test::MEDIUM: + $_timeout = $this->timeoutForMediumTests; + break; + case \PHPUnit\Util\Test::LARGE: + $_timeout = $this->timeoutForLargeTests; + break; + case \PHPUnit\Util\Test::UNKNOWN: + $_timeout = $this->defaultTimeLimit; + break; + } + $invoker->invoke([$test, 'runBare'], [], $_timeout); + } else { + $test->runBare(); + } + } catch (TimeoutException $e) { + $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError($e->getMessage()), $_timeout); + $risky = \true; + } catch (\PHPUnit\Framework\AssertionFailedError $e) { + $failure = \true; + if ($e instanceof \PHPUnit\Framework\RiskyTestError) { + $risky = \true; + } elseif ($e instanceof \PHPUnit\Framework\IncompleteTestError) { + $incomplete = \true; + } elseif ($e instanceof \PHPUnit\Framework\SkippedTestError) { + $skipped = \true; + } + } catch (AssertionError $e) { + $test->addToAssertionCount(1); + $failure = \true; + $frame = $e->getTrace()[0]; + $e = new \PHPUnit\Framework\AssertionFailedError(sprintf('%s in %s:%s', $e->getMessage(), $frame['file'], $frame['line'])); + } catch (\PHPUnit\Framework\Warning $e) { + $warning = \true; + } catch (\PHPUnit\Framework\Exception $e) { + $error = \true; + } catch (Throwable $e) { + $e = new \PHPUnit\Framework\ExceptionWrapper($e); + $error = \true; + } + $time = $timer->stop()->asSeconds(); + $test->addToAssertionCount(\PHPUnit\Framework\Assert::getCount()); + if ($monitorFunctions) { + $excludeList = new ExcludeList(); + /** @noinspection ForgottenDebugOutputInspection */ + $functions = \xdebug_get_monitored_functions(); + /* @noinspection ForgottenDebugOutputInspection */ + \xdebug_stop_function_monitor(); + foreach ($functions as $function) { + if (!$excludeList->isExcluded($function['filename'])) { + $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError(sprintf('%s() used in %s:%s', $function['function'], $function['filename'], $function['lineno'])), $time); + } + } + } + if ($this->beStrictAboutTestsThatDoNotTestAnything && $test->getNumAssertions() === 0) { + $risky = \true; + } + if ($this->forceCoversAnnotation && !$error && !$failure && !$warning && !$incomplete && !$skipped && !$risky) { + $annotations = TestUtil::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); + if (!isset($annotations['class']['covers']) && !isset($annotations['method']['covers']) && !isset($annotations['class']['coversNothing']) && !isset($annotations['method']['coversNothing'])) { + $this->addFailure($test, new \PHPUnit\Framework\MissingCoversAnnotationException('This test does not have a @covers annotation but is expected to have one'), $time); + $risky = \true; + } + } + if ($collectCodeCoverage) { + $append = !$risky && !$incomplete && !$skipped; + $linesToBeCovered = []; + $linesToBeUsed = []; + if ($append && $test instanceof \PHPUnit\Framework\TestCase) { + try { + $linesToBeCovered = \PHPUnit\Util\Test::getLinesToBeCovered(get_class($test), $test->getName(\false)); + $linesToBeUsed = \PHPUnit\Util\Test::getLinesToBeUsed(get_class($test), $test->getName(\false)); + } catch (\PHPUnit\Framework\InvalidCoversTargetException $cce) { + $this->addWarning($test, new \PHPUnit\Framework\Warning($cce->getMessage()), $time); + } + } + try { + $this->codeCoverage->stop($append, $linesToBeCovered, $linesToBeUsed); + } catch (UnintentionallyCoveredCodeException $cce) { + $unintentionallyCoveredCodeError = new \PHPUnit\Framework\UnintentionallyCoveredCodeError('This test executed code that is not listed as code to be covered or used:' . \PHP_EOL . $cce->getMessage()); + } catch (OriginalCodeCoverageException $cce) { + $error = \true; + $e = $e ?? $cce; + } + } + if (isset($errorHandler)) { + $errorHandler->unregister(); + unset($errorHandler); + } + if ($error) { + $this->addError($test, $e, $time); + } elseif ($failure) { + $this->addFailure($test, $e, $time); + } elseif ($warning) { + $this->addWarning($test, $e, $time); + } elseif (isset($unintentionallyCoveredCodeError)) { + $this->addFailure($test, $unintentionallyCoveredCodeError, $time); + } elseif ($this->beStrictAboutTestsThatDoNotTestAnything && !$test->doesNotPerformAssertions() && $test->getNumAssertions() === 0) { + try { + $reflected = new ReflectionClass($test); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + $name = $test->getName(\false); + if ($name && $reflected->hasMethod($name)) { + try { + $reflected = $reflected->getMethod($name); + // @codeCoverageIgnoreStart + } catch (ReflectionException $e) { + throw new \PHPUnit\Framework\Exception($e->getMessage(), (int) $e->getCode(), $e); + } + // @codeCoverageIgnoreEnd + } + $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError(sprintf("This test did not perform any assertions\n\n%s:%d", $reflected->getFileName(), $reflected->getStartLine())), $time); + } elseif ($this->beStrictAboutTestsThatDoNotTestAnything && $test->doesNotPerformAssertions() && $test->getNumAssertions() > 0) { + $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError(sprintf('This test is annotated with "@doesNotPerformAssertions" but performed %d assertions', $test->getNumAssertions())), $time); + } elseif ($this->beStrictAboutOutputDuringTests && $test->hasOutput()) { + $this->addFailure($test, new \PHPUnit\Framework\OutputError(sprintf('This test printed output: %s', $test->getActualOutput())), $time); + } elseif ($this->beStrictAboutTodoAnnotatedTests && $test instanceof \PHPUnit\Framework\TestCase) { + $annotations = TestUtil::parseTestMethodAnnotations(get_class($test), $test->getName(\false)); + if (isset($annotations['method']['todo'])) { + $this->addFailure($test, new \PHPUnit\Framework\RiskyTestError('Test method is annotated with @todo'), $time); + } + } + $this->endTest($test, $time); } -} -attributes = $attributes; - $this->exprs = $exprs; + return $this->runTests; } - public function getSubNodeNames() : array + /** + * Checks whether the test run should stop. + */ + public function shouldStop() : bool { - return ['exprs']; + return $this->stop; } - public function getType() : string + /** + * Marks that the test run should stop. + */ + public function stop() : void { - return 'Stmt_Echo'; + $this->stop = \true; } -} -attributes = $attributes; - $this->expr = $expr; + return $this->codeCoverage; } - public function getSubNodeNames() : array + /** + * Sets the code coverage object. + */ + public function setCodeCoverage(CodeCoverage $codeCoverage) : void { - return ['expr']; + $this->codeCoverage = $codeCoverage; } - public function getType() : string + /** + * Enables or disables the deprecation-to-exception conversion. + */ + public function convertDeprecationsToExceptions(bool $flag) : void { - return 'Stmt_Expression'; + $this->convertDeprecationsToExceptions = $flag; } -} -convertDeprecationsToExceptions; } - public function getType() : string + /** + * Enables or disables the error-to-exception conversion. + */ + public function convertErrorsToExceptions(bool $flag) : void { - return 'Stmt_Nop'; + $this->convertErrorsToExceptions = $flag; } -} -attributes = $attributes; - $this->declares = $declares; - $this->stmts = $stmts; + return $this->convertErrorsToExceptions; } - public function getSubNodeNames() : array + /** + * Enables or disables the notice-to-exception conversion. + */ + public function convertNoticesToExceptions(bool $flag) : void { - return ['declares', 'stmts']; + $this->convertNoticesToExceptions = $flag; } - public function getType() : string + /** + * Returns the notice-to-exception conversion setting. + */ + public function getConvertNoticesToExceptions() : bool { - return 'Stmt_Declare'; + return $this->convertNoticesToExceptions; } -} -attributes = $attributes; - $this->value = $value; + $this->convertWarningsToExceptions = $flag; } - public function getSubNodeNames() : array + /** + * Returns the warning-to-exception conversion setting. + */ + public function getConvertWarningsToExceptions() : bool { - return ['value']; + return $this->convertWarningsToExceptions; } - public function getType() : string + /** + * Enables or disables the stopping when an error occurs. + */ + public function stopOnError(bool $flag) : void { - return 'Stmt_InlineHTML'; + $this->stopOnError = $flag; + } + /** + * Enables or disables the stopping when a failure occurs. + */ + public function stopOnFailure(bool $flag) : void + { + $this->stopOnFailure = $flag; } -} - null : Variable to assign key to - * 'byRef' => false : Whether to assign value by reference - * 'stmts' => array(): Statements - * @param array $attributes Additional attributes + * Enables or disables the stopping when a warning occurs. */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $expr, \PHPUnit\PhpParser\Node\Expr $valueVar, array $subNodes = [], array $attributes = []) + public function stopOnWarning(bool $flag) : void { - $this->attributes = $attributes; - $this->expr = $expr; - $this->keyVar = $subNodes['keyVar'] ?? null; - $this->byRef = $subNodes['byRef'] ?? \false; - $this->valueVar = $valueVar; - $this->stmts = $subNodes['stmts'] ?? []; + $this->stopOnWarning = $flag; } - public function getSubNodeNames() : array + public function beStrictAboutTestsThatDoNotTestAnything(bool $flag) : void { - return ['expr', 'keyVar', 'byRef', 'valueVar', 'stmts']; + $this->beStrictAboutTestsThatDoNotTestAnything = $flag; } - public function getType() : string + public function isStrictAboutTestsThatDoNotTestAnything() : bool { - return 'Stmt_Foreach'; + return $this->beStrictAboutTestsThatDoNotTestAnything; } -} -attributes = $attributes; - $this->cond = $cond; - $this->stmts = $stmts; + $this->beStrictAboutOutputDuringTests = $flag; } - public function getSubNodeNames() : array + public function isStrictAboutOutputDuringTests() : bool { - return ['cond', 'stmts']; + return $this->beStrictAboutOutputDuringTests; } - public function getType() : string + public function beStrictAboutResourceUsageDuringSmallTests(bool $flag) : void { - return 'Stmt_ElseIf'; + $this->beStrictAboutResourceUsageDuringSmallTests = $flag; } -} -attributes = $attributes; - $this->name = $name; - $this->stmts = $stmts; + return $this->beStrictAboutResourceUsageDuringSmallTests; } - public function getSubNodeNames() : array + public function enforceTimeLimit(bool $flag) : void { - return ['name', 'stmts']; + $this->enforceTimeLimit = $flag; } - public function getType() : string + public function enforcesTimeLimit() : bool { - return 'Stmt_Namespace'; + return $this->enforceTimeLimit; } -} -attributes = $attributes; - $this->consts = $consts; + $this->beStrictAboutTodoAnnotatedTests = $flag; } - public function getSubNodeNames() : array + public function isStrictAboutTodoAnnotatedTests() : bool { - return ['consts']; + return $this->beStrictAboutTodoAnnotatedTests; } - public function getType() : string + public function forceCoversAnnotation() : void { - return 'Stmt_Const'; + $this->forceCoversAnnotation = \true; } -} -attributes = $attributes; - $this->trait = $trait; - $this->method = \is_string($method) ? new \PHPUnit\PhpParser\Node\Identifier($method) : $method; - $this->insteadof = $insteadof; + return $this->forceCoversAnnotation; } - public function getSubNodeNames() : array + /** + * Enables or disables the stopping for risky tests. + */ + public function stopOnRisky(bool $flag) : void { - return ['trait', 'method', 'insteadof']; + $this->stopOnRisky = $flag; } - public function getType() : string + /** + * Enables or disables the stopping for incomplete tests. + */ + public function stopOnIncomplete(bool $flag) : void { - return 'Stmt_TraitUseAdaptation_Precedence'; + $this->stopOnIncomplete = $flag; } -} -attributes = $attributes; - $this->trait = $trait; - $this->method = \is_string($method) ? new \PHPUnit\PhpParser\Node\Identifier($method) : $method; - $this->newModifier = $newModifier; - $this->newName = \is_string($newName) ? new \PHPUnit\PhpParser\Node\Identifier($newName) : $newName; + $this->stopOnSkipped = $flag; } - public function getSubNodeNames() : array + /** + * Enables or disables the stopping for defects: error, failure, warning. + */ + public function stopOnDefect(bool $flag) : void { - return ['trait', 'method', 'newModifier', 'newName']; + $this->stopOnDefect = $flag; } - public function getType() : string + /** + * Returns the time spent running the tests. + */ + public function time() : float { - return 'Stmt_TraitUseAdaptation_Alias'; + return $this->time; } -} -attributes = $attributes; - $this->var = $var; - $this->default = $default; + return $this->wasSuccessfulIgnoringWarnings() && empty($this->warnings); } - public function getSubNodeNames() : array + public function wasSuccessfulIgnoringWarnings() : bool { - return ['var', 'default']; + return empty($this->errors) && empty($this->failures); } - public function getType() : string + public function wasSuccessfulAndNoTestIsRiskyOrSkippedOrIncomplete() : bool { - return 'Stmt_StaticVar'; + return $this->wasSuccessful() && $this->allHarmless() && $this->allCompletelyImplemented() && $this->noneSkipped(); } -} -stmts as $stmt) { - if ($stmt instanceof \PHPUnit\PhpParser\Node\Stmt\TraitUse) { - $traitUses[] = $stmt; - } - } - return $traitUses; + $this->defaultTimeLimit = $timeout; } /** - * @return ClassConst[] + * Sets the timeout for small tests. */ - public function getConstants() : array + public function setTimeoutForSmallTests(int $timeout) : void { - $constants = []; - foreach ($this->stmts as $stmt) { - if ($stmt instanceof \PHPUnit\PhpParser\Node\Stmt\ClassConst) { - $constants[] = $stmt; - } - } - return $constants; + $this->timeoutForSmallTests = $timeout; } /** - * @return Property[] + * Sets the timeout for medium tests. */ - public function getProperties() : array + public function setTimeoutForMediumTests(int $timeout) : void { - $properties = []; - foreach ($this->stmts as $stmt) { - if ($stmt instanceof \PHPUnit\PhpParser\Node\Stmt\Property) { - $properties[] = $stmt; - } - } - return $properties; + $this->timeoutForMediumTests = $timeout; } /** - * Gets property with the given name defined directly in this class/interface/trait. - * - * @param string $name Name of the property - * - * @return Property|null Property node or null if the property does not exist + * Sets the timeout for large tests. */ - public function getProperty(string $name) + public function setTimeoutForLargeTests(int $timeout) : void { - foreach ($this->stmts as $stmt) { - if ($stmt instanceof \PHPUnit\PhpParser\Node\Stmt\Property) { - foreach ($stmt->props as $prop) { - if ($prop instanceof \PHPUnit\PhpParser\Node\Stmt\PropertyProperty && $name === $prop->name->toString()) { - return $stmt; - } - } - } - } - return null; + $this->timeoutForLargeTests = $timeout; } /** - * Gets all methods defined directly in this class/interface/trait - * - * @return ClassMethod[] + * Returns the set timeout for large tests. */ - public function getMethods() : array + public function getTimeoutForLargeTests() : int { - $methods = []; - foreach ($this->stmts as $stmt) { - if ($stmt instanceof \PHPUnit\PhpParser\Node\Stmt\ClassMethod) { - $methods[] = $stmt; - } - } - return $methods; + return $this->timeoutForLargeTests; } - /** - * Gets method with the given name defined directly in this class/interface/trait. - * - * @param string $name Name of the method (compared case-insensitively) - * - * @return ClassMethod|null Method node or null if the method does not exist - */ - public function getMethod(string $name) + public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag) : void { - $lowerName = \strtolower($name); - foreach ($this->stmts as $stmt) { - if ($stmt instanceof \PHPUnit\PhpParser\Node\Stmt\ClassMethod && $lowerName === $stmt->name->toLowerString()) { - return $stmt; - } - } - return null; + $this->registerMockObjectsFromTestArgumentsRecursively = $flag; + } + private function recordError(\PHPUnit\Framework\Test $test, Throwable $t) : void + { + $this->errors[] = new \PHPUnit\Framework\TestFailure($test, $t); + } + private function recordNotImplemented(\PHPUnit\Framework\Test $test, Throwable $t) : void + { + $this->notImplemented[] = new \PHPUnit\Framework\TestFailure($test, $t); + } + private function recordRisky(\PHPUnit\Framework\Test $test, Throwable $t) : void + { + $this->risky[] = new \PHPUnit\Framework\TestFailure($test, $t); + } + private function recordSkipped(\PHPUnit\Framework\Test $test, Throwable $t) : void + { + $this->skipped[] = new \PHPUnit\Framework\TestFailure($test, $t); + } + private function recordWarning(\PHPUnit\Framework\Test $test, Throwable $t) : void + { + $this->warnings[] = new \PHPUnit\Framework\TestFailure($test, $t); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; -use PHPUnit\PhpParser\Error; -use PHPUnit\PhpParser\Node; -class Class_ extends \PHPUnit\PhpParser\Node\Stmt\ClassLike +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface Reorderable { - const MODIFIER_PUBLIC = 1; - const MODIFIER_PROTECTED = 2; - const MODIFIER_PRIVATE = 4; - const MODIFIER_STATIC = 8; - const MODIFIER_ABSTRACT = 16; - const MODIFIER_FINAL = 32; - const VISIBILITY_MODIFIER_MASK = 7; - // 1 | 2 | 4 - /** @var int Type */ - public $flags; - /** @var null|Node\Name Name of extended class */ - public $extends; - /** @var Node\Name[] Names of implemented interfaces */ - public $implements; + public function sortId() : string; /** - * Constructs a class node. - * - * @param string|Node\Identifier|null $name Name - * @param array $subNodes Array of the following optional subnodes: - * 'flags' => 0 : Flags - * 'extends' => null : Name of extended class - * 'implements' => array(): Names of implemented interfaces - * 'stmts' => array(): Statements - * '$attrGroups' => array(): PHP attribute groups - * @param array $attributes Additional attributes + * @return list */ - public function __construct($name, array $subNodes = [], array $attributes = []) + public function provides() : array; + /** + * @return list + */ + public function requires() : array; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use Throwable; +/** + * @deprecated The `TestListener` interface is deprecated + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +trait TestListenerDefaultImplementation +{ + public function addError(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void { - $this->attributes = $attributes; - $this->flags = $subNodes['flags'] ?? $subNodes['type'] ?? 0; - $this->name = \is_string($name) ? new \PHPUnit\PhpParser\Node\Identifier($name) : $name; - $this->extends = $subNodes['extends'] ?? null; - $this->implements = $subNodes['implements'] ?? []; - $this->stmts = $subNodes['stmts'] ?? []; - $this->attrGroups = $subNodes['attrGroups'] ?? []; } - public function getSubNodeNames() : array + public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time) : void { - return ['attrGroups', 'flags', 'name', 'extends', 'implements', 'stmts']; } - /** - * Whether the class is explicitly abstract. - * - * @return bool - */ - public function isAbstract() : bool + public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time) : void { - return (bool) ($this->flags & self::MODIFIER_ABSTRACT); } - /** - * Whether the class is final. - * - * @return bool - */ - public function isFinal() : bool + public function addIncompleteTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void { - return (bool) ($this->flags & self::MODIFIER_FINAL); } - /** - * Whether the class is anonymous. - * - * @return bool - */ - public function isAnonymous() : bool + public function addRiskyTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void { - return null === $this->name; } - /** - * @internal - */ - public static function verifyModifier($a, $b) + public function addSkippedTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time) : void { - if ($a & self::VISIBILITY_MODIFIER_MASK && $b & self::VISIBILITY_MODIFIER_MASK) { - throw new \PHPUnit\PhpParser\Error('Multiple access type modifiers are not allowed'); - } - if ($a & self::MODIFIER_ABSTRACT && $b & self::MODIFIER_ABSTRACT) { - throw new \PHPUnit\PhpParser\Error('Multiple abstract modifiers are not allowed'); - } - if ($a & self::MODIFIER_STATIC && $b & self::MODIFIER_STATIC) { - throw new \PHPUnit\PhpParser\Error('Multiple static modifiers are not allowed'); - } - if ($a & self::MODIFIER_FINAL && $b & self::MODIFIER_FINAL) { - throw new \PHPUnit\PhpParser\Error('Multiple final modifiers are not allowed'); - } - if ($a & 48 && $b & 48) { - throw new \PHPUnit\PhpParser\Error('Cannot use the final modifier on an abstract class member'); - } } - public function getType() : string + public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) : void { - return 'Stmt_Class'; } + public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) : void + { + } + public function startTest(\PHPUnit\Framework\Test $test) : void + { + } + public function endTest(\PHPUnit\Framework\Test $test, float $time) : void + { + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Error; + +use PHPUnit\Framework\Exception; +/** + * @internal + */ +class Error extends Exception +{ + public function __construct(string $message, int $code, string $file, int $line, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + $this->file = $file; + $this->line = $line; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Error; + +/** + * @internal + */ +final class Warning extends \PHPUnit\Framework\Error\Error +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Error; + +/** + * @internal + */ +final class Deprecated extends \PHPUnit\Framework\Error\Error +{ } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Error; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\FunctionLike; -class ClassMethod extends \PHPUnit\PhpParser\Node\Stmt implements \PHPUnit\PhpParser\Node\FunctionLike +/** + * @internal + */ +final class Notice extends \PHPUnit\Framework\Error\Error +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function array_keys; +use function get_class; +use function spl_object_hash; +use PHPUnit\Util\Filter; +use Throwable; +/** + * Wraps Exceptions thrown by code under test. + * + * Re-instantiates Exceptions thrown by user-space code to retain their original + * class names, properties, and stack traces (but without arguments). + * + * Unlike PHPUnit\Framework\Exception, the complete stack of previous Exceptions + * is processed. + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ExceptionWrapper extends \PHPUnit\Framework\Exception { - /** @var int Flags */ - public $flags; - /** @var bool Whether to return by reference */ - public $byRef; - /** @var Node\Identifier Name */ - public $name; - /** @var Node\Param[] Parameters */ - public $params; - /** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType Return type */ - public $returnType; - /** @var Node\Stmt[]|null Statements */ - public $stmts; - /** @var Node\AttributeGroup[] PHP attribute groups */ - public $attrGroups; - private static $magicNames = ['__construct' => \true, '__destruct' => \true, '__call' => \true, '__callstatic' => \true, '__get' => \true, '__set' => \true, '__isset' => \true, '__unset' => \true, '__sleep' => \true, '__wakeup' => \true, '__tostring' => \true, '__set_state' => \true, '__clone' => \true, '__invoke' => \true, '__debuginfo' => \true]; /** - * Constructs a class method node. - * - * @param string|Node\Identifier $name Name - * @param array $subNodes Array of the following optional subnodes: - * 'flags => MODIFIER_PUBLIC: Flags - * 'byRef' => false : Whether to return by reference - * 'params' => array() : Parameters - * 'returnType' => null : Return type - * 'stmts' => array() : Statements - * 'attrGroups' => array() : PHP attribute groups - * @param array $attributes Additional attributes + * @var string */ - public function __construct($name, array $subNodes = [], array $attributes = []) + protected $className; + /** + * @var null|ExceptionWrapper + */ + protected $previous; + public function __construct(Throwable $t) { - $this->attributes = $attributes; - $this->flags = $subNodes['flags'] ?? $subNodes['type'] ?? 0; - $this->byRef = $subNodes['byRef'] ?? \false; - $this->name = \is_string($name) ? new \PHPUnit\PhpParser\Node\Identifier($name) : $name; - $this->params = $subNodes['params'] ?? []; - $returnType = $subNodes['returnType'] ?? null; - $this->returnType = \is_string($returnType) ? new \PHPUnit\PhpParser\Node\Identifier($returnType) : $returnType; - $this->stmts = \array_key_exists('stmts', $subNodes) ? $subNodes['stmts'] : []; - $this->attrGroups = $subNodes['attrGroups'] ?? []; + // PDOException::getCode() is a string. + // @see https://php.net/manual/en/class.pdoexception.php#95812 + parent::__construct($t->getMessage(), (int) $t->getCode()); + $this->setOriginalException($t); } - public function getSubNodeNames() : array + public function __toString() : string { - return ['attrGroups', 'flags', 'byRef', 'name', 'params', 'returnType', 'stmts']; + $string = \PHPUnit\Framework\TestFailure::exceptionToString($this); + if ($trace = Filter::getFilteredStacktrace($this)) { + $string .= "\n" . $trace; + } + if ($this->previous) { + $string .= "\nCaused by\n" . $this->previous; + } + return $string; } - public function returnsByRef() : bool + public function getClassName() : string { - return $this->byRef; + return $this->className; } - public function getParams() : array + public function getPreviousWrapped() : ?self { - return $this->params; + return $this->previous; } - public function getReturnType() + public function setClassName(string $className) : void { - return $this->returnType; + $this->className = $className; } - public function getStmts() + public function setOriginalException(Throwable $t) : void { - return $this->stmts; + $this->originalException($t); + $this->className = get_class($t); + $this->file = $t->getFile(); + $this->line = $t->getLine(); + $this->serializableTrace = $t->getTrace(); + foreach (array_keys($this->serializableTrace) as $key) { + unset($this->serializableTrace[$key]['args']); + } + if ($t->getPrevious()) { + $this->previous = new self($t->getPrevious()); + } } - public function getAttrGroups() : array + public function getOriginalException() : ?Throwable { - return $this->attrGroups; + return $this->originalException(); } /** - * Whether the method is explicitly or implicitly public. + * Method to contain static originalException to exclude it from stacktrace to prevent the stacktrace contents, + * which can be quite big, from being garbage-collected, thus blocking memory until shutdown. * - * @return bool + * Approach works both for var_dump() and var_export() and print_r(). */ - public function isPublic() : bool + private function originalException(Throwable $exceptionToStore = null) : ?Throwable { - return ($this->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC) !== 0 || ($this->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::VISIBILITY_MODIFIER_MASK) === 0; + static $originalExceptions; + $instanceId = spl_object_hash($this); + if ($exceptionToStore) { + $originalExceptions[$instanceId] = $exceptionToStore; + } + return $originalExceptions[$instanceId] ?? null; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class IncompleteTestCase extends \PHPUnit\Framework\TestCase +{ /** - * Whether the method is protected. - * - * @return bool + * @var bool */ - public function isProtected() : bool - { - return (bool) ($this->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED); - } + protected $backupGlobals = \false; /** - * Whether the method is private. - * - * @return bool + * @var bool */ - public function isPrivate() : bool - { - return (bool) ($this->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE); - } + protected $backupStaticAttributes = \false; /** - * Whether the method is abstract. - * - * @return bool + * @var bool */ - public function isAbstract() : bool - { - return (bool) ($this->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_ABSTRACT); - } + protected $runTestInSeparateProcess = \false; /** - * Whether the method is final. - * - * @return bool + * @var string */ - public function isFinal() : bool + private $message; + public function __construct(string $className, string $methodName, string $message = '') { - return (bool) ($this->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_FINAL); + parent::__construct($className . '::' . $methodName); + $this->message = $message; } - /** - * Whether the method is static. - * - * @return bool - */ - public function isStatic() : bool + public function getMessage() : string { - return (bool) ($this->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_STATIC); + return $this->message; } /** - * Whether the method is magic. + * Returns a string representation of the test case. * - * @return bool + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function isMagic() : bool + public function toString() : string { - return isset(self::$magicNames[$this->name->toLowerString()]); + return $this->getName(); } - public function getType() : string + /** + * @throws Exception + */ + protected function runTest() : void { - return 'Stmt_ClassMethod'; + $this->markTestIncomplete($this->message); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit; -use PHPUnit\PhpParser\Node; -class TraitUse extends \PHPUnit\PhpParser\Node\Stmt +use Throwable; +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface Exception extends Throwable { - /** @var Node\Name[] Traits */ - public $traits; - /** @var TraitUseAdaptation[] Adaptations */ - public $adaptations; - /** - * Constructs a trait use node. - * - * @param Node\Name[] $traits Traits - * @param TraitUseAdaptation[] $adaptations Adaptations - * @param array $attributes Additional attributes - */ - public function __construct(array $traits, array $adaptations = [], array $attributes = []) - { - $this->attributes = $attributes; - $this->traits = $traits; - $this->adaptations = $adaptations; - } - public function getSubNodeNames() : array - { - return ['traits', 'adaptations']; - } - public function getType() : string - { - return 'Stmt_TraitUse'; - } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Exporter; -use PHPUnit\PhpParser\Node; -class TryCatch extends \PHPUnit\PhpParser\Node\Stmt +use function bin2hex; +use function count; +use function function_exists; +use function get_class; +use function get_resource_type; +use function implode; +use function is_array; +use function is_float; +use function is_object; +use function is_resource; +use function is_string; +use function mb_strlen; +use function mb_substr; +use function preg_match; +use function spl_object_hash; +use function sprintf; +use function str_repeat; +use function str_replace; +use function strlen; +use function substr; +use function var_export; +use PHPUnit\SebastianBergmann\RecursionContext\Context; +use SplObjectStorage; +/** + * A nifty utility for visualizing PHP variables. + * + * + * export(new Exception); + * + */ +class Exporter { - /** @var Node\Stmt[] Statements */ - public $stmts; - /** @var Catch_[] Catches */ - public $catches; - /** @var null|Finally_ Optional finally node */ - public $finally; /** - * Constructs a try catch node. + * Exports a value as a string. * - * @param Node\Stmt[] $stmts Statements - * @param Catch_[] $catches Catches - * @param null|Finally_ $finally Optional finally node - * @param array $attributes Additional attributes + * The output of this method is similar to the output of print_r(), but + * improved in various aspects: + * + * - NULL is rendered as "null" (instead of "") + * - TRUE is rendered as "true" (instead of "1") + * - FALSE is rendered as "false" (instead of "") + * - Strings are always quoted with single quotes + * - Carriage returns and newlines are normalized to \n + * - Recursion and repeated rendering is treated properly + * + * @param int $indentation The indentation level of the 2nd+ line + * + * @return string */ - public function __construct(array $stmts, array $catches, \PHPUnit\PhpParser\Node\Stmt\Finally_ $finally = null, array $attributes = []) - { - $this->attributes = $attributes; - $this->stmts = $stmts; - $this->catches = $catches; - $this->finally = $finally; - } - public function getSubNodeNames() : array + public function export($value, $indentation = 0) { - return ['stmts', 'catches', 'finally']; + return $this->recursiveExport($value, $indentation); } - public function getType() : string + /** + * @param array $data + * @param Context $context + * + * @return string + */ + public function shortenedRecursiveExport(&$data, Context $context = null) { - return 'Stmt_TryCatch'; + $result = []; + $exporter = new self(); + if (!$context) { + $context = new Context(); + } + $array = $data; + $context->add($data); + foreach ($array as $key => $value) { + if (is_array($value)) { + if ($context->contains($data[$key]) !== \false) { + $result[] = '*RECURSION*'; + } else { + $result[] = sprintf('array(%s)', $this->shortenedRecursiveExport($data[$key], $context)); + } + } else { + $result[] = $exporter->shortenedExport($value); + } + } + return implode(', ', $result); } -} -attributes = $attributes; - $this->cond = $cond; - $this->stmts = $stmts; + if (is_string($value)) { + $string = str_replace("\n", '', $this->export($value)); + if (function_exists('mb_strlen')) { + if (mb_strlen($string) > 40) { + $string = mb_substr($string, 0, 30) . '...' . mb_substr($string, -7); + } + } else { + if (strlen($string) > 40) { + $string = substr($string, 0, 30) . '...' . substr($string, -7); + } + } + return $string; + } + if (is_object($value)) { + return sprintf('%s Object (%s)', get_class($value), count($this->toArray($value)) > 0 ? '...' : ''); + } + if (is_array($value)) { + return sprintf('Array (%s)', count($value) > 0 ? '...' : ''); + } + return $this->export($value); } - public function getSubNodeNames() : array + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @return array + */ + public function toArray($value) { - return ['cond', 'stmts']; + if (!is_object($value)) { + return (array) $value; + } + $array = []; + foreach ((array) $value as $key => $val) { + // Exception traces commonly reference hundreds to thousands of + // objects currently loaded in memory. Including them in the result + // has a severe negative performance impact. + if ("\0Error\0trace" === $key || "\0Exception\0trace" === $key) { + continue; + } + // properties are transformed to keys in the following way: + // private $property => "\0Classname\0property" + // protected $property => "\0*\0property" + // public $property => "property" + if (preg_match('/^\\0.+\\0(.+)$/', (string) $key, $matches)) { + $key = $matches[1]; + } + // See https://github.com/php/php-src/commit/5721132 + if ($key === "\0gcdata") { + continue; + } + $array[$key] = $val; + } + // Some internal classes like SplObjectStorage don't work with the + // above (fast) mechanism nor with reflection in Zend. + // Format the output similarly to print_r() in this case + if ($value instanceof SplObjectStorage) { + foreach ($value as $key => $val) { + $array[spl_object_hash($val)] = ['obj' => $val, 'inf' => $value->getInfo()]; + } + } + return $array; } - public function getType() : string + /** + * Recursive implementation of export. + * + * @param mixed $value The value to export + * @param int $indentation The indentation level of the 2nd+ line + * @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects + * + * @return string + * + * @see SebastianBergmann\Exporter\Exporter::export + */ + protected function recursiveExport(&$value, $indentation, $processed = null) { - return 'Stmt_While'; + if ($value === null) { + return 'null'; + } + if ($value === \true) { + return 'true'; + } + if ($value === \false) { + return 'false'; + } + if (is_float($value) && (float) (int) $value === $value) { + return "{$value}.0"; + } + if (is_resource($value)) { + return sprintf('resource(%d) of type (%s)', $value, get_resource_type($value)); + } + if (is_string($value)) { + // Match for most non printable chars somewhat taking multibyte chars into account + if (preg_match('/[^\\x09-\\x0d\\x1b\\x20-\\xff]/', $value)) { + return 'Binary String: 0x' . bin2hex($value); + } + return "'" . str_replace('', "\n", str_replace(["\r\n", "\n\r", "\r", "\n"], ['\\r\\n', '\\n\\r', '\\r', '\\n'], $value)) . "'"; + } + $whitespace = str_repeat(' ', (int) (4 * $indentation)); + if (!$processed) { + $processed = new Context(); + } + if (is_array($value)) { + if (($key = $processed->contains($value)) !== \false) { + return 'Array &' . $key; + } + $array = $value; + $key = $processed->add($value); + $values = ''; + if (count($array) > 0) { + foreach ($array as $k => $v) { + $values .= sprintf('%s %s => %s' . "\n", $whitespace, $this->recursiveExport($k, $indentation), $this->recursiveExport($value[$k], $indentation + 1, $processed)); + } + $values = "\n" . $values . $whitespace; + } + return sprintf('Array &%s (%s)', $key, $values); + } + if (is_object($value)) { + $class = get_class($value); + if ($hash = $processed->contains($value)) { + return sprintf('%s Object &%s', $class, $hash); + } + $hash = $processed->add($value); + $values = ''; + $array = $this->toArray($value); + if (count($array) > 0) { + foreach ($array as $k => $v) { + $values .= sprintf('%s %s => %s' . "\n", $whitespace, $this->recursiveExport($k, $indentation), $this->recursiveExport($v, $indentation + 1, $processed)); + } + $values = "\n" . $values . $whitespace; + } + return sprintf('%s Object &%s (%s)', $class, $hash, $values); + } + return var_export($value, \true); } } +Exporter + +Copyright (c) 2002-2020, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\FileIterator; -use PHPUnit\PhpParser\Node\Stmt; -class Static_ extends \PHPUnit\PhpParser\Node\Stmt +use const DIRECTORY_SEPARATOR; +use function array_unique; +use function count; +use function dirname; +use function explode; +use function is_file; +use function is_string; +use function realpath; +use function sort; +class Facade { - /** @var StaticVar[] Variable definitions */ - public $vars; /** - * Constructs a static variables list node. - * - * @param StaticVar[] $vars Variable definitions - * @param array $attributes Additional attributes + * @param array|string $paths + * @param array|string $suffixes + * @param array|string $prefixes */ - public function __construct(array $vars, array $attributes = []) - { - $this->attributes = $attributes; - $this->vars = $vars; - } - public function getSubNodeNames() : array + public function getFilesAsArray($paths, $suffixes = '', $prefixes = '', array $exclude = [], bool $commonPath = \false) : array { - return ['vars']; + if (is_string($paths)) { + $paths = [$paths]; + } + $iterator = (new Factory())->getFileIterator($paths, $suffixes, $prefixes, $exclude); + $files = []; + foreach ($iterator as $file) { + $file = $file->getRealPath(); + if ($file) { + $files[] = $file; + } + } + foreach ($paths as $path) { + if (is_file($path)) { + $files[] = realpath($path); + } + } + $files = array_unique($files); + sort($files); + if ($commonPath) { + return ['commonPath' => $this->getCommonPath($files), 'files' => $files]; + } + return $files; } - public function getType() : string + protected function getCommonPath(array $files) : string { - return 'Stmt_Static'; + $count = count($files); + if ($count === 0) { + return ''; + } + if ($count === 1) { + return dirname($files[0]) . \DIRECTORY_SEPARATOR; + } + $_files = []; + foreach ($files as $file) { + $_files[] = $_fileParts = explode(\DIRECTORY_SEPARATOR, $file); + if (empty($_fileParts[0])) { + $_fileParts[0] = \DIRECTORY_SEPARATOR; + } + } + $common = ''; + $done = \false; + $j = 0; + $count--; + while (!$done) { + for ($i = 0; $i < $count; $i++) { + if ($_files[$i][$j] != $_files[$i + 1][$j]) { + $done = \true; + break; + } + } + if (!$done) { + $common .= $_files[0][$j]; + if ($j > 0) { + $common .= \DIRECTORY_SEPARATOR; + } + } + $j++; + } + return \DIRECTORY_SEPARATOR . $common; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\FileIterator; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Expr; -class Catch_ extends \PHPUnit\PhpParser\Node\Stmt +use const GLOB_ONLYDIR; +use function array_filter; +use function array_map; +use function array_merge; +use function glob; +use function is_dir; +use function is_string; +use function realpath; +use AppendIterator; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; +class Factory { - /** @var Node\Name[] Types of exceptions to catch */ - public $types; - /** @var Expr\Variable|null Variable for exception */ - public $var; - /** @var Node\Stmt[] Statements */ - public $stmts; /** - * Constructs a catch node. - * - * @param Node\Name[] $types Types of exceptions to catch - * @param Expr\Variable|null $var Variable for exception - * @param Node\Stmt[] $stmts Statements - * @param array $attributes Additional attributes + * @param array|string $paths + * @param array|string $suffixes + * @param array|string $prefixes */ - public function __construct(array $types, \PHPUnit\PhpParser\Node\Expr\Variable $var = null, array $stmts = [], array $attributes = []) - { - $this->attributes = $attributes; - $this->types = $types; - $this->var = $var; - $this->stmts = $stmts; - } - public function getSubNodeNames() : array + public function getFileIterator($paths, $suffixes = '', $prefixes = '', array $exclude = []) : AppendIterator { - return ['types', 'var', 'stmts']; + if (is_string($paths)) { + $paths = [$paths]; + } + $paths = $this->getPathsAfterResolvingWildcards($paths); + $exclude = $this->getPathsAfterResolvingWildcards($exclude); + if (is_string($prefixes)) { + if ($prefixes !== '') { + $prefixes = [$prefixes]; + } else { + $prefixes = []; + } + } + if (is_string($suffixes)) { + if ($suffixes !== '') { + $suffixes = [$suffixes]; + } else { + $suffixes = []; + } + } + $iterator = new AppendIterator(); + foreach ($paths as $path) { + if (is_dir($path)) { + $iterator->append(new Iterator($path, new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::FOLLOW_SYMLINKS | RecursiveDirectoryIterator::SKIP_DOTS)), $suffixes, $prefixes, $exclude)); + } + } + return $iterator; } - public function getType() : string + protected function getPathsAfterResolvingWildcards(array $paths) : array { - return 'Stmt_Catch'; + $_paths = []; + foreach ($paths as $path) { + if ($locals = glob($path, \GLOB_ONLYDIR)) { + $_paths = array_merge($_paths, array_map('\\realpath', $locals)); + } else { + $_paths[] = realpath($path); + } + } + return array_filter($_paths); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\FileIterator; -use PHPUnit\PhpParser\Node; -class Case_ extends \PHPUnit\PhpParser\Node\Stmt +use function array_filter; +use function array_map; +use function preg_match; +use function realpath; +use function str_replace; +use function strlen; +use function strpos; +use function substr; +use FilterIterator; +class Iterator extends FilterIterator { - /** @var null|Node\Expr Condition (null for default) */ - public $cond; - /** @var Node\Stmt[] Statements */ - public $stmts; + public const PREFIX = 0; + public const SUFFIX = 1; /** - * Constructs a case node. - * - * @param null|Node\Expr $cond Condition (null for default) - * @param Node\Stmt[] $stmts Statements - * @param array $attributes Additional attributes + * @var string */ - public function __construct($cond, array $stmts = [], array $attributes = []) + private $basePath; + /** + * @var array + */ + private $suffixes = []; + /** + * @var array + */ + private $prefixes = []; + /** + * @var array + */ + private $exclude = []; + public function __construct(string $basePath, \Iterator $iterator, array $suffixes = [], array $prefixes = [], array $exclude = []) { - $this->attributes = $attributes; - $this->cond = $cond; - $this->stmts = $stmts; + $this->basePath = realpath($basePath); + $this->prefixes = $prefixes; + $this->suffixes = $suffixes; + $this->exclude = array_filter(array_map('realpath', $exclude)); + parent::__construct($iterator); } - public function getSubNodeNames() : array + public function accept() : bool { - return ['cond', 'stmts']; + $current = $this->getInnerIterator()->current(); + $filename = $current->getFilename(); + $realPath = $current->getRealPath(); + if ($realPath === \false) { + return \false; + } + return $this->acceptPath($realPath) && $this->acceptPrefix($filename) && $this->acceptSuffix($filename); } - public function getType() : string + private function acceptPath(string $path) : bool { - return 'Stmt_Case'; + // Filter files in hidden directories by checking path that is relative to the base path. + if (preg_match('=/\\.[^/]*/=', str_replace($this->basePath, '', $path))) { + return \false; + } + foreach ($this->exclude as $exclude) { + if (strpos($path, $exclude) === 0) { + return \false; + } + } + return \true; } -} - array(): Name of extended interfaces - * 'stmts' => array(): Statements - * 'attrGroups' => array(): PHP attribute groups - * @param array $attributes Additional attributes - */ - public function __construct($name, array $subNodes = [], array $attributes = []) + private function acceptPrefix(string $filename) : bool { - $this->attributes = $attributes; - $this->name = \is_string($name) ? new \PHPUnit\PhpParser\Node\Identifier($name) : $name; - $this->extends = $subNodes['extends'] ?? []; - $this->stmts = $subNodes['stmts'] ?? []; - $this->attrGroups = $subNodes['attrGroups'] ?? []; + return $this->acceptSubString($filename, $this->prefixes, self::PREFIX); } - public function getSubNodeNames() : array + private function acceptSuffix(string $filename) : bool { - return ['attrGroups', 'name', 'extends', 'stmts']; + return $this->acceptSubString($filename, $this->suffixes, self::SUFFIX); } - public function getType() : string + private function acceptSubString(string $filename, array $subStrings, int $type) : bool { - return 'Stmt_Interface'; + if (empty($subStrings)) { + return \true; + } + $matched = \false; + foreach ($subStrings as $string) { + if ($type === self::PREFIX && strpos($filename, $string) === 0 || $type === self::SUFFIX && substr($filename, -1 * strlen($string)) === $string) { + $matched = \true; + break; + } + } + return $matched; } } +php-file-iterator + +Copyright (c) 2009-2020, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Prophecy; -use PHPUnit\PhpParser\Node; -class DeclareDeclare extends \PHPUnit\PhpParser\Node\Stmt +/** + * Basic prophecies revealer. + * + * @author Konstantin Kudryashov + */ +class Revealer implements \Prophecy\Prophecy\RevealerInterface { - /** @var Node\Identifier Key */ - public $key; - /** @var Node\Expr Value */ - public $value; /** - * Constructs a declare key=>value pair node. + * Unwraps value(s). * - * @param string|Node\Identifier $key Key - * @param Node\Expr $value Value - * @param array $attributes Additional attributes + * @param mixed $value + * + * @return mixed */ - public function __construct($key, \PHPUnit\PhpParser\Node\Expr $value, array $attributes = []) - { - $this->attributes = $attributes; - $this->key = \is_string($key) ? new \PHPUnit\PhpParser\Node\Identifier($key) : $key; - $this->value = $value; - } - public function getSubNodeNames() : array - { - return ['key', 'value']; - } - public function getType() : string + public function reveal($value) { - return 'Stmt_DeclareDeclare'; + if (\is_array($value)) { + return \array_map(array($this, __FUNCTION__), $value); + } + if (!\is_object($value)) { + return $value; + } + if ($value instanceof \Prophecy\Prophecy\ProphecyInterface) { + $value = $value->reveal(); + } + return $value; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Prophecy; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Identifier; -class UseUse extends \PHPUnit\PhpParser\Node\Stmt +use Prophecy\Argument; +use Prophecy\Prophet; +use Prophecy\Promise; +use Prophecy\Prediction; +use Prophecy\Exception\Doubler\MethodNotFoundException; +use Prophecy\Exception\InvalidArgumentException; +use Prophecy\Exception\Prophecy\MethodProphecyException; +use ReflectionNamedType; +use ReflectionType; +use ReflectionUnionType; +/** + * Method prophecy. + * + * @author Konstantin Kudryashov + */ +class MethodProphecy { - /** @var int One of the Stmt\Use_::TYPE_* constants. Will only differ from TYPE_UNKNOWN for mixed group uses */ - public $type; - /** @var Node\Name Namespace, class, function or constant to alias */ - public $name; - /** @var Identifier|null Alias */ - public $alias; + private $objectProphecy; + private $methodName; + private $argumentsWildcard; + private $promise; + private $prediction; + private $checkedPredictions = array(); + private $bound = \false; + private $voidReturnType = \false; /** - * Constructs an alias (use) node. + * Initializes method prophecy. * - * @param Node\Name $name Namespace/Class to alias - * @param null|string|Identifier $alias Alias - * @param int $type Type of the use element (for mixed group use only) - * @param array $attributes Additional attributes + * @param ObjectProphecy $objectProphecy + * @param string $methodName + * @param null|Argument\ArgumentsWildcard|array $arguments + * + * @throws \Prophecy\Exception\Doubler\MethodNotFoundException If method not found */ - public function __construct(\PHPUnit\PhpParser\Node\Name $name, $alias = null, int $type = \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN, array $attributes = []) - { - $this->attributes = $attributes; - $this->type = $type; - $this->name = $name; - $this->alias = \is_string($alias) ? new \PHPUnit\PhpParser\Node\Identifier($alias) : $alias; - } - public function getSubNodeNames() : array + public function __construct(\Prophecy\Prophecy\ObjectProphecy $objectProphecy, $methodName, $arguments = null) { - return ['type', 'name', 'alias']; + $double = $objectProphecy->reveal(); + if (!\method_exists($double, $methodName)) { + throw new MethodNotFoundException(\sprintf('Method `%s::%s()` is not defined.', \get_class($double), $methodName), \get_class($double), $methodName, $arguments); + } + $this->objectProphecy = $objectProphecy; + $this->methodName = $methodName; + $reflectedMethod = new \ReflectionMethod($double, $methodName); + if ($reflectedMethod->isFinal()) { + throw new MethodProphecyException(\sprintf("Can not add prophecy for a method `%s::%s()`\n" . "as it is a final method.", \get_class($double), $methodName), $this); + } + if (null !== $arguments) { + $this->withArguments($arguments); + } + $hasTentativeReturnType = \method_exists($reflectedMethod, 'hasTentativeReturnType') && $reflectedMethod->hasTentativeReturnType(); + if (\true === $reflectedMethod->hasReturnType() || $hasTentativeReturnType) { + if ($hasTentativeReturnType) { + $reflectionType = $reflectedMethod->getTentativeReturnType(); + } else { + $reflectionType = $reflectedMethod->getReturnType(); + } + if ($reflectionType instanceof ReflectionNamedType) { + $types = [$reflectionType]; + } elseif ($reflectionType instanceof ReflectionUnionType) { + $types = $reflectionType->getTypes(); + } + $types = \array_map(function (ReflectionType $type) { + return $type->getName(); + }, $types); + \usort($types, static function (string $type1, string $type2) { + // null is lowest priority + if ($type2 == 'null') { + return -1; + } elseif ($type1 == 'null') { + return 1; + } + // objects are higher priority than scalars + $isObject = static function ($type) { + return \class_exists($type) || \interface_exists($type); + }; + if ($isObject($type1) && !$isObject($type2)) { + return -1; + } elseif (!$isObject($type1) && $isObject($type2)) { + return 1; + } + // don't sort both-scalars or both-objects + return 0; + }); + $defaultType = $types[0]; + if ('void' === $defaultType) { + $this->voidReturnType = \true; + } + $this->will(function () use($defaultType) { + switch ($defaultType) { + case 'void': + return; + case 'string': + return ''; + case 'float': + return 0.0; + case 'int': + return 0; + case 'bool': + return \false; + case 'array': + return array(); + case 'callable': + case 'Closure': + return function () { + }; + case 'Traversable': + case 'Generator': + return (function () { + yield; + })(); + default: + $prophet = new Prophet(); + return $prophet->prophesize($defaultType)->reveal(); + } + }); + } } /** - * Get alias. If not explicitly given this is the last component of the used name. + * Sets argument wildcard. * - * @return Identifier + * @param array|Argument\ArgumentsWildcard $arguments + * + * @return $this + * + * @throws \Prophecy\Exception\InvalidArgumentException */ - public function getAlias() : \PHPUnit\PhpParser\Node\Identifier + public function withArguments($arguments) { - if (null !== $this->alias) { - return $this->alias; + if (\is_array($arguments)) { + $arguments = new Argument\ArgumentsWildcard($arguments); } - return new \PHPUnit\PhpParser\Node\Identifier($this->name->getLast()); - } - public function getType() : string - { - return 'Stmt_UseUse'; + if (!$arguments instanceof Argument\ArgumentsWildcard) { + throw new InvalidArgumentException(\sprintf("Either an array or an instance of ArgumentsWildcard expected as\n" . 'a `MethodProphecy::withArguments()` argument, but got %s.', \gettype($arguments))); + } + $this->argumentsWildcard = $arguments; + return $this; } -} -attributes = $attributes; - $this->stmts = $stmts; - } - public function getSubNodeNames() : array - { - return ['stmts']; - } - public function getType() : string + public function will($promise) { - return 'Stmt_Else'; + if (\is_callable($promise)) { + $promise = new Promise\CallbackPromise($promise); + } + if (!$promise instanceof Promise\PromiseInterface) { + throw new InvalidArgumentException(\sprintf('Expected callable or instance of PromiseInterface, but got %s.', \gettype($promise))); + } + $this->bindToObjectProphecy(); + $this->promise = $promise; + return $this; } -} -attributes = $attributes; - $this->vars = $vars; - } - public function getSubNodeNames() : array - { - return ['vars']; - } - public function getType() : string + public function willReturn() { - return 'Stmt_Global'; + if ($this->voidReturnType) { + throw new MethodProphecyException("The method \"{$this->methodName}\" has a void return type, and so cannot return anything", $this); + } + return $this->will(new Promise\ReturnPromise(\func_get_args())); } -} -attributes = $attributes; - $this->vars = $vars; - } - public function getSubNodeNames() : array - { - return ['vars']; - } - public function getType() : string + public function willYield($items, $return = null) { - return 'Stmt_Unset'; + if ($this->voidReturnType) { + throw new MethodProphecyException("The method \"{$this->methodName}\" has a void return type, and so cannot yield anything", $this); + } + if (!\is_array($items)) { + throw new InvalidArgumentException(\sprintf('Expected array, but got %s.', \gettype($items))); + } + $generator = function () use($items, $return) { + yield from $items; + return $return; + }; + return $this->will($generator); } -} -attributes = $attributes; - $this->name = \is_string($name) ? new \PHPUnit\PhpParser\Node\VarLikeIdentifier($name) : $name; - $this->default = $default; - } - public function getSubNodeNames() : array - { - return ['name', 'default']; - } - public function getType() : string + public function willReturnArgument($index = 0) { - return 'Stmt_PropertyProperty'; + if ($this->voidReturnType) { + throw new MethodProphecyException("The method \"{$this->methodName}\" has a void return type", $this); + } + return $this->will(new Promise\ReturnArgumentPromise($index)); } -} -attributes = $attributes; - $this->num = $num; - } - public function getSubNodeNames() : array - { - return ['num']; - } - public function getType() : string + public function willThrow($exception) { - return 'Stmt_Break'; + return $this->will(new Promise\ThrowPromise($exception)); } -} - array(): Statements - * 'elseifs' => array(): Elseif clauses - * 'else' => null : Else clause - * @param array $attributes Additional attributes + * @param callable|Prediction\PredictionInterface $prediction + * + * @return $this + * + * @throws \Prophecy\Exception\InvalidArgumentException */ - public function __construct(\PHPUnit\PhpParser\Node\Expr $cond, array $subNodes = [], array $attributes = []) - { - $this->attributes = $attributes; - $this->cond = $cond; - $this->stmts = $subNodes['stmts'] ?? []; - $this->elseifs = $subNodes['elseifs'] ?? []; - $this->else = $subNodes['else'] ?? null; - } - public function getSubNodeNames() : array - { - return ['cond', 'stmts', 'elseifs', 'else']; - } - public function getType() : string + public function should($prediction) { - return 'Stmt_If'; + if (\is_callable($prediction)) { + $prediction = new Prediction\CallbackPrediction($prediction); + } + if (!$prediction instanceof Prediction\PredictionInterface) { + throw new InvalidArgumentException(\sprintf('Expected callable or instance of PredictionInterface, but got %s.', \gettype($prediction))); + } + $this->bindToObjectProphecy(); + $this->prediction = $prediction; + return $this; } -} -attributes = $attributes; - $this->name = \is_string($name) ? new \PHPUnit\PhpParser\Node\Identifier($name) : $name; - } - public function getSubNodeNames() : array - { - return ['name']; - } - public function getType() : string + public function shouldBeCalled() { - return 'Stmt_Label'; + return $this->should(new Prediction\CallPrediction()); } -} - false : Whether to return by reference - * 'params' => array(): Parameters - * 'returnType' => null : Return type - * 'stmts' => array(): Statements - * 'attrGroups' => array(): PHP attribute groups - * @param array $attributes Additional attributes + * @see \Prophecy\Prediction\NoCallsPrediction + * + * @return $this */ - public function __construct($name, array $subNodes = [], array $attributes = []) - { - $this->attributes = $attributes; - $this->byRef = $subNodes['byRef'] ?? \false; - $this->name = \is_string($name) ? new \PHPUnit\PhpParser\Node\Identifier($name) : $name; - $this->params = $subNodes['params'] ?? []; - $returnType = $subNodes['returnType'] ?? null; - $this->returnType = \is_string($returnType) ? new \PHPUnit\PhpParser\Node\Identifier($returnType) : $returnType; - $this->stmts = $subNodes['stmts'] ?? []; - $this->attrGroups = $subNodes['attrGroups'] ?? []; - } - public function getSubNodeNames() : array - { - return ['attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts']; - } - public function returnsByRef() : bool - { - return $this->byRef; - } - public function getParams() : array - { - return $this->params; - } - public function getReturnType() - { - return $this->returnType; - } - public function getAttrGroups() : array - { - return $this->attrGroups; - } - /** @return Node\Stmt[] */ - public function getStmts() : array - { - return $this->stmts; - } - public function getType() : string + public function shouldNotBeCalled() { - return 'Stmt_Function'; + return $this->should(new Prediction\NoCallsPrediction()); } -} -attributes = $attributes; - $this->type = $type; - $this->uses = $uses; + return $this->should(new Prediction\CallTimesPrediction($count)); } - public function getSubNodeNames() : array + /** + * Sets call times prediction to the prophecy. + * + * @see \Prophecy\Prediction\CallTimesPrediction + * + * @return $this + */ + public function shouldBeCalledOnce() { - return ['type', 'uses']; + return $this->shouldBeCalledTimes(1); } - public function getType() : string + /** + * Checks provided prediction immediately. + * + * @param callable|Prediction\PredictionInterface $prediction + * + * @return $this + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function shouldHave($prediction) { - return 'Stmt_Use'; + if (\is_callable($prediction)) { + $prediction = new Prediction\CallbackPrediction($prediction); + } + if (!$prediction instanceof Prediction\PredictionInterface) { + throw new InvalidArgumentException(\sprintf('Expected callable or instance of PredictionInterface, but got %s.', \gettype($prediction))); + } + if (null === $this->promise && !$this->voidReturnType) { + $this->willReturn(); + } + $calls = $this->getObjectProphecy()->findProphecyMethodCalls($this->getMethodName(), $this->getArgumentsWildcard()); + try { + $prediction->check($calls, $this->getObjectProphecy(), $this); + $this->checkedPredictions[] = $prediction; + } catch (\Exception $e) { + $this->checkedPredictions[] = $prediction; + throw $e; + } + return $this; } -} -attributes = $attributes; - $this->num = $num; + return $this->shouldHave(new Prediction\CallPrediction()); } - public function getSubNodeNames() : array + /** + * Checks no calls prediction. + * + * @see \Prophecy\Prediction\NoCallsPrediction + * + * @return $this + */ + public function shouldNotHaveBeenCalled() { - return ['num']; + return $this->shouldHave(new Prediction\NoCallsPrediction()); } - public function getType() : string + /** + * Checks no calls prediction. + * + * @see \Prophecy\Prediction\NoCallsPrediction + * @deprecated + * + * @return $this + */ + public function shouldNotBeenCalled() { - return 'Stmt_Continue'; + return $this->shouldNotHaveBeenCalled(); } -} -attributes = $attributes; - $this->cond = $cond; - $this->cases = $cases; + return $this->shouldHave(new Prediction\CallTimesPrediction($count)); } - public function getSubNodeNames() : array + /** + * Checks call times prediction. + * + * @see \Prophecy\Prediction\CallTimesPrediction + * + * @return $this + */ + public function shouldHaveBeenCalledOnce() { - return ['cond', 'cases']; + return $this->shouldHaveBeenCalledTimes(1); } - public function getType() : string + /** + * Checks currently registered [with should(...)] prediction. + */ + public function checkPrediction() { - return 'Stmt_Switch'; + if (null === $this->prediction) { + return; + } + $this->shouldHave($this->prediction); } -} - array(): Statements - * 'attrGroups' => array(): PHP attribute groups - * @param array $attributes Additional attributes + * @return null|Promise\PromiseInterface */ - public function __construct($name, array $subNodes = [], array $attributes = []) - { - $this->attributes = $attributes; - $this->name = \is_string($name) ? new \PHPUnit\PhpParser\Node\Identifier($name) : $name; - $this->stmts = $subNodes['stmts'] ?? []; - $this->attrGroups = $subNodes['attrGroups'] ?? []; - } - public function getSubNodeNames() : array + public function getPromise() { - return ['attrGroups', 'name', 'stmts']; + return $this->promise; } - public function getType() : string + /** + * Returns currently registered prediction. + * + * @return null|Prediction\PredictionInterface + */ + public function getPrediction() { - return 'Stmt_Trait'; + return $this->prediction; } -} -attributes = $attributes; - $this->remaining = $remaining; + return $this->checkedPredictions; } - public function getSubNodeNames() : array + /** + * Returns object prophecy this method prophecy is tied to. + * + * @return ObjectProphecy + */ + public function getObjectProphecy() { - return ['remaining']; + return $this->objectProphecy; } - public function getType() : string + /** + * Returns method name. + * + * @return string + */ + public function getMethodName() { - return 'Stmt_HaltCompiler'; + return $this->methodName; } -} - array(): Init expressions - * 'cond' => array(): Loop conditions - * 'loop' => array(): Loop expressions - * 'stmts' => array(): Statements - * @param array $attributes Additional attributes + * @return Argument\ArgumentsWildcard */ - public function __construct(array $subNodes = [], array $attributes = []) + public function getArgumentsWildcard() { - $this->attributes = $attributes; - $this->init = $subNodes['init'] ?? []; - $this->cond = $subNodes['cond'] ?? []; - $this->loop = $subNodes['loop'] ?? []; - $this->stmts = $subNodes['stmts'] ?? []; + return $this->argumentsWildcard; } - public function getSubNodeNames() : array + /** + * @return bool + */ + public function hasReturnVoid() { - return ['init', 'cond', 'loop', 'stmts']; + return $this->voidReturnType; } - public function getType() : string + private function bindToObjectProphecy() { - return 'Stmt_For'; + if ($this->bound) { + return; + } + $this->getObjectProphecy()->addMethodProphecy($this); + $this->bound = \true; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Prophecy; -interface Builder +/** + * Prophecies revealer interface. + * + * @author Konstantin Kudryashov + */ +interface RevealerInterface { /** - * Returns the built node. + * Unwraps value(s). * - * @return Node The built node + * @param mixed $value + * + * @return mixed */ - public function getNode() : \PHPUnit\PhpParser\Node; + public function reveal($value); } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Prophecy; -use PHPUnit\PhpParser\Internal\DiffElem; -use PHPUnit\PhpParser\Internal\PrintableNewAnonClassNode; -use PHPUnit\PhpParser\Internal\TokenStream; -use PHPUnit\PhpParser\Node\Expr; -use PHPUnit\PhpParser\Node\Expr\AssignOp; -use PHPUnit\PhpParser\Node\Expr\BinaryOp; -use PHPUnit\PhpParser\Node\Expr\Cast; -use PHPUnit\PhpParser\Node\Scalar; -use PHPUnit\PhpParser\Node\Stmt; -abstract class PrettyPrinterAbstract +/** + * Controllable doubles interface. + * + * @author Konstantin Kudryashov + */ +interface ProphecySubjectInterface { - const FIXUP_PREC_LEFT = 0; - // LHS operand affected by precedence - const FIXUP_PREC_RIGHT = 1; - // RHS operand affected by precedence - const FIXUP_CALL_LHS = 2; - // LHS of call - const FIXUP_DEREF_LHS = 3; - // LHS of dereferencing operation - const FIXUP_BRACED_NAME = 4; - // Name operand that may require bracing - const FIXUP_VAR_BRACED_NAME = 5; - // Name operand that may require ${} bracing - const FIXUP_ENCAPSED = 6; - // Encapsed string part - protected $precedenceMap = [ - // [precedence, associativity] - // where for precedence -1 is %left, 0 is %nonassoc and 1 is %right - \PHPUnit\PhpParser\Node\Expr\BinaryOp\Pow::class => [0, 1], - \PHPUnit\PhpParser\Node\Expr\BitwiseNot::class => [10, 1], - \PHPUnit\PhpParser\Node\Expr\PreInc::class => [10, 1], - \PHPUnit\PhpParser\Node\Expr\PreDec::class => [10, 1], - \PHPUnit\PhpParser\Node\Expr\PostInc::class => [10, -1], - \PHPUnit\PhpParser\Node\Expr\PostDec::class => [10, -1], - \PHPUnit\PhpParser\Node\Expr\UnaryPlus::class => [10, 1], - \PHPUnit\PhpParser\Node\Expr\UnaryMinus::class => [10, 1], - \PHPUnit\PhpParser\Node\Expr\Cast\Int_::class => [10, 1], - \PHPUnit\PhpParser\Node\Expr\Cast\Double::class => [10, 1], - \PHPUnit\PhpParser\Node\Expr\Cast\String_::class => [10, 1], - \PHPUnit\PhpParser\Node\Expr\Cast\Array_::class => [10, 1], - \PHPUnit\PhpParser\Node\Expr\Cast\Object_::class => [10, 1], - \PHPUnit\PhpParser\Node\Expr\Cast\Bool_::class => [10, 1], - \PHPUnit\PhpParser\Node\Expr\Cast\Unset_::class => [10, 1], - \PHPUnit\PhpParser\Node\Expr\ErrorSuppress::class => [10, 1], - \PHPUnit\PhpParser\Node\Expr\Instanceof_::class => [20, 0], - \PHPUnit\PhpParser\Node\Expr\BooleanNot::class => [30, 1], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\Mul::class => [40, -1], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\Div::class => [40, -1], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\Mod::class => [40, -1], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\Plus::class => [50, -1], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\Minus::class => [50, -1], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\Concat::class => [50, -1], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\ShiftLeft::class => [60, -1], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\ShiftRight::class => [60, -1], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\Smaller::class => [70, 0], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\SmallerOrEqual::class => [70, 0], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\Greater::class => [70, 0], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\GreaterOrEqual::class => [70, 0], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\Equal::class => [80, 0], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\NotEqual::class => [80, 0], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\Identical::class => [80, 0], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\NotIdentical::class => [80, 0], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\Spaceship::class => [80, 0], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseAnd::class => [90, -1], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseXor::class => [100, -1], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseOr::class => [110, -1], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanAnd::class => [120, -1], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanOr::class => [130, -1], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\Coalesce::class => [140, 1], - \PHPUnit\PhpParser\Node\Expr\Ternary::class => [150, -1], - // parser uses %left for assignments, but they really behave as %right - \PHPUnit\PhpParser\Node\Expr\Assign::class => [160, 1], - \PHPUnit\PhpParser\Node\Expr\AssignRef::class => [160, 1], - \PHPUnit\PhpParser\Node\Expr\AssignOp\Plus::class => [160, 1], - \PHPUnit\PhpParser\Node\Expr\AssignOp\Minus::class => [160, 1], - \PHPUnit\PhpParser\Node\Expr\AssignOp\Mul::class => [160, 1], - \PHPUnit\PhpParser\Node\Expr\AssignOp\Div::class => [160, 1], - \PHPUnit\PhpParser\Node\Expr\AssignOp\Concat::class => [160, 1], - \PHPUnit\PhpParser\Node\Expr\AssignOp\Mod::class => [160, 1], - \PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseAnd::class => [160, 1], - \PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseOr::class => [160, 1], - \PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseXor::class => [160, 1], - \PHPUnit\PhpParser\Node\Expr\AssignOp\ShiftLeft::class => [160, 1], - \PHPUnit\PhpParser\Node\Expr\AssignOp\ShiftRight::class => [160, 1], - \PHPUnit\PhpParser\Node\Expr\AssignOp\Pow::class => [160, 1], - \PHPUnit\PhpParser\Node\Expr\AssignOp\Coalesce::class => [160, 1], - \PHPUnit\PhpParser\Node\Expr\YieldFrom::class => [165, 1], - \PHPUnit\PhpParser\Node\Expr\Print_::class => [168, 1], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalAnd::class => [170, -1], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalXor::class => [180, -1], - \PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalOr::class => [190, -1], - \PHPUnit\PhpParser\Node\Expr\Include_::class => [200, -1], - ]; - /** @var int Current indentation level. */ - protected $indentLevel; - /** @var string Newline including current indentation. */ - protected $nl; - /** @var string Token placed at end of doc string to ensure it is followed by a newline. */ - protected $docStringEndToken; - /** @var bool Whether semicolon namespaces can be used (i.e. no global namespace is used) */ - protected $canUseSemicolonNamespaces; - /** @var array Pretty printer options */ - protected $options; - /** @var TokenStream Original tokens for use in format-preserving pretty print */ - protected $origTokens; - /** @var Internal\Differ Differ for node lists */ - protected $nodeListDiffer; - /** @var bool[] Map determining whether a certain character is a label character */ - protected $labelCharMap; - /** - * @var int[][] Map from token classes and subnode names to FIXUP_* constants. This is used - * during format-preserving prints to place additional parens/braces if necessary. - */ - protected $fixupMap; - /** - * @var int[][] Map from "{$node->getType()}->{$subNode}" to ['left' => $l, 'right' => $r], - * where $l and $r specify the token type that needs to be stripped when removing - * this node. - */ - protected $removalMap; - /** - * @var mixed[] Map from "{$node->getType()}->{$subNode}" to [$find, $beforeToken, $extraLeft, $extraRight]. - * $find is an optional token after which the insertion occurs. $extraLeft/Right - * are optionally added before/after the main insertions. - */ - protected $insertionMap; - /** - * @var string[] Map From "{$node->getType()}->{$subNode}" to string that should be inserted - * between elements of this list subnode. - */ - protected $listInsertionMap; - protected $emptyListInsertionMap; - /** @var int[] Map from "{$node->getType()}->{$subNode}" to token before which the modifiers - * should be reprinted. */ - protected $modifierChangeMap; /** - * Creates a pretty printer instance using the given options. - * - * Supported options: - * * bool $shortArraySyntax = false: Whether to use [] instead of array() as the default array - * syntax, if the node does not specify a format. + * Sets subject prophecy. * - * @param array $options Dictionary of formatting options + * @param ProphecyInterface $prophecy */ - public function __construct(array $options = []) - { - $this->docStringEndToken = '_DOC_STRING_END_' . \mt_rand(); - $defaultOptions = ['shortArraySyntax' => \false]; - $this->options = $options + $defaultOptions; - } + public function setProphecy(\Prophecy\Prophecy\ProphecyInterface $prophecy); /** - * Reset pretty printing state. + * Returns subject prophecy. + * + * @return ProphecyInterface */ - protected function resetState() - { - $this->indentLevel = 0; - $this->nl = "\n"; - $this->origTokens = null; - } + public function getProphecy(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Prophecy; + +/** + * Core Prophecy interface. + * + * @author Konstantin Kudryashov + */ +interface ProphecyInterface +{ /** - * Set indentation level + * Reveals prophecy object (double) . * - * @param int $level Level in number of spaces + * @return object */ - protected function setIndentLevel(int $level) - { - $this->indentLevel = $level; - $this->nl = "\n" . \str_repeat(' ', $level); - } + public function reveal(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Prophecy; + +use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; +use Prophecy\Comparator\Factory as ComparatorFactory; +use Prophecy\Call\Call; +use Prophecy\Doubler\LazyDouble; +use Prophecy\Argument\ArgumentsWildcard; +use Prophecy\Call\CallCenter; +use Prophecy\Exception\Prophecy\ObjectProphecyException; +use Prophecy\Exception\Prophecy\MethodProphecyException; +use Prophecy\Exception\Prediction\AggregateException; +use Prophecy\Exception\Prediction\PredictionException; +/** + * Object prophecy. + * + * @author Konstantin Kudryashov + */ +class ObjectProphecy implements \Prophecy\Prophecy\ProphecyInterface +{ + private $lazyDouble; + private $callCenter; + private $revealer; + private $comparatorFactory; /** - * Increase indentation level. + * @var MethodProphecy[][] */ - protected function indent() - { - $this->indentLevel += 4; - $this->nl .= ' '; - } + private $methodProphecies = array(); /** - * Decrease indentation level. + * Initializes object prophecy. + * + * @param LazyDouble $lazyDouble + * @param CallCenter $callCenter + * @param RevealerInterface $revealer + * @param ComparatorFactory $comparatorFactory */ - protected function outdent() + public function __construct(LazyDouble $lazyDouble, CallCenter $callCenter = null, \Prophecy\Prophecy\RevealerInterface $revealer = null, ComparatorFactory $comparatorFactory = null) { - \assert($this->indentLevel >= 4); - $this->indentLevel -= 4; - $this->nl = "\n" . \str_repeat(' ', $this->indentLevel); + $this->lazyDouble = $lazyDouble; + $this->callCenter = $callCenter ?: new CallCenter(); + $this->revealer = $revealer ?: new \Prophecy\Prophecy\Revealer(); + $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); } /** - * Pretty prints an array of statements. + * Forces double to extend specific class. * - * @param Node[] $stmts Array of statements + * @param string $class * - * @return string Pretty printed statements + * @return $this */ - public function prettyPrint(array $stmts) : string + public function willExtend($class) { - $this->resetState(); - $this->preprocessNodes($stmts); - return \ltrim($this->handleMagicTokens($this->pStmts($stmts, \false))); + $this->lazyDouble->setParentClass($class); + return $this; } /** - * Pretty prints an expression. + * Forces double to implement specific interface. * - * @param Expr $node Expression node + * @param string $interface * - * @return string Pretty printed node + * @return $this */ - public function prettyPrintExpr(\PHPUnit\PhpParser\Node\Expr $node) : string + public function willImplement($interface) { - $this->resetState(); - return $this->handleMagicTokens($this->p($node)); + $this->lazyDouble->addInterface($interface); + return $this; } /** - * Pretty prints a file of statements (includes the opening prettyPrint($stmts); - if ($stmts[0] instanceof \PHPUnit\PhpParser\Node\Stmt\InlineHTML) { - $p = \preg_replace('/^<\\?php\\s+\\?>\\n?/', '', $p); - } - if ($stmts[\count($stmts) - 1] instanceof \PHPUnit\PhpParser\Node\Stmt\InlineHTML) { - $p = \preg_replace('/<\\?php$/', '', \rtrim($p)); - } - return $p; + $this->lazyDouble->setArguments($arguments); + return $this; } /** - * Preprocesses the top-level nodes to initialize pretty printer state. + * Reveals double. * - * @param Node[] $nodes Array of nodes - */ - protected function preprocessNodes(array $nodes) - { - /* We can use semicolon-namespaces unless there is a global namespace declaration */ - $this->canUseSemicolonNamespaces = \true; - foreach ($nodes as $node) { - if ($node instanceof \PHPUnit\PhpParser\Node\Stmt\Namespace_ && null === $node->name) { - $this->canUseSemicolonNamespaces = \false; - break; - } - } - } - /** - * Handles (and removes) no-indent and doc-string-end tokens. + * @return object * - * @param string $str - * @return string + * @throws \Prophecy\Exception\Prophecy\ObjectProphecyException If double doesn't implement needed interface */ - protected function handleMagicTokens(string $str) : string + public function reveal() { - // Replace doc-string-end tokens with nothing or a newline - $str = \str_replace($this->docStringEndToken . ";\n", ";\n", $str); - $str = \str_replace($this->docStringEndToken, "\n", $str); - return $str; + $double = $this->lazyDouble->getInstance(); + if (null === $double || !$double instanceof \Prophecy\Prophecy\ProphecySubjectInterface) { + throw new ObjectProphecyException("Generated double must implement ProphecySubjectInterface, but it does not.\n" . 'It seems you have wrongly configured doubler without required ClassPatch.', $this); + } + $double->setProphecy($this); + return $double; } /** - * Pretty prints an array of nodes (statements) and indents them optionally. + * Adds method prophecy to object prophecy. * - * @param Node[] $nodes Array of nodes - * @param bool $indent Whether to indent the printed nodes + * @param MethodProphecy $methodProphecy * - * @return string Pretty printed statements + * @throws \Prophecy\Exception\Prophecy\MethodProphecyException If method prophecy doesn't + * have arguments wildcard */ - protected function pStmts(array $nodes, bool $indent = \true) : string + public function addMethodProphecy(\Prophecy\Prophecy\MethodProphecy $methodProphecy) { - if ($indent) { - $this->indent(); - } - $result = ''; - foreach ($nodes as $node) { - $comments = $node->getComments(); - if ($comments) { - $result .= $this->nl . $this->pComments($comments); - if ($node instanceof \PHPUnit\PhpParser\Node\Stmt\Nop) { - continue; - } - } - $result .= $this->nl . $this->p($node); + $argumentsWildcard = $methodProphecy->getArgumentsWildcard(); + if (null === $argumentsWildcard) { + throw new MethodProphecyException(\sprintf("Can not add prophecy for a method `%s::%s()`\n" . "as you did not specify arguments wildcard for it.", \get_class($this->reveal()), $methodProphecy->getMethodName()), $methodProphecy); } - if ($indent) { - $this->outdent(); + $methodName = \strtolower($methodProphecy->getMethodName()); + if (!isset($this->methodProphecies[$methodName])) { + $this->methodProphecies[$methodName] = array(); } - return $result; + $this->methodProphecies[$methodName][] = $methodProphecy; } /** - * Pretty-print an infix operation while taking precedence into account. + * Returns either all or related to single method prophecies. * - * @param string $class Node class of operator - * @param Node $leftNode Left-hand side node - * @param string $operatorString String representation of the operator - * @param Node $rightNode Right-hand side node + * @param null|string $methodName * - * @return string Pretty printed infix operation + * @return MethodProphecy[] */ - protected function pInfixOp(string $class, \PHPUnit\PhpParser\Node $leftNode, string $operatorString, \PHPUnit\PhpParser\Node $rightNode) : string + public function getMethodProphecies($methodName = null) { - list($precedence, $associativity) = $this->precedenceMap[$class]; - return $this->pPrec($leftNode, $precedence, $associativity, -1) . $operatorString . $this->pPrec($rightNode, $precedence, $associativity, 1); + if (null === $methodName) { + return $this->methodProphecies; + } + $methodName = \strtolower($methodName); + if (!isset($this->methodProphecies[$methodName])) { + return array(); + } + return $this->methodProphecies[$methodName]; } /** - * Pretty-print a prefix operation while taking precedence into account. + * Makes specific method call. * - * @param string $class Node class of operator - * @param string $operatorString String representation of the operator - * @param Node $node Node + * @param string $methodName + * @param array $arguments * - * @return string Pretty printed prefix operation + * @return mixed */ - protected function pPrefixOp(string $class, string $operatorString, \PHPUnit\PhpParser\Node $node) : string + public function makeProphecyMethodCall($methodName, array $arguments) { - list($precedence, $associativity) = $this->precedenceMap[$class]; - return $operatorString . $this->pPrec($node, $precedence, $associativity, 1); + $arguments = $this->revealer->reveal($arguments); + $return = $this->callCenter->makeCall($this, $methodName, $arguments); + return $this->revealer->reveal($return); } /** - * Pretty-print a postfix operation while taking precedence into account. + * Finds calls by method name & arguments wildcard. * - * @param string $class Node class of operator - * @param string $operatorString String representation of the operator - * @param Node $node Node + * @param string $methodName + * @param ArgumentsWildcard $wildcard * - * @return string Pretty printed postfix operation + * @return Call[] */ - protected function pPostfixOp(string $class, \PHPUnit\PhpParser\Node $node, string $operatorString) : string + public function findProphecyMethodCalls($methodName, ArgumentsWildcard $wildcard) { - list($precedence, $associativity) = $this->precedenceMap[$class]; - return $this->pPrec($node, $precedence, $associativity, -1) . $operatorString; + return $this->callCenter->findCalls($methodName, $wildcard); } /** - * Prints an expression node with the least amount of parentheses necessary to preserve the meaning. - * - * @param Node $node Node to pretty print - * @param int $parentPrecedence Precedence of the parent operator - * @param int $parentAssociativity Associativity of parent operator - * (-1 is left, 0 is nonassoc, 1 is right) - * @param int $childPosition Position of the node relative to the operator - * (-1 is left, 1 is right) + * Checks that registered method predictions do not fail. * - * @return string The pretty printed node + * @throws \Prophecy\Exception\Prediction\AggregateException If any of registered predictions fail + * @throws \Prophecy\Exception\Call\UnexpectedCallException */ - protected function pPrec(\PHPUnit\PhpParser\Node $node, int $parentPrecedence, int $parentAssociativity, int $childPosition) : string + public function checkProphecyMethodsPredictions() { - $class = \get_class($node); - if (isset($this->precedenceMap[$class])) { - $childPrecedence = $this->precedenceMap[$class][0]; - if ($childPrecedence > $parentPrecedence || $parentPrecedence === $childPrecedence && $parentAssociativity !== $childPosition) { - return '(' . $this->p($node) . ')'; + $exception = new AggregateException(\sprintf("%s:\n", \get_class($this->reveal()))); + $exception->setObjectProphecy($this); + $this->callCenter->checkUnexpectedCalls(); + foreach ($this->methodProphecies as $prophecies) { + foreach ($prophecies as $prophecy) { + try { + $prophecy->checkPrediction(); + } catch (PredictionException $e) { + $exception->append($e); + } } } - return $this->p($node); + if (\count($exception->getExceptions())) { + throw $exception; + } } /** - * Pretty prints an array of nodes and implodes the printed values. + * Creates new method prophecy using specified method name and arguments. * - * @param Node[] $nodes Array of Nodes to be printed - * @param string $glue Character to implode with + * @param string $methodName + * @param array $arguments * - * @return string Imploded pretty printed nodes + * @return MethodProphecy */ - protected function pImplode(array $nodes, string $glue = '') : string + public function __call($methodName, array $arguments) { - $pNodes = []; - foreach ($nodes as $node) { - if (null === $node) { - $pNodes[] = ''; - } else { - $pNodes[] = $this->p($node); + $arguments = new ArgumentsWildcard($this->revealer->reveal($arguments)); + foreach ($this->getMethodProphecies($methodName) as $prophecy) { + $argumentsWildcard = $prophecy->getArgumentsWildcard(); + $comparator = $this->comparatorFactory->getComparatorFor($argumentsWildcard, $arguments); + try { + $comparator->assertEquals($argumentsWildcard, $arguments); + return $prophecy; + } catch (ComparisonFailure $failure) { } } - return \implode($glue, $pNodes); + return new \Prophecy\Prophecy\MethodProphecy($this, $methodName, $arguments); } /** - * Pretty prints an array of nodes and implodes the printed values with commas. + * Tries to get property value from double. * - * @param Node[] $nodes Array of Nodes to be printed + * @param string $name * - * @return string Comma separated pretty printed nodes + * @return mixed */ - protected function pCommaSeparated(array $nodes) : string + public function __get($name) { - return $this->pImplode($nodes, ', '); + return $this->reveal()->{$name}; } /** - * Pretty prints a comma-separated list of nodes in multiline style, including comments. + * Tries to set property value to double. * - * The result includes a leading newline and one level of indentation (same as pStmts). + * @param string $name + * @param mixed $value + */ + public function __set($name, $value) + { + $this->reveal()->{$name} = $this->revealer->reveal($value); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy; + +use Prophecy\Argument\Token; +/** + * Argument tokens shortcuts. + * + * @author Konstantin Kudryashov + */ +class Argument +{ + /** + * Checks that argument is exact value or object. * - * @param Node[] $nodes Array of Nodes to be printed - * @param bool $trailingComma Whether to use a trailing comma + * @param mixed $value * - * @return string Comma separated pretty printed nodes in multiline style + * @return Token\ExactValueToken */ - protected function pCommaSeparatedMultiline(array $nodes, bool $trailingComma) : string + public static function exact($value) { - $this->indent(); - $result = ''; - $lastIdx = \count($nodes) - 1; - foreach ($nodes as $idx => $node) { - if ($node !== null) { - $comments = $node->getComments(); - if ($comments) { - $result .= $this->nl . $this->pComments($comments); - } - $result .= $this->nl . $this->p($node); - } else { - $result .= $this->nl; - } - if ($trailingComma || $idx !== $lastIdx) { - $result .= ','; - } - } - $this->outdent(); - return $result; + return new Token\ExactValueToken($value); } /** - * Prints reformatted text of the passed comments. + * Checks that argument is of specific type or instance of specific class. * - * @param Comment[] $comments List of comments + * @param string $type Type name (`integer`, `string`) or full class name * - * @return string Reformatted text of comments + * @return Token\TypeToken */ - protected function pComments(array $comments) : string + public static function type($type) { - $formattedComments = []; - foreach ($comments as $comment) { - $formattedComments[] = \str_replace("\n", $this->nl, $comment->getReformattedText()); - } - return \implode($this->nl, $formattedComments); + return new Token\TypeToken($type); } /** - * Perform a format-preserving pretty print of an AST. + * Checks that argument object has specific state. * - * The format preservation is best effort. For some changes to the AST the formatting will not - * be preserved (at least not locally). + * @param string $methodName + * @param mixed $value * - * In order to use this method a number of prerequisites must be satisfied: - * * The startTokenPos and endTokenPos attributes in the lexer must be enabled. - * * The CloningVisitor must be run on the AST prior to modification. - * * The original tokens must be provided, using the getTokens() method on the lexer. + * @return Token\ObjectStateToken + */ + public static function which($methodName, $value) + { + return new Token\ObjectStateToken($methodName, $value); + } + /** + * Checks that argument matches provided callback. * - * @param Node[] $stmts Modified AST with links to original AST - * @param Node[] $origStmts Original AST with token offset information - * @param array $origTokens Tokens of the original code + * @param callable $callback * - * @return string + * @return Token\CallbackToken */ - public function printFormatPreserving(array $stmts, array $origStmts, array $origTokens) : string + public static function that($callback) { - $this->initializeNodeListDiffer(); - $this->initializeLabelCharMap(); - $this->initializeFixupMap(); - $this->initializeRemovalMap(); - $this->initializeInsertionMap(); - $this->initializeListInsertionMap(); - $this->initializeEmptyListInsertionMap(); - $this->initializeModifierChangeMap(); - $this->resetState(); - $this->origTokens = new \PHPUnit\PhpParser\Internal\TokenStream($origTokens); - $this->preprocessNodes($stmts); - $pos = 0; - $result = $this->pArray($stmts, $origStmts, $pos, 0, 'File', 'stmts', null); - if (null !== $result) { - $result .= $this->origTokens->getTokenCode($pos, \count($origTokens), 0); - } else { - // Fallback - // TODO Add pStmts($stmts, \false); - } - return \ltrim($this->handleMagicTokens($result)); + return new Token\CallbackToken($callback); } - protected function pFallback(\PHPUnit\PhpParser\Node $node) + /** + * Matches any single value. + * + * @return Token\AnyValueToken + */ + public static function any() { - return $this->{'p' . $node->getType()}($node); + return new Token\AnyValueToken(); } /** - * Pretty prints a node. - * - * This method also handles formatting preservation for nodes. - * - * @param Node $node Node to be pretty printed - * @param bool $parentFormatPreserved Whether parent node has preserved formatting + * Matches all values to the rest of the signature. * - * @return string Pretty printed node + * @return Token\AnyValuesToken */ - protected function p(\PHPUnit\PhpParser\Node $node, $parentFormatPreserved = \false) : string + public static function cetera() { - // No orig tokens means this is a normal pretty print without preservation of formatting - if (!$this->origTokens) { - return $this->{'p' . $node->getType()}($node); - } - /** @var Node $origNode */ - $origNode = $node->getAttribute('origNode'); - if (null === $origNode) { - return $this->pFallback($node); - } - $class = \get_class($node); - \assert($class === \get_class($origNode)); - $startPos = $origNode->getStartTokenPos(); - $endPos = $origNode->getEndTokenPos(); - \assert($startPos >= 0 && $endPos >= 0); - $fallbackNode = $node; - if ($node instanceof \PHPUnit\PhpParser\Node\Expr\New_ && $node->class instanceof \PHPUnit\PhpParser\Node\Stmt\Class_) { - // Normalize node structure of anonymous classes - $node = \PHPUnit\PhpParser\Internal\PrintableNewAnonClassNode::fromNewNode($node); - $origNode = \PHPUnit\PhpParser\Internal\PrintableNewAnonClassNode::fromNewNode($origNode); - } - // InlineHTML node does not contain closing and opening PHP tags. If the parent formatting - // is not preserved, then we need to use the fallback code to make sure the tags are - // printed. - if ($node instanceof \PHPUnit\PhpParser\Node\Stmt\InlineHTML && !$parentFormatPreserved) { - return $this->pFallback($fallbackNode); - } - $indentAdjustment = $this->indentLevel - $this->origTokens->getIndentationBefore($startPos); - $type = $node->getType(); - $fixupInfo = $this->fixupMap[$class] ?? null; - $result = ''; - $pos = $startPos; - foreach ($node->getSubNodeNames() as $subNodeName) { - $subNode = $node->{$subNodeName}; - $origSubNode = $origNode->{$subNodeName}; - if (!$subNode instanceof \PHPUnit\PhpParser\Node && $subNode !== null || !$origSubNode instanceof \PHPUnit\PhpParser\Node && $origSubNode !== null) { - if ($subNode === $origSubNode) { - // Unchanged, can reuse old code - continue; - } - if (\is_array($subNode) && \is_array($origSubNode)) { - // Array subnode changed, we might be able to reconstruct it - $listResult = $this->pArray($subNode, $origSubNode, $pos, $indentAdjustment, $type, $subNodeName, $fixupInfo[$subNodeName] ?? null); - if (null === $listResult) { - return $this->pFallback($fallbackNode); - } - $result .= $listResult; - continue; - } - if (\is_int($subNode) && \is_int($origSubNode)) { - // Check if this is a modifier change - $key = $type . '->' . $subNodeName; - if (!isset($this->modifierChangeMap[$key])) { - return $this->pFallback($fallbackNode); - } - $findToken = $this->modifierChangeMap[$key]; - $result .= $this->pModifiers($subNode); - $pos = $this->origTokens->findRight($pos, $findToken); - continue; - } - // If a non-node, non-array subnode changed, we don't be able to do a partial - // reconstructions, as we don't have enough offset information. Pretty print the - // whole node instead. - return $this->pFallback($fallbackNode); - } - $extraLeft = ''; - $extraRight = ''; - if ($origSubNode !== null) { - $subStartPos = $origSubNode->getStartTokenPos(); - $subEndPos = $origSubNode->getEndTokenPos(); - \assert($subStartPos >= 0 && $subEndPos >= 0); - } else { - if ($subNode === null) { - // Both null, nothing to do - continue; - } - // A node has been inserted, check if we have insertion information for it - $key = $type . '->' . $subNodeName; - if (!isset($this->insertionMap[$key])) { - return $this->pFallback($fallbackNode); - } - list($findToken, $beforeToken, $extraLeft, $extraRight) = $this->insertionMap[$key]; - if (null !== $findToken) { - $subStartPos = $this->origTokens->findRight($pos, $findToken) + (int) (!$beforeToken); - } else { - $subStartPos = $pos; - } - if (null === $extraLeft && null !== $extraRight) { - // If inserting on the right only, skipping whitespace looks better - $subStartPos = $this->origTokens->skipRightWhitespace($subStartPos); - } - $subEndPos = $subStartPos - 1; - } - if (null === $subNode) { - // A node has been removed, check if we have removal information for it - $key = $type . '->' . $subNodeName; - if (!isset($this->removalMap[$key])) { - return $this->pFallback($fallbackNode); - } - // Adjust positions to account for additional tokens that must be skipped - $removalInfo = $this->removalMap[$key]; - if (isset($removalInfo['left'])) { - $subStartPos = $this->origTokens->skipLeft($subStartPos - 1, $removalInfo['left']) + 1; - } - if (isset($removalInfo['right'])) { - $subEndPos = $this->origTokens->skipRight($subEndPos + 1, $removalInfo['right']) - 1; - } - } - $result .= $this->origTokens->getTokenCode($pos, $subStartPos, $indentAdjustment); - if (null !== $subNode) { - $result .= $extraLeft; - $origIndentLevel = $this->indentLevel; - $this->setIndentLevel($this->origTokens->getIndentationBefore($subStartPos) + $indentAdjustment); - // If it's the same node that was previously in this position, it certainly doesn't - // need fixup. It's important to check this here, because our fixup checks are more - // conservative than strictly necessary. - if (isset($fixupInfo[$subNodeName]) && $subNode->getAttribute('origNode') !== $origSubNode) { - $fixup = $fixupInfo[$subNodeName]; - $res = $this->pFixup($fixup, $subNode, $class, $subStartPos, $subEndPos); - } else { - $res = $this->p($subNode, \true); - } - $this->safeAppend($result, $res); - $this->setIndentLevel($origIndentLevel); - $result .= $extraRight; - } - $pos = $subEndPos + 1; - } - $result .= $this->origTokens->getTokenCode($pos, $endPos + 1, $indentAdjustment); - return $result; + return new Token\AnyValuesToken(); } /** - * Perform a format-preserving pretty print of an array. + * Checks that argument matches all tokens * - * @param array $nodes New nodes - * @param array $origNodes Original nodes - * @param int $pos Current token position (updated by reference) - * @param int $indentAdjustment Adjustment for indentation - * @param string $parentNodeType Type of the containing node. - * @param string $subNodeName Name of array subnode. - * @param null|int $fixup Fixup information for array item nodes + * @param mixed ... a list of tokens * - * @return null|string Result of pretty print or null if cannot preserve formatting + * @return Token\LogicalAndToken */ - protected function pArray(array $nodes, array $origNodes, int &$pos, int $indentAdjustment, string $parentNodeType, string $subNodeName, $fixup) + public static function allOf() { - $diff = $this->nodeListDiffer->diffWithReplacements($origNodes, $nodes); - $mapKey = $parentNodeType . '->' . $subNodeName; - $insertStr = $this->listInsertionMap[$mapKey] ?? null; - $isStmtList = $subNodeName === 'stmts'; - $beforeFirstKeepOrReplace = \true; - $skipRemovedNode = \false; - $delayedAdd = []; - $lastElemIndentLevel = $this->indentLevel; - $insertNewline = \false; - if ($insertStr === "\n") { - $insertStr = ''; - $insertNewline = \true; - } - if ($isStmtList && \count($origNodes) === 1 && \count($nodes) !== 1) { - $startPos = $origNodes[0]->getStartTokenPos(); - $endPos = $origNodes[0]->getEndTokenPos(); - \assert($startPos >= 0 && $endPos >= 0); - if (!$this->origTokens->haveBraces($startPos, $endPos)) { - // This was a single statement without braces, but either additional statements - // have been added, or the single statement has been removed. This requires the - // addition of braces. For now fall back. - // TODO: Try to preserve formatting - return null; - } - } - $result = ''; - foreach ($diff as $i => $diffElem) { - $diffType = $diffElem->type; - /** @var Node|null $arrItem */ - $arrItem = $diffElem->new; - /** @var Node|null $origArrItem */ - $origArrItem = $diffElem->old; - if ($diffType === \PHPUnit\PhpParser\Internal\DiffElem::TYPE_KEEP || $diffType === \PHPUnit\PhpParser\Internal\DiffElem::TYPE_REPLACE) { - $beforeFirstKeepOrReplace = \false; - if ($origArrItem === null || $arrItem === null) { - // We can only handle the case where both are null - if ($origArrItem === $arrItem) { - continue; - } - return null; - } - if (!$arrItem instanceof \PHPUnit\PhpParser\Node || !$origArrItem instanceof \PHPUnit\PhpParser\Node) { - // We can only deal with nodes. This can occur for Names, which use string arrays. - return null; - } - $itemStartPos = $origArrItem->getStartTokenPos(); - $itemEndPos = $origArrItem->getEndTokenPos(); - \assert($itemStartPos >= 0 && $itemEndPos >= 0 && $itemStartPos >= $pos); - $origIndentLevel = $this->indentLevel; - $lastElemIndentLevel = $this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment; - $this->setIndentLevel($lastElemIndentLevel); - $comments = $arrItem->getComments(); - $origComments = $origArrItem->getComments(); - $commentStartPos = $origComments ? $origComments[0]->getStartTokenPos() : $itemStartPos; - \assert($commentStartPos >= 0); - if ($commentStartPos < $pos) { - // Comments may be assigned to multiple nodes if they start at the same position. - // Make sure we don't try to print them multiple times. - $commentStartPos = $itemStartPos; - } - if ($skipRemovedNode) { - if ($isStmtList && $this->origTokens->haveBracesInRange($pos, $itemStartPos)) { - // We'd remove the brace of a code block. - // TODO: Preserve formatting. - $this->setIndentLevel($origIndentLevel); - return null; - } - } else { - $result .= $this->origTokens->getTokenCode($pos, $commentStartPos, $indentAdjustment); - } - if (!empty($delayedAdd)) { - /** @var Node $delayedAddNode */ - foreach ($delayedAdd as $delayedAddNode) { - if ($insertNewline) { - $delayedAddComments = $delayedAddNode->getComments(); - if ($delayedAddComments) { - $result .= $this->pComments($delayedAddComments) . $this->nl; - } - } - $this->safeAppend($result, $this->p($delayedAddNode, \true)); - if ($insertNewline) { - $result .= $insertStr . $this->nl; - } else { - $result .= $insertStr; - } - } - $delayedAdd = []; - } - if ($comments !== $origComments) { - if ($comments) { - $result .= $this->pComments($comments) . $this->nl; - } - } else { - $result .= $this->origTokens->getTokenCode($commentStartPos, $itemStartPos, $indentAdjustment); - } - // If we had to remove anything, we have done so now. - $skipRemovedNode = \false; - } elseif ($diffType === \PHPUnit\PhpParser\Internal\DiffElem::TYPE_ADD) { - if (null === $insertStr) { - // We don't have insertion information for this list type - return null; - } - if ($insertStr === ', ' && $this->isMultiline($origNodes)) { - $insertStr = ','; - $insertNewline = \true; - } - if ($beforeFirstKeepOrReplace) { - // Will be inserted at the next "replace" or "keep" element - $delayedAdd[] = $arrItem; - continue; - } - $itemStartPos = $pos; - $itemEndPos = $pos - 1; - $origIndentLevel = $this->indentLevel; - $this->setIndentLevel($lastElemIndentLevel); - if ($insertNewline) { - $comments = $arrItem->getComments(); - if ($comments) { - $result .= $this->nl . $this->pComments($comments); - } - $result .= $insertStr . $this->nl; - } else { - $result .= $insertStr; - } - } elseif ($diffType === \PHPUnit\PhpParser\Internal\DiffElem::TYPE_REMOVE) { - if (!$origArrItem instanceof \PHPUnit\PhpParser\Node) { - // We only support removal for nodes - return null; - } - $itemStartPos = $origArrItem->getStartTokenPos(); - $itemEndPos = $origArrItem->getEndTokenPos(); - \assert($itemStartPos >= 0 && $itemEndPos >= 0); - // Consider comments part of the node. - $origComments = $origArrItem->getComments(); - if ($origComments) { - $itemStartPos = $origComments[0]->getStartTokenPos(); - } - if ($i === 0) { - // If we're removing from the start, keep the tokens before the node and drop those after it, - // instead of the other way around. - $result .= $this->origTokens->getTokenCode($pos, $itemStartPos, $indentAdjustment); - $skipRemovedNode = \true; - } else { - if ($isStmtList && $this->origTokens->haveBracesInRange($pos, $itemStartPos)) { - // We'd remove the brace of a code block. - // TODO: Preserve formatting. - return null; - } - } - $pos = $itemEndPos + 1; - continue; - } else { - throw new \Exception("Shouldn't happen"); - } - if (null !== $fixup && $arrItem->getAttribute('origNode') !== $origArrItem) { - $res = $this->pFixup($fixup, $arrItem, null, $itemStartPos, $itemEndPos); - } else { - $res = $this->p($arrItem, \true); - } - $this->safeAppend($result, $res); - $this->setIndentLevel($origIndentLevel); - $pos = $itemEndPos + 1; - } - if ($skipRemovedNode) { - // TODO: Support removing single node. - return null; - } - if (!empty($delayedAdd)) { - if (!isset($this->emptyListInsertionMap[$mapKey])) { - return null; - } - list($findToken, $extraLeft, $extraRight) = $this->emptyListInsertionMap[$mapKey]; - if (null !== $findToken) { - $insertPos = $this->origTokens->findRight($pos, $findToken) + 1; - $result .= $this->origTokens->getTokenCode($pos, $insertPos, $indentAdjustment); - $pos = $insertPos; - } - $first = \true; - $result .= $extraLeft; - foreach ($delayedAdd as $delayedAddNode) { - if (!$first) { - $result .= $insertStr; - } - $result .= $this->p($delayedAddNode, \true); - $first = \false; - } - $result .= $extraRight; - } - return $result; + return new Token\LogicalAndToken(\func_get_args()); + } + /** + * Checks that argument array or countable object has exact number of elements. + * + * @param integer $value array elements count + * + * @return Token\ArrayCountToken + */ + public static function size($value) + { + return new Token\ArrayCountToken($value); } /** - * Print node with fixups. + * Checks that argument array contains (key, value) pair * - * Fixups here refer to the addition of extra parentheses, braces or other characters, that - * are required to preserve program semantics in a certain context (e.g. to maintain precedence - * or because only certain expressions are allowed in certain places). + * @param mixed $key exact value or token + * @param mixed $value exact value or token * - * @param int $fixup Fixup type - * @param Node $subNode Subnode to print - * @param string|null $parentClass Class of parent node - * @param int $subStartPos Original start pos of subnode - * @param int $subEndPos Original end pos of subnode + * @return Token\ArrayEntryToken + */ + public static function withEntry($key, $value) + { + return new Token\ArrayEntryToken($key, $value); + } + /** + * Checks that arguments array entries all match value * - * @return string Result of fixed-up print of subnode + * @param mixed $value + * + * @return Token\ArrayEveryEntryToken */ - protected function pFixup(int $fixup, \PHPUnit\PhpParser\Node $subNode, $parentClass, int $subStartPos, int $subEndPos) : string + public static function withEveryEntry($value) { - switch ($fixup) { - case self::FIXUP_PREC_LEFT: - case self::FIXUP_PREC_RIGHT: - if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) { - list($precedence, $associativity) = $this->precedenceMap[$parentClass]; - return $this->pPrec($subNode, $precedence, $associativity, $fixup === self::FIXUP_PREC_LEFT ? -1 : 1); - } - break; - case self::FIXUP_CALL_LHS: - if ($this->callLhsRequiresParens($subNode) && !$this->origTokens->haveParens($subStartPos, $subEndPos)) { - return '(' . $this->p($subNode) . ')'; - } - break; - case self::FIXUP_DEREF_LHS: - if ($this->dereferenceLhsRequiresParens($subNode) && !$this->origTokens->haveParens($subStartPos, $subEndPos)) { - return '(' . $this->p($subNode) . ')'; - } - break; - case self::FIXUP_BRACED_NAME: - case self::FIXUP_VAR_BRACED_NAME: - if ($subNode instanceof \PHPUnit\PhpParser\Node\Expr && !$this->origTokens->haveBraces($subStartPos, $subEndPos)) { - return ($fixup === self::FIXUP_VAR_BRACED_NAME ? '$' : '') . '{' . $this->p($subNode) . '}'; - } - break; - case self::FIXUP_ENCAPSED: - if (!$subNode instanceof \PHPUnit\PhpParser\Node\Scalar\EncapsedStringPart && !$this->origTokens->haveBraces($subStartPos, $subEndPos)) { - return '{' . $this->p($subNode) . '}'; - } - break; - default: - throw new \Exception('Cannot happen'); - } - // Nothing special to do - return $this->p($subNode); + return new Token\ArrayEveryEntryToken($value); } /** - * Appends to a string, ensuring whitespace between label characters. + * Checks that argument array contains value * - * Example: "echo" and "$x" result in "echo$x", but "echo" and "x" result in "echo x". - * Without safeAppend the result would be "echox", which does not preserve semantics. + * @param mixed $value * - * @param string $str - * @param string $append + * @return Token\ArrayEntryToken */ - protected function safeAppend(string &$str, string $append) + public static function containing($value) { - if ($str === "") { - $str = $append; - return; - } - if ($append === "") { - return; - } - if (!$this->labelCharMap[$append[0]] || !$this->labelCharMap[$str[\strlen($str) - 1]]) { - $str .= $append; - } else { - $str .= " " . $append; - } + return new Token\ArrayEntryToken(self::any(), $value); } /** - * Determines whether the LHS of a call must be wrapped in parenthesis. + * Checks that argument array has key * - * @param Node $node LHS of a call + * @param mixed $key exact value or token * - * @return bool Whether parentheses are required + * @return Token\ArrayEntryToken */ - protected function callLhsRequiresParens(\PHPUnit\PhpParser\Node $node) : bool + public static function withKey($key) { - return !($node instanceof \PHPUnit\PhpParser\Node\Name || $node instanceof \PHPUnit\PhpParser\Node\Expr\Variable || $node instanceof \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch || $node instanceof \PHPUnit\PhpParser\Node\Expr\FuncCall || $node instanceof \PHPUnit\PhpParser\Node\Expr\MethodCall || $node instanceof \PHPUnit\PhpParser\Node\Expr\NullsafeMethodCall || $node instanceof \PHPUnit\PhpParser\Node\Expr\StaticCall || $node instanceof \PHPUnit\PhpParser\Node\Expr\Array_); + return new Token\ArrayEntryToken($key, self::any()); } /** - * Determines whether the LHS of a dereferencing operation must be wrapped in parenthesis. + * Checks that argument does not match the value|token. * - * @param Node $node LHS of dereferencing operation + * @param mixed $value either exact value or argument token * - * @return bool Whether parentheses are required + * @return Token\LogicalNotToken */ - protected function dereferenceLhsRequiresParens(\PHPUnit\PhpParser\Node $node) : bool + public static function not($value) { - return !($node instanceof \PHPUnit\PhpParser\Node\Expr\Variable || $node instanceof \PHPUnit\PhpParser\Node\Name || $node instanceof \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch || $node instanceof \PHPUnit\PhpParser\Node\Expr\PropertyFetch || $node instanceof \PHPUnit\PhpParser\Node\Expr\NullsafePropertyFetch || $node instanceof \PHPUnit\PhpParser\Node\Expr\StaticPropertyFetch || $node instanceof \PHPUnit\PhpParser\Node\Expr\FuncCall || $node instanceof \PHPUnit\PhpParser\Node\Expr\MethodCall || $node instanceof \PHPUnit\PhpParser\Node\Expr\NullsafeMethodCall || $node instanceof \PHPUnit\PhpParser\Node\Expr\StaticCall || $node instanceof \PHPUnit\PhpParser\Node\Expr\Array_ || $node instanceof \PHPUnit\PhpParser\Node\Scalar\String_ || $node instanceof \PHPUnit\PhpParser\Node\Expr\ConstFetch || $node instanceof \PHPUnit\PhpParser\Node\Expr\ClassConstFetch); + return new Token\LogicalNotToken($value); } /** - * Print modifiers, including trailing whitespace. + * @param string $value * - * @param int $modifiers Modifier mask to print + * @return Token\StringContainsToken + */ + public static function containingString($value) + { + return new Token\StringContainsToken($value); + } + /** + * Checks that argument is identical value. * - * @return string Printed modifiers + * @param mixed $value + * + * @return Token\IdenticalValueToken */ - protected function pModifiers(int $modifiers) + public static function is($value) { - return ($modifiers & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC ? 'public ' : '') . ($modifiers & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED ? 'protected ' : '') . ($modifiers & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE ? 'private ' : '') . ($modifiers & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_STATIC ? 'static ' : '') . ($modifiers & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_ABSTRACT ? 'abstract ' : '') . ($modifiers & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_FINAL ? 'final ' : ''); + return new Token\IdenticalValueToken($value); } /** - * Determine whether a list of nodes uses multiline formatting. + * Check that argument is same value when rounding to the + * given precision. * - * @param (Node|null)[] $nodes Node list + * @param float $value + * @param float $precision * - * @return bool Whether multiline formatting is used + * @return Token\ApproximateValueToken */ - protected function isMultiline(array $nodes) : bool + public static function approximate($value, $precision = 0) { - if (\count($nodes) < 2) { - return \false; - } - $pos = -1; - foreach ($nodes as $node) { - if (null === $node) { - continue; - } - $endPos = $node->getEndTokenPos() + 1; - if ($pos >= 0) { - $text = $this->origTokens->getTokenCode($pos, $endPos, 0); - if (\false === \strpos($text, "\n")) { - // We require that a newline is present between *every* item. If the formatting - // is inconsistent, with only some items having newlines, we don't consider it - // as multiline - return \false; - } - } - $pos = $endPos; - } - return \true; + return new Token\ApproximateValueToken($value, $precision); } /** - * Lazily initializes label char map. + * Checks that argument is in array. * - * The label char map determines whether a certain character may occur in a label. + * @param array $value + * + * @return Token\InArrayToken */ - protected function initializeLabelCharMap() + public static function in($value) { - if ($this->labelCharMap) { - return; - } - $this->labelCharMap = []; - for ($i = 0; $i < 256; $i++) { - // Since PHP 7.1 The lower range is 0x80. However, we also want to support code for - // older versions. - $this->labelCharMap[\chr($i)] = $i >= 0x7f || \ctype_alnum($i); - } + return new Token\InArrayToken($value); } /** - * Lazily initializes node list differ. + * Checks that argument is not in array. * - * The node list differ is used to determine differences between two array subnodes. + * @param array $value + * + * @return Token\NotInArrayToken */ - protected function initializeNodeListDiffer() + public static function notIn($value) { - if ($this->nodeListDiffer) { - return; - } - $this->nodeListDiffer = new \PHPUnit\PhpParser\Internal\Differ(function ($a, $b) { - if ($a instanceof \PHPUnit\PhpParser\Node && $b instanceof \PHPUnit\PhpParser\Node) { - return $a === $b->getAttribute('origNode'); - } - // Can happen for array destructuring - return $a === null && $b === null; - }); + return new Token\NotInArrayToken($value); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Util; + +use Prophecy\Call\Call; +/** + * String utility. + * + * @author Konstantin Kudryashov + */ +class StringUtil +{ + private $verbose; + /** + * @param bool $verbose + */ + public function __construct($verbose = \true) + { + $this->verbose = $verbose; } /** - * Lazily initializes fixup map. + * Stringifies any provided value. * - * The fixup map is used to determine whether a certain subnode of a certain node may require - * some kind of "fixup" operation, e.g. the addition of parenthesis or braces. + * @param mixed $value + * @param boolean $exportObject + * + * @return string */ - protected function initializeFixupMap() + public function stringify($value, $exportObject = \true) { - if ($this->fixupMap) { - return; + if (\is_array($value)) { + if (\range(0, \count($value) - 1) === \array_keys($value)) { + return '[' . \implode(', ', \array_map(array($this, __FUNCTION__), $value)) . ']'; + } + $stringify = array($this, __FUNCTION__); + return '[' . \implode(', ', \array_map(function ($item, $key) use($stringify) { + return (\is_integer($key) ? $key : '"' . $key . '"') . ' => ' . \call_user_func($stringify, $item); + }, $value, \array_keys($value))) . ']'; } - $this->fixupMap = [ - \PHPUnit\PhpParser\Node\Expr\PreInc::class => ['var' => self::FIXUP_PREC_RIGHT], - \PHPUnit\PhpParser\Node\Expr\PreDec::class => ['var' => self::FIXUP_PREC_RIGHT], - \PHPUnit\PhpParser\Node\Expr\PostInc::class => ['var' => self::FIXUP_PREC_LEFT], - \PHPUnit\PhpParser\Node\Expr\PostDec::class => ['var' => self::FIXUP_PREC_LEFT], - \PHPUnit\PhpParser\Node\Expr\Instanceof_::class => ['expr' => self::FIXUP_PREC_LEFT, 'class' => self::FIXUP_PREC_RIGHT], - \PHPUnit\PhpParser\Node\Expr\Ternary::class => ['cond' => self::FIXUP_PREC_LEFT, 'else' => self::FIXUP_PREC_RIGHT], - \PHPUnit\PhpParser\Node\Expr\FuncCall::class => ['name' => self::FIXUP_CALL_LHS], - \PHPUnit\PhpParser\Node\Expr\StaticCall::class => ['class' => self::FIXUP_DEREF_LHS], - \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch::class => ['var' => self::FIXUP_DEREF_LHS], - \PHPUnit\PhpParser\Node\Expr\ClassConstFetch::class => ['var' => self::FIXUP_DEREF_LHS], - \PHPUnit\PhpParser\Node\Expr\New_::class => ['class' => self::FIXUP_DEREF_LHS], - // TODO: FIXUP_NEW_VARIABLE - \PHPUnit\PhpParser\Node\Expr\MethodCall::class => ['var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], - \PHPUnit\PhpParser\Node\Expr\NullsafeMethodCall::class => ['var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], - \PHPUnit\PhpParser\Node\Expr\StaticPropertyFetch::class => ['class' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_VAR_BRACED_NAME], - \PHPUnit\PhpParser\Node\Expr\PropertyFetch::class => ['var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], - \PHPUnit\PhpParser\Node\Expr\NullsafePropertyFetch::class => ['var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME], - \PHPUnit\PhpParser\Node\Scalar\Encapsed::class => ['parts' => self::FIXUP_ENCAPSED], - ]; - $binaryOps = [\PHPUnit\PhpParser\Node\Expr\BinaryOp\Pow::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\Mul::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\Div::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\Mod::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\Plus::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\Minus::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\Concat::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\ShiftLeft::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\ShiftRight::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\Smaller::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\SmallerOrEqual::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\Greater::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\GreaterOrEqual::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\Equal::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\NotEqual::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\Identical::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\NotIdentical::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\Spaceship::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseAnd::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseXor::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseOr::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanAnd::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanOr::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\Coalesce::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalAnd::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalXor::class, \PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalOr::class]; - foreach ($binaryOps as $binaryOp) { - $this->fixupMap[$binaryOp] = ['left' => self::FIXUP_PREC_LEFT, 'right' => self::FIXUP_PREC_RIGHT]; + if (\is_resource($value)) { + return \get_resource_type($value) . ':' . $value; } - $assignOps = [\PHPUnit\PhpParser\Node\Expr\Assign::class, \PHPUnit\PhpParser\Node\Expr\AssignRef::class, \PHPUnit\PhpParser\Node\Expr\AssignOp\Plus::class, \PHPUnit\PhpParser\Node\Expr\AssignOp\Minus::class, \PHPUnit\PhpParser\Node\Expr\AssignOp\Mul::class, \PHPUnit\PhpParser\Node\Expr\AssignOp\Div::class, \PHPUnit\PhpParser\Node\Expr\AssignOp\Concat::class, \PHPUnit\PhpParser\Node\Expr\AssignOp\Mod::class, \PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseAnd::class, \PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseOr::class, \PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseXor::class, \PHPUnit\PhpParser\Node\Expr\AssignOp\ShiftLeft::class, \PHPUnit\PhpParser\Node\Expr\AssignOp\ShiftRight::class, \PHPUnit\PhpParser\Node\Expr\AssignOp\Pow::class, \PHPUnit\PhpParser\Node\Expr\AssignOp\Coalesce::class]; - foreach ($assignOps as $assignOp) { - $this->fixupMap[$assignOp] = ['var' => self::FIXUP_PREC_LEFT, 'expr' => self::FIXUP_PREC_RIGHT]; + if (\is_object($value)) { + return $exportObject ? \Prophecy\Util\ExportUtil::export($value) : \sprintf('%s:%s', \get_class($value), \spl_object_hash($value)); } - $prefixOps = [\PHPUnit\PhpParser\Node\Expr\BitwiseNot::class, \PHPUnit\PhpParser\Node\Expr\BooleanNot::class, \PHPUnit\PhpParser\Node\Expr\UnaryPlus::class, \PHPUnit\PhpParser\Node\Expr\UnaryMinus::class, \PHPUnit\PhpParser\Node\Expr\Cast\Int_::class, \PHPUnit\PhpParser\Node\Expr\Cast\Double::class, \PHPUnit\PhpParser\Node\Expr\Cast\String_::class, \PHPUnit\PhpParser\Node\Expr\Cast\Array_::class, \PHPUnit\PhpParser\Node\Expr\Cast\Object_::class, \PHPUnit\PhpParser\Node\Expr\Cast\Bool_::class, \PHPUnit\PhpParser\Node\Expr\Cast\Unset_::class, \PHPUnit\PhpParser\Node\Expr\ErrorSuppress::class, \PHPUnit\PhpParser\Node\Expr\YieldFrom::class, \PHPUnit\PhpParser\Node\Expr\Print_::class, \PHPUnit\PhpParser\Node\Expr\Include_::class]; - foreach ($prefixOps as $prefixOp) { - $this->fixupMap[$prefixOp] = ['expr' => self::FIXUP_PREC_RIGHT]; + if (\true === $value || \false === $value) { + return $value ? 'true' : 'false'; + } + if (\is_string($value)) { + $str = \sprintf('"%s"', \str_replace("\n", '\\n', $value)); + if (!$this->verbose && 50 <= \strlen($str)) { + return \substr($str, 0, 50) . '"...'; + } + return $str; + } + if (null === $value) { + return 'null'; } + return (string) $value; } /** - * Lazily initializes the removal map. + * Stringifies provided array of calls. * - * The removal map is used to determine which additional tokens should be returned when a - * certain node is replaced by null. + * @param Call[] $calls Array of Call instances + * + * @return string */ - protected function initializeRemovalMap() + public function stringifyCalls(array $calls) { - if ($this->removalMap) { - return; - } - $stripBoth = ['left' => \T_WHITESPACE, 'right' => \T_WHITESPACE]; - $stripLeft = ['left' => \T_WHITESPACE]; - $stripRight = ['right' => \T_WHITESPACE]; - $stripDoubleArrow = ['right' => \T_DOUBLE_ARROW]; - $stripColon = ['left' => ':']; - $stripEquals = ['left' => '=']; - $this->removalMap = ['Expr_ArrayDimFetch->dim' => $stripBoth, 'Expr_ArrayItem->key' => $stripDoubleArrow, 'Expr_ArrowFunction->returnType' => $stripColon, 'Expr_Closure->returnType' => $stripColon, 'Expr_Exit->expr' => $stripBoth, 'Expr_Ternary->if' => $stripBoth, 'Expr_Yield->key' => $stripDoubleArrow, 'Expr_Yield->value' => $stripBoth, 'Param->type' => $stripRight, 'Param->default' => $stripEquals, 'Stmt_Break->num' => $stripBoth, 'Stmt_Catch->var' => $stripLeft, 'Stmt_ClassMethod->returnType' => $stripColon, 'Stmt_Class->extends' => ['left' => \T_EXTENDS], 'Expr_PrintableNewAnonClass->extends' => ['left' => \T_EXTENDS], 'Stmt_Continue->num' => $stripBoth, 'Stmt_Foreach->keyVar' => $stripDoubleArrow, 'Stmt_Function->returnType' => $stripColon, 'Stmt_If->else' => $stripLeft, 'Stmt_Namespace->name' => $stripLeft, 'Stmt_Property->type' => $stripRight, 'Stmt_PropertyProperty->default' => $stripEquals, 'Stmt_Return->expr' => $stripBoth, 'Stmt_StaticVar->default' => $stripEquals, 'Stmt_TraitUseAdaptation_Alias->newName' => $stripLeft, 'Stmt_TryCatch->finally' => $stripLeft]; + $self = $this; + return \implode(\PHP_EOL, \array_map(function (Call $call) use($self) { + return \sprintf(' - %s(%s) @ %s', $call->getMethodName(), \implode(', ', \array_map(array($self, 'stringify'), $call->getArguments())), \str_replace(\GETCWD() . \DIRECTORY_SEPARATOR, '', $call->getCallPlace())); + }, $calls)); } - protected function initializeInsertionMap() +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +/** + * This class is a modification from sebastianbergmann/exporter + * @see https://github.com/sebastianbergmann/exporter + */ +class ExportUtil +{ + /** + * Exports a value as a string + * + * The output of this method is similar to the output of print_r(), but + * improved in various aspects: + * + * - NULL is rendered as "null" (instead of "") + * - TRUE is rendered as "true" (instead of "1") + * - FALSE is rendered as "false" (instead of "") + * - Strings are always quoted with single quotes + * - Carriage returns and newlines are normalized to \n + * - Recursion and repeated rendering is treated properly + * + * @param mixed $value + * @param int $indentation The indentation level of the 2nd+ line + * @return string + */ + public static function export($value, $indentation = 0) { - if ($this->insertionMap) { - return; - } - // TODO: "yield" where both key and value are inserted doesn't work - // [$find, $beforeToken, $extraLeft, $extraRight] - $this->insertionMap = [ - 'Expr_ArrayDimFetch->dim' => ['[', \false, null, null], - 'Expr_ArrayItem->key' => [null, \false, null, ' => '], - 'Expr_ArrowFunction->returnType' => [')', \false, ' : ', null], - 'Expr_Closure->returnType' => [')', \false, ' : ', null], - 'Expr_Ternary->if' => ['?', \false, ' ', ' '], - 'Expr_Yield->key' => [\T_YIELD, \false, null, ' => '], - 'Expr_Yield->value' => [\T_YIELD, \false, ' ', null], - 'Param->type' => [null, \false, null, ' '], - 'Param->default' => [null, \false, ' = ', null], - 'Stmt_Break->num' => [\T_BREAK, \false, ' ', null], - 'Stmt_Catch->var' => [null, \false, ' ', null], - 'Stmt_ClassMethod->returnType' => [')', \false, ' : ', null], - 'Stmt_Class->extends' => [null, \false, ' extends ', null], - 'Expr_PrintableNewAnonClass->extends' => [null, ' extends ', null], - 'Stmt_Continue->num' => [\T_CONTINUE, \false, ' ', null], - 'Stmt_Foreach->keyVar' => [\T_AS, \false, null, ' => '], - 'Stmt_Function->returnType' => [')', \false, ' : ', null], - 'Stmt_If->else' => [null, \false, ' ', null], - 'Stmt_Namespace->name' => [\T_NAMESPACE, \false, ' ', null], - 'Stmt_Property->type' => [\T_VARIABLE, \true, null, ' '], - 'Stmt_PropertyProperty->default' => [null, \false, ' = ', null], - 'Stmt_Return->expr' => [\T_RETURN, \false, ' ', null], - 'Stmt_StaticVar->default' => [null, \false, ' = ', null], - //'Stmt_TraitUseAdaptation_Alias->newName' => [T_AS, false, ' ', null], // TODO - 'Stmt_TryCatch->finally' => [null, \false, ' ', null], - ]; + return self::recursiveExport($value, $indentation); } - protected function initializeListInsertionMap() + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param mixed $value + * @return array + */ + public static function toArray($value) { - if ($this->listInsertionMap) { - return; + if (!\is_object($value)) { + return (array) $value; } - $this->listInsertionMap = [ - // special - //'Expr_ShellExec->parts' => '', // TODO These need to be treated more carefully - //'Scalar_Encapsed->parts' => '', - 'Stmt_Catch->types' => '|', - 'UnionType->types' => '|', - 'Stmt_If->elseifs' => ' ', - 'Stmt_TryCatch->catches' => ' ', - // comma-separated lists - 'Expr_Array->items' => ', ', - 'Expr_ArrowFunction->params' => ', ', - 'Expr_Closure->params' => ', ', - 'Expr_Closure->uses' => ', ', - 'Expr_FuncCall->args' => ', ', - 'Expr_Isset->vars' => ', ', - 'Expr_List->items' => ', ', - 'Expr_MethodCall->args' => ', ', - 'Expr_NullsafeMethodCall->args' => ', ', - 'Expr_New->args' => ', ', - 'Expr_PrintableNewAnonClass->args' => ', ', - 'Expr_StaticCall->args' => ', ', - 'Stmt_ClassConst->consts' => ', ', - 'Stmt_ClassMethod->params' => ', ', - 'Stmt_Class->implements' => ', ', - 'Expr_PrintableNewAnonClass->implements' => ', ', - 'Stmt_Const->consts' => ', ', - 'Stmt_Declare->declares' => ', ', - 'Stmt_Echo->exprs' => ', ', - 'Stmt_For->init' => ', ', - 'Stmt_For->cond' => ', ', - 'Stmt_For->loop' => ', ', - 'Stmt_Function->params' => ', ', - 'Stmt_Global->vars' => ', ', - 'Stmt_GroupUse->uses' => ', ', - 'Stmt_Interface->extends' => ', ', - 'Stmt_Match->arms' => ', ', - 'Stmt_Property->props' => ', ', - 'Stmt_StaticVar->vars' => ', ', - 'Stmt_TraitUse->traits' => ', ', - 'Stmt_TraitUseAdaptation_Precedence->insteadof' => ', ', - 'Stmt_Unset->vars' => ', ', - 'Stmt_Use->uses' => ', ', - 'MatchArm->conds' => ', ', - 'AttributeGroup->attrs' => ', ', - // statement lists - 'Expr_Closure->stmts' => "\n", - 'Stmt_Case->stmts' => "\n", - 'Stmt_Catch->stmts' => "\n", - 'Stmt_Class->stmts' => "\n", - 'Expr_PrintableNewAnonClass->stmts' => "\n", - 'Stmt_Interface->stmts' => "\n", - 'Stmt_Trait->stmts' => "\n", - 'Stmt_ClassMethod->stmts' => "\n", - 'Stmt_Declare->stmts' => "\n", - 'Stmt_Do->stmts' => "\n", - 'Stmt_ElseIf->stmts' => "\n", - 'Stmt_Else->stmts' => "\n", - 'Stmt_Finally->stmts' => "\n", - 'Stmt_Foreach->stmts' => "\n", - 'Stmt_For->stmts' => "\n", - 'Stmt_Function->stmts' => "\n", - 'Stmt_If->stmts' => "\n", - 'Stmt_Namespace->stmts' => "\n", - 'Stmt_Class->attrGroups' => "\n", - 'Stmt_Interface->attrGroups' => "\n", - 'Stmt_Trait->attrGroups' => "\n", - 'Stmt_Function->attrGroups' => "\n", - 'Stmt_ClassMethod->attrGroups' => "\n", - 'Stmt_ClassConst->attrGroups' => "\n", - 'Stmt_Property->attrGroups' => "\n", - 'Expr_PrintableNewAnonClass->attrGroups' => ' ', - 'Expr_Closure->attrGroups' => ' ', - 'Expr_ArrowFunction->attrGroups' => ' ', - 'Param->attrGroups' => ' ', - 'Stmt_Switch->cases' => "\n", - 'Stmt_TraitUse->adaptations' => "\n", - 'Stmt_TryCatch->stmts' => "\n", - 'Stmt_While->stmts' => "\n", - // dummy for top-level context - 'File->stmts' => "\n", - ]; + $array = array(); + foreach ((array) $value as $key => $val) { + // properties are transformed to keys in the following way: + // private $property => "\0Classname\0property" + // protected $property => "\0*\0property" + // public $property => "property" + if (\preg_match('/^\\0.+\\0(.+)$/', $key, $matches)) { + $key = $matches[1]; + } + // See https://github.com/php/php-src/commit/5721132 + if ($key === "\0gcdata") { + continue; + } + $array[$key] = $val; + } + // Some internal classes like SplObjectStorage don't work with the + // above (fast) mechanism nor with reflection in Zend. + // Format the output similarly to print_r() in this case + if ($value instanceof \SplObjectStorage) { + // However, the fast method does work in HHVM, and exposes the + // internal implementation. Hide it again. + if (\property_exists('\\SplObjectStorage', '__storage')) { + unset($array['__storage']); + } elseif (\property_exists('\\SplObjectStorage', 'storage')) { + unset($array['storage']); + } + if (\property_exists('\\SplObjectStorage', '__key')) { + unset($array['__key']); + } + foreach ($value as $key => $val) { + $array[\spl_object_hash($val)] = array('obj' => $val, 'inf' => $value->getInfo()); + } + } + return $array; } - protected function initializeEmptyListInsertionMap() + /** + * Recursive implementation of export + * + * @param mixed $value The value to export + * @param int $indentation The indentation level of the 2nd+ line + * @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects + * @return string + * @see SebastianBergmann\Exporter\Exporter::export + */ + protected static function recursiveExport(&$value, $indentation, $processed = null) { - if ($this->emptyListInsertionMap) { - return; + if ($value === null) { + return 'null'; + } + if ($value === \true) { + return 'true'; + } + if ($value === \false) { + return 'false'; + } + if (\is_float($value) && \floatval(\intval($value)) === $value) { + return "{$value}.0"; + } + if (\is_resource($value)) { + return \sprintf('resource(%d) of type (%s)', $value, \get_resource_type($value)); + } + if (\is_string($value)) { + // Match for most non printable chars somewhat taking multibyte chars into account + if (\preg_match('/[^\\x09-\\x0d\\x20-\\xff]/', $value)) { + return 'Binary String: 0x' . \bin2hex($value); + } + return "'" . \str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) . "'"; + } + $whitespace = \str_repeat(' ', 4 * $indentation); + if (!$processed) { + $processed = new Context(); + } + if (\is_array($value)) { + if (($key = $processed->contains($value)) !== \false) { + return 'Array &' . $key; + } + $array = $value; + $key = $processed->add($value); + $values = ''; + if (\count($array) > 0) { + foreach ($array as $k => $v) { + $values .= \sprintf('%s %s => %s' . "\n", $whitespace, self::recursiveExport($k, $indentation), self::recursiveExport($value[$k], $indentation + 1, $processed)); + } + $values = "\n" . $values . $whitespace; + } + return \sprintf('Array &%s (%s)', $key, $values); } - // TODO Insertion into empty statement lists. - // [$find, $extraLeft, $extraRight] - $this->emptyListInsertionMap = ['Expr_ArrowFunction->params' => ['(', '', ''], 'Expr_Closure->uses' => [')', ' use(', ')'], 'Expr_Closure->params' => ['(', '', ''], 'Expr_FuncCall->args' => ['(', '', ''], 'Expr_MethodCall->args' => ['(', '', ''], 'Expr_NullsafeMethodCall->args' => ['(', '', ''], 'Expr_New->args' => ['(', '', ''], 'Expr_PrintableNewAnonClass->args' => ['(', '', ''], 'Expr_PrintableNewAnonClass->implements' => [null, ' implements ', ''], 'Expr_StaticCall->args' => ['(', '', ''], 'Stmt_Class->implements' => [null, ' implements ', ''], 'Stmt_ClassMethod->params' => ['(', '', ''], 'Stmt_Interface->extends' => [null, ' extends ', ''], 'Stmt_Function->params' => ['(', '', '']]; - } - protected function initializeModifierChangeMap() - { - if ($this->modifierChangeMap) { - return; + if (\is_object($value)) { + $class = \get_class($value); + if ($hash = $processed->contains($value)) { + return \sprintf('%s:%s Object', $class, $hash); + } + $hash = $processed->add($value); + $values = ''; + $array = self::toArray($value); + if (\count($array) > 0) { + foreach ($array as $k => $v) { + $values .= \sprintf('%s %s => %s' . "\n", $whitespace, self::recursiveExport($k, $indentation), self::recursiveExport($v, $indentation + 1, $processed)); + } + $values = "\n" . $values . $whitespace; + } + return \sprintf('%s:%s Object (%s)', $class, $hash, $values); } - $this->modifierChangeMap = ['Stmt_ClassConst->flags' => \T_CONST, 'Stmt_ClassMethod->flags' => \T_FUNCTION, 'Stmt_Class->flags' => \T_CLASS, 'Stmt_Property->flags' => \T_VARIABLE, 'Param->flags' => \T_VARIABLE]; - // List of integer subnodes that are not modifiers: - // Expr_Include->type - // Stmt_GroupUse->type - // Stmt_Use->type - // Stmt_UseUse->type + return \var_export($value, \true); } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception\Prophecy; -class Doc extends \PHPUnit\PhpParser\Comment +use Prophecy\Exception\Exception; +interface ProphecyException extends Exception { } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception\Prophecy; -class Comment implements \JsonSerializable +use Prophecy\Prophecy\MethodProphecy; +class MethodProphecyException extends \Prophecy\Exception\Prophecy\ObjectProphecyException { - protected $text; - protected $startLine; - protected $startFilePos; - protected $startTokenPos; - protected $endLine; - protected $endFilePos; - protected $endTokenPos; - /** - * Constructs a comment node. - * - * @param string $text Comment text (including comment delimiters like /*) - * @param int $startLine Line number the comment started on - * @param int $startFilePos File offset the comment started on - * @param int $startTokenPos Token offset the comment started on - */ - public function __construct(string $text, int $startLine = -1, int $startFilePos = -1, int $startTokenPos = -1, int $endLine = -1, int $endFilePos = -1, int $endTokenPos = -1) + private $methodProphecy; + public function __construct($message, MethodProphecy $methodProphecy) { - $this->text = $text; - $this->startLine = $startLine; - $this->startFilePos = $startFilePos; - $this->startTokenPos = $startTokenPos; - $this->endLine = $endLine; - $this->endFilePos = $endFilePos; - $this->endTokenPos = $endTokenPos; + parent::__construct($message, $methodProphecy->getObjectProphecy()); + $this->methodProphecy = $methodProphecy; } /** - * Gets the comment text. - * - * @return string The comment text (including comment delimiters like /*) + * @return MethodProphecy */ - public function getText() : string + public function getMethodProphecy() { - return $this->text; + return $this->methodProphecy; } - /** - * Gets the line number the comment started on. - * - * @return int Line number (or -1 if not available) - */ - public function getStartLine() : int +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception\Prophecy; + +use Prophecy\Prophecy\ObjectProphecy; +class ObjectProphecyException extends \RuntimeException implements \Prophecy\Exception\Prophecy\ProphecyException +{ + private $objectProphecy; + public function __construct($message, ObjectProphecy $objectProphecy) { - return $this->startLine; + parent::__construct($message); + $this->objectProphecy = $objectProphecy; } /** - * Gets the file offset the comment started on. - * - * @return int File offset (or -1 if not available) + * @return ObjectProphecy */ - public function getStartFilePos() : int + public function getObjectProphecy() { - return $this->startFilePos; + return $this->objectProphecy; } - /** - * Gets the token offset the comment started on. - * - * @return int Token offset (or -1 if not available) - */ - public function getStartTokenPos() : int +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception\Prediction; + +use Prophecy\Prophecy\MethodProphecy; +class UnexpectedCallsCountException extends \Prophecy\Exception\Prediction\UnexpectedCallsException +{ + private $expectedCount; + public function __construct($message, MethodProphecy $methodProphecy, $count, array $calls) { - return $this->startTokenPos; + parent::__construct($message, $methodProphecy, $calls); + $this->expectedCount = \intval($count); } - /** - * Gets the line number the comment ends on. - * - * @return int Line number (or -1 if not available) - */ - public function getEndLine() : int + public function getExpectedCount() { - return $this->endLine; + return $this->expectedCount; } - /** - * Gets the file offset the comment ends on. - * - * @return int File offset (or -1 if not available) - */ - public function getEndFilePos() : int +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception\Prediction; + +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\Prophecy\MethodProphecyException; +class UnexpectedCallsException extends MethodProphecyException implements \Prophecy\Exception\Prediction\PredictionException +{ + private $calls = array(); + public function __construct($message, MethodProphecy $methodProphecy, array $calls) { - return $this->endFilePos; + parent::__construct($message, $methodProphecy); + $this->calls = $calls; } - /** - * Gets the token offset the comment ends on. - * - * @return int Token offset (or -1 if not available) - */ - public function getEndTokenPos() : int + public function getCalls() { - return $this->endTokenPos; + return $this->calls; } - /** - * Gets the line number the comment started on. - * - * @deprecated Use getStartLine() instead - * - * @return int Line number - */ - public function getLine() : int +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception\Prediction; + +use RuntimeException; +/** + * Basic failed prediction exception. + * Use it for custom prediction failures. + * + * @author Konstantin Kudryashov + */ +class FailedPredictionException extends RuntimeException implements \Prophecy\Exception\Prediction\PredictionException +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception\Prediction; + +use Prophecy\Prophecy\ObjectProphecy; +class AggregateException extends \RuntimeException implements \Prophecy\Exception\Prediction\PredictionException +{ + private $exceptions = array(); + private $objectProphecy; + public function append(\Prophecy\Exception\Prediction\PredictionException $exception) { - return $this->startLine; + $message = $exception->getMessage(); + $message = \strtr($message, array("\n" => "\n ")) . "\n"; + $message = empty($this->exceptions) ? $message : "\n" . $message; + $this->message = \rtrim($this->message . $message); + $this->exceptions[] = $exception; } /** - * Gets the file offset the comment started on. - * - * @deprecated Use getStartFilePos() instead - * - * @return int File offset + * @return PredictionException[] */ - public function getFilePos() : int + public function getExceptions() { - return $this->startFilePos; + return $this->exceptions; } - /** - * Gets the token offset the comment started on. - * - * @deprecated Use getStartTokenPos() instead - * - * @return int Token offset - */ - public function getTokenPos() : int + public function setObjectProphecy(ObjectProphecy $objectProphecy) { - return $this->startTokenPos; + $this->objectProphecy = $objectProphecy; } /** - * Gets the comment text. - * - * @return string The comment text (including comment delimiters like /*) + * @return ObjectProphecy */ - public function __toString() : string + public function getObjectProphecy() { - return $this->text; + return $this->objectProphecy; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception\Prediction; + +use Prophecy\Exception\Prophecy\MethodProphecyException; +class NoCallsException extends MethodProphecyException implements \Prophecy\Exception\Prediction\PredictionException +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception\Prediction; + +use Prophecy\Exception\Exception; +interface PredictionException extends Exception +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception\Call; + +use Prophecy\Exception\Prophecy\ObjectProphecyException; +use Prophecy\Prophecy\ObjectProphecy; +class UnexpectedCallException extends ObjectProphecyException +{ + private $methodName; + private $arguments; + public function __construct($message, ObjectProphecy $objectProphecy, $methodName, array $arguments) + { + parent::__construct($message, $objectProphecy); + $this->methodName = $methodName; + $this->arguments = $arguments; + } + public function getMethodName() + { + return $this->methodName; + } + public function getArguments() + { + return $this->arguments; } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception; + +/** + * Core Prophecy exception interface. + * All Prophecy exceptions implement it. + * + * @author Konstantin Kudryashov + */ +interface Exception extends \Throwable +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception\Doubler; + +class MethodNotFoundException extends \Prophecy\Exception\Doubler\DoubleException +{ /** - * Gets the reformatted comment text. - * - * "Reformatted" here means that we try to clean up the whitespace at the - * starts of the lines. This is necessary because we receive the comments - * without trailing whitespace on the first line, but with trailing whitespace - * on all subsequent lines. - * - * @return mixed|string + * @var string|object */ - public function getReformattedText() + private $classname; + /** + * @var string + */ + private $methodName; + /** + * @var array + */ + private $arguments; + /** + * @param string $message + * @param string|object $classname + * @param string $methodName + * @param null|Argument\ArgumentsWildcard|array $arguments + */ + public function __construct($message, $classname, $methodName, $arguments = null) { - $text = \trim($this->text); - $newlinePos = \strpos($text, "\n"); - if (\false === $newlinePos) { - // Single line comments don't need further processing - return $text; - } elseif (\preg_match('((*BSR_ANYCRLF)(*ANYCRLF)^.*(?:\\R\\s+\\*.*)+$)', $text)) { - // Multi line comment of the type - // - // /* - // * Some text. - // * Some more text. - // */ - // - // is handled by replacing the whitespace sequences before the * by a single space - return \preg_replace('(^\\s+\\*)m', ' *', $this->text); - } elseif (\preg_match('(^/\\*\\*?\\s*[\\r\\n])', $text) && \preg_match('(\\n(\\s*)\\*/$)', $text, $matches)) { - // Multi line comment of the type - // - // /* - // Some text. - // Some more text. - // */ - // - // is handled by removing the whitespace sequence on the line before the closing - // */ on all lines. So if the last line is " */", then " " is removed at the - // start of all lines. - return \preg_replace('(^' . \preg_quote($matches[1]) . ')m', '', $text); - } elseif (\preg_match('(^/\\*\\*?\\s*(?!\\s))', $text, $matches)) { - // Multi line comment of the type - // - // /* Some text. - // Some more text. - // Indented text. - // Even more text. */ - // - // is handled by removing the difference between the shortest whitespace prefix on all - // lines and the length of the "/* " opening sequence. - $prefixLen = $this->getShortestWhitespacePrefixLen(\substr($text, $newlinePos + 1)); - $removeLen = $prefixLen - \strlen($matches[0]); - return \preg_replace('(^\\s{' . $removeLen . '})m', '', $text); - } - // No idea how to format this comment, so simply return as is - return $text; + parent::__construct($message); + $this->classname = $classname; + $this->methodName = $methodName; + $this->arguments = $arguments; + } + public function getClassname() + { + return $this->classname; + } + public function getMethodName() + { + return $this->methodName; } + public function getArguments() + { + return $this->arguments; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception\Doubler; + +class ClassNotFoundException extends \Prophecy\Exception\Doubler\DoubleException +{ + private $classname; /** - * Get length of shortest whitespace prefix (at the start of a line). - * - * If there is a line with no prefix whitespace, 0 is a valid return value. - * - * @param string $str String to check - * @return int Length in characters. Tabs count as single characters. + * @param string $message + * @param string $classname */ - private function getShortestWhitespacePrefixLen(string $str) : int + public function __construct($message, $classname) + { + parent::__construct($message); + $this->classname = $classname; + } + public function getClassname() + { + return $this->classname; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception\Doubler; + +use ReflectionClass; +class ClassMirrorException extends \RuntimeException implements \Prophecy\Exception\Doubler\DoublerException +{ + private $class; + public function __construct($message, ReflectionClass $class) { - $lines = \explode("\n", $str); - $shortestPrefixLen = \INF; - foreach ($lines as $line) { - \preg_match('(^\\s*)', $line, $matches); - $prefixLen = \strlen($matches[0]); - if ($prefixLen < $shortestPrefixLen) { - $shortestPrefixLen = $prefixLen; - } - } - return $shortestPrefixLen; + parent::__construct($message); + $this->class = $class; } - /** - * @return array - * @psalm-return array{nodeType:string, text:mixed, line:mixed, filePos:mixed} - */ - public function jsonSerialize() : array + public function getReflectedClass() { - // Technically not a node, but we make it look like one anyway - $type = $this instanceof \PHPUnit\PhpParser\Comment\Doc ? 'Comment_Doc' : 'Comment'; - return [ - 'nodeType' => $type, - 'text' => $this->text, - // TODO: Rename these to include "start". - 'line' => $this->startLine, - 'filePos' => $this->startFilePos, - 'tokenPos' => $this->startTokenPos, - 'endLine' => $this->endLine, - 'endFilePos' => $this->endFilePos, - 'endTokenPos' => $this->endTokenPos, - ]; + return $this->class; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception\Doubler; -class JsonDecoder +use Prophecy\Exception\Exception; +interface DoublerException extends Exception { - /** @var \ReflectionClass[] Node type to reflection class map */ - private $reflectionClassCache; - public function decode(string $json) +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception\Doubler; + +class InterfaceNotFoundException extends \Prophecy\Exception\Doubler\ClassNotFoundException +{ + public function getInterfaceName() { - $value = \json_decode($json, \true); - if (\json_last_error()) { - throw new \RuntimeException('JSON decoding error: ' . \json_last_error_msg()); - } - return $this->decodeRecursive($value); + return $this->getClassname(); } - private function decodeRecursive($value) +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception\Doubler; + +class ReturnByReferenceException extends \Prophecy\Exception\Doubler\DoubleException +{ + private $classname; + private $methodName; + /** + * @param string $message + * @param string $classname + * @param string $methodName + */ + public function __construct($message, $classname, $methodName) { - if (\is_array($value)) { - if (isset($value['nodeType'])) { - if ($value['nodeType'] === 'Comment' || $value['nodeType'] === 'Comment_Doc') { - return $this->decodeComment($value); - } - return $this->decodeNode($value); - } - return $this->decodeArray($value); - } - return $value; + parent::__construct($message); + $this->classname = $classname; + $this->methodName = $methodName; } - private function decodeArray(array $array) : array + public function getClassname() { - $decodedArray = []; - foreach ($array as $key => $value) { - $decodedArray[$key] = $this->decodeRecursive($value); - } - return $decodedArray; + return $this->classname; } - private function decodeNode(array $value) : \PHPUnit\PhpParser\Node + public function getMethodName() { - $nodeType = $value['nodeType']; - if (!\is_string($nodeType)) { - throw new \RuntimeException('Node type must be a string'); - } - $reflectionClass = $this->reflectionClassFromNodeType($nodeType); - /** @var Node $node */ - $node = $reflectionClass->newInstanceWithoutConstructor(); - if (isset($value['attributes'])) { - if (!\is_array($value['attributes'])) { - throw new \RuntimeException('Attributes must be an array'); - } - $node->setAttributes($this->decodeArray($value['attributes'])); - } - foreach ($value as $name => $subNode) { - if ($name === 'nodeType' || $name === 'attributes') { - continue; - } - $node->{$name} = $this->decodeRecursive($subNode); - } - return $node; + return $this->methodName; } - private function decodeComment(array $value) : \PHPUnit\PhpParser\Comment +} +methodName = $methodName; + $this->className = $className; } - private function reflectionClassFromNodeType(string $nodeType) : \ReflectionClass + /** + * @return string + */ + public function getMethodName() { - if (!isset($this->reflectionClassCache[$nodeType])) { - $className = $this->classNameFromNodeType($nodeType); - $this->reflectionClassCache[$nodeType] = new \ReflectionClass($className); - } - return $this->reflectionClassCache[$nodeType]; + return $this->methodName; } - private function classNameFromNodeType(string $nodeType) : string + /** + * @return string + */ + public function getClassName() { - $className = 'PhpParser\\Node\\' . \strtr($nodeType, '_', '\\'); - if (\class_exists($className)) { - return $className; - } - $className .= '_'; - if (\class_exists($className)) { - return $className; - } - throw new \RuntimeException("Unknown node type \"{$nodeType}\""); + return $this->className; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception\Doubler; -/** - * @codeCoverageIgnore +use RuntimeException; +class DoubleException extends RuntimeException implements \Prophecy\Exception\Doubler\DoublerException +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -class NodeVisitorAbstract implements \PHPUnit\PhpParser\NodeVisitor +namespace Prophecy\Exception\Doubler; + +use Prophecy\Doubler\Generator\Node\ClassNode; +class ClassCreatorException extends \RuntimeException implements \Prophecy\Exception\Doubler\DoublerException { - public function beforeTraverse(array $nodes) - { - return null; - } - public function enterNode(\PHPUnit\PhpParser\Node $node) - { - return null; - } - public function leaveNode(\PHPUnit\PhpParser\Node $node) + private $node; + public function __construct($message, ClassNode $node) { - return null; + parent::__construct($message); + $this->node = $node; } - public function afterTraverse(array $nodes) + public function getClassNode() { - return null; + return $this->node; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Exception; -interface Parser +class InvalidArgumentException extends \InvalidArgumentException implements \Prophecy\Exception\Exception { - /** - * Parses PHP code into a node tree. - * - * @param string $code The source code to parse - * @param ErrorHandler|null $errorHandler Error handler to use for lexer/parser errors, defaults - * to ErrorHandler\Throwing. - * - * @return Node\Stmt[]|null Array of statements (or null non-throwing error handler is used and - * the parser was unable to recover from an error). - */ - public function parse(string $code, \PHPUnit\PhpParser\ErrorHandler $errorHandler = null); } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Promise; -interface Node +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +/** + * Promise interface. + * Promises are logical blocks, tied to `will...` keyword. + * + * @author Konstantin Kudryashov + */ +interface PromiseInterface { /** - * Gets the type of the node. - * - * @return string Type of the node - */ - public function getType() : string; - /** - * Gets the names of the sub nodes. - * - * @return array Names of sub nodes - */ - public function getSubNodeNames() : array; - /** - * Gets line the node started in (alias of getStartLine). - * - * @return int Start line (or -1 if not available) - */ - public function getLine() : int; - /** - * Gets line the node started in. - * - * Requires the 'startLine' attribute to be enabled in the lexer (enabled by default). - * - * @return int Start line (or -1 if not available) - */ - public function getStartLine() : int; - /** - * Gets the line the node ended in. - * - * Requires the 'endLine' attribute to be enabled in the lexer (enabled by default). - * - * @return int End line (or -1 if not available) - */ - public function getEndLine() : int; - /** - * Gets the token offset of the first token that is part of this node. - * - * The offset is an index into the array returned by Lexer::getTokens(). - * - * Requires the 'startTokenPos' attribute to be enabled in the lexer (DISABLED by default). - * - * @return int Token start position (or -1 if not available) - */ - public function getStartTokenPos() : int; - /** - * Gets the token offset of the last token that is part of this node. - * - * The offset is an index into the array returned by Lexer::getTokens(). - * - * Requires the 'endTokenPos' attribute to be enabled in the lexer (DISABLED by default). - * - * @return int Token end position (or -1 if not available) - */ - public function getEndTokenPos() : int; - /** - * Gets the file offset of the first character that is part of this node. - * - * Requires the 'startFilePos' attribute to be enabled in the lexer (DISABLED by default). - * - * @return int File start position (or -1 if not available) - */ - public function getStartFilePos() : int; - /** - * Gets the file offset of the last character that is part of this node. - * - * Requires the 'endFilePos' attribute to be enabled in the lexer (DISABLED by default). - * - * @return int File end position (or -1 if not available) - */ - public function getEndFilePos() : int; - /** - * Gets all comments directly preceding this node. - * - * The comments are also available through the "comments" attribute. - * - * @return Comment[] - */ - public function getComments() : array; - /** - * Gets the doc comment of the node. - * - * @return null|Comment\Doc Doc comment object or null - */ - public function getDocComment(); - /** - * Sets the doc comment of the node. - * - * This will either replace an existing doc comment or add it to the comments array. - * - * @param Comment\Doc $docComment Doc comment to set - */ - public function setDocComment(\PHPUnit\PhpParser\Comment\Doc $docComment); - /** - * Sets an attribute on a node. - * - * @param string $key - * @param mixed $value - */ - public function setAttribute(string $key, $value); - /** - * Returns whether an attribute exists. - * - * @param string $key - * - * @return bool - */ - public function hasAttribute(string $key) : bool; - /** - * Returns the value of an attribute. + * Evaluates promise. * - * @param string $key - * @param mixed $default + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method * * @return mixed */ - public function getAttribute(string $key, $default = null); - /** - * Returns all the attributes of this node. - * - * @return array - */ - public function getAttributes() : array; - /** - * Replaces all the attributes of this node. - * - * @param array $attributes - */ - public function setAttributes(array $attributes); + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method); } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Promise; -use PHPUnit\PhpParser\Node\Expr; -use PHPUnit\PhpParser\Node\Identifier; -use PHPUnit\PhpParser\Node\Name; -use PHPUnit\PhpParser\Node\NullableType; -use PHPUnit\PhpParser\Node\Scalar; -use PHPUnit\PhpParser\Node\Stmt; -use PHPUnit\PhpParser\Node\UnionType; +use PHPUnit\Doctrine\Instantiator\Instantiator; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\InvalidArgumentException; +use ReflectionClass; /** - * This class defines helpers used in the implementation of builders. Don't use it directly. + * Throw promise. * - * @internal + * @author Konstantin Kudryashov */ -final class BuilderHelpers +class ThrowPromise implements \Prophecy\Promise\PromiseInterface { + private $exception; /** - * Normalizes a node: Converts builder objects to nodes. - * - * @param Node|Builder $node The node to normalize - * - * @return Node The normalized node - */ - public static function normalizeNode($node) : \PHPUnit\PhpParser\Node - { - if ($node instanceof \PHPUnit\PhpParser\Builder) { - return $node->getNode(); - } elseif ($node instanceof \PHPUnit\PhpParser\Node) { - return $node; - } - throw new \LogicException('Expected node or builder object'); - } - /** - * Normalizes a node to a statement. - * - * Expressions are wrapped in a Stmt\Expression node. - * - * @param Node|Builder $node The node to normalize - * - * @return Stmt The normalized statement node - */ - public static function normalizeStmt($node) : \PHPUnit\PhpParser\Node\Stmt - { - $node = self::normalizeNode($node); - if ($node instanceof \PHPUnit\PhpParser\Node\Stmt) { - return $node; - } - if ($node instanceof \PHPUnit\PhpParser\Node\Expr) { - return new \PHPUnit\PhpParser\Node\Stmt\Expression($node); - } - throw new \LogicException('Expected statement or expression node'); - } - /** - * Normalizes strings to Identifier. - * - * @param string|Identifier $name The identifier to normalize - * - * @return Identifier The normalized identifier - */ - public static function normalizeIdentifier($name) : \PHPUnit\PhpParser\Node\Identifier - { - if ($name instanceof \PHPUnit\PhpParser\Node\Identifier) { - return $name; - } - if (\is_string($name)) { - return new \PHPUnit\PhpParser\Node\Identifier($name); - } - throw new \LogicException('PHPUnit\\Expected string or instance of Node\\Identifier'); - } - /** - * Normalizes strings to Identifier, also allowing expressions. - * - * @param string|Identifier|Expr $name The identifier to normalize - * - * @return Identifier|Expr The normalized identifier or expression - */ - public static function normalizeIdentifierOrExpr($name) - { - if ($name instanceof \PHPUnit\PhpParser\Node\Identifier || $name instanceof \PHPUnit\PhpParser\Node\Expr) { - return $name; - } - if (\is_string($name)) { - return new \PHPUnit\PhpParser\Node\Identifier($name); - } - throw new \LogicException('PHPUnit\\Expected string or instance of Node\\Identifier or Node\\Expr'); - } - /** - * Normalizes a name: Converts string names to Name nodes. - * - * @param Name|string $name The name to normalize - * - * @return Name The normalized name + * @var \Doctrine\Instantiator\Instantiator */ - public static function normalizeName($name) : \PHPUnit\PhpParser\Node\Name - { - return self::normalizeNameCommon($name, \false); - } + private $instantiator; /** - * Normalizes a name: Converts string names to Name nodes, while also allowing expressions. + * Initializes promise. * - * @param Expr|Name|string $name The name to normalize + * @param string|\Exception|\Throwable $exception Exception class name or instance * - * @return Name|Expr The normalized name or expression + * @throws \Prophecy\Exception\InvalidArgumentException */ - public static function normalizeNameOrExpr($name) + public function __construct($exception) { - return self::normalizeNameCommon($name, \true); + if (\is_string($exception)) { + if (!\class_exists($exception) && !\interface_exists($exception) || !$this->isAValidThrowable($exception)) { + throw new InvalidArgumentException(\sprintf('Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.', $exception)); + } + } elseif (!$exception instanceof \Exception && !$exception instanceof \Throwable) { + throw new InvalidArgumentException(\sprintf('Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.', \is_object($exception) ? \get_class($exception) : \gettype($exception))); + } + $this->exception = $exception; } /** - * Normalizes a name: Converts string names to Name nodes, optionally allowing expressions. + * Throws predefined exception. * - * @param Expr|Name|string $name The name to normalize - * @param bool $allowExpr Whether to also allow expressions + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method * - * @return Name|Expr The normalized name, or expression (if allowed) + * @throws object */ - private static function normalizeNameCommon($name, bool $allowExpr) + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) { - if ($name instanceof \PHPUnit\PhpParser\Node\Name) { - return $name; - } elseif (\is_string($name)) { - if (!$name) { - throw new \LogicException('Name cannot be empty'); - } - if ($name[0] === '\\') { - return new \PHPUnit\PhpParser\Node\Name\FullyQualified(\substr($name, 1)); - } elseif (0 === \strpos($name, 'namespace\\')) { - return new \PHPUnit\PhpParser\Node\Name\Relative(\substr($name, \strlen('namespace\\'))); - } else { - return new \PHPUnit\PhpParser\Node\Name($name); + if (\is_string($this->exception)) { + $classname = $this->exception; + $reflection = new ReflectionClass($classname); + $constructor = $reflection->getConstructor(); + if ($constructor->isPublic() && 0 == $constructor->getNumberOfRequiredParameters()) { + throw $reflection->newInstance(); } - } - if ($allowExpr) { - if ($name instanceof \PHPUnit\PhpParser\Node\Expr) { - return $name; + if (!$this->instantiator) { + $this->instantiator = new Instantiator(); } - throw new \LogicException('PHPUnit\\Name must be a string or an instance of Node\\Name or Node\\Expr'); - } else { - throw new \LogicException('PHPUnit\\Name must be a string or an instance of Node\\Name'); + throw $this->instantiator->instantiate($classname); } + throw $this->exception; } /** - * Normalizes a type: Converts plain-text type names into proper AST representation. - * - * In particular, builtin types become Identifiers, custom types become Names and nullables - * are wrapped in NullableType nodes. + * @param string $exception * - * @param string|Name|Identifier|NullableType|UnionType $type The type to normalize + * @return bool + */ + private function isAValidThrowable($exception) + { + return \is_a($exception, 'Exception', \true) || \is_a($exception, 'Throwable', \true); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Promise; + +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +/** + * Return promise. + * + * @author Konstantin Kudryashov + */ +class ReturnPromise implements \Prophecy\Promise\PromiseInterface +{ + private $returnValues = array(); + /** + * Initializes promise. * - * @return Name|Identifier|NullableType|UnionType The normalized type + * @param array $returnValues Array of values */ - public static function normalizeType($type) + public function __construct(array $returnValues) { - if (!\is_string($type)) { - if (!$type instanceof \PHPUnit\PhpParser\Node\Name && !$type instanceof \PHPUnit\PhpParser\Node\Identifier && !$type instanceof \PHPUnit\PhpParser\Node\NullableType && !$type instanceof \PHPUnit\PhpParser\Node\UnionType) { - throw new \LogicException('Type must be a string, or an instance of Name, Identifier, NullableType or UnionType'); - } - return $type; - } - $nullable = \false; - if (\strlen($type) > 0 && $type[0] === '?') { - $nullable = \true; - $type = \substr($type, 1); - } - $builtinTypes = ['array', 'callable', 'string', 'int', 'float', 'bool', 'iterable', 'void', 'object', 'mixed']; - $lowerType = \strtolower($type); - if (\in_array($lowerType, $builtinTypes)) { - $type = new \PHPUnit\PhpParser\Node\Identifier($lowerType); - } else { - $type = self::normalizeName($type); - } - if ($nullable && (string) $type === 'void') { - throw new \LogicException('void type cannot be nullable'); - } - if ($nullable && (string) $type === 'mixed') { - throw new \LogicException('mixed type cannot be nullable'); - } - return $nullable ? new \PHPUnit\PhpParser\Node\NullableType($type) : $type; + $this->returnValues = $returnValues; } /** - * Normalizes a value: Converts nulls, booleans, integers, - * floats, strings and arrays into their respective nodes + * Returns saved values one by one until last one, then continuously returns last value. * - * @param Node\Expr|bool|null|int|float|string|array $value The value to normalize + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method * - * @return Expr The normalized value + * @return mixed */ - public static function normalizeValue($value) : \PHPUnit\PhpParser\Node\Expr + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) { - if ($value instanceof \PHPUnit\PhpParser\Node\Expr) { - return $value; - } elseif (\is_null($value)) { - return new \PHPUnit\PhpParser\Node\Expr\ConstFetch(new \PHPUnit\PhpParser\Node\Name('null')); - } elseif (\is_bool($value)) { - return new \PHPUnit\PhpParser\Node\Expr\ConstFetch(new \PHPUnit\PhpParser\Node\Name($value ? 'true' : 'false')); - } elseif (\is_int($value)) { - return new \PHPUnit\PhpParser\Node\Scalar\LNumber($value); - } elseif (\is_float($value)) { - return new \PHPUnit\PhpParser\Node\Scalar\DNumber($value); - } elseif (\is_string($value)) { - return new \PHPUnit\PhpParser\Node\Scalar\String_($value); - } elseif (\is_array($value)) { - $items = []; - $lastKey = -1; - foreach ($value as $itemKey => $itemValue) { - // for consecutive, numeric keys don't generate keys - if (null !== $lastKey && ++$lastKey === $itemKey) { - $items[] = new \PHPUnit\PhpParser\Node\Expr\ArrayItem(self::normalizeValue($itemValue)); - } else { - $lastKey = null; - $items[] = new \PHPUnit\PhpParser\Node\Expr\ArrayItem(self::normalizeValue($itemValue), self::normalizeValue($itemKey)); - } - } - return new \PHPUnit\PhpParser\Node\Expr\Array_($items); - } else { - throw new \LogicException('Invalid value'); + $value = \array_shift($this->returnValues); + if (!\count($this->returnValues)) { + $this->returnValues[] = $value; } + return $value; } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Promise; + +use Prophecy\Exception\InvalidArgumentException; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +/** + * Return argument promise. + * + * @author Konstantin Kudryashov + */ +class ReturnArgumentPromise implements \Prophecy\Promise\PromiseInterface +{ /** - * Normalizes a doc comment: Converts plain strings to PhpParser\Comment\Doc. + * @var int + */ + private $index; + /** + * Initializes callback promise. * - * @param Comment\Doc|string $docComment The doc comment to normalize + * @param int $index The zero-indexed number of the argument to return * - * @return Comment\Doc The normalized doc comment + * @throws \Prophecy\Exception\InvalidArgumentException */ - public static function normalizeDocComment($docComment) : \PHPUnit\PhpParser\Comment\Doc + public function __construct($index = 0) { - if ($docComment instanceof \PHPUnit\PhpParser\Comment\Doc) { - return $docComment; - } elseif (\is_string($docComment)) { - return new \PHPUnit\PhpParser\Comment\Doc($docComment); - } else { - throw new \LogicException('PHPUnit\\Doc comment must be a string or an instance of PhpParser\\Comment\\Doc'); + if (!\is_int($index) || $index < 0) { + throw new InvalidArgumentException(\sprintf('Zero-based index expected as argument to ReturnArgumentPromise, but got %s.', $index)); } + $this->index = $index; } /** - * Adds a modifier and returns new modifier bitmask. + * Returns nth argument if has one, null otherwise. * - * @param int $modifiers Existing modifiers - * @param int $modifier Modifier to set + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method * - * @return int New modifiers + * @return null|mixed */ - public static function addModifier(int $modifiers, int $modifier) : int + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) { - \PHPUnit\PhpParser\Node\Stmt\Class_::verifyModifier($modifiers, $modifier); - return $modifiers | $modifier; + return \count($args) > $this->index ? $args[$this->index] : null; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Promise; -use PHPUnit\PhpParser\Lexer\Emulative; -final class AttributeEmulator extends \PHPUnit\PhpParser\Lexer\TokenEmulator\TokenEmulator +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\InvalidArgumentException; +use Closure; +use ReflectionFunction; +/** + * Callback promise. + * + * @author Konstantin Kudryashov + */ +class CallbackPromise implements \Prophecy\Promise\PromiseInterface { - public function getPhpVersion() : string - { - return \PHPUnit\PhpParser\Lexer\Emulative::PHP_8_0; - } - public function isEmulationNeeded(string $code) : bool - { - return \strpos($code, '#[') !== \false; - } - public function emulate(string $code, array $tokens) : array + private $callback; + /** + * Initializes callback promise. + * + * @param callable $callback Custom callback + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function __construct($callback) { - // We need to manually iterate and manage a count because we'll change - // the tokens array on the way. - $line = 1; - for ($i = 0, $c = \count($tokens); $i < $c; ++$i) { - if ($tokens[$i] === '#' && isset($tokens[$i + 1]) && $tokens[$i + 1] === '[') { - \array_splice($tokens, $i, 2, [[\T_ATTRIBUTE, '#[', $line]]); - $c--; - continue; - } - if (\is_array($tokens[$i])) { - $line += \substr_count($tokens[$i][1], "\n"); - } + if (!\is_callable($callback)) { + throw new InvalidArgumentException(\sprintf('Callable expected as an argument to CallbackPromise, but got %s.', \gettype($callback))); } - return $tokens; - } - public function reverseEmulate(string $code, array $tokens) : array - { - // TODO - return $tokens; + $this->callback = $callback; } - public function preprocessCode(string $code, array &$patches) : string + /** + * Evaluates promise callback. + * + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @return mixed + */ + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) { - $pos = 0; - while (\false !== ($pos = \strpos($code, '#[', $pos))) { - // Replace #[ with %[ - $code[$pos] = '%'; - $patches[] = [$pos, 'replace', '#']; - $pos += 2; + $callback = $this->callback; + if ($callback instanceof Closure && \method_exists('Closure', 'bind') && (new ReflectionFunction($callback))->getClosureThis() !== null) { + $callback = Closure::bind($callback, $object); } - return $code; + return \call_user_func($callback, $args, $object, $method); } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Prediction; -use PHPUnit\PhpParser\Lexer\Emulative; -final class CoaleseEqualTokenEmulator extends \PHPUnit\PhpParser\Lexer\TokenEmulator\TokenEmulator +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\InvalidArgumentException; +use Closure; +use ReflectionFunction; +/** + * Callback prediction. + * + * @author Konstantin Kudryashov + */ +class CallbackPrediction implements \Prophecy\Prediction\PredictionInterface { - public function getPhpVersion() : string - { - return \PHPUnit\PhpParser\Lexer\Emulative::PHP_7_4; - } - public function isEmulationNeeded(string $code) : bool - { - return \strpos($code, '??=') !== \false; - } - public function emulate(string $code, array $tokens) : array + private $callback; + /** + * Initializes callback prediction. + * + * @param callable $callback Custom callback + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function __construct($callback) { - // We need to manually iterate and manage a count because we'll change - // the tokens array on the way - $line = 1; - for ($i = 0, $c = \count($tokens); $i < $c; ++$i) { - if (isset($tokens[$i + 1])) { - if ($tokens[$i][0] === \T_COALESCE && $tokens[$i + 1] === '=') { - \array_splice($tokens, $i, 2, [[\T_COALESCE_EQUAL, '??=', $line]]); - $c--; - continue; - } - } - if (\is_array($tokens[$i])) { - $line += \substr_count($tokens[$i][1], "\n"); - } + if (!\is_callable($callback)) { + throw new InvalidArgumentException(\sprintf('Callable expected as an argument to CallbackPrediction, but got %s.', \gettype($callback))); } - return $tokens; + $this->callback = $callback; } - public function reverseEmulate(string $code, array $tokens) : array + /** + * Executes preset callback. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) { - // ??= was not valid code previously, don't bother. - return $tokens; + $callback = $this->callback; + if ($callback instanceof Closure && \method_exists('Closure', 'bind') && (new ReflectionFunction($callback))->getClosureThis() !== null) { + $callback = Closure::bind($callback, $object); + } + \call_user_func($callback, $calls, $object, $method); } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Prediction; -use PHPUnit\PhpParser\Lexer\Emulative; -final class NumericLiteralSeparatorEmulator extends \PHPUnit\PhpParser\Lexer\TokenEmulator\TokenEmulator +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Argument\ArgumentsWildcard; +use Prophecy\Argument\Token\AnyValuesToken; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Prediction\NoCallsException; +/** + * Call prediction. + * + * @author Konstantin Kudryashov + */ +class CallPrediction implements \Prophecy\Prediction\PredictionInterface { - const BIN = '(?:0b[01]+(?:_[01]+)*)'; - const HEX = '(?:0x[0-9a-f]+(?:_[0-9a-f]+)*)'; - const DEC = '(?:[0-9]+(?:_[0-9]+)*)'; - const SIMPLE_FLOAT = '(?:' . self::DEC . '\\.' . self::DEC . '?|\\.' . self::DEC . ')'; - const EXP = '(?:e[+-]?' . self::DEC . ')'; - const FLOAT = '(?:' . self::SIMPLE_FLOAT . self::EXP . '?|' . self::DEC . self::EXP . ')'; - const NUMBER = '~' . self::FLOAT . '|' . self::BIN . '|' . self::HEX . '|' . self::DEC . '~iA'; - public function getPhpVersion() : string - { - return \PHPUnit\PhpParser\Lexer\Emulative::PHP_7_4; - } - public function isEmulationNeeded(string $code) : bool + private $util; + /** + * Initializes prediction. + * + * @param StringUtil $util + */ + public function __construct(StringUtil $util = null) { - return \preg_match('~[0-9]_[0-9]~', $code) || \preg_match('~0x[0-9a-f]+_[0-9a-f]~i', $code); + $this->util = $util ?: new StringUtil(); } - public function emulate(string $code, array $tokens) : array + /** + * Tests that there was at least one call. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws \Prophecy\Exception\Prediction\NoCallsException + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) { - // We need to manually iterate and manage a count because we'll change - // the tokens array on the way - $codeOffset = 0; - for ($i = 0, $c = \count($tokens); $i < $c; ++$i) { - $token = $tokens[$i]; - $tokenLen = \strlen(\is_array($token) ? $token[1] : $token); - if ($token[0] !== \T_LNUMBER && $token[0] !== \T_DNUMBER) { - $codeOffset += $tokenLen; - continue; - } - $res = \preg_match(self::NUMBER, $code, $matches, 0, $codeOffset); - \assert($res, "No number at number token position"); - $match = $matches[0]; - $matchLen = \strlen($match); - if ($matchLen === $tokenLen) { - // Original token already holds the full number. - $codeOffset += $tokenLen; - continue; - } - $tokenKind = $this->resolveIntegerOrFloatToken($match); - $newTokens = [[$tokenKind, $match, $token[2]]]; - $numTokens = 1; - $len = $tokenLen; - while ($matchLen > $len) { - $nextToken = $tokens[$i + $numTokens]; - $nextTokenText = \is_array($nextToken) ? $nextToken[1] : $nextToken; - $nextTokenLen = \strlen($nextTokenText); - $numTokens++; - if ($matchLen < $len + $nextTokenLen) { - // Split trailing characters into a partial token. - \assert(\is_array($nextToken), "Partial token should be an array token"); - $partialText = \substr($nextTokenText, $matchLen - $len); - $newTokens[] = [$nextToken[0], $partialText, $nextToken[2]]; - break; - } - $len += $nextTokenLen; - } - \array_splice($tokens, $i, $numTokens, $newTokens); - $c -= $numTokens - \count($newTokens); - $codeOffset += $matchLen; + if (\count($calls)) { + return; } - return $tokens; - } - private function resolveIntegerOrFloatToken(string $str) : int - { - $str = \str_replace('_', '', $str); - if (\stripos($str, '0b') === 0) { - $num = \bindec($str); - } elseif (\stripos($str, '0x') === 0) { - $num = \hexdec($str); - } elseif (\stripos($str, '0') === 0 && \ctype_digit($str)) { - $num = \octdec($str); - } else { - $num = +$str; + $methodCalls = $object->findProphecyMethodCalls($method->getMethodName(), new ArgumentsWildcard(array(new AnyValuesToken()))); + if (\count($methodCalls)) { + throw new NoCallsException(\sprintf("No calls have been made that match:\n" . " %s->%s(%s)\n" . "but expected at least one.\n" . "Recorded `%s(...)` calls:\n%s", \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), $method->getMethodName(), $this->util->stringifyCalls($methodCalls)), $method); } - return \is_float($num) ? \T_DNUMBER : \T_LNUMBER; - } - public function reverseEmulate(string $code, array $tokens) : array - { - // Numeric separators were not legal code previously, don't bother. - return $tokens; + throw new NoCallsException(\sprintf("No calls have been made that match:\n" . " %s->%s(%s)\n" . "but expected at least one.", \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard()), $method); } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Prediction; +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Argument\ArgumentsWildcard; +use Prophecy\Argument\Token\AnyValuesToken; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Prediction\UnexpectedCallsCountException; /** - * Reverses emulation direction of the inner emulator. + * Prediction interface. + * Predictions are logical test blocks, tied to `should...` keyword. + * + * @author Konstantin Kudryashov */ -final class ReverseEmulator extends \PHPUnit\PhpParser\Lexer\TokenEmulator\TokenEmulator +class CallTimesPrediction implements \Prophecy\Prediction\PredictionInterface { - /** @var TokenEmulator Inner emulator */ - private $emulator; - public function __construct(\PHPUnit\PhpParser\Lexer\TokenEmulator\TokenEmulator $emulator) - { - $this->emulator = $emulator; - } - public function getPhpVersion() : string - { - return $this->emulator->getPhpVersion(); - } - public function isEmulationNeeded(string $code) : bool - { - return $this->emulator->isEmulationNeeded($code); - } - public function emulate(string $code, array $tokens) : array - { - return $this->emulator->reverseEmulate($code, $tokens); - } - public function reverseEmulate(string $code, array $tokens) : array + private $times; + private $util; + /** + * Initializes prediction. + * + * @param int $times + * @param StringUtil $util + */ + public function __construct($times, StringUtil $util = null) { - return $this->emulator->emulate($code, $tokens); + $this->times = \intval($times); + $this->util = $util ?: new StringUtil(); } - public function preprocessCode(string $code, array &$patches) : string + /** + * Tests that there was exact amount of calls made. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws \Prophecy\Exception\Prediction\UnexpectedCallsCountException + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) { - return $code; + if ($this->times == \count($calls)) { + return; + } + $methodCalls = $object->findProphecyMethodCalls($method->getMethodName(), new ArgumentsWildcard(array(new AnyValuesToken()))); + if (\count($calls)) { + $message = \sprintf("Expected exactly %d calls that match:\n" . " %s->%s(%s)\n" . "but %d were made:\n%s", $this->times, \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), \count($calls), $this->util->stringifyCalls($calls)); + } elseif (\count($methodCalls)) { + $message = \sprintf("Expected exactly %d calls that match:\n" . " %s->%s(%s)\n" . "but none were made.\n" . "Recorded `%s(...)` calls:\n%s", $this->times, \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), $method->getMethodName(), $this->util->stringifyCalls($methodCalls)); + } else { + $message = \sprintf("Expected exactly %d calls that match:\n" . " %s->%s(%s)\n" . "but none were made.", $this->times, \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard()); + } + throw new UnexpectedCallsCountException($message, $method, $this->times, $calls); } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Prediction; -use PHPUnit\PhpParser\Lexer\Emulative; -final class MatchTokenEmulator extends \PHPUnit\PhpParser\Lexer\TokenEmulator\KeywordEmulator +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +/** + * Prediction interface. + * Predictions are logical test blocks, tied to `should...` keyword. + * + * @author Konstantin Kudryashov + */ +interface PredictionInterface { - public function getPhpVersion() : string - { - return \PHPUnit\PhpParser\Lexer\Emulative::PHP_8_0; - } - public function getKeywordString() : string - { - return 'match'; - } - public function getKeywordToken() : int - { - return \T_MATCH; - } + /** + * Tests that double fulfilled prediction. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws object + * @return void + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method); } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Prediction; -abstract class KeywordEmulator extends \PHPUnit\PhpParser\Lexer\TokenEmulator\TokenEmulator +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Prediction\UnexpectedCallsException; +/** + * No calls prediction. + * + * @author Konstantin Kudryashov + */ +class NoCallsPrediction implements \Prophecy\Prediction\PredictionInterface { - abstract function getKeywordString() : string; - abstract function getKeywordToken() : int; - public function isEmulationNeeded(string $code) : bool - { - return \strpos(\strtolower($code), $this->getKeywordString()) !== \false; - } - public function emulate(string $code, array $tokens) : array - { - $keywordString = $this->getKeywordString(); - foreach ($tokens as $i => $token) { - if ($token[0] === \T_STRING && \strtolower($token[1]) === $keywordString) { - $previousNonSpaceToken = $this->getPreviousNonSpaceToken($tokens, $i); - if ($previousNonSpaceToken !== null && $previousNonSpaceToken[0] === \T_OBJECT_OPERATOR) { - continue; - } - $tokens[$i][0] = $this->getKeywordToken(); - } - } - return $tokens; - } + private $util; /** - * @param mixed[] $tokens - * @return mixed[]|null + * Initializes prediction. + * + * @param null|StringUtil $util */ - private function getPreviousNonSpaceToken(array $tokens, int $start) + public function __construct(StringUtil $util = null) { - for ($i = $start - 1; $i >= 0; --$i) { - if ($tokens[$i][0] === \T_WHITESPACE) { - continue; - } - return $tokens[$i]; - } - return null; + $this->util = $util ?: new StringUtil(); } - public function reverseEmulate(string $code, array $tokens) : array + /** + * Tests that there were no calls made. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws \Prophecy\Exception\Prediction\UnexpectedCallsException + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) { - $keywordToken = $this->getKeywordToken(); - foreach ($tokens as $i => $token) { - if ($token[0] === $keywordToken) { - $tokens[$i][0] = \T_STRING; - } + if (!\count($calls)) { + return; } - return $tokens; + $verb = \count($calls) === 1 ? 'was' : 'were'; + throw new UnexpectedCallsException(\sprintf("No calls expected that match:\n" . " %s->%s(%s)\n" . "but %d %s made:\n%s", \get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), \count($calls), $verb, $this->util->stringifyCalls($calls)), $method, $calls); } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\PhpDocumentor; -/** @internal */ -abstract class TokenEmulator +use PHPUnit\phpDocumentor\Reflection\DocBlock; +use PHPUnit\phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; +/** + * @author Théo FIDRY + * + * @internal + */ +final class LegacyClassTagRetriever implements \Prophecy\PhpDocumentor\MethodTagRetrieverInterface { - public abstract function getPhpVersion() : string; - public abstract function isEmulationNeeded(string $code) : bool; - /** - * @return array Modified Tokens - */ - public abstract function emulate(string $code, array $tokens) : array; /** - * @return array Modified Tokens + * @param \ReflectionClass $reflectionClass + * + * @return LegacyMethodTag[] */ - public abstract function reverseEmulate(string $code, array $tokens) : array; - public function preprocessCode(string $code, array &$patches) : string + public function getTagList(\ReflectionClass $reflectionClass) { - return $code; + $phpdoc = new DocBlock($reflectionClass->getDocComment()); + return $phpdoc->getTagsByName('method'); } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\PhpDocumentor; -use PHPUnit\PhpParser\Lexer\Emulative; -final class FlexibleDocStringEmulator extends \PHPUnit\PhpParser\Lexer\TokenEmulator\TokenEmulator +use PHPUnit\phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; +use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Method; +/** + * @author Théo FIDRY + * + * @internal + */ +interface MethodTagRetrieverInterface { - const FLEXIBLE_DOC_STRING_REGEX = <<<'REGEX' -/<<<[ \t]*(['"]?)([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\1\r?\n -(?:.*\r?\n)*? -(?\h*)\2(?![a-zA-Z0-9_\x80-\xff])(?(?:;?[\r\n])?)/x -REGEX; - public function getPhpVersion() : string - { - return \PHPUnit\PhpParser\Lexer\Emulative::PHP_7_3; - } - public function isEmulationNeeded(string $code) : bool - { - return \strpos($code, '<<<') !== \false; - } - public function emulate(string $code, array $tokens) : array - { - // Handled by preprocessing + fixup. - return $tokens; - } - public function reverseEmulate(string $code, array $tokens) : array - { - // Not supported. - return $tokens; - } - public function preprocessCode(string $code, array &$patches) : string - { - if (!\preg_match_all(self::FLEXIBLE_DOC_STRING_REGEX, $code, $matches, \PREG_SET_ORDER | \PREG_OFFSET_CAPTURE)) { - // No heredoc/nowdoc found - return $code; - } - // Keep track of how much we need to adjust string offsets due to the modifications we - // already made - $posDelta = 0; - foreach ($matches as $match) { - $indentation = $match['indentation'][0]; - $indentationStart = $match['indentation'][1]; - $separator = $match['separator'][0]; - $separatorStart = $match['separator'][1]; - if ($indentation === '' && $separator !== '') { - // Ordinary heredoc/nowdoc - continue; - } - if ($indentation !== '') { - // Remove indentation - $indentationLen = \strlen($indentation); - $code = \substr_replace($code, '', $indentationStart + $posDelta, $indentationLen); - $patches[] = [$indentationStart + $posDelta, 'add', $indentation]; - $posDelta -= $indentationLen; - } - if ($separator === '') { - // Insert newline as separator - $code = \substr_replace($code, "\n", $separatorStart + $posDelta, 0); - $patches[] = [$separatorStart + $posDelta, 'remove', "\n"]; - $posDelta += 1; - } - } - return $code; - } + /** + * @param \ReflectionClass $reflectionClass + * + * @return LegacyMethodTag[]|Method[] + */ + public function getTagList(\ReflectionClass $reflectionClass); } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\PhpDocumentor; -use PHPUnit\PhpParser\Lexer\Emulative; -final class FnTokenEmulator extends \PHPUnit\PhpParser\Lexer\TokenEmulator\KeywordEmulator +use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Method; +use PHPUnit\phpDocumentor\Reflection\DocBlockFactory; +use PHPUnit\phpDocumentor\Reflection\Types\ContextFactory; +/** + * @author Théo FIDRY + * + * @internal + */ +final class ClassTagRetriever implements \Prophecy\PhpDocumentor\MethodTagRetrieverInterface { - public function getPhpVersion() : string - { - return \PHPUnit\PhpParser\Lexer\Emulative::PHP_7_4; - } - public function getKeywordString() : string + private $docBlockFactory; + private $contextFactory; + public function __construct() { - return 'fn'; + $this->docBlockFactory = DocBlockFactory::createInstance(); + $this->contextFactory = new ContextFactory(); } - public function getKeywordToken() : int + /** + * @param \ReflectionClass $reflectionClass + * + * @return Method[] + */ + public function getTagList(\ReflectionClass $reflectionClass) { - return \T_FN; + try { + $phpdoc = $this->docBlockFactory->create($reflectionClass, $this->contextFactory->createFromReflector($reflectionClass)); + $methods = array(); + foreach ($phpdoc->getTagsByName('method') as $tag) { + if ($tag instanceof Method) { + $methods[] = $tag; + } + } + return $methods; + } catch (\InvalidArgumentException $e) { + return array(); + } } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\PhpDocumentor; -use PHPUnit\PhpParser\Lexer\Emulative; -final class NullsafeTokenEmulator extends \PHPUnit\PhpParser\Lexer\TokenEmulator\TokenEmulator +use PHPUnit\phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; +use PHPUnit\phpDocumentor\Reflection\DocBlock\Tags\Method; +/** + * @author Théo FIDRY + * + * @internal + */ +final class ClassAndInterfaceTagRetriever implements \Prophecy\PhpDocumentor\MethodTagRetrieverInterface { - public function getPhpVersion() : string + private $classRetriever; + public function __construct(\Prophecy\PhpDocumentor\MethodTagRetrieverInterface $classRetriever = null) { - return \PHPUnit\PhpParser\Lexer\Emulative::PHP_8_0; + if (null !== $classRetriever) { + $this->classRetriever = $classRetriever; + return; + } + $this->classRetriever = \class_exists('PHPUnit\\phpDocumentor\\Reflection\\DocBlockFactory') && \class_exists('PHPUnit\\phpDocumentor\\Reflection\\Types\\ContextFactory') ? new \Prophecy\PhpDocumentor\ClassTagRetriever() : new \Prophecy\PhpDocumentor\LegacyClassTagRetriever(); } - public function isEmulationNeeded(string $code) : bool + /** + * @param \ReflectionClass $reflectionClass + * + * @return LegacyMethodTag[]|Method[] + */ + public function getTagList(\ReflectionClass $reflectionClass) { - return \strpos($code, '?->') !== \false; + return \array_merge($this->classRetriever->getTagList($reflectionClass), $this->getInterfacesTagList($reflectionClass)); } - public function emulate(string $code, array $tokens) : array + /** + * @param \ReflectionClass $reflectionClass + * + * @return LegacyMethodTag[]|Method[] + */ + private function getInterfacesTagList(\ReflectionClass $reflectionClass) { - // We need to manually iterate and manage a count because we'll change - // the tokens array on the way - $line = 1; - for ($i = 0, $c = \count($tokens); $i < $c; ++$i) { - if ($tokens[$i] === '?' && isset($tokens[$i + 1]) && $tokens[$i + 1][0] === \T_OBJECT_OPERATOR) { - \array_splice($tokens, $i, 2, [[\T_NULLSAFE_OBJECT_OPERATOR, '?->', $line]]); - $c--; - continue; - } - // Handle ?-> inside encapsed string. - if ($tokens[$i][0] === \T_ENCAPSED_AND_WHITESPACE && isset($tokens[$i - 1]) && $tokens[$i - 1][0] === \T_VARIABLE && \preg_match('/^\\?->([a-zA-Z_\\x80-\\xff][a-zA-Z0-9_\\x80-\\xff]*)/', $tokens[$i][1], $matches)) { - $replacement = [[\T_NULLSAFE_OBJECT_OPERATOR, '?->', $line], [\T_STRING, $matches[1], $line]]; - if (\strlen($matches[0]) !== \strlen($tokens[$i][1])) { - $replacement[] = [\T_ENCAPSED_AND_WHITESPACE, \substr($tokens[$i][1], \strlen($matches[0])), $line]; - } - \array_splice($tokens, $i, 1, $replacement); - $c += \count($replacement) - 1; - continue; - } - if (\is_array($tokens[$i])) { - $line += \substr_count($tokens[$i][1], "\n"); - } + $interfaces = $reflectionClass->getInterfaces(); + $tagList = array(); + foreach ($interfaces as $interface) { + $tagList = \array_merge($tagList, $this->classRetriever->getTagList($interface)); } - return $tokens; - } - public function reverseEmulate(string $code, array $tokens) : array - { - // ?-> was not valid code previously, don't bother. - return $tokens; + return $tagList; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Call; -use PHPUnit\PhpParser\Error; -use PHPUnit\PhpParser\ErrorHandler; -use PHPUnit\PhpParser\Lexer; -use PHPUnit\PhpParser\Lexer\TokenEmulator\AttributeEmulator; -use PHPUnit\PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator; -use PHPUnit\PhpParser\Lexer\TokenEmulator\FlexibleDocStringEmulator; -use PHPUnit\PhpParser\Lexer\TokenEmulator\FnTokenEmulator; -use PHPUnit\PhpParser\Lexer\TokenEmulator\MatchTokenEmulator; -use PHPUnit\PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator; -use PHPUnit\PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator; -use PHPUnit\PhpParser\Lexer\TokenEmulator\ReverseEmulator; -use PHPUnit\PhpParser\Lexer\TokenEmulator\TokenEmulator; -use PHPUnit\PhpParser\Parser\Tokens; -class Emulative extends \PHPUnit\PhpParser\Lexer +use Prophecy\Exception\Prophecy\MethodProphecyException; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Argument\ArgumentsWildcard; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Call\UnexpectedCallException; +use SplObjectStorage; +/** + * Calls receiver & manager. + * + * @author Konstantin Kudryashov + */ +class CallCenter { - const PHP_7_3 = '7.3dev'; - const PHP_7_4 = '7.4dev'; - const PHP_8_0 = '8.0dev'; - /** @var mixed[] Patches used to reverse changes introduced in the code */ - private $patches = []; - /** @var TokenEmulator[] */ - private $emulators = []; - /** @var string */ - private $targetPhpVersion; + private $util; /** - * @param mixed[] $options Lexer options. In addition to the usual options, - * accepts a 'phpVersion' string that specifies the - * version to emulated. Defaults to newest supported. + * @var Call[] */ - public function __construct(array $options = []) + private $recordedCalls = array(); + /** + * @var SplObjectStorage + */ + private $unexpectedCalls; + /** + * Initializes call center. + * + * @param StringUtil $util + */ + public function __construct(StringUtil $util = null) { - $this->targetPhpVersion = $options['phpVersion'] ?? \PHPUnit\PhpParser\Lexer\Emulative::PHP_8_0; - unset($options['phpVersion']); - parent::__construct($options); - $emulators = [new \PHPUnit\PhpParser\Lexer\TokenEmulator\FlexibleDocStringEmulator(), new \PHPUnit\PhpParser\Lexer\TokenEmulator\FnTokenEmulator(), new \PHPUnit\PhpParser\Lexer\TokenEmulator\MatchTokenEmulator(), new \PHPUnit\PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator(), new \PHPUnit\PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator(), new \PHPUnit\PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator(), new \PHPUnit\PhpParser\Lexer\TokenEmulator\AttributeEmulator()]; - // Collect emulators that are relevant for the PHP version we're running - // and the PHP version we're targeting for emulation. - foreach ($emulators as $emulator) { - $emulatorPhpVersion = $emulator->getPhpVersion(); - if ($this->isForwardEmulationNeeded($emulatorPhpVersion)) { - $this->emulators[] = $emulator; - } else { - if ($this->isReverseEmulationNeeded($emulatorPhpVersion)) { - $this->emulators[] = new \PHPUnit\PhpParser\Lexer\TokenEmulator\ReverseEmulator($emulator); - } - } - } + $this->util = $util ?: new StringUtil(); + $this->unexpectedCalls = new SplObjectStorage(); } - public function startLexing(string $code, \PHPUnit\PhpParser\ErrorHandler $errorHandler = null) + /** + * Makes and records specific method call for object prophecy. + * + * @param ObjectProphecy $prophecy + * @param string $methodName + * @param array $arguments + * + * @return mixed Returns null if no promise for prophecy found or promise return value. + * + * @throws \Prophecy\Exception\Call\UnexpectedCallException If no appropriate method prophecy found + */ + public function makeCall(ObjectProphecy $prophecy, $methodName, array $arguments) { - $emulators = \array_filter($this->emulators, function ($emulator) use($code) { - return $emulator->isEmulationNeeded($code); - }); - if (empty($emulators)) { - // Nothing to emulate, yay - parent::startLexing($code, $errorHandler); - return; + // For efficiency exclude 'args' from the generated backtrace + // Limit backtrace to last 3 calls as we don't use the rest + $backtrace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 3); + $file = $line = null; + if (isset($backtrace[2]) && isset($backtrace[2]['file'])) { + $file = $backtrace[2]['file']; + $line = $backtrace[2]['line']; } - $this->patches = []; - foreach ($emulators as $emulator) { - $code = $emulator->preprocessCode($code, $this->patches); + // If no method prophecies defined, then it's a dummy, so we'll just return null + if ('__destruct' === \strtolower($methodName) || 0 == \count($prophecy->getMethodProphecies())) { + $this->recordedCalls[] = new \Prophecy\Call\Call($methodName, $arguments, null, null, $file, $line); + return null; } - $collector = new \PHPUnit\PhpParser\ErrorHandler\Collecting(); - parent::startLexing($code, $collector); - $this->sortPatches(); - $this->fixupTokens(); - $errors = $collector->getErrors(); - if (!empty($errors)) { - $this->fixupErrors($errors); - foreach ($errors as $error) { - $errorHandler->handleError($error); + // There are method prophecies, so it's a fake/stub. Searching prophecy for this call + $matches = $this->findMethodProphecies($prophecy, $methodName, $arguments); + // If fake/stub doesn't have method prophecy for this call - throw exception + if (!\count($matches)) { + $this->unexpectedCalls->attach(new \Prophecy\Call\Call($methodName, $arguments, null, null, $file, $line), $prophecy); + $this->recordedCalls[] = new \Prophecy\Call\Call($methodName, $arguments, null, null, $file, $line); + return null; + } + // Sort matches by their score value + @\usort($matches, function ($match1, $match2) { + return $match2[0] - $match1[0]; + }); + $score = $matches[0][0]; + // If Highest rated method prophecy has a promise - execute it or return null instead + $methodProphecy = $matches[0][1]; + $returnValue = null; + $exception = null; + if ($promise = $methodProphecy->getPromise()) { + try { + $returnValue = $promise->execute($arguments, $prophecy, $methodProphecy); + } catch (\Exception $e) { + $exception = $e; } } - foreach ($emulators as $emulator) { - $this->tokens = $emulator->emulate($code, $this->tokens); + if ($methodProphecy->hasReturnVoid() && $returnValue !== null) { + throw new MethodProphecyException("The method \"{$methodName}\" has a void return type, but the promise returned a value", $methodProphecy); } + $this->recordedCalls[] = $call = new \Prophecy\Call\Call($methodName, $arguments, $returnValue, $exception, $file, $line); + $call->addScore($methodProphecy->getArgumentsWildcard(), $score); + if (null !== $exception) { + throw $exception; + } + return $returnValue; } - private function isForwardEmulationNeeded(string $emulatorPhpVersion) : bool - { - return \version_compare(\PHP_VERSION, $emulatorPhpVersion, '<') && \version_compare($this->targetPhpVersion, $emulatorPhpVersion, '>='); - } - private function isReverseEmulationNeeded(string $emulatorPhpVersion) : bool - { - return \version_compare(\PHP_VERSION, $emulatorPhpVersion, '>=') && \version_compare($this->targetPhpVersion, $emulatorPhpVersion, '<'); - } - private function sortPatches() + /** + * Searches for calls by method name & arguments wildcard. + * + * @param string $methodName + * @param ArgumentsWildcard $wildcard + * + * @return Call[] + */ + public function findCalls($methodName, ArgumentsWildcard $wildcard) { - // Patches may be contributed by different emulators. - // Make sure they are sorted by increasing patch position. - \usort($this->patches, function ($p1, $p2) { - return $p1[0] <=> $p2[0]; - }); + $methodName = \strtolower($methodName); + return \array_values(\array_filter($this->recordedCalls, function (\Prophecy\Call\Call $call) use($methodName, $wildcard) { + return $methodName === \strtolower($call->getMethodName()) && 0 < $call->getScore($wildcard); + })); } - private function fixupTokens() + /** + * @throws UnexpectedCallException + */ + public function checkUnexpectedCalls() { - if (\count($this->patches) === 0) { - return; - } - // Load first patch - $patchIdx = 0; - list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; - // We use a manual loop over the tokens, because we modify the array on the fly - $pos = 0; - for ($i = 0, $c = \count($this->tokens); $i < $c; $i++) { - $token = $this->tokens[$i]; - if (\is_string($token)) { - if ($patchPos === $pos) { - // Only support replacement for string tokens. - \assert($patchType === 'replace'); - $this->tokens[$i] = $patchText; - // Fetch the next patch - $patchIdx++; - if ($patchIdx >= \count($this->patches)) { - // No more patches, we're done - return; - } - list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; - } - $pos += \strlen($token); - continue; - } - $len = \strlen($token[1]); - $posDelta = 0; - while ($patchPos >= $pos && $patchPos < $pos + $len) { - $patchTextLen = \strlen($patchText); - if ($patchType === 'remove') { - if ($patchPos === $pos && $patchTextLen === $len) { - // Remove token entirely - \array_splice($this->tokens, $i, 1, []); - $i--; - $c--; - } else { - // Remove from token string - $this->tokens[$i][1] = \substr_replace($token[1], '', $patchPos - $pos + $posDelta, $patchTextLen); - $posDelta -= $patchTextLen; - } - } elseif ($patchType === 'add') { - // Insert into the token string - $this->tokens[$i][1] = \substr_replace($token[1], $patchText, $patchPos - $pos + $posDelta, 0); - $posDelta += $patchTextLen; - } else { - if ($patchType === 'replace') { - // Replace inside the token string - $this->tokens[$i][1] = \substr_replace($token[1], $patchText, $patchPos - $pos + $posDelta, $patchTextLen); - } else { - \assert(\false); - } - } - // Fetch the next patch - $patchIdx++; - if ($patchIdx >= \count($this->patches)) { - // No more patches, we're done - return; - } - list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; - // Multiple patches may apply to the same token. Reload the current one to check - // If the new patch applies - $token = $this->tokens[$i]; + /** @var Call $call */ + foreach ($this->unexpectedCalls as $call) { + $prophecy = $this->unexpectedCalls[$call]; + // If fake/stub doesn't have method prophecy for this call - throw exception + if (!\count($this->findMethodProphecies($prophecy, $call->getMethodName(), $call->getArguments()))) { + throw $this->createUnexpectedCallException($prophecy, $call->getMethodName(), $call->getArguments()); } - $pos += $len; } - // A patch did not apply - \assert(\false); + } + private function createUnexpectedCallException(ObjectProphecy $prophecy, $methodName, array $arguments) + { + $classname = \get_class($prophecy->reveal()); + $indentationLength = 8; + // looks good + $argstring = \implode(",\n", $this->indentArguments(\array_map(array($this->util, 'stringify'), $arguments), $indentationLength)); + $expected = array(); + foreach (\array_merge(...\array_values($prophecy->getMethodProphecies())) as $methodProphecy) { + $expected[] = \sprintf(" - %s(\n" . "%s\n" . " )", $methodProphecy->getMethodName(), \implode(",\n", $this->indentArguments(\array_map('strval', $methodProphecy->getArgumentsWildcard()->getTokens()), $indentationLength))); + } + return new UnexpectedCallException(\sprintf("Unexpected method call on %s:\n" . " - %s(\n" . "%s\n" . " )\n" . "expected calls were:\n" . "%s", $classname, $methodName, $argstring, \implode("\n", $expected)), $prophecy, $methodName, $arguments); + } + private function indentArguments(array $arguments, $indentationLength) + { + return \preg_replace_callback('/^/m', function () use($indentationLength) { + return \str_repeat(' ', $indentationLength); + }, $arguments); } /** - * Fixup line and position information in errors. + * @param ObjectProphecy $prophecy + * @param string $methodName + * @param array $arguments * - * @param Error[] $errors + * @return array */ - private function fixupErrors(array $errors) + private function findMethodProphecies(ObjectProphecy $prophecy, $methodName, array $arguments) { - foreach ($errors as $error) { - $attrs = $error->getAttributes(); - $posDelta = 0; - $lineDelta = 0; - foreach ($this->patches as $patch) { - list($patchPos, $patchType, $patchText) = $patch; - if ($patchPos >= $attrs['startFilePos']) { - // No longer relevant - break; - } - if ($patchType === 'add') { - $posDelta += \strlen($patchText); - $lineDelta += \substr_count($patchText, "\n"); - } else { - if ($patchType === 'remove') { - $posDelta -= \strlen($patchText); - $lineDelta -= \substr_count($patchText, "\n"); - } - } + $matches = array(); + foreach ($prophecy->getMethodProphecies($methodName) as $methodProphecy) { + if (0 < ($score = $methodProphecy->getArgumentsWildcard()->scoreArguments($arguments))) { + $matches[] = array($score, $methodProphecy); } - $attrs['startFilePos'] += $posDelta; - $attrs['endFilePos'] += $posDelta; - $attrs['startLine'] += $lineDelta; - $attrs['endLine'] += $lineDelta; - $error->setAttributes($attrs); } + return $matches; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Call; +use Exception; +use Prophecy\Argument\ArgumentsWildcard; /** - * Implements the Myers diff algorithm. - * - * Myers, Eugene W. "An O (ND) difference algorithm and its variations." - * Algorithmica 1.1 (1986): 251-266. + * Call object. * - * @internal + * @author Konstantin Kudryashov */ -class Differ +class Call { - private $isEqual; + private $methodName; + private $arguments; + private $returnValue; + private $exception; + private $file; + private $line; + private $scores; /** - * Create differ over the given equality relation. + * Initializes call. * - * @param callable $isEqual Equality relation with signature function($a, $b) : bool + * @param string $methodName + * @param array $arguments + * @param mixed $returnValue + * @param Exception $exception + * @param null|string $file + * @param null|int $line */ - public function __construct(callable $isEqual) + public function __construct($methodName, array $arguments, $returnValue, Exception $exception = null, $file, $line) { - $this->isEqual = $isEqual; + $this->methodName = $methodName; + $this->arguments = $arguments; + $this->returnValue = $returnValue; + $this->exception = $exception; + $this->scores = new \SplObjectStorage(); + if ($file) { + $this->file = $file; + $this->line = \intval($line); + } } /** - * Calculate diff (edit script) from $old to $new. + * Returns called method name. * - * @param array $old Original array - * @param array $new New array + * @return string + */ + public function getMethodName() + { + return $this->methodName; + } + /** + * Returns called method arguments. * - * @return DiffElem[] Diff (edit script) + * @return array */ - public function diff(array $old, array $new) + public function getArguments() { - list($trace, $x, $y) = $this->calculateTrace($old, $new); - return $this->extractDiff($trace, $x, $y, $old, $new); + return $this->arguments; } /** - * Calculate diff, including "replace" operations. + * Returns called method return value. * - * If a sequence of remove operations is followed by the same number of add operations, these - * will be coalesced into replace operations. + * @return null|mixed + */ + public function getReturnValue() + { + return $this->returnValue; + } + /** + * Returns exception that call thrown. * - * @param array $old Original array - * @param array $new New array + * @return null|Exception + */ + public function getException() + { + return $this->exception; + } + /** + * Returns callee filename. * - * @return DiffElem[] Diff (edit script), including replace operations + * @return string */ - public function diffWithReplacements(array $old, array $new) + public function getFile() { - return $this->coalesceReplacements($this->diff($old, $new)); + return $this->file; } - private function calculateTrace(array $a, array $b) + /** + * Returns callee line number. + * + * @return int + */ + public function getLine() { - $n = \count($a); - $m = \count($b); - $max = $n + $m; - $v = [1 => 0]; - $trace = []; - for ($d = 0; $d <= $max; $d++) { - $trace[] = $v; - for ($k = -$d; $k <= $d; $k += 2) { - if ($k === -$d || $k !== $d && $v[$k - 1] < $v[$k + 1]) { - $x = $v[$k + 1]; - } else { - $x = $v[$k - 1] + 1; - } - $y = $x - $k; - while ($x < $n && $y < $m && ($this->isEqual)($a[$x], $b[$y])) { - $x++; - $y++; - } - $v[$k] = $x; - if ($x >= $n && $y >= $m) { - return [$trace, $x, $y]; - } - } - } - throw new \Exception('Should not happen'); + return $this->line; } - private function extractDiff(array $trace, int $x, int $y, array $a, array $b) + /** + * Returns short notation for callee place. + * + * @return string + */ + public function getCallPlace() { - $result = []; - for ($d = \count($trace) - 1; $d >= 0; $d--) { - $v = $trace[$d]; - $k = $x - $y; - if ($k === -$d || $k !== $d && $v[$k - 1] < $v[$k + 1]) { - $prevK = $k + 1; - } else { - $prevK = $k - 1; - } - $prevX = $v[$prevK]; - $prevY = $prevX - $prevK; - while ($x > $prevX && $y > $prevY) { - $result[] = new \PHPUnit\PhpParser\Internal\DiffElem(\PHPUnit\PhpParser\Internal\DiffElem::TYPE_KEEP, $a[$x - 1], $b[$y - 1]); - $x--; - $y--; - } - if ($d === 0) { - break; - } - while ($x > $prevX) { - $result[] = new \PHPUnit\PhpParser\Internal\DiffElem(\PHPUnit\PhpParser\Internal\DiffElem::TYPE_REMOVE, $a[$x - 1], null); - $x--; - } - while ($y > $prevY) { - $result[] = new \PHPUnit\PhpParser\Internal\DiffElem(\PHPUnit\PhpParser\Internal\DiffElem::TYPE_ADD, null, $b[$y - 1]); - $y--; - } + if (null === $this->file) { + return 'unknown'; } - return \array_reverse($result); + return \sprintf('%s:%d', $this->file, $this->line); } /** - * Coalesce equal-length sequences of remove+add into a replace operation. + * Adds the wildcard match score for the provided wildcard. * - * @param DiffElem[] $diff - * @return DiffElem[] + * @param ArgumentsWildcard $wildcard + * @param false|int $score + * + * @return $this */ - private function coalesceReplacements(array $diff) + public function addScore(ArgumentsWildcard $wildcard, $score) { - $newDiff = []; - $c = \count($diff); - for ($i = 0; $i < $c; $i++) { - $diffType = $diff[$i]->type; - if ($diffType !== \PHPUnit\PhpParser\Internal\DiffElem::TYPE_REMOVE) { - $newDiff[] = $diff[$i]; - continue; - } - $j = $i; - while ($j < $c && $diff[$j]->type === \PHPUnit\PhpParser\Internal\DiffElem::TYPE_REMOVE) { - $j++; - } - $k = $j; - while ($k < $c && $diff[$k]->type === \PHPUnit\PhpParser\Internal\DiffElem::TYPE_ADD) { - $k++; - } - if ($j - $i === $k - $j) { - $len = $j - $i; - for ($n = 0; $n < $len; $n++) { - $newDiff[] = new \PHPUnit\PhpParser\Internal\DiffElem(\PHPUnit\PhpParser\Internal\DiffElem::TYPE_REPLACE, $diff[$i + $n]->old, $diff[$j + $n]->new); - } - } else { - for (; $i < $k; $i++) { - $newDiff[] = $diff[$i]; - } - } - $i = $k - 1; + $this->scores[$wildcard] = $score; + return $this; + } + /** + * Returns wildcard match score for the provided wildcard. The score is + * calculated if not already done. + * + * @param ArgumentsWildcard $wildcard + * + * @return false|int False OR integer score (higher - better) + */ + public function getScore(ArgumentsWildcard $wildcard) + { + if (isset($this->scores[$wildcard])) { + return $this->scores[$wildcard]; } - return $newDiff; + return $this->scores[$wildcard] = $wildcard->scoreArguments($this->getArguments()); } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Comparator; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Expr; +use Prophecy\Prophecy\ProphecyInterface; +use PHPUnit\SebastianBergmann\Comparator\ObjectComparator; +class ProphecyComparator extends ObjectComparator +{ + public function accepts($expected, $actual) + { + return \is_object($expected) && \is_object($actual) && $actual instanceof ProphecyInterface; + } + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false, array &$processed = array()) + { + parent::assertEquals($expected, $actual->reveal(), $delta, $canonicalize, $ignoreCase, $processed); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Comparator; + +use PHPUnit\SebastianBergmann\Comparator\Factory as BaseFactory; /** - * This node is used internally by the format-preserving pretty printer to print anonymous classes. + * Prophecy comparator factory. * - * The normal anonymous class structure violates assumptions about the order of token offsets. - * Namely, the constructor arguments are part of the Expr\New_ node and follow the class node, even - * though they are actually interleaved with them. This special node type is used temporarily to - * restore a sane token offset order. + * @author Konstantin Kudryashov + */ +final class Factory extends BaseFactory +{ + /** + * @var Factory + */ + private static $instance; + public function __construct() + { + parent::__construct(); + $this->register(new \Prophecy\Comparator\ClosureComparator()); + $this->register(new \Prophecy\Comparator\ProphecyComparator()); + } + /** + * @return Factory + */ + public static function getInstance() + { + if (self::$instance === null) { + self::$instance = new \Prophecy\Comparator\Factory(); + } + return self::$instance; + } +} + + * Marcello Duarte * - * @internal + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Comparator; + +use PHPUnit\SebastianBergmann\Comparator\Comparator; +use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; +/** + * Closure comparator. + * + * @author Konstantin Kudryashov */ -class PrintableNewAnonClassNode extends \PHPUnit\PhpParser\Node\Expr +final class ClosureComparator extends Comparator { - /** @var Node\AttributeGroup[] PHP attribute groups */ - public $attrGroups; - /** @var Node\Arg[] Arguments */ - public $args; - /** @var null|Node\Name Name of extended class */ - public $extends; - /** @var Node\Name[] Names of implemented interfaces */ - public $implements; - /** @var Node\Stmt[] Statements */ - public $stmts; - public function __construct(array $attrGroups, array $args, \PHPUnit\PhpParser\Node\Name $extends = null, array $implements, array $stmts, array $attributes) + public function accepts($expected, $actual) + { + return \is_object($expected) && $expected instanceof \Closure && \is_object($actual) && $actual instanceof \Closure; + } + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = \false, $ignoreCase = \false, array &$processed = array()) + { + if ($expected !== $actual) { + throw new ComparisonFailure( + $expected, + $actual, + // we don't need a diff + '', + '', + \false, + 'all closures are different if not identical' + ); + } + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy; + +use Prophecy\Doubler\CachedDoubler; +use Prophecy\Doubler\Doubler; +use Prophecy\Doubler\LazyDouble; +use Prophecy\Doubler\ClassPatch; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\RevealerInterface; +use Prophecy\Prophecy\Revealer; +use Prophecy\Call\CallCenter; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Prediction\PredictionException; +use Prophecy\Exception\Prediction\AggregateException; +/** + * Prophet creates prophecies. + * + * @author Konstantin Kudryashov + */ +class Prophet +{ + private $doubler; + private $revealer; + private $util; + /** + * @var ObjectProphecy[] + */ + private $prophecies = array(); + /** + * Initializes Prophet. + * + * @param null|Doubler $doubler + * @param null|RevealerInterface $revealer + * @param null|StringUtil $util + */ + public function __construct(Doubler $doubler = null, RevealerInterface $revealer = null, StringUtil $util = null) + { + if (null === $doubler) { + $doubler = new CachedDoubler(); + $doubler->registerClassPatch(new ClassPatch\SplFileInfoPatch()); + $doubler->registerClassPatch(new ClassPatch\TraversablePatch()); + $doubler->registerClassPatch(new ClassPatch\ThrowablePatch()); + $doubler->registerClassPatch(new ClassPatch\DisableConstructorPatch()); + $doubler->registerClassPatch(new ClassPatch\ProphecySubjectPatch()); + $doubler->registerClassPatch(new ClassPatch\ReflectionClassNewInstancePatch()); + $doubler->registerClassPatch(new ClassPatch\HhvmExceptionPatch()); + $doubler->registerClassPatch(new ClassPatch\MagicCallPatch()); + $doubler->registerClassPatch(new ClassPatch\KeywordPatch()); + } + $this->doubler = $doubler; + $this->revealer = $revealer ?: new Revealer(); + $this->util = $util ?: new StringUtil(); + } + /** + * Creates new object prophecy. + * + * @param null|string $classOrInterface Class or interface name + * + * @return ObjectProphecy + */ + public function prophesize($classOrInterface = null) { - parent::__construct($attributes); - $this->attrGroups = $attrGroups; - $this->args = $args; - $this->extends = $extends; - $this->implements = $implements; - $this->stmts = $stmts; + $this->prophecies[] = $prophecy = new ObjectProphecy(new LazyDouble($this->doubler), new CallCenter($this->util), $this->revealer); + if ($classOrInterface && \class_exists($classOrInterface)) { + return $prophecy->willExtend($classOrInterface); + } + if ($classOrInterface && \interface_exists($classOrInterface)) { + return $prophecy->willImplement($classOrInterface); + } + return $prophecy; } - public static function fromNewNode(\PHPUnit\PhpParser\Node\Expr\New_ $newNode) + /** + * Returns all created object prophecies. + * + * @return ObjectProphecy[] + */ + public function getProphecies() { - $class = $newNode->class; - \assert($class instanceof \PHPUnit\PhpParser\Node\Stmt\Class_); - // We don't assert that $class->name is null here, to allow consumers to assign unique names - // to anonymous classes for their own purposes. We simplify ignore the name here. - return new self($class->attrGroups, $newNode->args, $class->extends, $class->implements, $class->stmts, $newNode->getAttributes()); + return $this->prophecies; } - public function getType() : string + /** + * Returns Doubler instance assigned to this Prophet. + * + * @return Doubler + */ + public function getDoubler() { - return 'Expr_PrintableNewAnonClass'; + return $this->doubler; } - public function getSubNodeNames() : array + /** + * Checks all predictions defined by prophecies of this Prophet. + * + * @throws Exception\Prediction\AggregateException If any prediction fails + */ + public function checkPredictions() { - return ['attrGroups', 'args', 'extends', 'implements', 'stmts']; + $exception = new AggregateException("Some predictions failed:\n"); + foreach ($this->prophecies as $prophecy) { + try { + $prophecy->checkProphecyMethodsPredictions(); + } catch (PredictionException $e) { + $exception->append($e); + } + } + if (\count($exception->getExceptions())) { + throw $exception; + } } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler; +use ReflectionClass; /** - * @internal + * Name generator. + * Generates classname for double. + * + * @author Konstantin Kudryashov */ -class DiffElem +class NameGenerator { - const TYPE_KEEP = 0; - const TYPE_REMOVE = 1; - const TYPE_ADD = 2; - const TYPE_REPLACE = 3; - /** @var int One of the TYPE_* constants */ - public $type; - /** @var mixed Is null for add operations */ - public $old; - /** @var mixed Is null for remove operations */ - public $new; - public function __construct(int $type, $old, $new) + private static $counter = 1; + /** + * Generates name. + * + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces + * + * @return string + */ + public function name(ReflectionClass $class = null, array $interfaces) { - $this->type = $type; - $this->old = $old; - $this->new = $new; + $parts = array(); + if (null !== $class) { + $parts[] = $class->getName(); + } else { + foreach ($interfaces as $interface) { + $parts[] = $interface->getShortName(); + } + } + if (!\count($parts)) { + $parts[] = 'stdClass'; + } + return \sprintf('Double\\%s\\P%d', \implode('\\', $parts), self::$counter++); } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler; +use Prophecy\Exception\Doubler\DoubleException; +use Prophecy\Exception\Doubler\ClassNotFoundException; +use Prophecy\Exception\Doubler\InterfaceNotFoundException; +use ReflectionClass; /** - * Provides operations on token streams, for use by pretty printer. + * Lazy double. + * Gives simple interface to describe double before creating it. * - * @internal + * @author Konstantin Kudryashov */ -class TokenStream +class LazyDouble { - /** @var array Tokens (in token_get_all format) */ - private $tokens; - /** @var int[] Map from position to indentation */ - private $indentMap; + private $doubler; + private $class; + private $interfaces = array(); + private $arguments = null; + private $double; /** - * Create token stream instance. + * Initializes lazy double. * - * @param array $tokens Tokens in token_get_all() format + * @param Doubler $doubler */ - public function __construct(array $tokens) + public function __construct(\Prophecy\Doubler\Doubler $doubler) { - $this->tokens = $tokens; - $this->indentMap = $this->calcIndentMap(); + $this->doubler = $doubler; } /** - * Whether the given position is immediately surrounded by parenthesis. + * Tells doubler to use specific class as parent one for double. * - * @param int $startPos Start position - * @param int $endPos End position + * @param string|ReflectionClass $class * - * @return bool + * @throws \Prophecy\Exception\Doubler\ClassNotFoundException + * @throws \Prophecy\Exception\Doubler\DoubleException */ - public function haveParens(int $startPos, int $endPos) : bool + public function setParentClass($class) { - return $this->haveTokenImmediatelyBefore($startPos, '(') && $this->haveTokenImmediatelyAfter($endPos, ')'); + if (null !== $this->double) { + throw new DoubleException('Can not extend class with already instantiated double.'); + } + if (!$class instanceof ReflectionClass) { + if (!\class_exists($class)) { + throw new ClassNotFoundException(\sprintf('Class %s not found.', $class), $class); + } + $class = new ReflectionClass($class); + } + $this->class = $class; } /** - * Whether the given position is immediately surrounded by braces. + * Tells doubler to implement specific interface with double. * - * @param int $startPos Start position - * @param int $endPos End position + * @param string|ReflectionClass $interface * - * @return bool + * @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException + * @throws \Prophecy\Exception\Doubler\DoubleException */ - public function haveBraces(int $startPos, int $endPos) : bool + public function addInterface($interface) { - return $this->haveTokenImmediatelyBefore($startPos, '{') && $this->haveTokenImmediatelyAfter($endPos, '}'); + if (null !== $this->double) { + throw new DoubleException('Can not implement interface with already instantiated double.'); + } + if (!$interface instanceof ReflectionClass) { + if (!\interface_exists($interface)) { + throw new InterfaceNotFoundException(\sprintf('Interface %s not found.', $interface), $interface); + } + $interface = new ReflectionClass($interface); + } + $this->interfaces[] = $interface; } /** - * Check whether the position is directly preceded by a certain token type. - * - * During this check whitespace and comments are skipped. - * - * @param int $pos Position before which the token should occur - * @param int|string $expectedTokenType Token to check for + * Sets constructor arguments. * - * @return bool Whether the expected token was found + * @param array $arguments */ - public function haveTokenImmediatelyBefore(int $pos, $expectedTokenType) : bool + public function setArguments(array $arguments = null) { - $tokens = $this->tokens; - $pos--; - for (; $pos >= 0; $pos--) { - $tokenType = $tokens[$pos][0]; - if ($tokenType === $expectedTokenType) { - return \true; - } - if ($tokenType !== \T_WHITESPACE && $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) { - break; - } - } - return \false; + $this->arguments = $arguments; } /** - * Check whether the position is directly followed by a certain token type. - * - * During this check whitespace and comments are skipped. - * - * @param int $pos Position after which the token should occur - * @param int|string $expectedTokenType Token to check for + * Creates double instance or returns already created one. * - * @return bool Whether the expected token was found + * @return DoubleInterface */ - public function haveTokenImmediatelyAfter(int $pos, $expectedTokenType) : bool + public function getInstance() { - $tokens = $this->tokens; - $pos++; - for (; $pos < \count($tokens); $pos++) { - $tokenType = $tokens[$pos][0]; - if ($tokenType === $expectedTokenType) { - return \true; - } - if ($tokenType !== \T_WHITESPACE && $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) { - break; + if (null === $this->double) { + if (null !== $this->arguments) { + return $this->double = $this->doubler->double($this->class, $this->interfaces, $this->arguments); } + $this->double = $this->doubler->double($this->class, $this->interfaces); } - return \false; - } - public function skipLeft(int $pos, $skipTokenType) - { - $tokens = $this->tokens; - $pos = $this->skipLeftWhitespace($pos); - if ($skipTokenType === \T_WHITESPACE) { - return $pos; - } - if ($tokens[$pos][0] !== $skipTokenType) { - // Shouldn't happen. The skip token MUST be there - throw new \Exception('Encountered unexpected token'); - } - $pos--; - return $this->skipLeftWhitespace($pos); - } - public function skipRight(int $pos, $skipTokenType) - { - $tokens = $this->tokens; - $pos = $this->skipRightWhitespace($pos); - if ($skipTokenType === \T_WHITESPACE) { - return $pos; - } - if ($tokens[$pos][0] !== $skipTokenType) { - // Shouldn't happen. The skip token MUST be there - throw new \Exception('Encountered unexpected token'); - } - $pos++; - return $this->skipRightWhitespace($pos); + return $this->double; } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler; + +use ReflectionClass; +/** + * Cached class doubler. + * Prevents mirroring/creation of the same structure twice. + * + * @author Konstantin Kudryashov + */ +class CachedDoubler extends \Prophecy\Doubler\Doubler +{ + private static $classes = array(); /** - * Return first non-whitespace token position smaller or equal to passed position. - * - * @param int $pos Token position - * @return int Non-whitespace token position + * {@inheritdoc} */ - public function skipLeftWhitespace(int $pos) + protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) { - $tokens = $this->tokens; - for (; $pos >= 0; $pos--) { - $type = $tokens[$pos][0]; - if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) { - break; - } + $classId = $this->generateClassId($class, $interfaces); + if (isset(self::$classes[$classId])) { + return self::$classes[$classId]; } - return $pos; + return self::$classes[$classId] = parent::createDoubleClass($class, $interfaces); } /** - * Return first non-whitespace position greater or equal to passed position. + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces * - * @param int $pos Token position - * @return int Non-whitespace token position + * @return string */ - public function skipRightWhitespace(int $pos) + private function generateClassId(ReflectionClass $class = null, array $interfaces) { - $tokens = $this->tokens; - for ($count = \count($tokens); $pos < $count; $pos++) { - $type = $tokens[$pos][0]; - if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) { - break; - } + $parts = array(); + if (null !== $class) { + $parts[] = $class->getName(); } - return $pos; + foreach ($interfaces as $interface) { + $parts[] = $interface->getName(); + } + foreach ($this->getClassPatches() as $patch) { + $parts[] = \get_class($patch); + } + \sort($parts); + return \md5(\implode('', $parts)); } - public function findRight(int $pos, $findTokenType) + public function resetCache() { - $tokens = $this->tokens; - for ($count = \count($tokens); $pos < $count; $pos++) { - $type = $tokens[$pos][0]; - if ($type === $findTokenType) { - return $pos; - } - } - return -1; + self::$classes = array(); } - /** - * Whether the given position range contains a certain token type. - * - * @param int $startPos Starting position (inclusive) - * @param int $endPos Ending position (exclusive) - * @param int|string $tokenType Token type to look for - * @return bool Whether the token occurs in the given range - */ - public function haveTokenInRange(int $startPos, int $endPos, $tokenType) +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler; + +/** + * Core double interface. + * All doubled classes will implement this one. + * + * @author Konstantin Kudryashov + */ +interface DoubleInterface +{ +} +tokens; - for ($pos = $startPos; $pos < $endPos; $pos++) { - if ($tokens[$pos][0] === $tokenType) { + switch ($type) { + case 'self': + case 'array': + case 'callable': + case 'bool': + case 'float': + case 'int': + case 'string': + case 'iterable': + case 'object': return \true; - } + case 'mixed': + return \PHP_VERSION_ID >= 80000; + default: + return \false; } - return \false; } - public function haveBracesInRange(int $startPos, int $endPos) + public function isBuiltInReturnTypeHint($type) { - return $this->haveTokenInRange($startPos, $endPos, '{') || $this->haveTokenInRange($startPos, $endPos, '}'); + if ($type === 'void') { + return \true; + } + return $this->isBuiltInParamTypeHint($type); } - /** - * Get indentation before token position. - * - * @param int $pos Token position - * - * @return int Indentation depth (in spaces) - */ - public function getIndentationBefore(int $pos) : int +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler\Generator; + +use Prophecy\Doubler\Generator\Node\ReturnTypeNode; +use Prophecy\Doubler\Generator\Node\TypeNodeAbstract; +/** + * Class code creator. + * Generates PHP code for specific class node tree. + * + * @author Konstantin Kudryashov + */ +class ClassCodeGenerator +{ + public function __construct(\Prophecy\Doubler\Generator\TypeHintReference $typeHintReference = null) { - return $this->indentMap[$pos]; } /** - * Get the code corresponding to a token offset range, optionally adjusted for indentation. + * Generates PHP code for class node. * - * @param int $from Token start position (inclusive) - * @param int $to Token end position (exclusive) - * @param int $indent By how much the code should be indented (can be negative as well) + * @param string $classname + * @param Node\ClassNode $class * - * @return string Code corresponding to token range, adjusted for indentation + * @return string */ - public function getTokenCode(int $from, int $to, int $indent) : string + public function generate($classname, \Prophecy\Doubler\Generator\Node\ClassNode $class) { - $tokens = $this->tokens; - $result = ''; - for ($pos = $from; $pos < $to; $pos++) { - $token = $tokens[$pos]; - if (\is_array($token)) { - $type = $token[0]; - $content = $token[1]; - if ($type === \T_CONSTANT_ENCAPSED_STRING || $type === \T_ENCAPSED_AND_WHITESPACE) { - $result .= $content; - } else { - // TODO Handle non-space indentation - if ($indent < 0) { - $result .= \str_replace("\n" . \str_repeat(" ", -$indent), "\n", $content); - } elseif ($indent > 0) { - $result .= \str_replace("\n", "\n" . \str_repeat(" ", $indent), $content); - } else { - $result .= $content; - } - } - } else { - $result .= $token; - } + $parts = \explode('\\', $classname); + $classname = \array_pop($parts); + $namespace = \implode('\\', $parts); + $code = \sprintf("class %s extends \\%s implements %s {\n", $classname, $class->getParentClass(), \implode(', ', \array_map(function ($interface) { + return '\\' . $interface; + }, $class->getInterfaces()))); + foreach ($class->getProperties() as $name => $visibility) { + $code .= \sprintf("%s \$%s;\n", $visibility, $name); } - return $result; + $code .= "\n"; + foreach ($class->getMethods() as $method) { + $code .= $this->generateMethod($method) . "\n"; + } + $code .= "\n}"; + return \sprintf("namespace %s {\n%s\n}", $namespace, $code); } - /** - * Precalculate the indentation at every token position. - * - * @return int[] Token position to indentation map - */ - private function calcIndentMap() + private function generateMethod(\Prophecy\Doubler\Generator\Node\MethodNode $method) { - $indentMap = []; - $indent = 0; - foreach ($this->tokens as $token) { - $indentMap[] = $indent; - if ($token[0] === \T_WHITESPACE) { - $content = $token[1]; - $newlinePos = \strrpos($content, "\n"); - if (\false !== $newlinePos) { - $indent = \strlen($content) - $newlinePos - 1; - } - } + $php = \sprintf("%s %s function %s%s(%s)%s {\n", $method->getVisibility(), $method->isStatic() ? 'static' : '', $method->returnsReference() ? '&' : '', $method->getName(), \implode(', ', $this->generateArguments($method->getArguments())), ($ret = $this->generateTypes($method->getReturnTypeNode())) ? ': ' . $ret : ''); + $php .= $method->getCode() . "\n"; + return $php . '}'; + } + private function generateTypes(TypeNodeAbstract $typeNode) : string + { + if (!$typeNode->getTypes()) { + return ''; + } + // When we require PHP 8 we can stop generating ?foo nullables and remove this first block + if ($typeNode->canUseNullShorthand()) { + return \sprintf('?%s', $typeNode->getNonNullTypes()[0]); + } else { + return \join('|', $typeNode->getTypes()); } - // Add a sentinel for one past end of the file - $indentMap[] = $indent; - return $indentMap; + } + private function generateArguments(array $arguments) + { + return \array_map(function (\Prophecy\Doubler\Generator\Node\ArgumentNode $argument) { + $php = $this->generateTypes($argument->getTypeNode()); + $php .= ' ' . ($argument->isPassedByReference() ? '&' : ''); + $php .= $argument->isVariadic() ? '...' : ''; + $php .= '$' . $argument->getName(); + if ($argument->isOptional() && !$argument->isVariadic()) { + $php .= ' = ' . \var_export($argument->getDefault(), \true); + } + return $php; + }, $arguments); } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler\Generator; -use PHPUnit\PhpParser\NodeVisitor\FindingVisitor; -use PHPUnit\PhpParser\NodeVisitor\FirstFindingVisitor; -class NodeFinder +use Prophecy\Exception\Doubler\ClassCreatorException; +/** + * Class creator. + * Creates specific class in current environment. + * + * @author Konstantin Kudryashov + */ +class ClassCreator { + private $generator; /** - * Find all nodes satisfying a filter callback. - * - * @param Node|Node[] $nodes Single node or array of nodes to search in - * @param callable $filter Filter callback: function(Node $node) : bool + * Initializes creator. * - * @return Node[] Found nodes satisfying the filter callback + * @param ClassCodeGenerator $generator */ - public function find($nodes, callable $filter) : array + public function __construct(\Prophecy\Doubler\Generator\ClassCodeGenerator $generator = null) { - if (!\is_array($nodes)) { - $nodes = [$nodes]; - } - $visitor = new \PHPUnit\PhpParser\NodeVisitor\FindingVisitor($filter); - $traverser = new \PHPUnit\PhpParser\NodeTraverser(); - $traverser->addVisitor($visitor); - $traverser->traverse($nodes); - return $visitor->getFoundNodes(); + $this->generator = $generator ?: new \Prophecy\Doubler\Generator\ClassCodeGenerator(); } /** - * Find all nodes that are instances of a certain class. + * Creates class. * - * @param Node|Node[] $nodes Single node or array of nodes to search in - * @param string $class Class name + * @param string $classname + * @param Node\ClassNode $class * - * @return Node[] Found nodes (all instances of $class) + * @return mixed + * + * @throws \Prophecy\Exception\Doubler\ClassCreatorException */ - public function findInstanceOf($nodes, string $class) : array + public function create($classname, \Prophecy\Doubler\Generator\Node\ClassNode $class) { - return $this->find($nodes, function ($node) use($class) { - return $node instanceof $class; - }); + $code = $this->generator->generate($classname, $class); + $return = eval($code); + if (!\class_exists($classname, \false)) { + if (\count($class->getInterfaces())) { + throw new ClassCreatorException(\sprintf('Could not double `%s` and implement interfaces: [%s].', $class->getParentClass(), \implode(', ', $class->getInterfaces())), $class); + } + throw new ClassCreatorException(\sprintf('Could not double `%s`.', $class->getParentClass()), $class); + } + return $return; } - /** - * Find first node satisfying a filter callback. - * - * @param Node|Node[] $nodes Single node or array of nodes to search in - * @param callable $filter Filter callback: function(Node $node) : bool - * - * @return null|Node Found node (or null if none found) - */ - public function findFirst($nodes, callable $filter) +} +addVisitor($visitor); - $traverser->traverse($nodes); - return $visitor->getFoundNode(); + } + protected function guardIsValidType() + { + if (isset($this->types['void']) && \count($this->types) !== 1) { + throw new DoubleException('void cannot be part of a union'); + } + if (isset($this->types['never']) && \count($this->types) !== 1) { + throw new DoubleException('never cannot be part of a union'); + } + parent::guardIsValidType(); } /** - * Find first node that is an instance of a certain class. - * - * @param Node|Node[] $nodes Single node or array of nodes to search in - * @param string $class Class name - * - * @return null|Node Found node, which is an instance of $class (or null if none found) + * @deprecated use hasReturnStatement */ - public function findFirstInstanceOf($nodes, string $class) + public function isVoid() { - return $this->findFirst($nodes, function ($node) use($class) { - return $node instanceof $class; - }); + return $this->types == ['void' => 'void']; + } + public function hasReturnStatement() : bool + { + return $this->types !== ['void' => 'void'] && $this->types !== ['never' => 'never']; } } getRealType($type); + $this->types[$type] = $type; + } + $this->guardIsValidType(); + } + public function canUseNullShorthand() : bool + { + return isset($this->types['null']) && \count($this->types) <= 2; + } + public function getTypes() : array + { + return \array_values($this->types); + } + public function getNonNullTypes() : array + { + $nonNullTypes = $this->types; + unset($nonNullTypes['null']); + return \array_values($nonNullTypes); + } + protected function prefixWithNsSeparator(string $type) : string + { + return '\\' . \ltrim($type, '\\'); + } + protected function getRealType(string $type) : string + { + switch ($type) { + // type aliases + case 'double': + case 'real': + return 'float'; + case 'boolean': + return 'bool'; + case 'integer': + return 'int'; + // built in types + case 'self': + case 'array': + case 'callable': + case 'bool': + case 'false': + case 'float': + case 'int': + case 'string': + case 'iterable': + case 'object': + case 'null': + return $type; + case 'mixed': + return \PHP_VERSION_ID < 80000 ? $this->prefixWithNsSeparator($type) : $type; + default: + return $this->prefixWithNsSeparator($type); + } + } + protected function guardIsValidType() + { + if ($this->types == ['null' => 'null']) { + throw new DoubleException('Type cannot be standalone null'); + } + if ($this->types == ['false' => 'false']) { + throw new DoubleException('Type cannot be standalone false'); + } + if ($this->types == ['false' => 'false', 'null' => 'null']) { + throw new DoubleException('Type cannot be nullable false'); + } + if (\PHP_VERSION_ID >= 80000 && isset($this->types['mixed']) && \count($this->types) !== 1) { + throw new DoubleException('mixed cannot be part of a union'); + } + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -use PHPUnit\PhpParser\Node\Expr; -use PHPUnit\PhpParser\Node\Expr\Cast\Double; -use PHPUnit\PhpParser\Node\Name; -use PHPUnit\PhpParser\Node\Param; -use PHPUnit\PhpParser\Node\Scalar\Encapsed; -use PHPUnit\PhpParser\Node\Scalar\LNumber; -use PHPUnit\PhpParser\Node\Scalar\String_; -use PHPUnit\PhpParser\Node\Stmt\Class_; -use PHPUnit\PhpParser\Node\Stmt\ClassConst; -use PHPUnit\PhpParser\Node\Stmt\ClassMethod; -use PHPUnit\PhpParser\Node\Stmt\Interface_; -use PHPUnit\PhpParser\Node\Stmt\Namespace_; -use PHPUnit\PhpParser\Node\Stmt\Property; -use PHPUnit\PhpParser\Node\Stmt\TryCatch; -use PHPUnit\PhpParser\Node\Stmt\UseUse; -use PHPUnit\PhpParser\Node\VarLikeIdentifier; -abstract class ParserAbstract implements \PHPUnit\PhpParser\Parser +namespace Prophecy\Doubler\Generator\Node; + +use Prophecy\Exception\Doubler\MethodNotExtendableException; +use Prophecy\Exception\InvalidArgumentException; +/** + * Class node. + * + * @author Konstantin Kudryashov + */ +class ClassNode { - const SYMBOL_NONE = -1; - /* - * The following members will be filled with generated parsing data: - */ - /** @var int Size of $tokenToSymbol map */ - protected $tokenToSymbolMapSize; - /** @var int Size of $action table */ - protected $actionTableSize; - /** @var int Size of $goto table */ - protected $gotoTableSize; - /** @var int Symbol number signifying an invalid token */ - protected $invalidSymbol; - /** @var int Symbol number of error recovery token */ - protected $errorSymbol; - /** @var int Action number signifying default action */ - protected $defaultAction; - /** @var int Rule number signifying that an unexpected token was encountered */ - protected $unexpectedTokenRule; - protected $YY2TBLSTATE; - /** @var int Number of non-leaf states */ - protected $numNonLeafStates; - /** @var int[] Map of lexer tokens to internal symbols */ - protected $tokenToSymbol; - /** @var string[] Map of symbols to their names */ - protected $symbolToName; - /** @var array Names of the production rules (only necessary for debugging) */ - protected $productions; - /** @var int[] Map of states to a displacement into the $action table. The corresponding action for this - * state/symbol pair is $action[$actionBase[$state] + $symbol]. If $actionBase[$state] is 0, the - action is defaulted, i.e. $actionDefault[$state] should be used instead. */ - protected $actionBase; - /** @var int[] Table of actions. Indexed according to $actionBase comment. */ - protected $action; - /** @var int[] Table indexed analogously to $action. If $actionCheck[$actionBase[$state] + $symbol] != $symbol - * then the action is defaulted, i.e. $actionDefault[$state] should be used instead. */ - protected $actionCheck; - /** @var int[] Map of states to their default action */ - protected $actionDefault; - /** @var callable[] Semantic action callbacks */ - protected $reduceCallbacks; - /** @var int[] Map of non-terminals to a displacement into the $goto table. The corresponding goto state for this - * non-terminal/state pair is $goto[$gotoBase[$nonTerminal] + $state] (unless defaulted) */ - protected $gotoBase; - /** @var int[] Table of states to goto after reduction. Indexed according to $gotoBase comment. */ - protected $goto; - /** @var int[] Table indexed analogously to $goto. If $gotoCheck[$gotoBase[$nonTerminal] + $state] != $nonTerminal - * then the goto state is defaulted, i.e. $gotoDefault[$nonTerminal] should be used. */ - protected $gotoCheck; - /** @var int[] Map of non-terminals to the default state to goto after their reduction */ - protected $gotoDefault; - /** @var int[] Map of rules to the non-terminal on their left-hand side, i.e. the non-terminal to use for - * determining the state to goto after reduction. */ - protected $ruleToNonTerminal; - /** @var int[] Map of rules to the length of their right-hand side, which is the number of elements that have to - * be popped from the stack(s) on reduction. */ - protected $ruleToLength; - /* - * The following members are part of the parser state: - */ - /** @var Lexer Lexer that is used when parsing */ - protected $lexer; - /** @var mixed Temporary value containing the result of last semantic action (reduction) */ - protected $semValue; - /** @var array Semantic value stack (contains values of tokens and semantic action results) */ - protected $semStack; - /** @var array[] Start attribute stack */ - protected $startAttributeStack; - /** @var array[] End attribute stack */ - protected $endAttributeStack; - /** @var array End attributes of last *shifted* token */ - protected $endAttributes; - /** @var array Start attributes of last *read* token */ - protected $lookaheadStartAttributes; - /** @var ErrorHandler Error handler */ - protected $errorHandler; - /** @var int Error state, used to avoid error floods */ - protected $errorState; - /** - * Initialize $reduceCallbacks map. - */ - protected abstract function initReduceCallbacks(); + private $parentClass = 'stdClass'; + private $interfaces = array(); + private $properties = array(); + private $unextendableMethods = array(); /** - * Creates a parser instance. - * - * Options: Currently none. - * - * @param Lexer $lexer A lexer - * @param array $options Options array. + * @var MethodNode[] */ - public function __construct(\PHPUnit\PhpParser\Lexer $lexer, array $options = []) + private $methods = array(); + public function getParentClass() { - $this->lexer = $lexer; - if (isset($options['throwOnError'])) { - throw new \LogicException('"throwOnError" is no longer supported, use "errorHandler" instead'); - } - $this->initReduceCallbacks(); + return $this->parentClass; } /** - * Parses PHP code into a node tree. - * - * If a non-throwing error handler is used, the parser will continue parsing after an error - * occurred and attempt to build a partial AST. - * - * @param string $code The source code to parse - * @param ErrorHandler|null $errorHandler Error handler to use for lexer/parser errors, defaults - * to ErrorHandler\Throwing. - * - * @return Node\Stmt[]|null Array of statements (or null non-throwing error handler is used and - * the parser was unable to recover from an error). + * @param string $class */ - public function parse(string $code, \PHPUnit\PhpParser\ErrorHandler $errorHandler = null) - { - $this->errorHandler = $errorHandler ?: new \PHPUnit\PhpParser\ErrorHandler\Throwing(); - $this->lexer->startLexing($code, $this->errorHandler); - $result = $this->doParse(); - // Clear out some of the interior state, so we don't hold onto unnecessary - // memory between uses of the parser - $this->startAttributeStack = []; - $this->endAttributeStack = []; - $this->semStack = []; - $this->semValue = null; - return $result; - } - protected function doParse() - { - // We start off with no lookahead-token - $symbol = self::SYMBOL_NONE; - // The attributes for a node are taken from the first and last token of the node. - // From the first token only the startAttributes are taken and from the last only - // the endAttributes. Both are merged using the array union operator (+). - $startAttributes = []; - $endAttributes = []; - $this->endAttributes = $endAttributes; - // Keep stack of start and end attributes - $this->startAttributeStack = []; - $this->endAttributeStack = [$endAttributes]; - // Start off in the initial state and keep a stack of previous states - $state = 0; - $stateStack = [$state]; - // Semantic value stack (contains values of tokens and semantic action results) - $this->semStack = []; - // Current position in the stack(s) - $stackPos = 0; - $this->errorState = 0; - for (;;) { - //$this->traceNewState($state, $symbol); - if ($this->actionBase[$state] === 0) { - $rule = $this->actionDefault[$state]; - } else { - if ($symbol === self::SYMBOL_NONE) { - // Fetch the next token id from the lexer and fetch additional info by-ref. - // The end attributes are fetched into a temporary variable and only set once the token is really - // shifted (not during read). Otherwise you would sometimes get off-by-one errors, when a rule is - // reduced after a token was read but not yet shifted. - $tokenId = $this->lexer->getNextToken($tokenValue, $startAttributes, $endAttributes); - // map the lexer token id to the internally used symbols - $symbol = $tokenId >= 0 && $tokenId < $this->tokenToSymbolMapSize ? $this->tokenToSymbol[$tokenId] : $this->invalidSymbol; - if ($symbol === $this->invalidSymbol) { - throw new \RangeException(\sprintf('The lexer returned an invalid token (id=%d, value=%s)', $tokenId, $tokenValue)); - } - // This is necessary to assign some meaningful attributes to /* empty */ productions. They'll get - // the attributes of the next token, even though they don't contain it themselves. - $this->startAttributeStack[$stackPos + 1] = $startAttributes; - $this->endAttributeStack[$stackPos + 1] = $endAttributes; - $this->lookaheadStartAttributes = $startAttributes; - //$this->traceRead($symbol); - } - $idx = $this->actionBase[$state] + $symbol; - if (($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol || $state < $this->YY2TBLSTATE && ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $symbol) >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol) && ($action = $this->action[$idx]) !== $this->defaultAction) { - /* - * >= numNonLeafStates: shift and reduce - * > 0: shift - * = 0: accept - * < 0: reduce - * = -YYUNEXPECTED: error - */ - if ($action > 0) { - /* shift */ - //$this->traceShift($symbol); - ++$stackPos; - $stateStack[$stackPos] = $state = $action; - $this->semStack[$stackPos] = $tokenValue; - $this->startAttributeStack[$stackPos] = $startAttributes; - $this->endAttributeStack[$stackPos] = $endAttributes; - $this->endAttributes = $endAttributes; - $symbol = self::SYMBOL_NONE; - if ($this->errorState) { - --$this->errorState; - } - if ($action < $this->numNonLeafStates) { - continue; - } - /* $yyn >= numNonLeafStates means shift-and-reduce */ - $rule = $action - $this->numNonLeafStates; - } else { - $rule = -$action; - } - } else { - $rule = $this->actionDefault[$state]; - } - } - for (;;) { - if ($rule === 0) { - /* accept */ - //$this->traceAccept(); - return $this->semValue; - } elseif ($rule !== $this->unexpectedTokenRule) { - /* reduce */ - //$this->traceReduce($rule); - try { - $this->reduceCallbacks[$rule]($stackPos); - } catch (\PHPUnit\PhpParser\Error $e) { - if (-1 === $e->getStartLine() && isset($startAttributes['startLine'])) { - $e->setStartLine($startAttributes['startLine']); - } - $this->emitError($e); - // Can't recover from this type of error - return null; - } - /* Goto - shift nonterminal */ - $lastEndAttributes = $this->endAttributeStack[$stackPos]; - $stackPos -= $this->ruleToLength[$rule]; - $nonTerminal = $this->ruleToNonTerminal[$rule]; - $idx = $this->gotoBase[$nonTerminal] + $stateStack[$stackPos]; - if ($idx >= 0 && $idx < $this->gotoTableSize && $this->gotoCheck[$idx] === $nonTerminal) { - $state = $this->goto[$idx]; - } else { - $state = $this->gotoDefault[$nonTerminal]; - } - ++$stackPos; - $stateStack[$stackPos] = $state; - $this->semStack[$stackPos] = $this->semValue; - $this->endAttributeStack[$stackPos] = $lastEndAttributes; - } else { - /* error */ - switch ($this->errorState) { - case 0: - $msg = $this->getErrorMessage($symbol, $state); - $this->emitError(new \PHPUnit\PhpParser\Error($msg, $startAttributes + $endAttributes)); - // Break missing intentionally - case 1: - case 2: - $this->errorState = 3; - // Pop until error-expecting state uncovered - while (!(($idx = $this->actionBase[$state] + $this->errorSymbol) >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $this->errorSymbol || $state < $this->YY2TBLSTATE && ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $this->errorSymbol) >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $this->errorSymbol) || ($action = $this->action[$idx]) === $this->defaultAction) { - // Not totally sure about this - if ($stackPos <= 0) { - // Could not recover from error - return null; - } - $state = $stateStack[--$stackPos]; - //$this->tracePop($state); - } - //$this->traceShift($this->errorSymbol); - ++$stackPos; - $stateStack[$stackPos] = $state = $action; - // We treat the error symbol as being empty, so we reset the end attributes - // to the end attributes of the last non-error symbol - $this->endAttributeStack[$stackPos] = $this->endAttributeStack[$stackPos - 1]; - $this->endAttributes = $this->endAttributeStack[$stackPos - 1]; - break; - case 3: - if ($symbol === 0) { - // Reached EOF without recovering from error - return null; - } - //$this->traceDiscard($symbol); - $symbol = self::SYMBOL_NONE; - break 2; - } - } - if ($state < $this->numNonLeafStates) { - break; - } - /* >= numNonLeafStates means shift-and-reduce */ - $rule = $state - $this->numNonLeafStates; - } - } - throw new \RuntimeException('Reached end of parser loop'); + public function setParentClass($class) + { + $this->parentClass = $class ?: 'stdClass'; } - protected function emitError(\PHPUnit\PhpParser\Error $error) + /** + * @return string[] + */ + public function getInterfaces() { - $this->errorHandler->handleError($error); + return $this->interfaces; } /** - * Format error message including expected tokens. - * - * @param int $symbol Unexpected symbol - * @param int $state State at time of error + * @param string $interface + */ + public function addInterface($interface) + { + if ($this->hasInterface($interface)) { + return; + } + \array_unshift($this->interfaces, $interface); + } + /** + * @param string $interface * - * @return string Formatted error message + * @return bool */ - protected function getErrorMessage(int $symbol, int $state) : string + public function hasInterface($interface) { - $expectedString = ''; - if ($expected = $this->getExpectedTokens($state)) { - $expectedString = ', expecting ' . \implode(' or ', $expected); + return \in_array($interface, $this->interfaces); + } + public function getProperties() + { + return $this->properties; + } + public function addProperty($name, $visibility = 'public') + { + $visibility = \strtolower($visibility); + if (!\in_array($visibility, array('public', 'private', 'protected'))) { + throw new InvalidArgumentException(\sprintf('`%s` property visibility is not supported.', $visibility)); } - return 'Syntax error, unexpected ' . $this->symbolToName[$symbol] . $expectedString; + $this->properties[$name] = $visibility; } /** - * Get limited number of expected tokens in given state. + * @return MethodNode[] + */ + public function getMethods() + { + return $this->methods; + } + public function addMethod(\Prophecy\Doubler\Generator\Node\MethodNode $method, $force = \false) + { + if (!$this->isExtendable($method->getName())) { + $message = \sprintf('Method `%s` is not extendable, so can not be added.', $method->getName()); + throw new MethodNotExtendableException($message, $this->getParentClass(), $method->getName()); + } + if ($force || !isset($this->methods[$method->getName()])) { + $this->methods[$method->getName()] = $method; + } + } + public function removeMethod($name) + { + unset($this->methods[$name]); + } + /** + * @param string $name * - * @param int $state State + * @return MethodNode|null + */ + public function getMethod($name) + { + return $this->hasMethod($name) ? $this->methods[$name] : null; + } + /** + * @param string $name * - * @return string[] Expected tokens. If too many, an empty array is returned. + * @return bool */ - protected function getExpectedTokens(int $state) : array + public function hasMethod($name) { - $expected = []; - $base = $this->actionBase[$state]; - foreach ($this->symbolToName as $symbol => $name) { - $idx = $base + $symbol; - if ($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol || $state < $this->YY2TBLSTATE && ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $symbol) >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol) { - if ($this->action[$idx] !== $this->unexpectedTokenRule && $this->action[$idx] !== $this->defaultAction && $symbol !== $this->errorSymbol) { - if (\count($expected) === 4) { - /* Too many expected tokens */ - return []; - } - $expected[] = $name; - } - } + return isset($this->methods[$name]); + } + /** + * @return string[] + */ + public function getUnextendableMethods() + { + return $this->unextendableMethods; + } + /** + * @param string $unextendableMethod + */ + public function addUnextendableMethod($unextendableMethod) + { + if (!$this->isExtendable($unextendableMethod)) { + return; } - return $expected; + $this->unextendableMethods[] = $unextendableMethod; } - /* - * Tracing functions used for debugging the parser. + /** + * @param string $method + * @return bool */ - /* - protected function traceNewState($state, $symbol) { - echo '% State ' . $state - . ', Lookahead ' . ($symbol == self::SYMBOL_NONE ? '--none--' : $this->symbolToName[$symbol]) . "\n"; + public function isExtendable($method) + { + return !\in_array($method, $this->unextendableMethods); } - - protected function traceRead($symbol) { - echo '% Reading ' . $this->symbolToName[$symbol] . "\n"; +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler\Generator\Node; + +use Prophecy\Doubler\Generator\TypeHintReference; +use Prophecy\Exception\InvalidArgumentException; +/** + * Method node. + * + * @author Konstantin Kudryashov + */ +class MethodNode +{ + private $name; + private $code; + private $visibility = 'public'; + private $static = \false; + private $returnsReference = \false; + /** @var ReturnTypeNode */ + private $returnTypeNode; + /** + * @var ArgumentNode[] + */ + private $arguments = array(); + /** + * @param string $name + * @param string $code + */ + public function __construct($name, $code = null, TypeHintReference $typeHintReference = null) + { + $this->name = $name; + $this->code = $code; + $this->returnTypeNode = new \Prophecy\Doubler\Generator\Node\ReturnTypeNode(); } - - protected function traceShift($symbol) { - echo '% Shift ' . $this->symbolToName[$symbol] . "\n"; + public function getVisibility() + { + return $this->visibility; } - - protected function traceAccept() { - echo "% Accepted.\n"; + /** + * @param string $visibility + */ + public function setVisibility($visibility) + { + $visibility = \strtolower($visibility); + if (!\in_array($visibility, array('public', 'private', 'protected'))) { + throw new InvalidArgumentException(\sprintf('`%s` method visibility is not supported.', $visibility)); + } + $this->visibility = $visibility; } - - protected function traceReduce($n) { - echo '% Reduce by (' . $n . ') ' . $this->productions[$n] . "\n"; + public function isStatic() + { + return $this->static; } - - protected function tracePop($state) { - echo '% Recovering, uncovered state ' . $state . "\n"; + public function setStatic($static = \true) + { + $this->static = (bool) $static; } - - protected function traceDiscard($symbol) { - echo '% Discard ' . $this->symbolToName[$symbol] . "\n"; + public function returnsReference() + { + return $this->returnsReference; } - */ - /* - * Helper functions invoked by semantic actions + public function setReturnsReference() + { + $this->returnsReference = \true; + } + public function getName() + { + return $this->name; + } + public function addArgument(\Prophecy\Doubler\Generator\Node\ArgumentNode $argument) + { + $this->arguments[] = $argument; + } + /** + * @return ArgumentNode[] */ + public function getArguments() + { + return $this->arguments; + } /** - * Moves statements of semicolon-style namespaces into $ns->stmts and checks various error conditions. - * - * @param Node\Stmt[] $stmts - * @return Node\Stmt[] + * @deprecated use getReturnTypeNode instead + * @return bool */ - protected function handleNamespaces(array $stmts) : array + public function hasReturnType() { - $hasErrored = \false; - $style = $this->getNamespacingStyle($stmts); - if (null === $style) { - // not namespaced, nothing to do - return $stmts; - } elseif ('brace' === $style) { - // For braced namespaces we only have to check that there are no invalid statements between the namespaces - $afterFirstNamespace = \false; - foreach ($stmts as $stmt) { - if ($stmt instanceof \PHPUnit\PhpParser\Node\Stmt\Namespace_) { - $afterFirstNamespace = \true; - } elseif (!$stmt instanceof \PHPUnit\PhpParser\Node\Stmt\HaltCompiler && !$stmt instanceof \PHPUnit\PhpParser\Node\Stmt\Nop && $afterFirstNamespace && !$hasErrored) { - $this->emitError(new \PHPUnit\PhpParser\Error('No code may exist outside of namespace {}', $stmt->getAttributes())); - $hasErrored = \true; - // Avoid one error for every statement - } - } - return $stmts; - } else { - // For semicolon namespaces we have to move the statements after a namespace declaration into ->stmts - $resultStmts = []; - $targetStmts =& $resultStmts; - $lastNs = null; - foreach ($stmts as $stmt) { - if ($stmt instanceof \PHPUnit\PhpParser\Node\Stmt\Namespace_) { - if ($lastNs !== null) { - $this->fixupNamespaceAttributes($lastNs); - } - if ($stmt->stmts === null) { - $stmt->stmts = []; - $targetStmts =& $stmt->stmts; - $resultStmts[] = $stmt; - } else { - // This handles the invalid case of mixed style namespaces - $resultStmts[] = $stmt; - $targetStmts =& $resultStmts; - } - $lastNs = $stmt; - } elseif ($stmt instanceof \PHPUnit\PhpParser\Node\Stmt\HaltCompiler) { - // __halt_compiler() is not moved into the namespace - $resultStmts[] = $stmt; - } else { - $targetStmts[] = $stmt; - } - } - if ($lastNs !== null) { - $this->fixupNamespaceAttributes($lastNs); - } - return $resultStmts; - } + return (bool) $this->returnTypeNode->getNonNullTypes(); } - private function fixupNamespaceAttributes(\PHPUnit\PhpParser\Node\Stmt\Namespace_ $stmt) + public function setReturnTypeNode(\Prophecy\Doubler\Generator\Node\ReturnTypeNode $returnTypeNode) : void { - // We moved the statements into the namespace node, as such the end of the namespace node - // needs to be extended to the end of the statements. - if (empty($stmt->stmts)) { - return; - } - // We only move the builtin end attributes here. This is the best we can do with the - // knowledge we have. - $endAttributes = ['endLine', 'endFilePos', 'endTokenPos']; - $lastStmt = $stmt->stmts[\count($stmt->stmts) - 1]; - foreach ($endAttributes as $endAttribute) { - if ($lastStmt->hasAttribute($endAttribute)) { - $stmt->setAttribute($endAttribute, $lastStmt->getAttribute($endAttribute)); - } - } + $this->returnTypeNode = $returnTypeNode; } /** - * Determine namespacing style (semicolon or brace) - * - * @param Node[] $stmts Top-level statements. - * - * @return null|string One of "semicolon", "brace" or null (no namespaces) + * @deprecated use setReturnTypeNode instead + * @param string $type */ - private function getNamespacingStyle(array $stmts) + public function setReturnType($type = null) { - $style = null; - $hasNotAllowedStmts = \false; - foreach ($stmts as $i => $stmt) { - if ($stmt instanceof \PHPUnit\PhpParser\Node\Stmt\Namespace_) { - $currentStyle = null === $stmt->stmts ? 'semicolon' : 'brace'; - if (null === $style) { - $style = $currentStyle; - if ($hasNotAllowedStmts) { - $this->emitError(new \PHPUnit\PhpParser\Error('Namespace declaration statement has to be the very first statement in the script', $stmt->getLine())); - } - } elseif ($style !== $currentStyle) { - $this->emitError(new \PHPUnit\PhpParser\Error('Cannot mix bracketed namespace declarations with unbracketed namespace declarations', $stmt->getLine())); - // Treat like semicolon style for namespace normalization - return 'semicolon'; - } - continue; - } - /* declare(), __halt_compiler() and nops can be used before a namespace declaration */ - if ($stmt instanceof \PHPUnit\PhpParser\Node\Stmt\Declare_ || $stmt instanceof \PHPUnit\PhpParser\Node\Stmt\HaltCompiler || $stmt instanceof \PHPUnit\PhpParser\Node\Stmt\Nop) { - continue; - } - /* There may be a hashbang line at the very start of the file */ - if ($i === 0 && $stmt instanceof \PHPUnit\PhpParser\Node\Stmt\InlineHTML && \preg_match('/\\A#!.*\\r?\\n\\z/', $stmt->value)) { - continue; - } - /* Everything else if forbidden before namespace declarations */ - $hasNotAllowedStmts = \true; - } - return $style; + $this->returnTypeNode = $type === '' || $type === null ? new \Prophecy\Doubler\Generator\Node\ReturnTypeNode() : new \Prophecy\Doubler\Generator\Node\ReturnTypeNode($type); } /** - * Fix up parsing of static property calls in PHP 5. - * - * In PHP 5 A::$b[c][d] and A::$b[c][d]() have very different interpretation. The former is - * interpreted as (A::$b)[c][d], while the latter is the same as A::{$b[c][d]}(). We parse the - * latter as the former initially and this method fixes the AST into the correct form when we - * encounter the "()". - * - * @param Node\Expr\StaticPropertyFetch|Node\Expr\ArrayDimFetch $prop - * @param Node\Arg[] $args - * @param array $attributes - * - * @return Expr\StaticCall + * @deprecated use setReturnTypeNode instead + * @param bool $bool */ - protected function fixupPhp5StaticPropCall($prop, array $args, array $attributes) : \PHPUnit\PhpParser\Node\Expr\StaticCall + public function setNullableReturnType($bool = \true) { - if ($prop instanceof \PHPUnit\PhpParser\Node\Expr\StaticPropertyFetch) { - $name = $prop->name instanceof \PHPUnit\PhpParser\Node\VarLikeIdentifier ? $prop->name->toString() : $prop->name; - $var = new \PHPUnit\PhpParser\Node\Expr\Variable($name, $prop->name->getAttributes()); - return new \PHPUnit\PhpParser\Node\Expr\StaticCall($prop->class, $var, $args, $attributes); - } elseif ($prop instanceof \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch) { - $tmp = $prop; - while ($tmp->var instanceof \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch) { - $tmp = $tmp->var; - } - /** @var Expr\StaticPropertyFetch $staticProp */ - $staticProp = $tmp->var; - // Set start attributes to attributes of innermost node - $tmp = $prop; - $this->fixupStartAttributes($tmp, $staticProp->name); - while ($tmp->var instanceof \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch) { - $tmp = $tmp->var; - $this->fixupStartAttributes($tmp, $staticProp->name); - } - $name = $staticProp->name instanceof \PHPUnit\PhpParser\Node\VarLikeIdentifier ? $staticProp->name->toString() : $staticProp->name; - $tmp->var = new \PHPUnit\PhpParser\Node\Expr\Variable($name, $staticProp->name->getAttributes()); - return new \PHPUnit\PhpParser\Node\Expr\StaticCall($staticProp->class, $prop, $args, $attributes); + if ($bool) { + $this->returnTypeNode = new \Prophecy\Doubler\Generator\Node\ReturnTypeNode('null', ...$this->returnTypeNode->getTypes()); } else { - throw new \Exception(); + $this->returnTypeNode = new \Prophecy\Doubler\Generator\Node\ReturnTypeNode(...$this->returnTypeNode->getNonNullTypes()); } } - protected function fixupStartAttributes(\PHPUnit\PhpParser\Node $to, \PHPUnit\PhpParser\Node $from) + /** + * @deprecated use getReturnTypeNode instead + * @return string|null + */ + public function getReturnType() { - $startAttributes = ['startLine', 'startFilePos', 'startTokenPos']; - foreach ($startAttributes as $startAttribute) { - if ($from->hasAttribute($startAttribute)) { - $to->setAttribute($startAttribute, $from->getAttribute($startAttribute)); - } + if ($types = $this->returnTypeNode->getNonNullTypes()) { + return $types[0]; } + return null; } - protected function handleBuiltinTypes(\PHPUnit\PhpParser\Node\Name $name) + public function getReturnTypeNode() : \Prophecy\Doubler\Generator\Node\ReturnTypeNode { - $builtinTypes = ['bool' => \true, 'int' => \true, 'float' => \true, 'string' => \true, 'iterable' => \true, 'void' => \true, 'object' => \true, 'null' => \true, 'false' => \true, 'mixed' => \true]; - if (!$name->isUnqualified()) { - return $name; + return $this->returnTypeNode; + } + /** + * @deprecated use getReturnTypeNode instead + * @return bool + */ + public function hasNullableReturnType() + { + return $this->returnTypeNode->canUseNullShorthand(); + } + /** + * @param string $code + */ + public function setCode($code) + { + $this->code = $code; + } + public function getCode() + { + if ($this->returnsReference) { + return "throw new \\Prophecy\\Exception\\Doubler\\ReturnByReferenceException('Returning by reference not supported', get_class(\$this), '{$this->name}');"; } - $lowerName = $name->toLowerString(); - if (!isset($builtinTypes[$lowerName])) { - return $name; + return (string) $this->code; + } + public function useParentCode() + { + $this->code = \sprintf('return parent::%s(%s);', $this->getName(), \implode(', ', \array_map(array($this, 'generateArgument'), $this->arguments))); + } + private function generateArgument(\Prophecy\Doubler\Generator\Node\ArgumentNode $arg) + { + $argument = '$' . $arg->getName(); + if ($arg->isVariadic()) { + $argument = '...' . $argument; } - return new \PHPUnit\PhpParser\Node\Identifier($lowerName, $name->getAttributes()); + return $argument; } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler\Generator\Node; + +/** + * Argument node. + * + * @author Konstantin Kudryashov + */ +class ArgumentNode +{ + private $name; + private $default; + private $optional = \false; + private $byReference = \false; + private $isVariadic = \false; + /** @var ArgumentTypeNode */ + private $typeNode; /** - * Get combined start and end attributes at a stack location - * - * @param int $pos Stack location - * - * @return array Combined start and end attributes + * @param string $name */ - protected function getAttributesAt(int $pos) : array + public function __construct($name) { - return $this->startAttributeStack[$pos] + $this->endAttributeStack[$pos]; + $this->name = $name; + $this->typeNode = new \Prophecy\Doubler\Generator\Node\ArgumentTypeNode(); } - protected function getFloatCastKind(string $cast) : int + public function getName() + { + return $this->name; + } + public function setTypeNode(\Prophecy\Doubler\Generator\Node\ArgumentTypeNode $typeNode) + { + $this->typeNode = $typeNode; + } + public function getTypeNode() : \Prophecy\Doubler\Generator\Node\ArgumentTypeNode + { + return $this->typeNode; + } + public function hasDefault() + { + return $this->isOptional() && !$this->isVariadic(); + } + public function getDefault() + { + return $this->default; + } + public function setDefault($default = null) + { + $this->optional = \true; + $this->default = $default; + } + public function isOptional() + { + return $this->optional; + } + public function setAsPassedByReference($byReference = \true) + { + $this->byReference = $byReference; + } + public function isPassedByReference() + { + return $this->byReference; + } + public function setAsVariadic($isVariadic = \true) + { + $this->isVariadic = $isVariadic; + } + public function isVariadic() + { + return $this->isVariadic; + } + /** + * @deprecated use getArgumentTypeNode instead + * @return string|null + */ + public function getTypeHint() + { + $type = $this->typeNode->getNonNullTypes() ? $this->typeNode->getNonNullTypes()[0] : null; + return $type ? \ltrim($type, '\\') : null; + } + /** + * @deprecated use setArgumentTypeNode instead + * @param string|null $typeHint + */ + public function setTypeHint($typeHint = null) + { + $this->typeNode = $typeHint === null ? new \Prophecy\Doubler\Generator\Node\ArgumentTypeNode() : new \Prophecy\Doubler\Generator\Node\ArgumentTypeNode($typeHint); + } + /** + * @deprecated use getArgumentTypeNode instead + * @return bool + */ + public function isNullable() { - $cast = \strtolower($cast); - if (\strpos($cast, 'float') !== \false) { - return \PHPUnit\PhpParser\Node\Expr\Cast\Double::KIND_FLOAT; - } - if (\strpos($cast, 'real') !== \false) { - return \PHPUnit\PhpParser\Node\Expr\Cast\Double::KIND_REAL; - } - return \PHPUnit\PhpParser\Node\Expr\Cast\Double::KIND_DOUBLE; + return $this->typeNode->canUseNullShorthand(); } - protected function parseLNumber($str, $attributes, $allowInvalidOctal = \false) + /** + * @deprecated use getArgumentTypeNode instead + * @param bool $isNullable + */ + public function setAsNullable($isNullable = \true) { - try { - return \PHPUnit\PhpParser\Node\Scalar\LNumber::fromString($str, $attributes, $allowInvalidOctal); - } catch (\PHPUnit\PhpParser\Error $error) { - $this->emitError($error); - // Use dummy value - return new \PHPUnit\PhpParser\Node\Scalar\LNumber(0, $attributes); - } + $nonNullTypes = $this->typeNode->getNonNullTypes(); + $this->typeNode = $isNullable ? new \Prophecy\Doubler\Generator\Node\ArgumentTypeNode('null', ...$nonNullTypes) : new \Prophecy\Doubler\Generator\Node\ArgumentTypeNode(...$nonNullTypes); } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler\Generator; + +use Prophecy\Doubler\Generator\Node\ArgumentTypeNode; +use Prophecy\Doubler\Generator\Node\ReturnTypeNode; +use Prophecy\Exception\InvalidArgumentException; +use Prophecy\Exception\Doubler\ClassMirrorException; +use ReflectionClass; +use PHPUnit\ReflectionIntersectionType; +use ReflectionMethod; +use ReflectionNamedType; +use ReflectionParameter; +use ReflectionType; +use ReflectionUnionType; +/** + * Class mirror. + * Core doubler class. Mirrors specific class and/or interfaces into class node tree. + * + * @author Konstantin Kudryashov + */ +class ClassMirror +{ + private static $reflectableMethods = array('__construct', '__destruct', '__sleep', '__wakeup', '__toString', '__call', '__invoke'); /** - * Parse a T_NUM_STRING token into either an integer or string node. + * Reflects provided arguments into class node. * - * @param string $str Number string - * @param array $attributes Attributes + * @param ReflectionClass|null $class + * @param ReflectionClass[] $interfaces + * + * @return Node\ClassNode * - * @return LNumber|String_ Integer or string node. */ - protected function parseNumString(string $str, array $attributes) - { - if (!\preg_match('/^(?:0|-?[1-9][0-9]*)$/', $str)) { - return new \PHPUnit\PhpParser\Node\Scalar\String_($str, $attributes); - } - $num = +$str; - if (!\is_int($num)) { - return new \PHPUnit\PhpParser\Node\Scalar\String_($str, $attributes); - } - return new \PHPUnit\PhpParser\Node\Scalar\LNumber($num, $attributes); - } - protected function stripIndentation(string $string, int $indentLen, string $indentChar, bool $newlineAtStart, bool $newlineAtEnd, array $attributes) + public function reflect(?ReflectionClass $class, array $interfaces) { - if ($indentLen === 0) { - return $string; - } - $start = $newlineAtStart ? '(?:(?<=\\n)|\\A)' : '(?<=\\n)'; - $end = $newlineAtEnd ? '(?:(?=[\\r\\n])|\\z)' : '(?=[\\r\\n])'; - $regex = '/' . $start . '([ \\t]*)(' . $end . ')?/'; - return \preg_replace_callback($regex, function ($matches) use($indentLen, $indentChar, $attributes) { - $prefix = \substr($matches[1], 0, $indentLen); - if (\false !== \strpos($prefix, $indentChar === " " ? "\t" : " ")) { - $this->emitError(new \PHPUnit\PhpParser\Error('Invalid indentation - tabs and spaces cannot be mixed', $attributes)); - } elseif (\strlen($prefix) < $indentLen && !isset($matches[2])) { - $this->emitError(new \PHPUnit\PhpParser\Error('Invalid body indentation level ' . '(expecting an indentation level of at least ' . $indentLen . ')', $attributes)); + $node = new \Prophecy\Doubler\Generator\Node\ClassNode(); + if (null !== $class) { + if (\true === $class->isInterface()) { + throw new InvalidArgumentException(\sprintf("Could not reflect %s as a class, because it\n" . "is interface - use the second argument instead.", $class->getName())); } - return \substr($matches[0], \strlen($prefix)); - }, $string); - } - protected function parseDocString(string $startToken, $contents, string $endToken, array $attributes, array $endTokenAttributes, bool $parseUnicodeEscape) - { - $kind = \strpos($startToken, "'") === \false ? \PHPUnit\PhpParser\Node\Scalar\String_::KIND_HEREDOC : \PHPUnit\PhpParser\Node\Scalar\String_::KIND_NOWDOC; - $regex = '/\\A[bB]?<<<[ \\t]*[\'"]?([a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*)[\'"]?(?:\\r\\n|\\n|\\r)\\z/'; - $result = \preg_match($regex, $startToken, $matches); - \assert($result === 1); - $label = $matches[1]; - $result = \preg_match('/\\A[ \\t]*/', $endToken, $matches); - \assert($result === 1); - $indentation = $matches[0]; - $attributes['kind'] = $kind; - $attributes['docLabel'] = $label; - $attributes['docIndentation'] = $indentation; - $indentHasSpaces = \false !== \strpos($indentation, " "); - $indentHasTabs = \false !== \strpos($indentation, "\t"); - if ($indentHasSpaces && $indentHasTabs) { - $this->emitError(new \PHPUnit\PhpParser\Error('Invalid indentation - tabs and spaces cannot be mixed', $endTokenAttributes)); - // Proceed processing as if this doc string is not indented - $indentation = ''; + $this->reflectClassToNode($class, $node); } - $indentLen = \strlen($indentation); - $indentChar = $indentHasSpaces ? " " : "\t"; - if (\is_string($contents)) { - if ($contents === '') { - return new \PHPUnit\PhpParser\Node\Scalar\String_('', $attributes); - } - $contents = $this->stripIndentation($contents, $indentLen, $indentChar, \true, \true, $attributes); - $contents = \preg_replace('~(\\r\\n|\\n|\\r)\\z~', '', $contents); - if ($kind === \PHPUnit\PhpParser\Node\Scalar\String_::KIND_HEREDOC) { - $contents = \PHPUnit\PhpParser\Node\Scalar\String_::parseEscapeSequences($contents, null, $parseUnicodeEscape); - } - return new \PHPUnit\PhpParser\Node\Scalar\String_($contents, $attributes); - } else { - \assert(\count($contents) > 0); - if (!$contents[0] instanceof \PHPUnit\PhpParser\Node\Scalar\EncapsedStringPart) { - // If there is no leading encapsed string part, pretend there is an empty one - $this->stripIndentation('', $indentLen, $indentChar, \true, \false, $contents[0]->getAttributes()); + foreach ($interfaces as $interface) { + if (!$interface instanceof ReflectionClass) { + throw new InvalidArgumentException(\sprintf("[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n" . "a second argument to `ClassMirror::reflect(...)`, but got %s.", \is_object($interface) ? \get_class($interface) . ' class' : \gettype($interface))); } - $newContents = []; - foreach ($contents as $i => $part) { - if ($part instanceof \PHPUnit\PhpParser\Node\Scalar\EncapsedStringPart) { - $isLast = $i === \count($contents) - 1; - $part->value = $this->stripIndentation($part->value, $indentLen, $indentChar, $i === 0, $isLast, $part->getAttributes()); - $part->value = \PHPUnit\PhpParser\Node\Scalar\String_::parseEscapeSequences($part->value, null, $parseUnicodeEscape); - if ($isLast) { - $part->value = \preg_replace('~(\\r\\n|\\n|\\r)\\z~', '', $part->value); - } - if ('' === $part->value) { - continue; - } - } - $newContents[] = $part; + if (\false === $interface->isInterface()) { + throw new InvalidArgumentException(\sprintf("Could not reflect %s as an interface, because it\n" . "is class - use the first argument instead.", $interface->getName())); } - return new \PHPUnit\PhpParser\Node\Scalar\Encapsed($newContents, $attributes); + $this->reflectInterfaceToNode($interface, $node); } + $node->addInterface('Prophecy\\Doubler\\Generator\\ReflectionInterface'); + return $node; } - /** - * Create attributes for a zero-length common-capturing nop. - * - * @param Comment[] $comments - * @return array - */ - protected function createCommentNopAttributes(array $comments) + private function reflectClassToNode(ReflectionClass $class, \Prophecy\Doubler\Generator\Node\ClassNode $node) { - $comment = $comments[\count($comments) - 1]; - $commentEndLine = $comment->getEndLine(); - $commentEndFilePos = $comment->getEndFilePos(); - $commentEndTokenPos = $comment->getEndTokenPos(); - $attributes = ['comments' => $comments]; - if (-1 !== $commentEndLine) { - $attributes['startLine'] = $commentEndLine; - $attributes['endLine'] = $commentEndLine; - } - if (-1 !== $commentEndFilePos) { - $attributes['startFilePos'] = $commentEndFilePos + 1; - $attributes['endFilePos'] = $commentEndFilePos; + if (\true === $class->isFinal()) { + throw new ClassMirrorException(\sprintf('Could not reflect class %s as it is marked final.', $class->getName()), $class); } - if (-1 !== $commentEndTokenPos) { - $attributes['startTokenPos'] = $commentEndTokenPos + 1; - $attributes['endTokenPos'] = $commentEndTokenPos; + $node->setParentClass($class->getName()); + foreach ($class->getMethods(ReflectionMethod::IS_ABSTRACT) as $method) { + if (\false === $method->isProtected()) { + continue; + } + $this->reflectMethodToNode($method, $node); } - return $attributes; - } - protected function checkModifier($a, $b, $modifierPos) - { - // Jumping through some hoops here because verifyModifier() is also used elsewhere - try { - \PHPUnit\PhpParser\Node\Stmt\Class_::verifyModifier($a, $b); - } catch (\PHPUnit\PhpParser\Error $error) { - $error->setAttributes($this->getAttributesAt($modifierPos)); - $this->emitError($error); + foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { + if (0 === \strpos($method->getName(), '_') && !\in_array($method->getName(), self::$reflectableMethods)) { + continue; + } + if (\true === $method->isFinal()) { + $node->addUnextendableMethod($method->getName()); + continue; + } + $this->reflectMethodToNode($method, $node); } } - protected function checkParam(\PHPUnit\PhpParser\Node\Param $node) + private function reflectInterfaceToNode(ReflectionClass $interface, \Prophecy\Doubler\Generator\Node\ClassNode $node) { - if ($node->variadic && null !== $node->default) { - $this->emitError(new \PHPUnit\PhpParser\Error('Variadic parameter cannot have a default value', $node->default->getAttributes())); + $node->addInterface($interface->getName()); + foreach ($interface->getMethods() as $method) { + $this->reflectMethodToNode($method, $node); } } - protected function checkTryCatch(\PHPUnit\PhpParser\Node\Stmt\TryCatch $node) + private function reflectMethodToNode(ReflectionMethod $method, \Prophecy\Doubler\Generator\Node\ClassNode $classNode) { - if (empty($node->catches) && null === $node->finally) { - $this->emitError(new \PHPUnit\PhpParser\Error('Cannot use try without catch or finally', $node->getAttributes())); + $node = new \Prophecy\Doubler\Generator\Node\MethodNode($method->getName()); + if (\true === $method->isProtected()) { + $node->setVisibility('protected'); } - } - protected function checkNamespace(\PHPUnit\PhpParser\Node\Stmt\Namespace_ $node) - { - if (null !== $node->stmts) { - foreach ($node->stmts as $stmt) { - if ($stmt instanceof \PHPUnit\PhpParser\Node\Stmt\Namespace_) { - $this->emitError(new \PHPUnit\PhpParser\Error('Namespace declarations cannot be nested', $stmt->getAttributes())); - } - } + if (\true === $method->isStatic()) { + $node->setStatic(); } - } - protected function checkClass(\PHPUnit\PhpParser\Node\Stmt\Class_ $node, $namePos) - { - if (null !== $node->name && $node->name->isSpecialClassName()) { - $this->emitError(new \PHPUnit\PhpParser\Error(\sprintf('Cannot use \'%s\' as class name as it is reserved', $node->name), $this->getAttributesAt($namePos))); + if (\true === $method->returnsReference()) { + $node->setReturnsReference(); } - if ($node->extends && $node->extends->isSpecialClassName()) { - $this->emitError(new \PHPUnit\PhpParser\Error(\sprintf('Cannot use \'%s\' as class name as it is reserved', $node->extends), $node->extends->getAttributes())); + if ($method->hasReturnType()) { + $returnTypes = $this->getTypeHints($method->getReturnType(), $method->getDeclaringClass(), $method->getReturnType()->allowsNull()); + $node->setReturnTypeNode(new ReturnTypeNode(...$returnTypes)); + } elseif (\method_exists($method, 'hasTentativeReturnType') && $method->hasTentativeReturnType()) { + $returnTypes = $this->getTypeHints($method->getTentativeReturnType(), $method->getDeclaringClass(), $method->getTentativeReturnType()->allowsNull()); + $node->setReturnTypeNode(new ReturnTypeNode(...$returnTypes)); } - foreach ($node->implements as $interface) { - if ($interface->isSpecialClassName()) { - $this->emitError(new \PHPUnit\PhpParser\Error(\sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface), $interface->getAttributes())); + if (\is_array($params = $method->getParameters()) && \count($params)) { + foreach ($params as $param) { + $this->reflectArgumentToNode($param, $node); } } + $classNode->addMethod($node); } - protected function checkInterface(\PHPUnit\PhpParser\Node\Stmt\Interface_ $node, $namePos) + private function reflectArgumentToNode(ReflectionParameter $parameter, \Prophecy\Doubler\Generator\Node\MethodNode $methodNode) { - if (null !== $node->name && $node->name->isSpecialClassName()) { - $this->emitError(new \PHPUnit\PhpParser\Error(\sprintf('Cannot use \'%s\' as class name as it is reserved', $node->name), $this->getAttributesAt($namePos))); + $name = $parameter->getName() == '...' ? '__dot_dot_dot__' : $parameter->getName(); + $node = new \Prophecy\Doubler\Generator\Node\ArgumentNode($name); + $typeHints = $this->getTypeHints($parameter->getType(), $parameter->getDeclaringClass(), $parameter->allowsNull()); + $node->setTypeNode(new ArgumentTypeNode(...$typeHints)); + if ($parameter->isVariadic()) { + $node->setAsVariadic(); } - foreach ($node->extends as $interface) { - if ($interface->isSpecialClassName()) { - $this->emitError(new \PHPUnit\PhpParser\Error(\sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface), $interface->getAttributes())); - } + if ($this->hasDefaultValue($parameter)) { + $node->setDefault($this->getDefaultValue($parameter)); } - } - protected function checkClassMethod(\PHPUnit\PhpParser\Node\Stmt\ClassMethod $node, $modifierPos) - { - if ($node->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_STATIC) { - switch ($node->name->toLowerString()) { - case '__construct': - $this->emitError(new \PHPUnit\PhpParser\Error(\sprintf('Constructor %s() cannot be static', $node->name), $this->getAttributesAt($modifierPos))); - break; - case '__destruct': - $this->emitError(new \PHPUnit\PhpParser\Error(\sprintf('Destructor %s() cannot be static', $node->name), $this->getAttributesAt($modifierPos))); - break; - case '__clone': - $this->emitError(new \PHPUnit\PhpParser\Error(\sprintf('Clone method %s() cannot be static', $node->name), $this->getAttributesAt($modifierPos))); - break; - } + if ($parameter->isPassedByReference()) { + $node->setAsPassedByReference(); } + $methodNode->addArgument($node); } - protected function checkClassConst(\PHPUnit\PhpParser\Node\Stmt\ClassConst $node, $modifierPos) + private function hasDefaultValue(ReflectionParameter $parameter) { - if ($node->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_STATIC) { - $this->emitError(new \PHPUnit\PhpParser\Error("Cannot use 'static' as constant modifier", $this->getAttributesAt($modifierPos))); - } - if ($node->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_ABSTRACT) { - $this->emitError(new \PHPUnit\PhpParser\Error("Cannot use 'abstract' as constant modifier", $this->getAttributesAt($modifierPos))); + if ($parameter->isVariadic()) { + return \false; } - if ($node->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_FINAL) { - $this->emitError(new \PHPUnit\PhpParser\Error("Cannot use 'final' as constant modifier", $this->getAttributesAt($modifierPos))); + if ($parameter->isDefaultValueAvailable()) { + return \true; } + return $parameter->isOptional() || $parameter->allowsNull() && $parameter->getType() && \PHP_VERSION_ID < 80100; } - protected function checkProperty(\PHPUnit\PhpParser\Node\Stmt\Property $node, $modifierPos) + private function getDefaultValue(ReflectionParameter $parameter) { - if ($node->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_ABSTRACT) { - $this->emitError(new \PHPUnit\PhpParser\Error('Properties cannot be declared abstract', $this->getAttributesAt($modifierPos))); - } - if ($node->flags & \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_FINAL) { - $this->emitError(new \PHPUnit\PhpParser\Error('Properties cannot be declared final', $this->getAttributesAt($modifierPos))); + if (!$parameter->isDefaultValueAvailable()) { + return null; } + return $parameter->getDefaultValue(); } - protected function checkUseUse(\PHPUnit\PhpParser\Node\Stmt\UseUse $node, $namePos) + private function getTypeHints(?ReflectionType $type, ?ReflectionClass $class, bool $allowsNull) : array { - if ($node->alias && $node->alias->isSpecialClassName()) { - $this->emitError(new \PHPUnit\PhpParser\Error(\sprintf('Cannot use %s as %s because \'%2$s\' is a special class name', $node->name, $node->alias), $this->getAttributesAt($namePos))); + $types = []; + if ($type instanceof ReflectionNamedType) { + $types = [$type->getName()]; + } elseif ($type instanceof ReflectionUnionType) { + $types = $type->getTypes(); + } elseif ($type instanceof ReflectionIntersectionType) { + throw new ClassMirrorException('Doubling intersection types is not supported', $class); + } elseif (\is_object($type)) { + throw new ClassMirrorException('Unknown reflection type ' . \get_class($type), $class); } + $types = \array_map(function (string $type) use($class) { + if ($type === 'self') { + return $class->getName(); + } + if ($type === 'parent') { + return $class->getParentClass()->getName(); + } + return $type; + }, $types); + if ($types && $types != ['mixed'] && $allowsNull) { + $types[] = 'null'; + } + return $types; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler\Generator; -class ParserFactory +/** + * Reflection interface. + * All reflected classes implement this interface. + * + * @author Konstantin Kudryashov + */ +interface ReflectionInterface { - const PREFER_PHP7 = 1; - const PREFER_PHP5 = 2; - const ONLY_PHP7 = 3; - const ONLY_PHP5 = 4; - /** - * Creates a Parser instance, according to the provided kind. - * - * @param int $kind One of ::PREFER_PHP7, ::PREFER_PHP5, ::ONLY_PHP7 or ::ONLY_PHP5 - * @param Lexer|null $lexer Lexer to use. Defaults to emulative lexer when not specified - * @param array $parserOptions Parser options. See ParserAbstract::__construct() argument - * - * @return Parser The parser instance - */ - public function create(int $kind, \PHPUnit\PhpParser\Lexer $lexer = null, array $parserOptions = []) : \PHPUnit\PhpParser\Parser - { - if (null === $lexer) { - $lexer = new \PHPUnit\PhpParser\Lexer\Emulative(); - } - switch ($kind) { - case self::PREFER_PHP7: - return new \PHPUnit\PhpParser\Parser\Multiple([new \PHPUnit\PhpParser\Parser\Php7($lexer, $parserOptions), new \PHPUnit\PhpParser\Parser\Php5($lexer, $parserOptions)]); - case self::PREFER_PHP5: - return new \PHPUnit\PhpParser\Parser\Multiple([new \PHPUnit\PhpParser\Parser\Php5($lexer, $parserOptions), new \PHPUnit\PhpParser\Parser\Php7($lexer, $parserOptions)]); - case self::ONLY_PHP7: - return new \PHPUnit\PhpParser\Parser\Php7($lexer, $parserOptions); - case self::ONLY_PHP5: - return new \PHPUnit\PhpParser\Parser\Php5($lexer, $parserOptions); - default: - throw new \LogicException('Kind must be one of ::PREFER_PHP7, ::PREFER_PHP5, ::ONLY_PHP7 or ::ONLY_PHP5'); - } - } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler; -interface NodeVisitor +use PHPUnit\Doctrine\Instantiator\Instantiator; +use Prophecy\Doubler\ClassPatch\ClassPatchInterface; +use Prophecy\Doubler\Generator\ClassMirror; +use Prophecy\Doubler\Generator\ClassCreator; +use Prophecy\Exception\InvalidArgumentException; +use ReflectionClass; +/** + * Cached class doubler. + * Prevents mirroring/creation of the same structure twice. + * + * @author Konstantin Kudryashov + */ +class Doubler { + private $mirror; + private $creator; + private $namer; /** - * Called once before traversal. - * - * Return value semantics: - * * null: $nodes stays as-is - * * otherwise: $nodes is set to the return value - * - * @param Node[] $nodes Array of nodes - * - * @return null|Node[] Array of nodes + * @var ClassPatchInterface[] */ - public function beforeTraverse(array $nodes); + private $patches = array(); /** - * Called when entering a node. - * - * Return value semantics: - * * null - * => $node stays as-is - * * NodeTraverser::DONT_TRAVERSE_CHILDREN - * => Children of $node are not traversed. $node stays as-is - * * NodeTraverser::STOP_TRAVERSAL - * => Traversal is aborted. $node stays as-is - * * otherwise - * => $node is set to the return value - * - * @param Node $node Node - * - * @return null|int|Node Replacement node (or special return value) + * @var \Doctrine\Instantiator\Instantiator */ - public function enterNode(\PHPUnit\PhpParser\Node $node); + private $instantiator; /** - * Called when leaving a node. + * Initializes doubler. * - * Return value semantics: - * * null - * => $node stays as-is - * * NodeTraverser::REMOVE_NODE - * => $node is removed from the parent array - * * NodeTraverser::STOP_TRAVERSAL - * => Traversal is aborted. $node stays as-is - * * array (of Nodes) - * => The return value is merged into the parent array (at the position of the $node) - * * otherwise - * => $node is set to the return value + * @param ClassMirror $mirror + * @param ClassCreator $creator + * @param NameGenerator $namer + */ + public function __construct(ClassMirror $mirror = null, ClassCreator $creator = null, \Prophecy\Doubler\NameGenerator $namer = null) + { + $this->mirror = $mirror ?: new ClassMirror(); + $this->creator = $creator ?: new ClassCreator(); + $this->namer = $namer ?: new \Prophecy\Doubler\NameGenerator(); + } + /** + * Returns list of registered class patches. * - * @param Node $node Node + * @return ClassPatchInterface[] + */ + public function getClassPatches() + { + return $this->patches; + } + /** + * Registers new class patch. * - * @return null|int|Node|Node[] Replacement node (or special return value) + * @param ClassPatchInterface $patch */ - public function leaveNode(\PHPUnit\PhpParser\Node $node); + public function registerClassPatch(ClassPatchInterface $patch) + { + $this->patches[] = $patch; + @\usort($this->patches, function (ClassPatchInterface $patch1, ClassPatchInterface $patch2) { + return $patch2->getPriority() - $patch1->getPriority(); + }); + } /** - * Called once after traversal. + * Creates double from specific class or/and list of interfaces. * - * Return value semantics: - * * null: $nodes stays as-is - * * otherwise: $nodes is set to the return value + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces Array of ReflectionClass instances + * @param array $args Constructor arguments * - * @param Node[] $nodes Array of nodes + * @return DoubleInterface * - * @return null|Node[] Array of nodes + * @throws \Prophecy\Exception\InvalidArgumentException */ - public function afterTraverse(array $nodes); -} -createDoubleClass($class, $interfaces); + $reflection = new ReflectionClass($classname); + if (null !== $args) { + return $reflection->newInstanceArgs($args); + } + if (null === ($constructor = $reflection->getConstructor()) || $constructor->isPublic() && !$constructor->isFinal()) { + return $reflection->newInstance(); + } + if (!$this->instantiator) { + $this->instantiator = new Instantiator(); + } + return $this->instantiator->instantiate($classname); + } /** - * Handle an error generated during lexing, parsing or some other operation. + * Creates double class and returns its FQN. * - * @param Error $error The error that needs to be handled + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces + * + * @return string */ - public function handleError(\PHPUnit\PhpParser\Error $error); + protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) + { + $name = $this->namer->name($class, $interfaces); + $node = $this->mirror->reflect($class, $interfaces); + foreach ($this->patches as $patch) { + if ($patch->supports($node)) { + $patch->apply($node); + } + } + $this->creator->create($name, $node); + return $name; + } } attributes = $attributes; + return $this->implementsAThrowableInterface($node) && $this->doesNotExtendAThrowableClass($node); } /** - * Gets line the node started in (alias of getStartLine). - * - * @return int Start line (or -1 if not available) + * @param ClassNode $node + * @return bool */ - public function getLine() : int + private function implementsAThrowableInterface(ClassNode $node) { - return $this->attributes['startLine'] ?? -1; + foreach ($node->getInterfaces() as $type) { + if (\is_a($type, 'Throwable', \true)) { + return \true; + } + } + return \false; } /** - * Gets line the node started in. - * - * Requires the 'startLine' attribute to be enabled in the lexer (enabled by default). - * - * @return int Start line (or -1 if not available) + * @param ClassNode $node + * @return bool */ - public function getStartLine() : int + private function doesNotExtendAThrowableClass(ClassNode $node) { - return $this->attributes['startLine'] ?? -1; + return !\is_a($node->getParentClass(), 'Throwable', \true); } /** - * Gets the line the node ended in. + * Applies patch to the specific class node. * - * Requires the 'endLine' attribute to be enabled in the lexer (enabled by default). + * @param ClassNode $node * - * @return int End line (or -1 if not available) + * @return void */ - public function getEndLine() : int + public function apply(ClassNode $node) { - return $this->attributes['endLine'] ?? -1; + $this->checkItCanBeDoubled($node); + $this->setParentClassToException($node); } - /** - * Gets the token offset of the first token that is part of this node. - * - * The offset is an index into the array returned by Lexer::getTokens(). - * - * Requires the 'startTokenPos' attribute to be enabled in the lexer (DISABLED by default). - * - * @return int Token start position (or -1 if not available) - */ - public function getStartTokenPos() : int + private function checkItCanBeDoubled(ClassNode $node) { - return $this->attributes['startTokenPos'] ?? -1; + $className = $node->getParentClass(); + if ($className !== 'stdClass') { + throw new ClassCreatorException(\sprintf('Cannot double concrete class %s as well as implement Traversable', $className), $node); + } + } + private function setParentClassToException(ClassNode $node) + { + $node->setParentClass('Exception'); + $node->removeMethod('getMessage'); + $node->removeMethod('getCode'); + $node->removeMethod('getFile'); + $node->removeMethod('getLine'); + $node->removeMethod('getTrace'); + $node->removeMethod('getPrevious'); + $node->removeMethod('getNext'); + $node->removeMethod('getTraceAsString'); } /** - * Gets the token offset of the last token that is part of this node. - * - * The offset is an index into the array returned by Lexer::getTokens(). - * - * Requires the 'endTokenPos' attribute to be enabled in the lexer (DISABLED by default). + * Returns patch priority, which determines when patch will be applied. * - * @return int Token end position (or -1 if not available) + * @return int Priority number (higher - earlier) */ - public function getEndTokenPos() : int + public function getPriority() { - return $this->attributes['endTokenPos'] ?? -1; + return 100; } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; +/** + * Disable constructor. + * Makes all constructor arguments optional. + * + * @author Konstantin Kudryashov + */ +class DisableConstructorPatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface +{ /** - * Gets the file offset of the first character that is part of this node. + * Checks if class has `__construct` method. * - * Requires the 'startFilePos' attribute to be enabled in the lexer (DISABLED by default). + * @param ClassNode $node * - * @return int File start position (or -1 if not available) + * @return bool */ - public function getStartFilePos() : int + public function supports(ClassNode $node) { - return $this->attributes['startFilePos'] ?? -1; + return \true; } /** - * Gets the file offset of the last character that is part of this node. - * - * Requires the 'endFilePos' attribute to be enabled in the lexer (DISABLED by default). + * Makes all class constructor arguments optional. * - * @return int File end position (or -1 if not available) + * @param ClassNode $node */ - public function getEndFilePos() : int + public function apply(ClassNode $node) { - return $this->attributes['endFilePos'] ?? -1; + if (!$node->isExtendable('__construct')) { + return; + } + if (!$node->hasMethod('__construct')) { + $node->addMethod(new MethodNode('__construct', '')); + return; + } + $constructor = $node->getMethod('__construct'); + foreach ($constructor->getArguments() as $argument) { + $argument->setDefault(null); + } + $constructor->setCode(<<attributes['comments'] ?? []; + return 100; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ArgumentNode; +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; +use Prophecy\PhpDocumentor\ClassAndInterfaceTagRetriever; +use Prophecy\PhpDocumentor\MethodTagRetrieverInterface; +/** + * Discover Magical API using "@method" PHPDoc format. + * + * @author Thomas Tourlourat + * @author Kévin Dunglas + * @author Théo FIDRY + */ +class MagicCallPatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface +{ + const MAGIC_METHODS_WITH_ARGUMENTS = ['__call', '__callStatic', '__get', '__isset', '__set', '__set_state', '__unserialize', '__unset']; + private $tagRetriever; + public function __construct(MethodTagRetrieverInterface $tagRetriever = null) + { + $this->tagRetriever = null === $tagRetriever ? new ClassAndInterfaceTagRetriever() : $tagRetriever; } /** - * Gets the doc comment of the node. + * Support any class * - * @return null|Comment\Doc Doc comment object or null + * @param ClassNode $node + * + * @return boolean */ - public function getDocComment() + public function supports(ClassNode $node) { - $comments = $this->getComments(); - for ($i = \count($comments) - 1; $i >= 0; $i--) { - $comment = $comments[$i]; - if ($comment instanceof \PHPUnit\PhpParser\Comment\Doc) { - return $comment; - } - } - return null; + return \true; } /** - * Sets the doc comment of the node. - * - * This will either replace an existing doc comment or add it to the comments array. + * Discover Magical API * - * @param Comment\Doc $docComment Doc comment to set + * @param ClassNode $node */ - public function setDocComment(\PHPUnit\PhpParser\Comment\Doc $docComment) + public function apply(ClassNode $node) { - $comments = $this->getComments(); - for ($i = \count($comments) - 1; $i >= 0; $i--) { - if ($comments[$i] instanceof \PHPUnit\PhpParser\Comment\Doc) { - // Replace existing doc comment. - $comments[$i] = $docComment; - $this->setAttribute('comments', $comments); - return; + $types = \array_filter($node->getInterfaces(), function ($interface) { + return 0 !== \strpos($interface, 'Prophecy\\'); + }); + $types[] = $node->getParentClass(); + foreach ($types as $type) { + $reflectionClass = new \ReflectionClass($type); + while ($reflectionClass) { + $tagList = $this->tagRetriever->getTagList($reflectionClass); + foreach ($tagList as $tag) { + $methodName = $tag->getMethodName(); + if (empty($methodName)) { + continue; + } + if (!$reflectionClass->hasMethod($methodName)) { + $methodNode = new MethodNode($methodName); + // only magic methods can have a contract that needs to be enforced + if (\in_array($methodName, self::MAGIC_METHODS_WITH_ARGUMENTS)) { + foreach ($tag->getArguments() as $argument) { + $argumentNode = new ArgumentNode($argument['name']); + $methodNode->addArgument($argumentNode); + } + } + $methodNode->setStatic($tag->isStatic()); + $node->addMethod($methodNode); + } + } + $reflectionClass = $reflectionClass->getParentClass(); } } - // Append new doc comment. - $comments[] = $docComment; - $this->setAttribute('comments', $comments); - } - public function setAttribute(string $key, $value) - { - $this->attributes[$key] = $value; - } - public function hasAttribute(string $key) : bool - { - return \array_key_exists($key, $this->attributes); - } - public function getAttribute(string $key, $default = null) - { - if (\array_key_exists($key, $this->attributes)) { - return $this->attributes[$key]; - } - return $default; - } - public function getAttributes() : array - { - return $this->attributes; - } - public function setAttributes(array $attributes) - { - $this->attributes = $attributes; } /** - * @return array + * Returns patch priority, which determines when patch will be applied. + * + * @return integer Priority number (higher - earlier) */ - public function jsonSerialize() : array + public function getPriority() { - return ['nodeType' => $this->getType()] + \get_object_vars($this); + return 50; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler\ClassPatch; -class NodeTraverser implements \PHPUnit\PhpParser\NodeTraverserInterface +use Prophecy\Doubler\Generator\Node\ClassNode; +/** + * Class patch interface. + * Class patches extend doubles functionality or help + * Prophecy to avoid some internal PHP bugs. + * + * @author Konstantin Kudryashov + */ +interface ClassPatchInterface { /** - * If NodeVisitor::enterNode() returns DONT_TRAVERSE_CHILDREN, child nodes - * of the current node will not be traversed for any visitors. + * Checks if patch supports specific class node. * - * For subsequent visitors enterNode() will still be called on the current - * node and leaveNode() will also be invoked for the current node. - */ - const DONT_TRAVERSE_CHILDREN = 1; - /** - * If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns - * STOP_TRAVERSAL, traversal is aborted. + * @param ClassNode $node * - * The afterTraverse() method will still be invoked. + * @return bool */ - const STOP_TRAVERSAL = 2; + public function supports(ClassNode $node); /** - * If NodeVisitor::leaveNode() returns REMOVE_NODE for a node that occurs - * in an array, it will be removed from the array. + * Applies patch to the specific class node. * - * For subsequent visitors leaveNode() will still be invoked for the - * removed node. + * @param ClassNode $node + * @return void */ - const REMOVE_NODE = 3; + public function apply(ClassNode $node); /** - * If NodeVisitor::enterNode() returns DONT_TRAVERSE_CURRENT_AND_CHILDREN, child nodes - * of the current node will not be traversed for any visitors. + * Returns patch priority, which determines when patch will be applied. * - * For subsequent visitors enterNode() will not be called as well. - * leaveNode() will be invoked for visitors that has enterNode() method invoked. + * @return int Priority number (higher - earlier) */ - const DONT_TRAVERSE_CURRENT_AND_CHILDREN = 4; - /** @var NodeVisitor[] Visitors */ - protected $visitors = []; - /** @var bool Whether traversal should be stopped */ - protected $stopTraversal; - public function __construct() - { - // for BC - } + public function getPriority(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ArgumentTypeNode; +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; +use Prophecy\Doubler\Generator\Node\ArgumentNode; +use Prophecy\Doubler\Generator\Node\ReturnTypeNode; +/** + * Add Prophecy functionality to the double. + * This is a core class patch for Prophecy. + * + * @author Konstantin Kudryashov + */ +class ProphecySubjectPatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface +{ /** - * Adds a visitor. + * Always returns true. * - * @param NodeVisitor $visitor Visitor to add + * @param ClassNode $node + * + * @return bool */ - public function addVisitor(\PHPUnit\PhpParser\NodeVisitor $visitor) + public function supports(ClassNode $node) { - $this->visitors[] = $visitor; + return \true; } /** - * Removes an added visitor. + * Apply Prophecy functionality to class node. * - * @param NodeVisitor $visitor + * @param ClassNode $node */ - public function removeVisitor(\PHPUnit\PhpParser\NodeVisitor $visitor) + public function apply(ClassNode $node) { - foreach ($this->visitors as $index => $storedVisitor) { - if ($storedVisitor === $visitor) { - unset($this->visitors[$index]); - break; + $node->addInterface('Prophecy\\Prophecy\\ProphecySubjectInterface'); + $node->addProperty('objectProphecyClosure', 'private'); + foreach ($node->getMethods() as $name => $method) { + if ('__construct' === \strtolower($name)) { + continue; + } + if (!$method->getReturnTypeNode()->hasReturnStatement()) { + $method->setCode('$this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());'); + } else { + $method->setCode('return $this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());'); } } + $prophecySetter = new MethodNode('setProphecy'); + $prophecyArgument = new ArgumentNode('prophecy'); + $prophecyArgument->setTypeNode(new ArgumentTypeNode('Prophecy\\Prophecy\\ProphecyInterface')); + $prophecySetter->addArgument($prophecyArgument); + $prophecySetter->setCode(<<objectProphecyClosure) { + \$this->objectProphecyClosure = static function () use (\$prophecy) { + return \$prophecy; + }; +} +PHP +); + $prophecyGetter = new MethodNode('getProphecy'); + $prophecyGetter->setCode('return \\call_user_func($this->objectProphecyClosure);'); + if ($node->hasMethod('__call')) { + $__call = $node->getMethod('__call'); + } else { + $__call = new MethodNode('__call'); + $__call->addArgument(new ArgumentNode('name')); + $__call->addArgument(new ArgumentNode('arguments')); + $node->addMethod($__call, \true); + } + $__call->setCode(<<addMethod($prophecySetter, \true); + $node->addMethod($prophecyGetter, \true); } /** - * Traverses an array of nodes using the registered visitors. - * - * @param Node[] $nodes Array of nodes + * Returns patch priority, which determines when patch will be applied. * - * @return Node[] Traversed array of nodes + * @return int Priority number (higher - earlier) */ - public function traverse(array $nodes) : array + public function getPriority() { - $this->stopTraversal = \false; - foreach ($this->visitors as $visitor) { - if (null !== ($return = $visitor->beforeTraverse($nodes))) { - $nodes = $return; - } - } - $nodes = $this->traverseArray($nodes); - foreach ($this->visitors as $visitor) { - if (null !== ($return = $visitor->afterTraverse($nodes))) { - $nodes = $return; - } - } - return $nodes; + return 0; } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; +/** + * SplFileInfo patch. + * Makes SplFileInfo and derivative classes usable with Prophecy. + * + * @author Konstantin Kudryashov + */ +class SplFileInfoPatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface +{ /** - * Recursively traverse a node. + * Supports everything that extends SplFileInfo. * - * @param Node $node Node to traverse. + * @param ClassNode $node * - * @return Node Result of traversal (may be original node or new one) + * @return bool */ - protected function traverseNode(\PHPUnit\PhpParser\Node $node) : \PHPUnit\PhpParser\Node + public function supports(ClassNode $node) { - foreach ($node->getSubNodeNames() as $name) { - $subNode =& $node->{$name}; - if (\is_array($subNode)) { - $subNode = $this->traverseArray($subNode); - if ($this->stopTraversal) { - break; - } - } elseif ($subNode instanceof \PHPUnit\PhpParser\Node) { - $traverseChildren = \true; - $breakVisitorIndex = null; - foreach ($this->visitors as $visitorIndex => $visitor) { - $return = $visitor->enterNode($subNode); - if (null !== $return) { - if ($return instanceof \PHPUnit\PhpParser\Node) { - $this->ensureReplacementReasonable($subNode, $return); - $subNode = $return; - } elseif (self::DONT_TRAVERSE_CHILDREN === $return) { - $traverseChildren = \false; - } elseif (self::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) { - $traverseChildren = \false; - $breakVisitorIndex = $visitorIndex; - break; - } elseif (self::STOP_TRAVERSAL === $return) { - $this->stopTraversal = \true; - break 2; - } else { - throw new \LogicException('enterNode() returned invalid value of type ' . \gettype($return)); - } - } - } - if ($traverseChildren) { - $subNode = $this->traverseNode($subNode); - if ($this->stopTraversal) { - break; - } - } - foreach ($this->visitors as $visitorIndex => $visitor) { - $return = $visitor->leaveNode($subNode); - if (null !== $return) { - if ($return instanceof \PHPUnit\PhpParser\Node) { - $this->ensureReplacementReasonable($subNode, $return); - $subNode = $return; - } elseif (self::STOP_TRAVERSAL === $return) { - $this->stopTraversal = \true; - break 2; - } elseif (\is_array($return)) { - throw new \LogicException('leaveNode() may only return an array ' . 'if the parent structure is an array'); - } else { - throw new \LogicException('leaveNode() returned invalid value of type ' . \gettype($return)); - } - } - if ($breakVisitorIndex === $visitorIndex) { - break; - } - } - } + if (null === $node->getParentClass()) { + return \false; } - return $node; + return 'SplFileInfo' === $node->getParentClass() || \is_subclass_of($node->getParentClass(), 'SplFileInfo'); } /** - * Recursively traverse array (usually of nodes). - * - * @param array $nodes Array to traverse + * Updated constructor code to call parent one with dummy file argument. * - * @return array Result of traversal (may be original array or changed one) + * @param ClassNode $node */ - protected function traverseArray(array $nodes) : array + public function apply(ClassNode $node) { - $doNodes = []; - foreach ($nodes as $i => &$node) { - if ($node instanceof \PHPUnit\PhpParser\Node) { - $traverseChildren = \true; - $breakVisitorIndex = null; - foreach ($this->visitors as $visitorIndex => $visitor) { - $return = $visitor->enterNode($node); - if (null !== $return) { - if ($return instanceof \PHPUnit\PhpParser\Node) { - $this->ensureReplacementReasonable($node, $return); - $node = $return; - } elseif (self::DONT_TRAVERSE_CHILDREN === $return) { - $traverseChildren = \false; - } elseif (self::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) { - $traverseChildren = \false; - $breakVisitorIndex = $visitorIndex; - break; - } elseif (self::STOP_TRAVERSAL === $return) { - $this->stopTraversal = \true; - break 2; - } else { - throw new \LogicException('enterNode() returned invalid value of type ' . \gettype($return)); - } - } - } - if ($traverseChildren) { - $node = $this->traverseNode($node); - if ($this->stopTraversal) { - break; - } - } - foreach ($this->visitors as $visitorIndex => $visitor) { - $return = $visitor->leaveNode($node); - if (null !== $return) { - if ($return instanceof \PHPUnit\PhpParser\Node) { - $this->ensureReplacementReasonable($node, $return); - $node = $return; - } elseif (\is_array($return)) { - $doNodes[] = [$i, $return]; - break; - } elseif (self::REMOVE_NODE === $return) { - $doNodes[] = [$i, []]; - break; - } elseif (self::STOP_TRAVERSAL === $return) { - $this->stopTraversal = \true; - break 2; - } elseif (\false === $return) { - throw new \LogicException('bool(false) return from leaveNode() no longer supported. ' . 'Return NodeTraverser::REMOVE_NODE instead'); - } else { - throw new \LogicException('leaveNode() returned invalid value of type ' . \gettype($return)); - } - } - if ($breakVisitorIndex === $visitorIndex) { - break; - } - } - } elseif (\is_array($node)) { - throw new \LogicException('Invalid node structure: Contains nested arrays'); - } + if ($node->hasMethod('__construct')) { + $constructor = $node->getMethod('__construct'); + } else { + $constructor = new MethodNode('__construct'); + $node->addMethod($constructor); + } + if ($this->nodeIsDirectoryIterator($node)) { + $constructor->setCode('return parent::__construct("' . __DIR__ . '");'); + return; + } + if ($this->nodeIsSplFileObject($node)) { + $filePath = \str_replace('\\', '\\\\', __FILE__); + $constructor->setCode('return parent::__construct("' . $filePath . '");'); + return; } - if (!empty($doNodes)) { - while (list($i, $replace) = \array_pop($doNodes)) { - \array_splice($nodes, $i, 1, $replace); - } + if ($this->nodeIsSymfonySplFileInfo($node)) { + $filePath = \str_replace('\\', '\\\\', __FILE__); + $constructor->setCode('return parent::__construct("' . $filePath . '", "", "");'); + return; } - return $nodes; + $constructor->useParentCode(); } - private function ensureReplacementReasonable($old, $new) + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority() { - if ($old instanceof \PHPUnit\PhpParser\Node\Stmt && $new instanceof \PHPUnit\PhpParser\Node\Expr) { - throw new \LogicException("Trying to replace statement ({$old->getType()}) " . "with expression ({$new->getType()}). Are you missing a " . "Stmt_Expression wrapper?"); - } - if ($old instanceof \PHPUnit\PhpParser\Node\Expr && $new instanceof \PHPUnit\PhpParser\Node\Stmt) { - throw new \LogicException("Trying to replace expression ({$old->getType()}) " . "with statement ({$new->getType()})"); - } + return 50; + } + /** + * @param ClassNode $node + * @return boolean + */ + private function nodeIsDirectoryIterator(ClassNode $node) + { + $parent = $node->getParentClass(); + return 'DirectoryIterator' === $parent || \is_subclass_of($parent, 'DirectoryIterator'); + } + /** + * @param ClassNode $node + * @return boolean + */ + private function nodeIsSplFileObject(ClassNode $node) + { + $parent = $node->getParentClass(); + return 'SplFileObject' === $parent || \is_subclass_of($parent, 'SplFileObject'); + } + /** + * @param ClassNode $node + * @return boolean + */ + private function nodeIsSymfonySplFileInfo(ClassNode $node) + { + $parent = $node->getParentClass(); + return 'Symfony\\Component\\Finder\\SplFileInfo' === $parent; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler\ClassPatch; -use PHPUnit\PhpParser\Error; -use PHPUnit\PhpParser\ErrorHandler; +use Prophecy\Doubler\Generator\Node\ClassNode; /** - * Error handler that collects all errors into an array. + * Remove method functionality from the double which will clash with php keywords. * - * This allows graceful handling of errors. + * @author Milan Magudia */ -class Collecting implements \PHPUnit\PhpParser\ErrorHandler +class KeywordPatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface { - /** @var Error[] Collected errors */ - private $errors = []; - public function handleError(\PHPUnit\PhpParser\Error $error) + /** + * Support any class + * + * @param ClassNode $node + * + * @return boolean + */ + public function supports(ClassNode $node) { - $this->errors[] = $error; + return \true; } /** - * Get collected errors. + * Remove methods that clash with php keywords * - * @return Error[] + * @param ClassNode $node */ - public function getErrors() : array + public function apply(ClassNode $node) { - return $this->errors; + $methodNames = \array_keys($node->getMethods()); + $methodsToRemove = \array_intersect($methodNames, $this->getKeywords()); + foreach ($methodsToRemove as $methodName) { + $node->removeMethod($methodName); + } } /** - * Check whether there are any errors. + * Returns patch priority, which determines when patch will be applied. * - * @return bool + * @return int Priority number (higher - earlier) */ - public function hasErrors() : bool + public function getPriority() { - return !empty($this->errors); + return 49; } /** - * Reset/clear collected errors. + * Returns array of php keywords. + * + * @return array */ - public function clearErrors() + private function getKeywords() { - $this->errors = []; + return ['__halt_compiler']; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler\ClassPatch; -use PHPUnit\PhpParser\Error; -use PHPUnit\PhpParser\ErrorHandler; +use Prophecy\Doubler\Generator\Node\ClassNode; /** - * Error handler that handles all errors by throwing them. + * Exception patch for HHVM to remove the stubs from special methods * - * This is the default strategy used by all components. + * @author Christophe Coevoet */ -class Throwing implements \PHPUnit\PhpParser\ErrorHandler +class HhvmExceptionPatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface { - public function handleError(\PHPUnit\PhpParser\Error $error) + /** + * Supports exceptions on HHVM. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) { - throw $error; + if (!\defined('HHVM_VERSION')) { + return \false; + } + return 'Exception' === $node->getParentClass() || \is_subclass_of($node->getParentClass(), 'Exception'); + } + /** + * Removes special exception static methods from the doubled methods. + * + * @param ClassNode $node + * + * @return void + */ + public function apply(ClassNode $node) + { + if ($node->hasMethod('setTraceOptions')) { + $node->getMethod('setTraceOptions')->useParentCode(); + } + if ($node->hasMethod('getTraceOptions')) { + $node->getMethod('getTraceOptions')->useParentCode(); + } + } + /** + * {@inheritdoc} + */ + public function getPriority() + { + return -50; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler\ClassPatch; -use PHPUnit\PhpParser\ErrorHandler; -use PHPUnit\PhpParser\NameContext; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Expr; -use PHPUnit\PhpParser\Node\Name; -use PHPUnit\PhpParser\Node\Name\FullyQualified; -use PHPUnit\PhpParser\Node\Stmt; -use PHPUnit\PhpParser\NodeVisitorAbstract; -class NameResolver extends \PHPUnit\PhpParser\NodeVisitorAbstract +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; +use Prophecy\Doubler\Generator\Node\ReturnTypeNode; +/** + * Traversable interface patch. + * Forces classes that implement interfaces, that extend Traversable to also implement Iterator. + * + * @author Konstantin Kudryashov + */ +class TraversablePatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface { - /** @var NameContext Naming context */ - protected $nameContext; - /** @var bool Whether to preserve original names */ - protected $preserveOriginalNames; - /** @var bool Whether to replace resolved nodes in place, or to add resolvedNode attributes */ - protected $replaceNodes; /** - * Constructs a name resolution visitor. + * Supports nodetree, that implement Traversable, but not Iterator or IteratorAggregate. * - * Options: - * * preserveOriginalNames (default false): An "originalName" attribute will be added to - * all name nodes that underwent resolution. - * * replaceNodes (default true): Resolved names are replaced in-place. Otherwise, a - * resolvedName attribute is added. (Names that cannot be statically resolved receive a - * namespacedName attribute, as usual.) + * @param ClassNode $node * - * @param ErrorHandler|null $errorHandler Error handler - * @param array $options Options + * @return bool */ - public function __construct(\PHPUnit\PhpParser\ErrorHandler $errorHandler = null, array $options = []) + public function supports(ClassNode $node) { - $this->nameContext = new \PHPUnit\PhpParser\NameContext($errorHandler ?? new \PHPUnit\PhpParser\ErrorHandler\Throwing()); - $this->preserveOriginalNames = $options['preserveOriginalNames'] ?? \false; - $this->replaceNodes = $options['replaceNodes'] ?? \true; + if (\in_array('Iterator', $node->getInterfaces())) { + return \false; + } + if (\in_array('IteratorAggregate', $node->getInterfaces())) { + return \false; + } + foreach ($node->getInterfaces() as $interface) { + if ('Traversable' !== $interface && !\is_subclass_of($interface, 'Traversable')) { + continue; + } + if ('Iterator' === $interface || \is_subclass_of($interface, 'Iterator')) { + continue; + } + if ('IteratorAggregate' === $interface || \is_subclass_of($interface, 'IteratorAggregate')) { + continue; + } + return \true; + } + return \false; } /** - * Get name resolution context. + * Forces class to implement Iterator interface. * - * @return NameContext + * @param ClassNode $node */ - public function getNameContext() : \PHPUnit\PhpParser\NameContext + public function apply(ClassNode $node) { - return $this->nameContext; + $node->addInterface('Iterator'); + $currentMethod = new MethodNode('current'); + \PHP_VERSION_ID >= 80100 && $currentMethod->setReturnTypeNode(new ReturnTypeNode('mixed')); + $node->addMethod($currentMethod); + $keyMethod = new MethodNode('key'); + \PHP_VERSION_ID >= 80100 && $keyMethod->setReturnTypeNode(new ReturnTypeNode('mixed')); + $node->addMethod($keyMethod); + $nextMethod = new MethodNode('next'); + \PHP_VERSION_ID >= 80100 && $nextMethod->setReturnTypeNode(new ReturnTypeNode('void')); + $node->addMethod($nextMethod); + $rewindMethod = new MethodNode('rewind'); + \PHP_VERSION_ID >= 80100 && $rewindMethod->setReturnTypeNode(new ReturnTypeNode('void')); + $node->addMethod($rewindMethod); + $validMethod = new MethodNode('valid'); + \PHP_VERSION_ID >= 80100 && $validMethod->setReturnTypeNode(new ReturnTypeNode('bool')); + $node->addMethod($validMethod); } - public function beforeTraverse(array $nodes) + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority() { - $this->nameContext->startNamespace(); - return null; + return 100; } - public function enterNode(\PHPUnit\PhpParser\Node $node) +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; +/** + * ReflectionClass::newInstance patch. + * Makes first argument of newInstance optional, since it works but signature is misleading + * + * @author Florian Klein + */ +class ReflectionClassNewInstancePatch implements \Prophecy\Doubler\ClassPatch\ClassPatchInterface +{ + /** + * Supports ReflectionClass + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) { - if ($node instanceof \PHPUnit\PhpParser\Node\Stmt\Namespace_) { - $this->nameContext->startNamespace($node->name); - } elseif ($node instanceof \PHPUnit\PhpParser\Node\Stmt\Use_) { - foreach ($node->uses as $use) { - $this->addAlias($use, $node->type, null); - } - } elseif ($node instanceof \PHPUnit\PhpParser\Node\Stmt\GroupUse) { - foreach ($node->uses as $use) { - $this->addAlias($use, $node->type, $node->prefix); - } - } elseif ($node instanceof \PHPUnit\PhpParser\Node\Stmt\Class_) { - if (null !== $node->extends) { - $node->extends = $this->resolveClassName($node->extends); - } - foreach ($node->implements as &$interface) { - $interface = $this->resolveClassName($interface); - } - $this->resolveAttrGroups($node); - if (null !== $node->name) { - $this->addNamespacedName($node); - } - } elseif ($node instanceof \PHPUnit\PhpParser\Node\Stmt\Interface_) { - foreach ($node->extends as &$interface) { - $interface = $this->resolveClassName($interface); - } - $this->resolveAttrGroups($node); - $this->addNamespacedName($node); - } elseif ($node instanceof \PHPUnit\PhpParser\Node\Stmt\Trait_) { - $this->resolveAttrGroups($node); - $this->addNamespacedName($node); - } elseif ($node instanceof \PHPUnit\PhpParser\Node\Stmt\Function_) { - $this->resolveSignature($node); - $this->resolveAttrGroups($node); - $this->addNamespacedName($node); - } elseif ($node instanceof \PHPUnit\PhpParser\Node\Stmt\ClassMethod || $node instanceof \PHPUnit\PhpParser\Node\Expr\Closure || $node instanceof \PHPUnit\PhpParser\Node\Expr\ArrowFunction) { - $this->resolveSignature($node); - $this->resolveAttrGroups($node); - } elseif ($node instanceof \PHPUnit\PhpParser\Node\Stmt\Property) { - if (null !== $node->type) { - $node->type = $this->resolveType($node->type); - } - $this->resolveAttrGroups($node); - } elseif ($node instanceof \PHPUnit\PhpParser\Node\Stmt\Const_) { - foreach ($node->consts as $const) { - $this->addNamespacedName($const); - } - } else { - if ($node instanceof \PHPUnit\PhpParser\Node\Stmt\ClassConst) { - $this->resolveAttrGroups($node); - } elseif ($node instanceof \PHPUnit\PhpParser\Node\Expr\StaticCall || $node instanceof \PHPUnit\PhpParser\Node\Expr\StaticPropertyFetch || $node instanceof \PHPUnit\PhpParser\Node\Expr\ClassConstFetch || $node instanceof \PHPUnit\PhpParser\Node\Expr\New_ || $node instanceof \PHPUnit\PhpParser\Node\Expr\Instanceof_) { - if ($node->class instanceof \PHPUnit\PhpParser\Node\Name) { - $node->class = $this->resolveClassName($node->class); - } - } elseif ($node instanceof \PHPUnit\PhpParser\Node\Stmt\Catch_) { - foreach ($node->types as &$type) { - $type = $this->resolveClassName($type); - } - } elseif ($node instanceof \PHPUnit\PhpParser\Node\Expr\FuncCall) { - if ($node->name instanceof \PHPUnit\PhpParser\Node\Name) { - $node->name = $this->resolveName($node->name, \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_FUNCTION); - } - } elseif ($node instanceof \PHPUnit\PhpParser\Node\Expr\ConstFetch) { - $node->name = $this->resolveName($node->name, \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_CONSTANT); - } elseif ($node instanceof \PHPUnit\PhpParser\Node\Stmt\TraitUse) { - foreach ($node->traits as &$trait) { - $trait = $this->resolveClassName($trait); - } - foreach ($node->adaptations as $adaptation) { - if (null !== $adaptation->trait) { - $adaptation->trait = $this->resolveClassName($adaptation->trait); - } - if ($adaptation instanceof \PHPUnit\PhpParser\Node\Stmt\TraitUseAdaptation\Precedence) { - foreach ($adaptation->insteadof as &$insteadof) { - $insteadof = $this->resolveClassName($insteadof); - } - } - } - } - } - return null; + return 'ReflectionClass' === $node->getParentClass(); } - private function addAlias(\PHPUnit\PhpParser\Node\Stmt\UseUse $use, $type, \PHPUnit\PhpParser\Node\Name $prefix = null) + /** + * Updates newInstance's first argument to make it optional + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) { - // Add prefix for group uses - $name = $prefix ? \PHPUnit\PhpParser\Node\Name::concat($prefix, $use->name) : $use->name; - // Type is determined either by individual element or whole use declaration - $type |= $use->type; - $this->nameContext->addAlias($name, (string) $use->getAlias(), $type, $use->getAttributes()); + foreach ($node->getMethod('newInstance')->getArguments() as $argument) { + $argument->setDefault(null); + } } - /** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure $node */ - private function resolveSignature($node) + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher = earlier) + */ + public function getPriority() { - foreach ($node->params as $param) { - $param->type = $this->resolveType($param->type); - $this->resolveAttrGroups($param); - } - $node->returnType = $this->resolveType($node->returnType); + return 50; } - private function resolveType($node) +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Argument\Token; + +/** + * Logical NOT token. + * + * @author Boris Mikhaylov + */ +class LogicalNotToken implements \Prophecy\Argument\Token\TokenInterface +{ + /** @var \Prophecy\Argument\Token\TokenInterface */ + private $token; + /** + * @param mixed $value exact value or token + */ + public function __construct($value) { - if ($node instanceof \PHPUnit\PhpParser\Node\Name) { - return $this->resolveClassName($node); - } - if ($node instanceof \PHPUnit\PhpParser\Node\NullableType) { - $node->type = $this->resolveType($node->type); - return $node; - } - if ($node instanceof \PHPUnit\PhpParser\Node\UnionType) { - foreach ($node->types as &$type) { - $type = $this->resolveType($type); - } - return $node; - } - return $node; + $this->token = $value instanceof \Prophecy\Argument\Token\TokenInterface ? $value : new \Prophecy\Argument\Token\ExactValueToken($value); } /** - * Resolve name, according to name resolver options. + * Scores 4 when preset token does not match the argument. * - * @param Name $name Function or constant name to resolve - * @param int $type One of Stmt\Use_::TYPE_* + * @param $argument * - * @return Name Resolved name, or original name with attribute + * @return bool|int */ - protected function resolveName(\PHPUnit\PhpParser\Node\Name $name, int $type) : \PHPUnit\PhpParser\Node\Name + public function scoreArgument($argument) { - if (!$this->replaceNodes) { - $resolvedName = $this->nameContext->getResolvedName($name, $type); - if (null !== $resolvedName) { - $name->setAttribute('resolvedName', $resolvedName); - } else { - $name->setAttribute('namespacedName', \PHPUnit\PhpParser\Node\Name\FullyQualified::concat($this->nameContext->getNamespace(), $name, $name->getAttributes())); - } - return $name; - } - if ($this->preserveOriginalNames) { - // Save the original name - $originalName = $name; - $name = clone $originalName; - $name->setAttribute('originalName', $originalName); - } - $resolvedName = $this->nameContext->getResolvedName($name, $type); - if (null !== $resolvedName) { - return $resolvedName; - } - // unqualified names inside a namespace cannot be resolved at compile-time - // add the namespaced version of the name as an attribute - $name->setAttribute('namespacedName', \PHPUnit\PhpParser\Node\Name\FullyQualified::concat($this->nameContext->getNamespace(), $name, $name->getAttributes())); - return $name; + return \false === $this->token->scoreArgument($argument) ? 4 : \false; } - protected function resolveClassName(\PHPUnit\PhpParser\Node\Name $name) + /** + * Returns true if preset token is last. + * + * @return bool|int + */ + public function isLast() { - return $this->resolveName($name, \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_NORMAL); + return $this->token->isLast(); } - protected function addNamespacedName(\PHPUnit\PhpParser\Node $node) + /** + * Returns originating token. + * + * @return TokenInterface + */ + public function getOriginatingToken() { - $node->namespacedName = \PHPUnit\PhpParser\Node\Name::concat($this->nameContext->getNamespace(), (string) $node->name); + return $this->token; } - protected function resolveAttrGroups(\PHPUnit\PhpParser\Node $node) + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() { - foreach ($node->attrGroups as $attrGroup) { - foreach ($attrGroup->attrs as $attr) { - $attr->name = $this->resolveClassName($attr->name); - } - } + return \sprintf('not(%s)', $this->token); } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Argument\Token; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\NodeVisitorAbstract; /** - * Visitor that connects a child node to its parent node - * as well as its sibling nodes. + * Array every entry token. * - * On the child node, the parent node can be accessed through - * $node->getAttribute('parent'), the previous - * node can be accessed through $node->getAttribute('previous'), - * and the next node can be accessed through $node->getAttribute('next'). + * @author Adrien Brault */ -final class NodeConnectingVisitor extends \PHPUnit\PhpParser\NodeVisitorAbstract +class ArrayEveryEntryToken implements \Prophecy\Argument\Token\TokenInterface { /** - * @var Node[] + * @var TokenInterface */ - private $stack = []; + private $value; /** - * @var ?Node + * @param mixed $value exact value or token */ - private $previous; - public function beforeTraverse(array $nodes) + public function __construct($value) { - $this->stack = []; - $this->previous = null; + if (!$value instanceof \Prophecy\Argument\Token\TokenInterface) { + $value = new \Prophecy\Argument\Token\ExactValueToken($value); + } + $this->value = $value; } - public function enterNode(\PHPUnit\PhpParser\Node $node) + /** + * {@inheritdoc} + */ + public function scoreArgument($argument) { - if (!empty($this->stack)) { - $node->setAttribute('parent', $this->stack[\count($this->stack) - 1]); + if (!$argument instanceof \Traversable && !\is_array($argument)) { + return \false; } - if ($this->previous !== null && $this->previous->getAttribute('parent') === $node->getAttribute('parent')) { - $node->setAttribute('previous', $this->previous); - $this->previous->setAttribute('next', $node); + $scores = array(); + foreach ($argument as $key => $argumentEntry) { + $scores[] = $this->value->scoreArgument($argumentEntry); } - $this->stack[] = $node; + if (empty($scores) || \in_array(\false, $scores, \true)) { + return \false; + } + return \array_sum($scores) / \count($scores); + } + /** + * {@inheritdoc} + */ + public function isLast() + { + return \false; } - public function leaveNode(\PHPUnit\PhpParser\Node $node) + /** + * {@inheritdoc} + */ + public function __toString() + { + return \sprintf('[%s, ..., %s]', $this->value, $this->value); + } + /** + * @return TokenInterface + */ + public function getValue() { - $this->previous = $node; - \array_pop($this->stack); + return $this->value; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Argument\Token; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\NodeTraverser; -use PHPUnit\PhpParser\NodeVisitorAbstract; /** - * This visitor can be used to find the first node satisfying some criterion determined by - * a filter callback. + * Logical AND token. + * + * @author Boris Mikhaylov */ -class FirstFindingVisitor extends \PHPUnit\PhpParser\NodeVisitorAbstract +class LogicalAndToken implements \Prophecy\Argument\Token\TokenInterface { - /** @var callable Filter callback */ - protected $filterCallback; - /** @var null|Node Found node */ - protected $foundNode; - public function __construct(callable $filterCallback) + private $tokens = array(); + /** + * @param array $arguments exact values or tokens + */ + public function __construct(array $arguments) { - $this->filterCallback = $filterCallback; + foreach ($arguments as $argument) { + if (!$argument instanceof \Prophecy\Argument\Token\TokenInterface) { + $argument = new \Prophecy\Argument\Token\ExactValueToken($argument); + } + $this->tokens[] = $argument; + } } /** - * Get found node satisfying the filter callback. + * Scores maximum score from scores returned by tokens for this argument if all of them score. * - * Returns null if no node satisfies the filter callback. + * @param $argument * - * @return null|Node Found node (or null if not found) + * @return bool|int */ - public function getFoundNode() + public function scoreArgument($argument) { - return $this->foundNode; + if (0 === \count($this->tokens)) { + return \false; + } + $maxScore = 0; + foreach ($this->tokens as $token) { + $score = $token->scoreArgument($argument); + if (\false === $score) { + return \false; + } + $maxScore = \max($score, $maxScore); + } + return $maxScore; } - public function beforeTraverse(array $nodes) + /** + * Returns false. + * + * @return boolean + */ + public function isLast() { - $this->foundNode = null; - return null; + return \false; } - public function enterNode(\PHPUnit\PhpParser\Node $node) + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() { - $filterCallback = $this->filterCallback; - if ($filterCallback($node)) { - $this->foundNode = $node; - return \PHPUnit\PhpParser\NodeTraverser::STOP_TRAVERSAL; - } - return null; + return \sprintf('bool(%s)', \implode(' AND ', $this->tokens)); } } + * Marcello Duarte * - * This visitor is required to perform format-preserving pretty prints. + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -class CloningVisitor extends \PHPUnit\PhpParser\NodeVisitorAbstract -{ - public function enterNode(\PHPUnit\PhpParser\Node $origNode) - { - $node = clone $origNode; - $node->setAttribute('origNode', $origNode); - return $node; - } -} -$node->getAttribute('parent'). + * @author Boris Mikhaylov */ -final class ParentConnectingVisitor extends \PHPUnit\PhpParser\NodeVisitorAbstract +class ArrayEntryToken implements \Prophecy\Argument\Token\TokenInterface { + /** @var \Prophecy\Argument\Token\TokenInterface */ + private $key; + /** @var \Prophecy\Argument\Token\TokenInterface */ + private $value; /** - * @var Node[] + * @param mixed $key exact value or token + * @param mixed $value exact value or token */ - private $stack = []; - public function beforeTraverse(array $nodes) + public function __construct($key, $value) { - $this->stack = []; + $this->key = $this->wrapIntoExactValueToken($key); + $this->value = $this->wrapIntoExactValueToken($value); } - public function enterNode(\PHPUnit\PhpParser\Node $node) + /** + * Scores half of combined scores from key and value tokens for same entry. Capped at 8. + * If argument implements \ArrayAccess without \Traversable, then key token is restricted to ExactValueToken. + * + * @param array|\ArrayAccess|\Traversable $argument + * + * @throws \Prophecy\Exception\InvalidArgumentException + * @return bool|int + */ + public function scoreArgument($argument) { - if (!empty($this->stack)) { - $node->setAttribute('parent', $this->stack[\count($this->stack) - 1]); + if ($argument instanceof \Traversable) { + $argument = \iterator_to_array($argument); } - $this->stack[] = $node; + if ($argument instanceof \ArrayAccess) { + $argument = $this->convertArrayAccessToEntry($argument); + } + if (!\is_array($argument) || empty($argument)) { + return \false; + } + $keyScores = \array_map(array($this->key, 'scoreArgument'), \array_keys($argument)); + $valueScores = \array_map(array($this->value, 'scoreArgument'), $argument); + $scoreEntry = function ($value, $key) { + return $value && $key ? \min(8, ($key + $value) / 2) : \false; + }; + return \max(\array_map($scoreEntry, $valueScores, $keyScores)); } - public function leaveNode(\PHPUnit\PhpParser\Node $node) + /** + * Returns false. + * + * @return boolean + */ + public function isLast() { - \array_pop($this->stack); + return \false; } -} -filterCallback = $filterCallback; + return \sprintf('[..., %s => %s, ...]', $this->key, $this->value); } /** - * Get found nodes satisfying the filter callback. + * Returns key * - * Nodes are returned in pre-order. + * @return TokenInterface + */ + public function getKey() + { + return $this->key; + } + /** + * Returns value * - * @return Node[] Found nodes + * @return TokenInterface */ - public function getFoundNodes() : array + public function getValue() { - return $this->foundNodes; + return $this->value; } - public function beforeTraverse(array $nodes) + /** + * Wraps non token $value into ExactValueToken + * + * @param $value + * @return TokenInterface + */ + private function wrapIntoExactValueToken($value) { - $this->foundNodes = []; - return null; + return $value instanceof \Prophecy\Argument\Token\TokenInterface ? $value : new \Prophecy\Argument\Token\ExactValueToken($value); } - public function enterNode(\PHPUnit\PhpParser\Node $node) + /** + * Converts instance of \ArrayAccess to key => value array entry + * + * @param \ArrayAccess $object + * + * @return array|null + * @throws \Prophecy\Exception\InvalidArgumentException + */ + private function convertArrayAccessToEntry(\ArrayAccess $object) { - $filterCallback = $this->filterCallback; - if ($filterCallback($node)) { - $this->foundNodes[] = $node; + if (!$this->key instanceof \Prophecy\Argument\Token\ExactValueToken) { + throw new InvalidArgumentException(\sprintf('You can only use exact value tokens to match key of ArrayAccess object' . \PHP_EOL . 'But you used `%s`.', $this->key)); } - return null; + $key = $this->key->getValue(); + return $object->offsetExists($key) ? array($key => $object[$key]) : array(); } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Argument\Token; -use PHPUnit\PhpParser; -use PHPUnit\PhpParser\BuilderHelpers; -use PHPUnit\PhpParser\Node\Identifier; -use PHPUnit\PhpParser\Node\Name; -use PHPUnit\PhpParser\Node\NullableType; -use PHPUnit\PhpParser\Node\Stmt; -class Property implements \PHPUnit\PhpParser\Builder +/** + * Check if values is not in array + * + * @author Vinícius Alonso + */ +class NotInArrayToken implements \Prophecy\Argument\Token\TokenInterface { - protected $name; - protected $flags = 0; - protected $default = null; - protected $attributes = []; - /** @var null|Identifier|Name|NullableType */ - protected $type; + private $token = array(); + private $strict; /** - * Creates a property builder. - * - * @param string $name Name of the property + * @param array $arguments tokens + * @param bool $strict */ - public function __construct(string $name) + public function __construct(array $arguments, $strict = \true) { - $this->name = $name; + $this->token = $arguments; + $this->strict = $strict; } /** - * Makes the property public. + * Return scores 8 score if argument is in array. * - * @return $this The builder instance (for fluid interface) - */ - public function makePublic() - { - $this->flags = \PHPUnit\PhpParser\BuilderHelpers::addModifier($this->flags, \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC); - return $this; - } - /** - * Makes the property protected. + * @param $argument * - * @return $this The builder instance (for fluid interface) + * @return bool|int */ - public function makeProtected() + public function scoreArgument($argument) { - $this->flags = \PHPUnit\PhpParser\BuilderHelpers::addModifier($this->flags, \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED); - return $this; + if (\count($this->token) === 0) { + return \false; + } + if (!\in_array($argument, $this->token, $this->strict)) { + return 8; + } + return \false; } /** - * Makes the property private. + * Returns false. * - * @return $this The builder instance (for fluid interface) + * @return boolean */ - public function makePrivate() + public function isLast() { - $this->flags = \PHPUnit\PhpParser\BuilderHelpers::addModifier($this->flags, \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE); - return $this; + return \false; } /** - * Makes the property static. + * Returns string representation for token. * - * @return $this The builder instance (for fluid interface) + * @return string */ - public function makeStatic() + public function __toString() { - $this->flags = \PHPUnit\PhpParser\BuilderHelpers::addModifier($this->flags, \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_STATIC); - return $this; + $arrayAsString = \implode(', ', $this->token); + return "[{$arrayAsString}]"; } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Argument\Token; + +use Prophecy\Util\StringUtil; +/** + * Identical value token. + * + * @author Florian Voutzinos + */ +class IdenticalValueToken implements \Prophecy\Argument\Token\TokenInterface +{ + private $value; + private $string; + private $util; /** - * Sets default value for the property. - * - * @param mixed $value Default value to use + * Initializes token. * - * @return $this The builder instance (for fluid interface) + * @param mixed $value + * @param StringUtil $util */ - public function setDefault($value) + public function __construct($value, StringUtil $util = null) { - $this->default = \PHPUnit\PhpParser\BuilderHelpers::normalizeValue($value); - return $this; + $this->value = $value; + $this->util = $util ?: new StringUtil(); } /** - * Sets doc comment for the property. + * Scores 11 if argument matches preset value. * - * @param PhpParser\Comment\Doc|string $docComment Doc comment to set + * @param $argument * - * @return $this The builder instance (for fluid interface) + * @return bool|int */ - public function setDocComment($docComment) + public function scoreArgument($argument) { - $this->attributes = ['comments' => [\PHPUnit\PhpParser\BuilderHelpers::normalizeDocComment($docComment)]]; - return $this; + return $argument === $this->value ? 11 : \false; } /** - * Sets the property type for PHP 7.4+. - * - * @param string|Name|NullableType|Identifier $type + * Returns false. * - * @return $this + * @return bool */ - public function setType($type) + public function isLast() { - $this->type = \PHPUnit\PhpParser\BuilderHelpers::normalizeType($type); - return $this; + return \false; } /** - * Returns the built class node. + * Returns string representation for token. * - * @return Stmt\Property The built property node + * @return string */ - public function getNode() : \PHPUnit\PhpParser\Node + public function __toString() { - return new \PHPUnit\PhpParser\Node\Stmt\Property($this->flags !== 0 ? $this->flags : \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC, [new \PHPUnit\PhpParser\Node\Stmt\PropertyProperty($this->name, $this->default)], $this->attributes, $this->type); + if (null === $this->string) { + $this->string = \sprintf('identical(%s)', $this->util->stringify($this->value)); + } + return $this->string; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Argument\Token; -use PHPUnit\PhpParser\BuilderHelpers; -use PHPUnit\PhpParser\Node; -abstract class FunctionLike extends \PHPUnit\PhpParser\Builder\Declaration +use Prophecy\Exception\InvalidArgumentException; +/** + * Value type token. + * + * @author Konstantin Kudryashov + */ +class TypeToken implements \Prophecy\Argument\Token\TokenInterface { - protected $returnByRef = \false; - protected $params = []; - /** @var string|Node\Name|Node\NullableType|null */ - protected $returnType = null; + private $type; /** - * Make the function return by reference. - * - * @return $this The builder instance (for fluid interface) + * @param string $type */ - public function makeReturnByRef() + public function __construct($type) { - $this->returnByRef = \true; - return $this; + $checker = "is_{$type}"; + if (!\function_exists($checker) && !\interface_exists($type) && !\class_exists($type)) { + throw new InvalidArgumentException(\sprintf('Type or class name expected as an argument to TypeToken, but got %s.', $type)); + } + $this->type = $type; } /** - * Adds a parameter. + * Scores 5 if argument has the same type this token was constructed with. * - * @param Node\Param|Param $param The parameter to add + * @param $argument * - * @return $this The builder instance (for fluid interface) + * @return bool|int */ - public function addParam($param) + public function scoreArgument($argument) { - $param = \PHPUnit\PhpParser\BuilderHelpers::normalizeNode($param); - if (!$param instanceof \PHPUnit\PhpParser\Node\Param) { - throw new \LogicException(\sprintf('Expected parameter node, got "%s"', $param->getType())); + $checker = "is_{$this->type}"; + if (\function_exists($checker)) { + return \call_user_func($checker, $argument) ? 5 : \false; } - $this->params[] = $param; - return $this; + return $argument instanceof $this->type ? 5 : \false; } /** - * Adds multiple parameters. - * - * @param array $params The parameters to add + * Returns false. * - * @return $this The builder instance (for fluid interface) + * @return bool */ - public function addParams(array $params) + public function isLast() { - foreach ($params as $param) { - $this->addParam($param); - } - return $this; + return \false; } /** - * Sets the return type for PHP 7. - * - * @param string|Node\Name|Node\NullableType $type One of array, callable, string, int, float, - * bool, iterable, or a class/interface name. + * Returns string representation for token. * - * @return $this The builder instance (for fluid interface) + * @return string */ - public function setReturnType($type) + public function __toString() { - $this->returnType = \PHPUnit\PhpParser\BuilderHelpers::normalizeType($type); - return $this; + return \sprintf('type(%s)', $this->type); } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Argument\Token; -use PHPUnit\PhpParser; -use PHPUnit\PhpParser\BuilderHelpers; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Stmt; -class Namespace_ extends \PHPUnit\PhpParser\Builder\Declaration +use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; +use Prophecy\Comparator\Factory as ComparatorFactory; +use Prophecy\Util\StringUtil; +/** + * Object state-checker token. + * + * @author Konstantin Kudryashov + */ +class ObjectStateToken implements \Prophecy\Argument\Token\TokenInterface { private $name; - private $stmts = []; + private $value; + private $util; + private $comparatorFactory; /** - * Creates a namespace builder. + * Initializes token. * - * @param Node\Name|string|null $name Name of the namespace + * @param string $methodName + * @param mixed $value Expected return value + * @param null|StringUtil $util + * @param ComparatorFactory $comparatorFactory */ - public function __construct($name) + public function __construct($methodName, $value, StringUtil $util = null, ComparatorFactory $comparatorFactory = null) + { + $this->name = $methodName; + $this->value = $value; + $this->util = $util ?: new StringUtil(); + $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); + } + /** + * Scores 8 if argument is an object, which method returns expected value. + * + * @param mixed $argument + * + * @return bool|int + */ + public function scoreArgument($argument) { - $this->name = null !== $name ? \PHPUnit\PhpParser\BuilderHelpers::normalizeName($name) : null; + if (\is_object($argument) && \method_exists($argument, $this->name)) { + $actual = \call_user_func(array($argument, $this->name)); + $comparator = $this->comparatorFactory->getComparatorFor($this->value, $actual); + try { + $comparator->assertEquals($this->value, $actual); + return 8; + } catch (ComparisonFailure $failure) { + return \false; + } + } + if (\is_object($argument) && \property_exists($argument, $this->name)) { + return $argument->{$this->name} === $this->value ? 8 : \false; + } + return \false; } /** - * Adds a statement. - * - * @param Node|PhpParser\Builder $stmt The statement to add + * Returns false. * - * @return $this The builder instance (for fluid interface) + * @return bool */ - public function addStmt($stmt) + public function isLast() { - $this->stmts[] = \PHPUnit\PhpParser\BuilderHelpers::normalizeStmt($stmt); - return $this; + return \false; } /** - * Returns the built node. + * Returns string representation for token. * - * @return Node The built node + * @return string */ - public function getNode() : \PHPUnit\PhpParser\Node + public function __toString() { - return new \PHPUnit\PhpParser\Node\Stmt\Namespace_($this->name, $this->stmts, $this->attributes); + return \sprintf('state(%s(), %s)', $this->name, $this->util->stringify($this->value)); } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Argument\Token; -use PHPUnit\PhpParser; -use PHPUnit\PhpParser\BuilderHelpers; -use PHPUnit\PhpParser\Node; -class Param implements \PHPUnit\PhpParser\Builder +/** + * Check if values is in array + * + * @author Vinícius Alonso + */ +class InArrayToken implements \Prophecy\Argument\Token\TokenInterface { - protected $name; - protected $default = null; - /** @var Node\Identifier|Node\Name|Node\NullableType|null */ - protected $type = null; - protected $byRef = \false; - protected $variadic = \false; + private $token = array(); + private $strict; /** - * Creates a parameter builder. - * - * @param string $name Name of the parameter + * @param array $arguments tokens + * @param bool $strict */ - public function __construct(string $name) + public function __construct(array $arguments, $strict = \true) { - $this->name = $name; + $this->token = $arguments; + $this->strict = $strict; } /** - * Sets default value for the parameter. + * Return scores 8 score if argument is in array. * - * @param mixed $value Default value to use + * @param $argument * - * @return $this The builder instance (for fluid interface) + * @return bool|int */ - public function setDefault($value) + public function scoreArgument($argument) { - $this->default = \PHPUnit\PhpParser\BuilderHelpers::normalizeValue($value); - return $this; + if (\count($this->token) === 0) { + return \false; + } + if (\in_array($argument, $this->token, $this->strict)) { + return 8; + } + return \false; } /** - * Sets type for the parameter. - * - * @param string|Node\Name|Node\NullableType|Node\UnionType $type Parameter type + * Returns false. * - * @return $this The builder instance (for fluid interface) + * @return boolean */ - public function setType($type) + public function isLast() { - $this->type = \PHPUnit\PhpParser\BuilderHelpers::normalizeType($type); - if ($this->type == 'void') { - throw new \LogicException('Parameter type cannot be void'); - } - return $this; + return \false; } /** - * Sets type for the parameter. - * - * @param string|Node\Name|Node\NullableType|Node\UnionType $type Parameter type - * - * @return $this The builder instance (for fluid interface) + * Returns string representation for token. * - * @deprecated Use setType() instead + * @return string */ - public function setTypeHint($type) + public function __toString() { - return $this->setType($type); + $arrayAsString = \implode(', ', $this->token); + return "[{$arrayAsString}]"; } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Argument\Token; + +/** + * Any single value token. + * + * @author Konstantin Kudryashov + */ +class AnyValueToken implements \Prophecy\Argument\Token\TokenInterface +{ /** - * Make the parameter accept the value by reference. + * Always scores 3 for any argument. * - * @return $this The builder instance (for fluid interface) + * @param $argument + * + * @return int */ - public function makeByRef() + public function scoreArgument($argument) { - $this->byRef = \true; - return $this; + return 3; } /** - * Make the parameter variadic + * Returns false. * - * @return $this The builder instance (for fluid interface) + * @return bool */ - public function makeVariadic() + public function isLast() { - $this->variadic = \true; - return $this; + return \false; } /** - * Returns the built parameter node. + * Returns string representation for token. * - * @return Node\Param The built parameter node + * @return string */ - public function getNode() : \PHPUnit\PhpParser\Node + public function __toString() { - return new \PHPUnit\PhpParser\Node\Param(new \PHPUnit\PhpParser\Node\Expr\Variable($this->name), $this->default, $this->type, $this->byRef, $this->variadic); + return '*'; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Argument\Token; -use PHPUnit\PhpParser; -use PHPUnit\PhpParser\BuilderHelpers; -use PHPUnit\PhpParser\Node\Name; -use PHPUnit\PhpParser\Node\Stmt; -class Class_ extends \PHPUnit\PhpParser\Builder\Declaration +/** + * Approximate value token + * + * @author Daniel Leech + */ +class ApproximateValueToken implements \Prophecy\Argument\Token\TokenInterface { - protected $name; - protected $extends = null; - protected $implements = []; - protected $flags = 0; - protected $uses = []; - protected $constants = []; - protected $properties = []; - protected $methods = []; - /** - * Creates a class builder. - * - * @param string $name Name of the class - */ - public function __construct(string $name) + private $value; + private $precision; + public function __construct($value, $precision = 0) { - $this->name = $name; + $this->value = $value; + $this->precision = $precision; } /** - * Extends a class. - * - * @param Name|string $class Name of class to extend - * - * @return $this The builder instance (for fluid interface) + * {@inheritdoc} */ - public function extend($class) + public function scoreArgument($argument) { - $this->extends = \PHPUnit\PhpParser\BuilderHelpers::normalizeName($class); - return $this; + return \round((float) $argument, $this->precision) === \round($this->value, $this->precision) ? 10 : \false; } /** - * Implements one or more interfaces. - * - * @param Name|string ...$interfaces Names of interfaces to implement - * - * @return $this The builder instance (for fluid interface) + * {@inheritdoc} */ - public function implement(...$interfaces) + public function isLast() { - foreach ($interfaces as $interface) { - $this->implements[] = \PHPUnit\PhpParser\BuilderHelpers::normalizeName($interface); - } - return $this; + return \false; } /** - * Makes the class abstract. + * Returns string representation for token. * - * @return $this The builder instance (for fluid interface) + * @return string */ - public function makeAbstract() + public function __toString() { - $this->flags = \PHPUnit\PhpParser\BuilderHelpers::addModifier($this->flags, \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_ABSTRACT); - return $this; + return \sprintf('≅%s', \round($this->value, $this->precision)); } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Argument\Token; + +/** + * Any values token. + * + * @author Konstantin Kudryashov + */ +class AnyValuesToken implements \Prophecy\Argument\Token\TokenInterface +{ /** - * Makes the class final. + * Always scores 2 for any argument. * - * @return $this The builder instance (for fluid interface) + * @param $argument + * + * @return int */ - public function makeFinal() + public function scoreArgument($argument) { - $this->flags = \PHPUnit\PhpParser\BuilderHelpers::addModifier($this->flags, \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_FINAL); - return $this; + return 2; } /** - * Adds a statement. - * - * @param Stmt|PhpParser\Builder $stmt The statement to add + * Returns true to stop wildcard from processing other tokens. * - * @return $this The builder instance (for fluid interface) + * @return bool */ - public function addStmt($stmt) + public function isLast() { - $stmt = \PHPUnit\PhpParser\BuilderHelpers::normalizeNode($stmt); - $targets = [\PHPUnit\PhpParser\Node\Stmt\TraitUse::class => &$this->uses, \PHPUnit\PhpParser\Node\Stmt\ClassConst::class => &$this->constants, \PHPUnit\PhpParser\Node\Stmt\Property::class => &$this->properties, \PHPUnit\PhpParser\Node\Stmt\ClassMethod::class => &$this->methods]; - $class = \get_class($stmt); - if (!isset($targets[$class])) { - throw new \LogicException(\sprintf('Unexpected node of type "%s"', $stmt->getType())); - } - $targets[$class][] = $stmt; - return $this; + return \true; } /** - * Returns the built class node. + * Returns string representation for token. * - * @return Stmt\Class_ The built class node + * @return string */ - public function getNode() : \PHPUnit\PhpParser\Node + public function __toString() { - return new \PHPUnit\PhpParser\Node\Stmt\Class_($this->name, ['flags' => $this->flags, 'extends' => $this->extends, 'implements' => $this->implements, 'stmts' => \array_merge($this->uses, $this->constants, $this->properties, $this->methods)], $this->attributes); + return '* [, ...]'; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Argument\Token; -use PHPUnit\PhpParser; -use PHPUnit\PhpParser\BuilderHelpers; -abstract class Declaration implements \PHPUnit\PhpParser\Builder +use PHPUnit\SebastianBergmann\Comparator\ComparisonFailure; +use Prophecy\Comparator\Factory as ComparatorFactory; +use Prophecy\Util\StringUtil; +/** + * Exact value token. + * + * @author Konstantin Kudryashov + */ +class ExactValueToken implements \Prophecy\Argument\Token\TokenInterface { - protected $attributes = []; - public abstract function addStmt($stmt); + private $value; + private $string; + private $util; + private $comparatorFactory; /** - * Adds multiple statements. - * - * @param array $stmts The statements to add + * Initializes token. * - * @return $this The builder instance (for fluid interface) + * @param mixed $value + * @param StringUtil $util + * @param ComparatorFactory $comparatorFactory */ - public function addStmts(array $stmts) + public function __construct($value, StringUtil $util = null, ComparatorFactory $comparatorFactory = null) { - foreach ($stmts as $stmt) { - $this->addStmt($stmt); - } - return $this; + $this->value = $value; + $this->util = $util ?: new StringUtil(); + $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); } /** - * Sets doc comment for the declaration. - * - * @param PhpParser\Comment\Doc|string $docComment Doc comment to set + * Scores 10 if argument matches preset value. * - * @return $this The builder instance (for fluid interface) - */ - public function setDocComment($docComment) - { - $this->attributes['comments'] = [\PHPUnit\PhpParser\BuilderHelpers::normalizeDocComment($docComment)]; - return $this; - } -} -and($trait); + if (\is_object($argument) && \is_object($this->value)) { + $comparator = $this->comparatorFactory->getComparatorFor($argument, $this->value); + try { + $comparator->assertEquals($argument, $this->value); + return 10; + } catch (ComparisonFailure $failure) { + return \false; + } + } + // If either one is an object it should be castable to a string + if (\is_object($argument) xor \is_object($this->value)) { + if (\is_object($argument) && !\method_exists($argument, '__toString')) { + return \false; + } + if (\is_object($this->value) && !\method_exists($this->value, '__toString')) { + return \false; + } + } elseif (\is_numeric($argument) && \is_numeric($this->value)) { + // noop + } elseif (\gettype($argument) !== \gettype($this->value)) { + return \false; } + return $argument == $this->value ? 10 : \false; } /** - * Adds used trait. - * - * @param Node\Name|string $trait Trait name + * Returns preset value against which token checks arguments. * - * @return $this The builder instance (for fluid interface) + * @return mixed */ - public function and($trait) + public function getValue() { - $this->traits[] = \PHPUnit\PhpParser\BuilderHelpers::normalizeName($trait); - return $this; + return $this->value; } /** - * Adds trait adaptation. - * - * @param Stmt\TraitUseAdaptation|Builder\TraitUseAdaptation $adaptation Trait adaptation + * Returns false. * - * @return $this The builder instance (for fluid interface) + * @return bool */ - public function with($adaptation) + public function isLast() { - $adaptation = \PHPUnit\PhpParser\BuilderHelpers::normalizeNode($adaptation); - if (!$adaptation instanceof \PHPUnit\PhpParser\Node\Stmt\TraitUseAdaptation) { - throw new \LogicException('Adaptation must have type TraitUseAdaptation'); - } - $this->adaptations[] = $adaptation; - return $this; + return \false; } /** - * Returns the built node. + * Returns string representation for token. * - * @return Node The built node + * @return string */ - public function getNode() : \PHPUnit\PhpParser\Node + public function __toString() { - return new \PHPUnit\PhpParser\Node\Stmt\TraitUse($this->traits, $this->adaptations); + if (null === $this->string) { + $this->string = \sprintf('exact(%s)', $this->util->stringify($this->value)); + } + return $this->string; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Argument\Token; -use PHPUnit\PhpParser; -use PHPUnit\PhpParser\BuilderHelpers; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Stmt; -class Method extends \PHPUnit\PhpParser\Builder\FunctionLike +/** + * Array elements count token. + * + * @author Boris Mikhaylov + */ +class ArrayCountToken implements \Prophecy\Argument\Token\TokenInterface { - protected $name; - protected $flags = 0; - /** @var array|null */ - protected $stmts = []; + private $count; /** - * Creates a method builder. + * @param integer $value + */ + public function __construct($value) + { + $this->count = $value; + } + /** + * Scores 6 when argument has preset number of elements. * - * @param string $name Name of the method + * @param $argument + * + * @return bool|int */ - public function __construct(string $name) + public function scoreArgument($argument) { - $this->name = $name; + return $this->isCountable($argument) && $this->hasProperCount($argument) ? 6 : \false; } /** - * Makes the method public. + * Returns false. * - * @return $this The builder instance (for fluid interface) + * @return boolean */ - public function makePublic() + public function isLast() { - $this->flags = \PHPUnit\PhpParser\BuilderHelpers::addModifier($this->flags, \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC); - return $this; + return \false; } /** - * Makes the method protected. + * Returns string representation for token. * - * @return $this The builder instance (for fluid interface) + * @return string */ - public function makeProtected() + public function __toString() { - $this->flags = \PHPUnit\PhpParser\BuilderHelpers::addModifier($this->flags, \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED); - return $this; + return \sprintf('count(%s)', $this->count); } /** - * Makes the method private. + * Returns true if object is either array or instance of \Countable * - * @return $this The builder instance (for fluid interface) + * @param $argument + * @return bool */ - public function makePrivate() + private function isCountable($argument) { - $this->flags = \PHPUnit\PhpParser\BuilderHelpers::addModifier($this->flags, \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE); - return $this; + return \is_array($argument) || $argument instanceof \Countable; } /** - * Makes the method static. + * Returns true if $argument has expected number of elements * - * @return $this The builder instance (for fluid interface) + * @param array|\Countable $argument + * + * @return bool */ - public function makeStatic() + private function hasProperCount($argument) { - $this->flags = \PHPUnit\PhpParser\BuilderHelpers::addModifier($this->flags, \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_STATIC); - return $this; + return $this->count === \count($argument); } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Argument\Token; + +/** + * String contains token. + * + * @author Peter Mitchell + */ +class StringContainsToken implements \Prophecy\Argument\Token\TokenInterface +{ + private $value; /** - * Makes the method abstract. + * Initializes token. * - * @return $this The builder instance (for fluid interface) + * @param string $value */ - public function makeAbstract() + public function __construct($value) { - if (!empty($this->stmts)) { - throw new \LogicException('Cannot make method with statements abstract'); - } - $this->flags = \PHPUnit\PhpParser\BuilderHelpers::addModifier($this->flags, \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_ABSTRACT); - $this->stmts = null; - // abstract methods don't have statements - return $this; + $this->value = $value; + } + public function scoreArgument($argument) + { + return \is_string($argument) && \strpos($argument, $this->value) !== \false ? 6 : \false; } /** - * Makes the method final. + * Returns preset value against which token checks arguments. * - * @return $this The builder instance (for fluid interface) + * @return mixed */ - public function makeFinal() + public function getValue() { - $this->flags = \PHPUnit\PhpParser\BuilderHelpers::addModifier($this->flags, \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_FINAL); - return $this; + return $this->value; } /** - * Adds a statement. - * - * @param Node|PhpParser\Builder $stmt The statement to add + * Returns false. * - * @return $this The builder instance (for fluid interface) + * @return bool */ - public function addStmt($stmt) + public function isLast() { - if (null === $this->stmts) { - throw new \LogicException('Cannot add statements to an abstract method'); - } - $this->stmts[] = \PHPUnit\PhpParser\BuilderHelpers::normalizeStmt($stmt); - return $this; + return \false; } /** - * Returns the built method node. + * Returns string representation for token. * - * @return Stmt\ClassMethod The built method node + * @return string */ - public function getNode() : \PHPUnit\PhpParser\Node + public function __toString() { - return new \PHPUnit\PhpParser\Node\Stmt\ClassMethod($this->name, ['flags' => $this->flags, 'byRef' => $this->returnByRef, 'params' => $this->params, 'returnType' => $this->returnType, 'stmts' => $this->stmts], $this->attributes); + return \sprintf('contains("%s")', $this->value); } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Argument\Token; -use PHPUnit\PhpParser; -use PHPUnit\PhpParser\BuilderHelpers; -use PHPUnit\PhpParser\Node\Name; -use PHPUnit\PhpParser\Node\Stmt; -class Interface_ extends \PHPUnit\PhpParser\Builder\Declaration +/** + * Argument token interface. + * + * @author Konstantin Kudryashov + */ +interface TokenInterface { - protected $name; - protected $extends = []; - protected $constants = []; - protected $methods = []; - /** - * Creates an interface builder. - * - * @param string $name Name of the interface - */ - public function __construct(string $name) - { - $this->name = $name; - } /** - * Extends one or more interfaces. + * Calculates token match score for provided argument. * - * @param Name|string ...$interfaces Names of interfaces to extend + * @param $argument * - * @return $this The builder instance (for fluid interface) + * @return bool|int */ - public function extend(...$interfaces) - { - foreach ($interfaces as $interface) { - $this->extends[] = \PHPUnit\PhpParser\BuilderHelpers::normalizeName($interface); - } - return $this; - } + public function scoreArgument($argument); /** - * Adds a statement. - * - * @param Stmt|PhpParser\Builder $stmt The statement to add + * Returns true if this token prevents check of other tokens (is last one). * - * @return $this The builder instance (for fluid interface) + * @return bool|int */ - public function addStmt($stmt) - { - $stmt = \PHPUnit\PhpParser\BuilderHelpers::normalizeNode($stmt); - if ($stmt instanceof \PHPUnit\PhpParser\Node\Stmt\ClassConst) { - $this->constants[] = $stmt; - } elseif ($stmt instanceof \PHPUnit\PhpParser\Node\Stmt\ClassMethod) { - // we erase all statements in the body of an interface method - $stmt->stmts = null; - $this->methods[] = $stmt; - } else { - throw new \LogicException(\sprintf('Unexpected node of type "%s"', $stmt->getType())); - } - return $this; - } + public function isLast(); /** - * Returns the built interface node. + * Returns string representation for token. * - * @return Stmt\Interface_ The built interface node + * @return string */ - public function getNode() : \PHPUnit\PhpParser\Node - { - return new \PHPUnit\PhpParser\Node\Stmt\Interface_($this->name, ['extends' => $this->extends, 'stmts' => \array_merge($this->constants, $this->methods)], $this->attributes); - } + public function __toString(); } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Argument\Token; -use PHPUnit\PhpParser; -use PHPUnit\PhpParser\BuilderHelpers; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Stmt; -class Function_ extends \PHPUnit\PhpParser\Builder\FunctionLike +use Prophecy\Exception\InvalidArgumentException; +/** + * Callback-verified token. + * + * @author Konstantin Kudryashov + */ +class CallbackToken implements \Prophecy\Argument\Token\TokenInterface { - protected $name; - protected $stmts = []; + private $callback; /** - * Creates a function builder. + * Initializes token. * - * @param string $name Name of the function + * @param callable $callback + * + * @throws \Prophecy\Exception\InvalidArgumentException */ - public function __construct(string $name) + public function __construct($callback) { - $this->name = $name; + if (!\is_callable($callback)) { + throw new InvalidArgumentException(\sprintf('Callable expected as an argument to CallbackToken, but got %s.', \gettype($callback))); + } + $this->callback = $callback; } /** - * Adds a statement. + * Scores 7 if callback returns true, false otherwise. * - * @param Node|PhpParser\Builder $stmt The statement to add + * @param $argument * - * @return $this The builder instance (for fluid interface) + * @return bool|int */ - public function addStmt($stmt) + public function scoreArgument($argument) { - $this->stmts[] = \PHPUnit\PhpParser\BuilderHelpers::normalizeStmt($stmt); - return $this; + return \call_user_func($this->callback, $argument) ? 7 : \false; } /** - * Returns the built function node. + * Returns false. * - * @return Stmt\Function_ The built function node + * @return bool + */ + public function isLast() + { + return \false; + } + /** + * Returns string representation for token. + * + * @return string */ - public function getNode() : \PHPUnit\PhpParser\Node + public function __toString() { - return new \PHPUnit\PhpParser\Node\Stmt\Function_($this->name, ['byRef' => $this->returnByRef, 'params' => $this->params, 'returnType' => $this->returnType, 'stmts' => $this->stmts], $this->attributes); + return 'callback()'; } } + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Prophecy\Argument; -use PHPUnit\PhpParser\Builder; -use PHPUnit\PhpParser\BuilderHelpers; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Stmt; -class Use_ implements \PHPUnit\PhpParser\Builder +/** + * Arguments wildcarding. + * + * @author Konstantin Kudryashov + */ +class ArgumentsWildcard { - protected $name; - protected $type; - protected $alias = null; /** - * Creates a name use (alias) builder. + * @var Token\TokenInterface[] + */ + private $tokens = array(); + private $string; + /** + * Initializes wildcard. * - * @param Node\Name|string $name Name of the entity (namespace, class, function, constant) to alias - * @param int $type One of the Stmt\Use_::TYPE_* constants + * @param array $arguments Array of argument tokens or values */ - public function __construct($name, int $type) + public function __construct(array $arguments) { - $this->name = \PHPUnit\PhpParser\BuilderHelpers::normalizeName($name); - $this->type = $type; + foreach ($arguments as $argument) { + if (!$argument instanceof \Prophecy\Argument\Token\TokenInterface) { + $argument = new \Prophecy\Argument\Token\ExactValueToken($argument); + } + $this->tokens[] = $argument; + } } /** - * Sets alias for used name. + * Calculates wildcard match score for provided arguments. * - * @param string $alias Alias to use (last component of full name by default) + * @param array $arguments * - * @return $this The builder instance (for fluid interface) + * @return false|int False OR integer score (higher - better) */ - public function as(string $alias) + public function scoreArguments(array $arguments) { - $this->alias = $alias; - return $this; + if (0 == \count($arguments) && 0 == \count($this->tokens)) { + return 1; + } + $arguments = \array_values($arguments); + $totalScore = 0; + foreach ($this->tokens as $i => $token) { + $argument = isset($arguments[$i]) ? $arguments[$i] : null; + if (1 >= ($score = $token->scoreArgument($argument))) { + return \false; + } + $totalScore += $score; + if (\true === $token->isLast()) { + return $totalScore; + } + } + if (\count($arguments) > \count($this->tokens)) { + return \false; + } + return $totalScore; } /** - * Returns the built node. + * Returns string representation for wildcard. * - * @return Node The built node + * @return string + */ + public function __toString() + { + if (null === $this->string) { + $this->string = \implode(', ', \array_map(function ($token) { + return (string) $token; + }, $this->tokens)); + } + return $this->string; + } + /** + * @return array */ - public function getNode() : \PHPUnit\PhpParser\Node + public function getTokens() { - return new \PHPUnit\PhpParser\Node\Stmt\Use_([new \PHPUnit\PhpParser\Node\Stmt\UseUse($this->name, $this->alias)], $this->type); + return $this->tokens; } } +Copyright (c) 2013 Konstantin Kudryashov +Copyright (c) 2013 Marcello Duarte + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Diff\Output; -use PHPUnit\PhpParser\Builder; -use PHPUnit\PhpParser\BuilderHelpers; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Stmt; -class TraitUseAdaptation implements \PHPUnit\PhpParser\Builder +use function fclose; +use function fopen; +use function fwrite; +use function stream_get_contents; +use function substr; +use PHPUnit\SebastianBergmann\Diff\Differ; +/** + * Builds a diff string representation in a loose unified diff format + * listing only changes lines. Does not include line numbers. + */ +final class DiffOnlyOutputBuilder implements DiffOutputBuilderInterface { - const TYPE_UNDEFINED = 0; - const TYPE_ALIAS = 1; - const TYPE_PRECEDENCE = 2; - /** @var int Type of building adaptation */ - protected $type; - protected $trait; - protected $method; - protected $modifier = null; - protected $alias = null; - protected $insteadof = []; /** - * Creates a trait use adaptation builder. - * - * @param Node\Name|string|null $trait Name of adaptated trait - * @param Node\Identifier|string $method Name of adaptated method + * @var string */ - public function __construct($trait, $method) + private $header; + public function __construct(string $header = "--- Original\n+++ New\n") { - $this->type = self::TYPE_UNDEFINED; - $this->trait = \is_null($trait) ? null : \PHPUnit\PhpParser\BuilderHelpers::normalizeName($trait); - $this->method = \PHPUnit\PhpParser\BuilderHelpers::normalizeIdentifier($method); + $this->header = $header; } - /** - * Sets alias of method. - * - * @param Node\Identifier|string $alias Alias for adaptated method - * - * @return $this The builder instance (for fluid interface) - */ - public function as($alias) + public function getDiff(array $diff) : string { - if ($this->type === self::TYPE_UNDEFINED) { - $this->type = self::TYPE_ALIAS; + $buffer = fopen('php://memory', 'r+b'); + if ('' !== $this->header) { + fwrite($buffer, $this->header); + if ("\n" !== substr($this->header, -1, 1)) { + fwrite($buffer, "\n"); + } } - if ($this->type !== self::TYPE_ALIAS) { - throw new \LogicException('Cannot set alias for not alias adaptation buider'); + foreach ($diff as $diffEntry) { + if ($diffEntry[1] === Differ::ADDED) { + fwrite($buffer, '+' . $diffEntry[0]); + } elseif ($diffEntry[1] === Differ::REMOVED) { + fwrite($buffer, '-' . $diffEntry[0]); + } elseif ($diffEntry[1] === Differ::DIFF_LINE_END_WARNING) { + fwrite($buffer, ' ' . $diffEntry[0]); + continue; + // Warnings should not be tested for line break, it will always be there + } else { + /* Not changed (old) 0 */ + continue; + // we didn't write the non changs line, so do not add a line break either + } + $lc = substr($diffEntry[0], -1); + if ($lc !== "\n" && $lc !== "\r") { + fwrite($buffer, "\n"); + // \No newline at end of file + } } - $this->alias = $alias; - return $this; + $diff = stream_get_contents($buffer, -1, 0); + fclose($buffer); + return $diff; } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Diff\Output; + +/** + * Defines how an output builder should take a generated + * diff array and return a string representation of that diff. + */ +interface DiffOutputBuilderInterface +{ + public function getDiff(array $diff) : string; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Diff\Output; + +use function array_splice; +use function count; +use function fclose; +use function fopen; +use function fwrite; +use function max; +use function min; +use function stream_get_contents; +use function strlen; +use function substr; +use PHPUnit\SebastianBergmann\Diff\Differ; +/** + * Builds a diff string representation in unified diff format in chunks. + */ +final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder +{ /** - * Sets adaptated method public. - * - * @return $this The builder instance (for fluid interface) + * @var bool */ - public function makePublic() - { - $this->setModifier(\PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC); - return $this; - } + private $collapseRanges = \true; /** - * Sets adaptated method protected. - * - * @return $this The builder instance (for fluid interface) + * @var int >= 0 */ - public function makeProtected() - { - $this->setModifier(\PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED); - return $this; - } + private $commonLineThreshold = 6; /** - * Sets adaptated method private. - * - * @return $this The builder instance (for fluid interface) + * @var int >= 0 */ - public function makePrivate() - { - $this->setModifier(\PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE); - return $this; - } + private $contextLines = 3; /** - * Adds overwritten traits. - * - * @param Node\Name|string ...$traits Traits for overwrite - * - * @return $this The builder instance (for fluid interface) + * @var string */ - public function insteadof(...$traits) + private $header; + /** + * @var bool + */ + private $addLineNumbers; + public function __construct(string $header = "--- Original\n+++ New\n", bool $addLineNumbers = \false) { - if ($this->type === self::TYPE_UNDEFINED) { - if (\is_null($this->trait)) { - throw new \LogicException('Precedence adaptation must have trait'); + $this->header = $header; + $this->addLineNumbers = $addLineNumbers; + } + public function getDiff(array $diff) : string + { + $buffer = fopen('php://memory', 'r+b'); + if ('' !== $this->header) { + fwrite($buffer, $this->header); + if ("\n" !== substr($this->header, -1, 1)) { + fwrite($buffer, "\n"); + } + } + if (0 !== count($diff)) { + $this->writeDiffHunks($buffer, $diff); + } + $diff = stream_get_contents($buffer, -1, 0); + fclose($buffer); + // If the diff is non-empty and last char is not a linebreak: add it. + // This might happen when both the `from` and `to` do not have a trailing linebreak + $last = substr($diff, -1); + return 0 !== strlen($diff) && "\n" !== $last && "\r" !== $last ? $diff . "\n" : $diff; + } + private function writeDiffHunks($output, array $diff) : void + { + // detect "No newline at end of file" and insert into `$diff` if needed + $upperLimit = count($diff); + if (0 === $diff[$upperLimit - 1][1]) { + $lc = substr($diff[$upperLimit - 1][0], -1); + if ("\n" !== $lc) { + array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); + } + } else { + // search back for the last `+` and `-` line, + // check if has trailing linebreak, else add under it warning under it + $toFind = [1 => \true, 2 => \true]; + for ($i = $upperLimit - 1; $i >= 0; --$i) { + if (isset($toFind[$diff[$i][1]])) { + unset($toFind[$diff[$i][1]]); + $lc = substr($diff[$i][0], -1); + if ("\n" !== $lc) { + array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); + } + if (!count($toFind)) { + break; + } + } + } + } + // write hunks to output buffer + $cutOff = max($this->commonLineThreshold, $this->contextLines); + $hunkCapture = \false; + $sameCount = $toRange = $fromRange = 0; + $toStart = $fromStart = 1; + $i = 0; + /** @var int $i */ + foreach ($diff as $i => $entry) { + if (0 === $entry[1]) { + // same + if (\false === $hunkCapture) { + ++$fromStart; + ++$toStart; + continue; + } + ++$sameCount; + ++$toRange; + ++$fromRange; + if ($sameCount === $cutOff) { + $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; + // note: $contextEndOffset = $this->contextLines; + // + // because we never go beyond the end of the diff. + // with the cutoff/contextlines here the follow is never true; + // + // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { + // $contextEndOffset = count($diff) - 1; + // } + // + // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop + $this->writeHunk($diff, $hunkCapture - $contextStartOffset, $i - $cutOff + $this->contextLines + 1, $fromStart - $contextStartOffset, $fromRange - $cutOff + $contextStartOffset + $this->contextLines, $toStart - $contextStartOffset, $toRange - $cutOff + $contextStartOffset + $this->contextLines, $output); + $fromStart += $fromRange; + $toStart += $toRange; + $hunkCapture = \false; + $sameCount = $toRange = $fromRange = 0; + } + continue; + } + $sameCount = 0; + if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { + continue; + } + if (\false === $hunkCapture) { + $hunkCapture = $i; + } + if (Differ::ADDED === $entry[1]) { + ++$toRange; + } + if (Differ::REMOVED === $entry[1]) { + ++$fromRange; } - $this->type = self::TYPE_PRECEDENCE; - } - if ($this->type !== self::TYPE_PRECEDENCE) { - throw new \LogicException('Cannot add overwritten traits for not precedence adaptation buider'); } - foreach ($traits as $trait) { - $this->insteadof[] = \PHPUnit\PhpParser\BuilderHelpers::normalizeName($trait); + if (\false === $hunkCapture) { + return; } - return $this; + // we end here when cutoff (commonLineThreshold) was not reached, but we where capturing a hunk, + // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold + $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; + // prevent trying to write out more common lines than there are in the diff _and_ + // do not write more than configured through the context lines + $contextEndOffset = min($sameCount, $this->contextLines); + $fromRange -= $sameCount; + $toRange -= $sameCount; + $this->writeHunk($diff, $hunkCapture - $contextStartOffset, $i - $sameCount + $contextEndOffset + 1, $fromStart - $contextStartOffset, $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, $output); } - protected function setModifier(int $modifier) + private function writeHunk(array $diff, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output) : void { - if ($this->type === self::TYPE_UNDEFINED) { - $this->type = self::TYPE_ALIAS; - } - if ($this->type !== self::TYPE_ALIAS) { - throw new \LogicException('Cannot set access modifier for not alias adaptation buider'); - } - if (\is_null($this->modifier)) { - $this->modifier = $modifier; + if ($this->addLineNumbers) { + fwrite($output, '@@ -' . $fromStart); + if (!$this->collapseRanges || 1 !== $fromRange) { + fwrite($output, ',' . $fromRange); + } + fwrite($output, ' +' . $toStart); + if (!$this->collapseRanges || 1 !== $toRange) { + fwrite($output, ',' . $toRange); + } + fwrite($output, " @@\n"); } else { - throw new \LogicException('Multiple access type modifiers are not allowed'); + fwrite($output, "@@ @@\n"); } - } - /** - * Returns the built node. - * - * @return Node The built node - */ - public function getNode() : \PHPUnit\PhpParser\Node - { - switch ($this->type) { - case self::TYPE_ALIAS: - return new \PHPUnit\PhpParser\Node\Stmt\TraitUseAdaptation\Alias($this->trait, $this->method, $this->modifier, $this->alias); - case self::TYPE_PRECEDENCE: - return new \PHPUnit\PhpParser\Node\Stmt\TraitUseAdaptation\Precedence($this->trait, $this->method, $this->insteadof); - default: - throw new \LogicException('Type of adaptation is not defined'); + for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { + if ($diff[$i][1] === Differ::ADDED) { + fwrite($output, '+' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::REMOVED) { + fwrite($output, '-' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::OLD) { + fwrite($output, ' ' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { + fwrite($output, "\n"); + // $diff[$i][0] + } else { + /* Not changed (old) Differ::OLD or Warning Differ::DIFF_LINE_END_WARNING */ + fwrite($output, ' ' . $diff[$i][0]); + } } } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Diff\Output; -use PHPUnit\PhpParser; -use PHPUnit\PhpParser\BuilderHelpers; -use PHPUnit\PhpParser\Node\Stmt; -class Trait_ extends \PHPUnit\PhpParser\Builder\Declaration +use function array_merge; +use function array_splice; +use function count; +use function fclose; +use function fopen; +use function fwrite; +use function is_bool; +use function is_int; +use function is_string; +use function max; +use function min; +use function sprintf; +use function stream_get_contents; +use function substr; +use PHPUnit\SebastianBergmann\Diff\ConfigurationException; +use PHPUnit\SebastianBergmann\Diff\Differ; +/** + * Strict Unified diff output builder. + * + * Generates (strict) Unified diff's (unidiffs) with hunks. + */ +final class StrictUnifiedDiffOutputBuilder implements DiffOutputBuilderInterface { - protected $name; - protected $uses = []; - protected $properties = []; - protected $methods = []; - /** - * Creates an interface builder. - * - * @param string $name Name of the interface - */ - public function __construct(string $name) - { - $this->name = $name; - } + private static $default = [ + 'collapseRanges' => \true, + // ranges of length one are rendered with the trailing `,1` + 'commonLineThreshold' => 6, + // number of same lines before ending a new hunk and creating a new one (if needed) + 'contextLines' => 3, + // like `diff: -u, -U NUM, --unified[=NUM]`, for patch/git apply compatibility best to keep at least @ 3 + 'fromFile' => null, + 'fromFileDate' => null, + 'toFile' => null, + 'toFileDate' => null, + ]; /** - * Adds a statement. - * - * @param Stmt|PhpParser\Builder $stmt The statement to add - * - * @return $this The builder instance (for fluid interface) + * @var bool */ - public function addStmt($stmt) - { - $stmt = \PHPUnit\PhpParser\BuilderHelpers::normalizeNode($stmt); - if ($stmt instanceof \PHPUnit\PhpParser\Node\Stmt\Property) { - $this->properties[] = $stmt; - } elseif ($stmt instanceof \PHPUnit\PhpParser\Node\Stmt\ClassMethod) { - $this->methods[] = $stmt; - } elseif ($stmt instanceof \PHPUnit\PhpParser\Node\Stmt\TraitUse) { - $this->uses[] = $stmt; - } else { - throw new \LogicException(\sprintf('Unexpected node of type "%s"', $stmt->getType())); - } - return $this; - } + private $changed; /** - * Returns the built trait node. - * - * @return Stmt\Trait_ The built interface node + * @var bool */ - public function getNode() : \PHPUnit\PhpParser\Node - { - return new \PHPUnit\PhpParser\Node\Stmt\Trait_($this->name, ['stmts' => \array_merge($this->uses, $this->properties, $this->methods)], $this->attributes); - } -} - [aliasName => originalName]] */ - protected $aliases = []; - /** @var Name[][] Same as $aliases but preserving original case */ - protected $origAliases = []; - /** @var ErrorHandler Error handler */ - protected $errorHandler; + private $collapseRanges; /** - * Create a name context. - * - * @param ErrorHandler $errorHandler Error handling used to report errors + * @var int >= 0 */ - public function __construct(\PHPUnit\PhpParser\ErrorHandler $errorHandler) - { - $this->errorHandler = $errorHandler; - } + private $commonLineThreshold; /** - * Start a new namespace. - * - * This also resets the alias table. - * - * @param Name|null $namespace Null is the global namespace + * @var string */ - public function startNamespace(\PHPUnit\PhpParser\Node\Name $namespace = null) - { - $this->namespace = $namespace; - $this->origAliases = $this->aliases = [\PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_NORMAL => [], \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_FUNCTION => [], \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_CONSTANT => []]; - } + private $header; /** - * Add an alias / import. - * - * @param Name $name Original name - * @param string $aliasName Aliased name - * @param int $type One of Stmt\Use_::TYPE_* - * @param array $errorAttrs Attributes to use to report an error + * @var int >= 0 */ - public function addAlias(\PHPUnit\PhpParser\Node\Name $name, string $aliasName, int $type, array $errorAttrs = []) + private $contextLines; + public function __construct(array $options = []) { - // Constant names are case sensitive, everything else case insensitive - if ($type === \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_CONSTANT) { - $aliasLookupName = $aliasName; - } else { - $aliasLookupName = \strtolower($aliasName); + $options = array_merge(self::$default, $options); + if (!is_bool($options['collapseRanges'])) { + throw new ConfigurationException('collapseRanges', 'a bool', $options['collapseRanges']); } - if (isset($this->aliases[$type][$aliasLookupName])) { - $typeStringMap = [\PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_NORMAL => '', \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_FUNCTION => 'function ', \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_CONSTANT => 'const ']; - $this->errorHandler->handleError(new \PHPUnit\PhpParser\Error(\sprintf('Cannot use %s%s as %s because the name is already in use', $typeStringMap[$type], $name, $aliasName), $errorAttrs)); - return; + if (!is_int($options['contextLines']) || $options['contextLines'] < 0) { + throw new ConfigurationException('contextLines', 'an int >= 0', $options['contextLines']); } - $this->aliases[$type][$aliasLookupName] = $name; - $this->origAliases[$type][$aliasName] = $name; + if (!is_int($options['commonLineThreshold']) || $options['commonLineThreshold'] <= 0) { + throw new ConfigurationException('commonLineThreshold', 'an int > 0', $options['commonLineThreshold']); + } + $this->assertString($options, 'fromFile'); + $this->assertString($options, 'toFile'); + $this->assertStringOrNull($options, 'fromFileDate'); + $this->assertStringOrNull($options, 'toFileDate'); + $this->header = sprintf("--- %s%s\n+++ %s%s\n", $options['fromFile'], null === $options['fromFileDate'] ? '' : "\t" . $options['fromFileDate'], $options['toFile'], null === $options['toFileDate'] ? '' : "\t" . $options['toFileDate']); + $this->collapseRanges = $options['collapseRanges']; + $this->commonLineThreshold = $options['commonLineThreshold']; + $this->contextLines = $options['contextLines']; } - /** - * Get current namespace. - * - * @return null|Name Namespace (or null if global namespace) - */ - public function getNamespace() + public function getDiff(array $diff) : string { - return $this->namespace; + if (0 === count($diff)) { + return ''; + } + $this->changed = \false; + $buffer = fopen('php://memory', 'r+b'); + fwrite($buffer, $this->header); + $this->writeDiffHunks($buffer, $diff); + if (!$this->changed) { + fclose($buffer); + return ''; + } + $diff = stream_get_contents($buffer, -1, 0); + fclose($buffer); + // If the last char is not a linebreak: add it. + // This might happen when both the `from` and `to` do not have a trailing linebreak + $last = substr($diff, -1); + return "\n" !== $last && "\r" !== $last ? $diff . "\n" : $diff; } - /** - * Get resolved name. - * - * @param Name $name Name to resolve - * @param int $type One of Stmt\Use_::TYPE_{FUNCTION|CONSTANT} - * - * @return null|Name Resolved name, or null if static resolution is not possible - */ - public function getResolvedName(\PHPUnit\PhpParser\Node\Name $name, int $type) + private function writeDiffHunks($output, array $diff) : void { - // don't resolve special class names - if ($type === \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_NORMAL && $name->isSpecialClassName()) { - if (!$name->isUnqualified()) { - $this->errorHandler->handleError(new \PHPUnit\PhpParser\Error(\sprintf("'\\%s' is an invalid class name", $name->toString()), $name->getAttributes())); + // detect "No newline at end of file" and insert into `$diff` if needed + $upperLimit = count($diff); + if (0 === $diff[$upperLimit - 1][1]) { + $lc = substr($diff[$upperLimit - 1][0], -1); + if ("\n" !== $lc) { + array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); + } + } else { + // search back for the last `+` and `-` line, + // check if has trailing linebreak, else add under it warning under it + $toFind = [1 => \true, 2 => \true]; + for ($i = $upperLimit - 1; $i >= 0; --$i) { + if (isset($toFind[$diff[$i][1]])) { + unset($toFind[$diff[$i][1]]); + $lc = substr($diff[$i][0], -1); + if ("\n" !== $lc) { + array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); + } + if (!count($toFind)) { + break; + } + } } - return $name; } - // fully qualified names are already resolved - if ($name->isFullyQualified()) { - return $name; + // write hunks to output buffer + $cutOff = max($this->commonLineThreshold, $this->contextLines); + $hunkCapture = \false; + $sameCount = $toRange = $fromRange = 0; + $toStart = $fromStart = 1; + $i = 0; + /** @var int $i */ + foreach ($diff as $i => $entry) { + if (0 === $entry[1]) { + // same + if (\false === $hunkCapture) { + ++$fromStart; + ++$toStart; + continue; + } + ++$sameCount; + ++$toRange; + ++$fromRange; + if ($sameCount === $cutOff) { + $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; + // note: $contextEndOffset = $this->contextLines; + // + // because we never go beyond the end of the diff. + // with the cutoff/contextlines here the follow is never true; + // + // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { + // $contextEndOffset = count($diff) - 1; + // } + // + // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop + $this->writeHunk($diff, $hunkCapture - $contextStartOffset, $i - $cutOff + $this->contextLines + 1, $fromStart - $contextStartOffset, $fromRange - $cutOff + $contextStartOffset + $this->contextLines, $toStart - $contextStartOffset, $toRange - $cutOff + $contextStartOffset + $this->contextLines, $output); + $fromStart += $fromRange; + $toStart += $toRange; + $hunkCapture = \false; + $sameCount = $toRange = $fromRange = 0; + } + continue; + } + $sameCount = 0; + if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { + continue; + } + $this->changed = \true; + if (\false === $hunkCapture) { + $hunkCapture = $i; + } + if (Differ::ADDED === $entry[1]) { + // added + ++$toRange; + } + if (Differ::REMOVED === $entry[1]) { + // removed + ++$fromRange; + } } - // Try to resolve aliases - if (null !== ($resolvedName = $this->resolveAlias($name, $type))) { - return $resolvedName; + if (\false === $hunkCapture) { + return; } - if ($type !== \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_NORMAL && $name->isUnqualified()) { - if (null === $this->namespace) { - // outside of a namespace unaliased unqualified is same as fully qualified - return new \PHPUnit\PhpParser\Node\Name\FullyQualified($name, $name->getAttributes()); + // we end here when cutoff (commonLineThreshold) was not reached, but we where capturing a hunk, + // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold + $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; + // prevent trying to write out more common lines than there are in the diff _and_ + // do not write more than configured through the context lines + $contextEndOffset = min($sameCount, $this->contextLines); + $fromRange -= $sameCount; + $toRange -= $sameCount; + $this->writeHunk($diff, $hunkCapture - $contextStartOffset, $i - $sameCount + $contextEndOffset + 1, $fromStart - $contextStartOffset, $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, $output); + } + private function writeHunk(array $diff, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output) : void + { + fwrite($output, '@@ -' . $fromStart); + if (!$this->collapseRanges || 1 !== $fromRange) { + fwrite($output, ',' . $fromRange); + } + fwrite($output, ' +' . $toStart); + if (!$this->collapseRanges || 1 !== $toRange) { + fwrite($output, ',' . $toRange); + } + fwrite($output, " @@\n"); + for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { + if ($diff[$i][1] === Differ::ADDED) { + $this->changed = \true; + fwrite($output, '+' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::REMOVED) { + $this->changed = \true; + fwrite($output, '-' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::OLD) { + fwrite($output, ' ' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { + $this->changed = \true; + fwrite($output, $diff[$i][0]); } - // Cannot resolve statically - return null; + //} elseif ($diff[$i][1] === Differ::DIFF_LINE_END_WARNING) { // custom comment inserted by PHPUnit/diff package + // skip + //} else { + // unknown/invalid + //} } - // if no alias exists prepend current namespace - return \PHPUnit\PhpParser\Node\Name\FullyQualified::concat($this->namespace, $name, $name->getAttributes()); } - /** - * Get resolved class name. - * - * @param Name $name Class ame to resolve - * - * @return Name Resolved name - */ - public function getResolvedClassName(\PHPUnit\PhpParser\Node\Name $name) : \PHPUnit\PhpParser\Node\Name + private function assertString(array $options, string $option) : void + { + if (!is_string($options[$option])) { + throw new ConfigurationException($option, 'a string', $options[$option]); + } + } + private function assertStringOrNull(array $options, string $option) : void { - return $this->getResolvedName($name, \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_NORMAL); + if (null !== $options[$option] && !is_string($options[$option])) { + throw new ConfigurationException($option, 'a string or ', $options[$option]); + } } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Diff\Output; + +use function count; +abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface +{ /** - * Get possible ways of writing a fully qualified name (e.g., by making use of aliases). - * - * @param string $name Fully-qualified name (without leading namespace separator) - * @param int $type One of Stmt\Use_::TYPE_* - * - * @return Name[] Possible representations of the name + * Takes input of the diff array and returns the common parts. + * Iterates through diff line by line. */ - public function getPossibleNames(string $name, int $type) : array + protected function getCommonChunks(array $diff, int $lineThreshold = 5) : array { - $lcName = \strtolower($name); - if ($type === \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_NORMAL) { - // self, parent and static must always be unqualified - if ($lcName === "self" || $lcName === "parent" || $lcName === "static") { - return [new \PHPUnit\PhpParser\Node\Name($name)]; - } - } - // Collect possible ways to write this name, starting with the fully-qualified name - $possibleNames = [new \PHPUnit\PhpParser\Node\Name\FullyQualified($name)]; - if (null !== ($nsRelativeName = $this->getNamespaceRelativeName($name, $lcName, $type))) { - // Make sure there is no alias that makes the normally namespace-relative name - // into something else - if (null === $this->resolveAlias($nsRelativeName, $type)) { - $possibleNames[] = $nsRelativeName; - } - } - // Check for relevant namespace use statements - foreach ($this->origAliases[\PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_NORMAL] as $alias => $orig) { - $lcOrig = $orig->toLowerString(); - if (0 === \strpos($lcName, $lcOrig . '\\')) { - $possibleNames[] = new \PHPUnit\PhpParser\Node\Name($alias . \substr($name, \strlen($lcOrig))); - } - } - // Check for relevant type-specific use statements - foreach ($this->origAliases[$type] as $alias => $orig) { - if ($type === \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_CONSTANT) { - // Constants are are complicated-sensitive - $normalizedOrig = $this->normalizeConstName($orig->toString()); - if ($normalizedOrig === $this->normalizeConstName($name)) { - $possibleNames[] = new \PHPUnit\PhpParser\Node\Name($alias); + $diffSize = count($diff); + $capturing = \false; + $chunkStart = 0; + $chunkSize = 0; + $commonChunks = []; + for ($i = 0; $i < $diffSize; ++$i) { + if ($diff[$i][1] === 0) { + if ($capturing === \false) { + $capturing = \true; + $chunkStart = $i; + $chunkSize = 0; + } else { + ++$chunkSize; } - } else { - // Everything else is case-insensitive - if ($orig->toLowerString() === $lcName) { - $possibleNames[] = new \PHPUnit\PhpParser\Node\Name($alias); + } elseif ($capturing !== \false) { + if ($chunkSize >= $lineThreshold) { + $commonChunks[$chunkStart] = $chunkStart + $chunkSize; } + $capturing = \false; } } - return $possibleNames; + if ($capturing !== \false && $chunkSize >= $lineThreshold) { + $commonChunks[$chunkStart] = $chunkStart + $chunkSize; + } + return $commonChunks; } - /** - * Get shortest representation of this fully-qualified name. - * - * @param string $name Fully-qualified name (without leading namespace separator) - * @param int $type One of Stmt\Use_::TYPE_* - * - * @return Name Shortest representation - */ - public function getShortName(string $name, int $type) : \PHPUnit\PhpParser\Node\Name +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Diff; + +use function get_class; +use function gettype; +use function is_object; +use function sprintf; +use Exception; +final class ConfigurationException extends InvalidArgumentException +{ + public function __construct(string $option, string $expected, $value, int $code = 0, Exception $previous = null) { - $possibleNames = $this->getPossibleNames($name, $type); - // Find shortest name - $shortestName = null; - $shortestLength = \INF; - foreach ($possibleNames as $possibleName) { - $length = \strlen($possibleName->toCodeString()); - if ($length < $shortestLength) { - $shortestName = $possibleName; - $shortestLength = $length; - } - } - return $shortestName; + parent::__construct(sprintf('Option "%s" must be %s, got "%s".', $option, $expected, is_object($value) ? get_class($value) : (null === $value ? '' : gettype($value) . '#' . $value)), $code, $previous); } - private function resolveAlias(\PHPUnit\PhpParser\Node\Name $name, $type) +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Diff; + +use Throwable; +interface Exception extends Throwable +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Diff; + +class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Diff; + +use function array_reverse; +use function count; +use function max; +use SplFixedArray; +final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator +{ + /** + * {@inheritdoc} + */ + public function calculate(array $from, array $to) : array { - $firstPart = $name->getFirst(); - if ($name->isQualified()) { - // resolve aliases for qualified names, always against class alias table - $checkName = \strtolower($firstPart); - if (isset($this->aliases[\PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_NORMAL][$checkName])) { - $alias = $this->aliases[\PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_NORMAL][$checkName]; - return \PHPUnit\PhpParser\Node\Name\FullyQualified::concat($alias, $name->slice(1), $name->getAttributes()); - } - } elseif ($name->isUnqualified()) { - // constant aliases are case-sensitive, function aliases case-insensitive - $checkName = $type === \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_CONSTANT ? $firstPart : \strtolower($firstPart); - if (isset($this->aliases[$type][$checkName])) { - // resolve unqualified aliases - return new \PHPUnit\PhpParser\Node\Name\FullyQualified($this->aliases[$type][$checkName], $name->getAttributes()); - } + $common = []; + $fromLength = count($from); + $toLength = count($to); + $width = $fromLength + 1; + $matrix = new SplFixedArray($width * ($toLength + 1)); + for ($i = 0; $i <= $fromLength; ++$i) { + $matrix[$i] = 0; } - // No applicable aliases - return null; - } - private function getNamespaceRelativeName(string $name, string $lcName, int $type) - { - if (null === $this->namespace) { - return new \PHPUnit\PhpParser\Node\Name($name); + for ($j = 0; $j <= $toLength; ++$j) { + $matrix[$j * $width] = 0; } - if ($type === \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_CONSTANT) { - // The constants true/false/null always resolve to the global symbols, even inside a - // namespace, so they may be used without qualification - if ($lcName === "true" || $lcName === "false" || $lcName === "null") { - return new \PHPUnit\PhpParser\Node\Name($name); + for ($i = 1; $i <= $fromLength; ++$i) { + for ($j = 1; $j <= $toLength; ++$j) { + $o = $j * $width + $i; + $matrix[$o] = max($matrix[$o - 1], $matrix[$o - $width], $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0); } } - $namespacePrefix = \strtolower($this->namespace . '\\'); - if (0 === \strpos($lcName, $namespacePrefix)) { - return new \PHPUnit\PhpParser\Node\Name(\substr($name, \strlen($namespacePrefix))); - } - return null; - } - private function normalizeConstName(string $name) - { - $nsSep = \strrpos($name, '\\'); - if (\false === $nsSep) { - return $name; + $i = $fromLength; + $j = $toLength; + while ($i > 0 && $j > 0) { + if ($from[$i - 1] === $to[$j - 1]) { + $common[] = $from[$i - 1]; + --$i; + --$j; + } else { + $o = $j * $width + $i; + if ($matrix[$o - $width] > $matrix[$o - 1]) { + --$j; + } else { + --$i; + } + } } - // Constants have case-insensitive namespace and case-sensitive short-name - $ns = \substr($name, 0, $nsSep); - $shortName = \substr($name, $nsSep + 1); - return \strtolower($ns) . '\\' . $shortName; + return array_reverse($common); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Diff; -use PHPUnit\PhpParser\Error; -use PHPUnit\PhpParser\ErrorHandler; -use PHPUnit\PhpParser\Parser; -class Multiple implements \PHPUnit\PhpParser\Parser +use function array_fill; +use function array_merge; +use function array_reverse; +use function array_slice; +use function count; +use function in_array; +use function max; +final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator { - /** @var Parser[] List of parsers to try, in order of preference */ - private $parsers; /** - * Create a parser which will try multiple parsers in an order of preference. - * - * Parsers will be invoked in the order they're provided to the constructor. If one of the - * parsers runs without throwing, it's output is returned. Otherwise the exception that the - * first parser generated is thrown. - * - * @param Parser[] $parsers + * {@inheritdoc} */ - public function __construct(array $parsers) - { - $this->parsers = $parsers; - } - public function parse(string $code, \PHPUnit\PhpParser\ErrorHandler $errorHandler = null) + public function calculate(array $from, array $to) : array { - if (null === $errorHandler) { - $errorHandler = new \PHPUnit\PhpParser\ErrorHandler\Throwing(); + $cFrom = count($from); + $cTo = count($to); + if ($cFrom === 0) { + return []; } - list($firstStmts, $firstError) = $this->tryParse($this->parsers[0], $errorHandler, $code); - if ($firstError === null) { - return $firstStmts; + if ($cFrom === 1) { + if (in_array($from[0], $to, \true)) { + return [$from[0]]; + } + return []; } - for ($i = 1, $c = \count($this->parsers); $i < $c; ++$i) { - list($stmts, $error) = $this->tryParse($this->parsers[$i], $errorHandler, $code); - if ($error === null) { - return $stmts; + $i = (int) ($cFrom / 2); + $fromStart = array_slice($from, 0, $i); + $fromEnd = array_slice($from, $i); + $llB = $this->length($fromStart, $to); + $llE = $this->length(array_reverse($fromEnd), array_reverse($to)); + $jMax = 0; + $max = 0; + for ($j = 0; $j <= $cTo; $j++) { + $m = $llB[$j] + $llE[$cTo - $j]; + if ($m >= $max) { + $max = $m; + $jMax = $j; } } - throw $firstError; + $toStart = array_slice($to, 0, $jMax); + $toEnd = array_slice($to, $jMax); + return array_merge($this->calculate($fromStart, $toStart), $this->calculate($fromEnd, $toEnd)); } - private function tryParse(\PHPUnit\PhpParser\Parser $parser, \PHPUnit\PhpParser\ErrorHandler $errorHandler, $code) + private function length(array $from, array $to) : array { - $stmts = null; - $error = null; - try { - $stmts = $parser->parse($code, $errorHandler); - } catch (\PHPUnit\PhpParser\Error $error) { + $current = array_fill(0, count($to) + 1, 0); + $cFrom = count($from); + $cTo = count($to); + for ($i = 0; $i < $cFrom; $i++) { + $prev = $current; + for ($j = 0; $j < $cTo; $j++) { + if ($from[$i] === $to[$j]) { + $current[$j + 1] = $prev[$j] + 1; + } else { + $current[$j + 1] = max($current[$j], $prev[$j + 1]); + } + } } - return [$stmts, $error]; + return $current; } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -class Php7 extends \PHPUnit\PhpParser\ParserAbstract -{ - protected $tokenToSymbolMapSize = 392; - protected $actionTableSize = 1162; - protected $gotoTableSize = 611; - protected $invalidSymbol = 165; - protected $errorSymbol = 1; - protected $defaultAction = -32766; - protected $unexpectedTokenRule = 32767; - protected $YY2TBLSTATE = 397; - protected $numNonLeafStates = 694; - protected $symbolToName = array("EOF", "error", "T_THROW", "T_INCLUDE", "T_INCLUDE_ONCE", "T_EVAL", "T_REQUIRE", "T_REQUIRE_ONCE", "','", "T_LOGICAL_OR", "T_LOGICAL_XOR", "T_LOGICAL_AND", "T_PRINT", "T_YIELD", "T_DOUBLE_ARROW", "T_YIELD_FROM", "'='", "T_PLUS_EQUAL", "T_MINUS_EQUAL", "T_MUL_EQUAL", "T_DIV_EQUAL", "T_CONCAT_EQUAL", "T_MOD_EQUAL", "T_AND_EQUAL", "T_OR_EQUAL", "T_XOR_EQUAL", "T_SL_EQUAL", "T_SR_EQUAL", "T_POW_EQUAL", "T_COALESCE_EQUAL", "'?'", "':'", "T_COALESCE", "T_BOOLEAN_OR", "T_BOOLEAN_AND", "'|'", "'^'", "'&'", "T_IS_EQUAL", "T_IS_NOT_EQUAL", "T_IS_IDENTICAL", "T_IS_NOT_IDENTICAL", "T_SPACESHIP", "'<'", "T_IS_SMALLER_OR_EQUAL", "'>'", "T_IS_GREATER_OR_EQUAL", "T_SL", "T_SR", "'+'", "'-'", "'.'", "'*'", "'/'", "'%'", "'!'", "T_INSTANCEOF", "'~'", "T_INC", "T_DEC", "T_INT_CAST", "T_DOUBLE_CAST", "T_STRING_CAST", "T_ARRAY_CAST", "T_OBJECT_CAST", "T_BOOL_CAST", "T_UNSET_CAST", "'@'", "T_POW", "'['", "T_NEW", "T_CLONE", "T_EXIT", "T_IF", "T_ELSEIF", "T_ELSE", "T_ENDIF", "T_LNUMBER", "T_DNUMBER", "T_STRING", "T_STRING_VARNAME", "T_VARIABLE", "T_NUM_STRING", "T_INLINE_HTML", "T_ENCAPSED_AND_WHITESPACE", "T_CONSTANT_ENCAPSED_STRING", "T_ECHO", "T_DO", "T_WHILE", "T_ENDWHILE", "T_FOR", "T_ENDFOR", "T_FOREACH", "T_ENDFOREACH", "T_DECLARE", "T_ENDDECLARE", "T_AS", "T_SWITCH", "T_MATCH", "T_ENDSWITCH", "T_CASE", "T_DEFAULT", "T_BREAK", "T_CONTINUE", "T_GOTO", "T_FUNCTION", "T_FN", "T_CONST", "T_RETURN", "T_TRY", "T_CATCH", "T_FINALLY", "T_USE", "T_INSTEADOF", "T_GLOBAL", "T_STATIC", "T_ABSTRACT", "T_FINAL", "T_PRIVATE", "T_PROTECTED", "T_PUBLIC", "T_VAR", "T_UNSET", "T_ISSET", "T_EMPTY", "T_HALT_COMPILER", "T_CLASS", "T_TRAIT", "T_INTERFACE", "T_EXTENDS", "T_IMPLEMENTS", "T_OBJECT_OPERATOR", "T_NULLSAFE_OBJECT_OPERATOR", "T_LIST", "T_ARRAY", "T_CALLABLE", "T_CLASS_C", "T_TRAIT_C", "T_METHOD_C", "T_FUNC_C", "T_LINE", "T_FILE", "T_START_HEREDOC", "T_END_HEREDOC", "T_DOLLAR_OPEN_CURLY_BRACES", "T_CURLY_OPEN", "T_PAAMAYIM_NEKUDOTAYIM", "T_NAMESPACE", "T_NS_C", "T_DIR", "T_NS_SEPARATOR", "T_ELLIPSIS", "T_NAME_FULLY_QUALIFIED", "T_NAME_QUALIFIED", "T_NAME_RELATIVE", "T_ATTRIBUTE", "';'", "']'", "'{'", "'}'", "'('", "')'", "'`'", "'\"'", "'\$'"); - protected $tokenToSymbol = array(0, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 55, 163, 165, 164, 54, 37, 165, 160, 161, 52, 49, 8, 50, 51, 53, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 31, 156, 43, 16, 45, 30, 67, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 69, 165, 157, 36, 165, 162, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 158, 35, 159, 57, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 38, 39, 40, 41, 42, 44, 46, 47, 48, 56, 58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155); - protected $action = array(130, 131, 132, 555, 133, 134, -32766, 704, 705, 706, 135, 36, -543, -552, 455, -32766, -543, -32766, -32766, -32766, -552, 1152, 778, 927, -549, 969, 970, 0, -32766, -32766, -32766, -549, -32766, 1219, -32766, 245, -32766, 962, -32766, -32766, -32766, -32766, -32766, 461, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, 124, -331, 707, -331, -32766, 388, 1031, 1032, 1033, 1030, 1029, 1028, -32766, 435, 430, 2, 261, 136, 371, 711, 712, 713, 714, 391, 789, 397, 1031, 1032, 1033, 1030, 1029, 1028, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 745, 556, 746, 747, 748, 749, 737, 738, 372, 373, 740, 741, 726, 727, 728, 730, 731, 732, 332, 771, 772, 773, 774, 775, 733, 734, 557, 558, 766, 757, 755, 756, 752, 753, -294, -189, 559, 560, 751, 561, 562, 563, 564, 565, 566, 1235, 456, 783, -503, 889, 754, 567, 568, 928, 137, -32766, -32766, -32766, 130, 131, 132, 555, 133, 134, 983, 704, 705, 706, 135, 36, -32766, -32766, -32766, -32766, -552, -32766, -32766, -32766, -552, 1152, 547, 101, 102, 103, 583, -549, -32766, -32766, -32766, -549, -32766, -32766, -32766, 245, -32766, 80, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, 959, 958, 957, -32766, -32766, -503, -503, 707, 1264, -32766, 388, 1265, -32766, -32766, -32766, 235, 784, -32766, 778, 19, -503, 261, 136, 371, 711, 712, 713, 714, -32766, -32766, 397, 788, -503, -32766, -509, -32766, -32766, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 745, 556, 746, 747, 748, 749, 737, 738, 372, 373, 740, 741, 726, 727, 728, 730, 731, 732, 332, 771, 772, 773, 774, 775, 733, 734, 557, 558, 766, 757, 755, 756, 752, 753, -294, -189, 559, 560, 751, 561, 562, 563, 564, 565, 566, 309, 81, 82, 83, 139, 754, 567, 568, 681, 137, 729, 699, 700, 701, 702, 703, 1239, 704, 705, 706, 742, 743, 33, 1238, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 31, 263, -32766, -32766, -32766, 104, 105, 106, 577, 263, 1216, 126, -188, 107, 142, 440, 441, 707, -32766, -32766, -32766, 107, -254, -32766, 247, -32766, -32766, -32766, -32766, -32766, -32766, 708, 709, 710, 711, 712, 713, 714, 293, -32766, 776, -32766, -32766, -32766, -32766, -32766, 295, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 745, 768, 746, 747, 748, 749, 737, 738, 739, 767, 740, 741, 726, 727, 728, 730, 731, 732, 770, 771, 772, 773, 774, 775, 733, 734, 735, 736, 766, 757, 755, 756, 752, 753, 529, 311, 744, 750, 751, 758, 759, 761, 760, 762, 763, 234, -32766, -32766, -32766, 307, 754, 765, 764, 48, 49, 50, 486, 51, 52, 481, 397, 18, 321, 53, 54, 345, 55, -32766, 982, -32766, -32766, -32766, -32766, -32766, -32766, -32767, -32767, -32767, -32767, -32767, 349, -32767, -32767, -32767, -32767, 99, 100, 101, 102, 103, 814, 354, 815, 1191, 356, 1152, 871, 271, 408, 871, 56, 57, 409, 814, 410, 815, 58, -188, 59, 240, 241, 60, 61, 62, 63, 64, 65, 66, 67, -32766, 26, 262, 68, 412, 487, 411, 672, 967, 1185, 1186, 488, 1150, 1216, 1154, 1153, 1155, 1183, 40, 23, 489, 1009, 490, -82, 491, 147, 492, 969, 970, 493, 494, 786, 429, 430, 42, 43, 413, 418, 415, 871, 44, 495, 391, 496, 497, 248, 344, 320, 1159, 1154, 1153, 1155, 793, 896, 498, 499, 500, 148, 1008, 861, 692, 787, 861, 967, 1254, 501, 502, 150, 1173, 1174, 1175, 1176, 1170, 1171, 281, 624, 24, 26, -14, 151, 1177, 1172, 969, 970, 1154, 1153, 1155, 282, -82, 1216, -502, 152, 69, 1183, 305, 306, 311, 34, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 154, -149, -149, -149, 639, 640, 146, 376, 1159, 1159, 861, 615, 616, 32, 243, 35, -149, 1216, -149, 121, -149, 873, -149, 667, 873, 122, 667, 242, 1067, 1069, 501, 502, 414, 1173, 1174, 1175, 1176, 1170, 1171, -501, 127, -502, -502, 496, 497, 1177, 1172, -504, 128, 871, 424, 425, 847, 896, -107, -107, -502, 71, 442, 443, 306, 311, -107, -32766, 432, 433, -49, 141, -502, 1152, -508, 155, 156, 780, 157, -84, -32766, -32766, -32766, 673, -32766, -76, -32766, 873, -32766, 667, -149, -32766, 1216, 1216, 1179, 282, -32766, -32766, -32766, -73, 73, -71, -32766, -32766, 311, -501, -501, 129, -32766, 388, -70, -69, -32766, -504, -504, -68, -32766, -67, 1152, -66, -501, -65, 871, -64, 275, -32766, -32766, -32766, -504, -32766, -45, -32766, -501, -32766, -16, 861, -32766, 145, -107, 264, -504, -32766, -32766, -32766, 682, 72, 244, -32766, -32766, -32766, 685, 782, 674, -32766, 388, 1152, 669, 871, -501, 870, 144, -32766, -32766, -32766, -32766, 272, -32766, 282, -32766, 273, -32766, 73, 73, -32766, 1216, 311, 311, 276, -32766, -32766, -32766, 885, -32766, 246, -32766, -32766, 277, 677, 1152, 314, -32766, 388, -4, 871, 263, -32766, -32766, -32766, -32766, -32766, 107, -32766, 143, -32766, 861, 778, -32766, 871, 873, -32766, 667, -32766, -32766, -32766, 625, 647, 871, -32766, -32766, -32766, -501, -501, 787, -32766, 388, 1152, 1037, -32766, 969, 970, 1266, -32766, -32766, -32766, -32766, -501, -32766, 531, -32766, 861, -32766, 660, 871, -32766, 630, 535, 683, -501, -32766, -32766, -32766, 138, -32766, 642, -32766, -32766, 1023, 311, 1152, 20, -32766, 388, 437, 466, 631, -32766, -32766, -32766, -32766, -32766, 643, -32766, 286, -32766, -506, 861, -32766, 913, 407, 667, 613, -32766, -32766, -32766, -32766, 284, -467, -32766, -32766, 861, 46, 283, 282, -32766, 388, 686, 897, 414, 861, 402, 898, -32766, 294, 38, 280, -232, -232, -232, 496, 497, 1007, 414, 873, 26, 667, 1190, 786, 806, 896, -107, -107, 1192, 496, 497, 861, 1216, 47, -457, 8, 1183, 22, 847, 896, -107, -107, 347, -506, -506, 541, 9, -231, -231, -231, 581, 1180, 887, 414, 39, 873, 848, 667, -4, 289, 290, 689, 690, 852, 496, 497, 937, 914, 1261, 873, -506, 667, -232, 847, 896, -107, -107, 921, 873, 911, 667, 922, 850, 909, -537, 502, 123, 1173, 1174, 1175, 1176, 1170, 1171, 1012, 1015, 291, 292, 1016, 1013, 1177, 1172, 1014, 1020, 1263, 873, 30, 667, -231, 304, 798, 348, 71, 1205, 1223, 306, 311, 1257, 618, -535, 346, 668, -107, 125, -107, 671, 675, 676, 678, 285, 679, 680, -107, -107, -107, -107, -107, -107, -107, 684, 670, -257, 809, 808, 817, 895, 929, 816, 1262, 894, 892, 893, 1138, 880, 888, 878, 919, 920, 1260, 1217, 1206, 1224, 1230, 1233, 0, -509, -508, -507, 1, 27, 28, 37, 41, 45, 70, 74, -308, -255, 75, 76, 77, 78, 79, 140, 149, 153, 239, 310, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 403, 404, 0, -254, 12, 13, 14, 15, 17, 375, 457, 458, 465, 468, 469, 470, 471, 475, 476, 477, 484, 654, 1163, 1106, 1181, 984, 1142, -259, -99, 11, 16, 25, 279, 374, 574, 578, 605, 659, 1110, 1158, 1107, 1236, 0, -471, 1123, 0, 1184); - protected $actionCheck = array(2, 3, 4, 5, 6, 7, 115, 9, 10, 11, 12, 13, 157, 1, 31, 73, 161, 9, 10, 11, 8, 79, 79, 31, 1, 134, 135, 0, 86, 87, 88, 8, 90, 1, 92, 37, 94, 1, 30, 97, 32, 33, 34, 101, 102, 103, 104, 9, 10, 11, 108, 109, 14, 105, 56, 107, 114, 115, 115, 116, 117, 118, 119, 120, 122, 105, 106, 8, 70, 71, 72, 73, 74, 75, 76, 115, 1, 79, 115, 116, 117, 118, 119, 120, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 8, 8, 133, 134, 135, 136, 137, 138, 139, 140, 141, 1, 158, 79, 69, 1, 147, 148, 149, 156, 151, 9, 10, 11, 2, 3, 4, 5, 6, 7, 161, 9, 10, 11, 12, 13, 9, 10, 11, 73, 157, 9, 10, 11, 161, 79, 80, 49, 50, 51, 50, 157, 86, 87, 88, 161, 90, 30, 92, 37, 94, 158, 30, 97, 32, 33, 34, 35, 102, 103, 104, 118, 119, 120, 108, 109, 131, 132, 56, 79, 114, 115, 82, 9, 10, 11, 14, 153, 122, 79, 8, 146, 70, 71, 72, 73, 74, 75, 76, 9, 10, 79, 156, 158, 30, 160, 32, 33, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 161, 161, 133, 134, 135, 136, 137, 138, 139, 140, 141, 69, 9, 10, 11, 158, 147, 148, 149, 158, 151, 2, 3, 4, 5, 6, 7, 1, 9, 10, 11, 12, 13, 30, 8, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 8, 56, 9, 10, 11, 52, 53, 54, 1, 56, 81, 8, 8, 68, 8, 131, 132, 56, 9, 10, 11, 68, 161, 30, 8, 32, 33, 34, 35, 36, 37, 70, 71, 72, 73, 74, 75, 76, 8, 30, 79, 32, 33, 34, 35, 36, 8, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 84, 164, 133, 134, 135, 136, 137, 138, 139, 140, 141, 96, 9, 10, 11, 8, 147, 148, 149, 2, 3, 4, 5, 6, 7, 105, 79, 107, 8, 12, 13, 8, 15, 30, 1, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 8, 43, 44, 45, 46, 47, 48, 49, 50, 51, 105, 8, 107, 143, 8, 79, 1, 30, 8, 1, 49, 50, 8, 105, 8, 107, 55, 161, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 9, 69, 70, 71, 72, 73, 8, 31, 115, 77, 78, 79, 115, 81, 152, 153, 154, 85, 86, 87, 88, 159, 90, 31, 92, 14, 94, 134, 135, 97, 98, 152, 105, 106, 102, 103, 104, 105, 106, 1, 108, 109, 115, 116, 117, 37, 114, 115, 1, 152, 153, 154, 8, 126, 122, 123, 124, 14, 156, 83, 158, 81, 83, 115, 84, 133, 134, 14, 136, 137, 138, 139, 140, 141, 142, 74, 75, 69, 31, 14, 148, 149, 134, 135, 152, 153, 154, 155, 96, 81, 69, 14, 160, 85, 162, 163, 164, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 14, 74, 75, 76, 74, 75, 100, 101, 1, 1, 83, 110, 111, 144, 145, 14, 89, 81, 91, 16, 93, 156, 95, 158, 156, 16, 158, 37, 58, 59, 133, 134, 105, 136, 137, 138, 139, 140, 141, 69, 16, 131, 132, 116, 117, 148, 149, 69, 16, 1, 105, 106, 125, 126, 127, 128, 146, 160, 105, 106, 163, 164, 126, 73, 127, 128, 31, 16, 158, 79, 160, 16, 16, 79, 16, 31, 86, 87, 88, 31, 90, 31, 92, 156, 94, 158, 159, 97, 81, 81, 1, 155, 102, 103, 104, 31, 160, 31, 108, 109, 164, 131, 132, 31, 114, 115, 31, 31, 73, 131, 132, 31, 122, 31, 79, 31, 146, 31, 1, 31, 30, 86, 87, 88, 146, 90, 31, 92, 158, 94, 31, 83, 97, 31, 126, 31, 158, 102, 103, 104, 31, 151, 37, 108, 109, 73, 31, 153, 31, 114, 115, 79, 158, 1, 69, 31, 31, 122, 86, 87, 88, 35, 90, 155, 92, 35, 94, 160, 160, 97, 81, 164, 164, 35, 102, 103, 104, 37, 73, 37, 108, 109, 35, 31, 79, 35, 114, 115, 0, 1, 56, 86, 87, 88, 122, 90, 68, 92, 69, 94, 83, 79, 97, 1, 156, 115, 158, 102, 103, 104, 89, 76, 1, 108, 109, 73, 131, 132, 81, 114, 115, 79, 81, 84, 134, 135, 82, 122, 86, 87, 88, 146, 90, 84, 92, 83, 94, 91, 1, 97, 95, 88, 31, 158, 102, 103, 104, 158, 73, 93, 108, 109, 121, 164, 79, 96, 114, 115, 96, 96, 99, 86, 87, 88, 122, 90, 99, 92, 113, 94, 69, 83, 97, 156, 126, 158, 112, 102, 103, 104, 115, 130, 146, 108, 109, 83, 69, 129, 155, 114, 115, 159, 126, 105, 83, 107, 126, 122, 129, 156, 112, 99, 100, 101, 116, 117, 1, 105, 156, 69, 158, 143, 152, 125, 126, 127, 128, 143, 116, 117, 83, 81, 69, 146, 146, 85, 146, 125, 126, 127, 128, 146, 131, 132, 150, 147, 99, 100, 101, 150, 157, 151, 105, 156, 156, 159, 158, 159, 131, 132, 156, 156, 156, 116, 117, 156, 156, 159, 156, 158, 158, 159, 125, 126, 127, 128, 156, 156, 156, 158, 156, 156, 156, 160, 134, 158, 136, 137, 138, 139, 140, 141, 156, 156, 131, 132, 156, 156, 148, 149, 156, 156, 159, 156, 158, 158, 159, 158, 157, 146, 160, 157, 157, 163, 164, 157, 157, 160, 158, 158, 105, 158, 107, 158, 158, 158, 158, 112, 158, 158, 115, 116, 117, 118, 119, 120, 121, 158, 158, 161, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, -1, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 159, 161, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, -1, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, -1, 162, 162, -1, 163); - protected $actionBase = array(0, -2, 151, 555, 816, 830, 865, 379, 717, 622, 862, 676, 780, 780, 839, 780, 493, 745, 301, 301, -57, 301, 301, 496, 496, 496, 618, 618, 618, 618, -58, -58, 95, 700, 733, 770, 663, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 75, -8, 347, 629, 986, 992, 988, 993, 984, 983, 987, 989, 994, 915, 916, 753, 917, 918, 919, 920, 990, 877, 985, 991, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 508, 38, 219, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 156, 156, 156, 203, 525, 525, 8, 598, 161, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 349, 333, 435, 435, 435, 435, 435, 436, 436, 436, 436, 933, 564, 636, 635, 465, -52, 127, 127, 718, 718, 759, 410, 410, 410, 444, -109, -109, -109, 74, 538, 396, 348, 414, 414, 414, 414, 414, 802, 998, 139, 139, 139, 139, 414, 414, 414, 606, 713, 713, 881, 293, 293, 293, 713, 383, 777, 497, 383, 497, 129, 793, 32, -40, -145, 793, 829, 845, 23, 12, 788, 573, 788, 767, 863, 898, 995, 82, 789, 913, 795, 914, 224, 678, 981, 981, 981, 981, 981, 981, 981, 981, 981, 981, 981, 269, 982, 63, 269, 269, 269, 529, 63, 518, 558, 63, 778, 982, 75, 805, 75, 75, 75, 75, 944, 75, 75, 75, 75, 75, 75, 949, 727, 723, 692, -17, 75, -8, 143, 143, 419, 36, 143, 143, 143, 143, 75, 75, 565, 573, 762, 812, 581, 817, 344, 762, 762, 762, 509, 121, 201, 122, 352, 750, 750, 768, 769, 924, 924, 750, 765, 750, 769, 929, 750, 768, 768, 750, 924, 768, 761, 343, 488, 452, 470, 768, 768, 492, 924, 370, 768, 768, 750, 750, 750, 797, 768, 494, 750, 356, 346, 750, 750, 768, 768, 797, 786, 59, 779, 924, 924, 924, 797, 455, 779, 779, 822, 823, 792, 732, 439, 378, 561, 332, 768, 732, 732, 750, 481, 792, 732, 792, 732, 818, 732, 732, 732, 792, 732, 765, 484, 732, 768, 515, 211, 732, 27, 930, 931, 672, 934, 927, 935, 955, 936, 937, 879, 794, 798, 942, 928, 938, 926, 925, 752, 631, 637, 806, 764, 923, 756, 756, 756, 921, 756, 756, 756, 756, 756, 756, 756, 756, 631, 811, 813, 776, 781, 945, 652, 660, 796, 814, 996, 997, 944, 976, 939, 771, 679, 962, 946, 760, 867, 947, 948, 963, 977, 978, 826, 757, 861, 899, 869, 950, 883, 756, 930, 937, 928, 938, 926, 925, 716, 714, 710, 712, 708, 704, 694, 703, 730, 875, 841, 872, 949, 922, 631, 873, 958, 864, 964, 965, 878, 790, 772, 876, 900, 951, 952, 953, 884, 979, 885, 815, 959, 896, 966, 791, 901, 967, 968, 969, 970, 886, 902, 888, 824, 749, 932, 773, 903, 528, 766, 775, 956, 560, 943, 889, 904, 905, 971, 972, 973, 906, 907, 940, 827, 960, 784, 961, 957, 828, 838, 570, 754, 758, 582, 594, 908, 909, 941, 737, 763, 840, 842, 980, 910, 614, 843, 683, 911, 975, 684, 686, 774, 897, 808, 783, 787, 954, 743, 844, 912, 854, 855, 858, 974, 859, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 449, 449, 449, 449, 449, 449, 301, 301, 301, 301, 449, 449, 449, 449, 449, 449, 449, 0, 0, 301, 0, 0, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 414, 414, 285, 0, 285, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 285, 285, 285, 285, 285, 285, 285, 293, 293, 293, 293, 761, 414, 414, 414, 414, -37, 293, 293, 414, 414, -37, 414, 414, 414, 761, 414, 414, 414, 0, 0, 63, 497, 0, 0, 0, 0, 0, 497, 497, 269, 269, 269, 269, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 497, 0, 63, 0, 765, 414, 269, 761, 308, 414, 0, 0, 0, 0, 63, 765, 63, 497, 143, 75, 308, 0, 534, 534, 534, 534, 0, 573, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 0, 761, 0, 0, 0, 0, 0, 0, 0, 0, 0, 765, 768, 0, 924, 0, 0, 0, 0, 750, 0, 0, 0, 0, 0, 0, 750, 929, 768, 768, 0, 0, 0, 0, 0, 0, 765, 0, 0, 0, 0, 0, 0, 0, 0, 756, 790, 0, 790, 0, 756, 756, 756); - protected $actionDefault = array(3, 32767, 99, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 97, 32767, 32767, 32767, 32767, 32767, 32767, 555, 555, 555, 555, 236, 99, 32767, 32767, 32767, 32767, 431, 350, 350, 350, 32767, 32767, 499, 499, 499, 499, 499, 499, 32767, 32767, 32767, 32767, 32767, 32767, 431, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 97, 32767, 32767, 32767, 35, 5, 6, 8, 9, 48, 15, 32767, 32767, 32767, 32767, 32767, 99, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 548, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 435, 414, 415, 417, 418, 349, 500, 554, 293, 551, 348, 142, 305, 295, 224, 296, 240, 241, 267, 345, 146, 379, 432, 381, 430, 434, 380, 355, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 353, 354, 433, 436, 437, 440, 441, 411, 410, 409, 377, 32767, 32767, 378, 352, 382, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 99, 32767, 384, 383, 400, 401, 398, 399, 402, 403, 404, 405, 406, 32767, 32767, 32767, 32767, 32767, 328, 391, 392, 284, 284, 330, 32767, 32767, 32767, 108, 32767, 32767, 32767, 493, 408, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 99, 32767, 97, 495, 374, 376, 463, 386, 387, 385, 356, 32767, 470, 32767, 99, 472, 32767, 32767, 32767, 32767, 32767, 32767, 494, 32767, 501, 501, 32767, 456, 97, 32767, 32767, 32767, 32767, 262, 32767, 32767, 32767, 32767, 562, 456, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 32767, 107, 32767, 32767, 32767, 97, 185, 32767, 250, 252, 99, 516, 190, 32767, 475, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 468, 190, 190, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 456, 396, 135, 32767, 135, 501, 388, 389, 390, 458, 501, 501, 501, 32767, 32767, 32767, 190, 32767, 473, 473, 97, 97, 97, 97, 468, 32767, 190, 190, 32767, 32767, 190, 108, 96, 96, 96, 96, 190, 190, 96, 100, 98, 190, 190, 32767, 32767, 32767, 205, 190, 96, 32767, 98, 98, 32767, 32767, 190, 190, 205, 207, 98, 209, 32767, 520, 521, 205, 98, 209, 209, 229, 229, 447, 286, 98, 96, 98, 98, 190, 286, 286, 32767, 98, 447, 286, 447, 286, 192, 286, 286, 286, 447, 286, 32767, 98, 286, 190, 96, 96, 286, 32767, 32767, 32767, 458, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 488, 32767, 505, 518, 394, 395, 397, 503, 419, 420, 421, 422, 423, 424, 425, 427, 550, 32767, 462, 32767, 32767, 32767, 32767, 304, 560, 32767, 560, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 561, 32767, 501, 32767, 32767, 32767, 32767, 393, 7, 74, 41, 42, 50, 56, 479, 480, 481, 482, 476, 477, 483, 478, 32767, 484, 526, 32767, 32767, 502, 553, 32767, 32767, 32767, 32767, 32767, 32767, 135, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 488, 32767, 133, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 501, 32767, 32767, 32767, 281, 283, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 501, 32767, 32767, 32767, 269, 271, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 266, 32767, 32767, 344, 32767, 32767, 32767, 32767, 324, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 148, 148, 3, 3, 307, 148, 148, 148, 307, 307, 148, 307, 307, 148, 148, 148, 148, 148, 148, 180, 244, 247, 229, 229, 148, 316, 148); - protected $goto = array(190, 190, 655, 781, 663, 399, 629, 964, 971, 972, 393, 297, 298, 317, 549, 303, 398, 318, 400, 607, 361, 365, 534, 572, 576, 161, 161, 161, 161, 187, 187, 171, 173, 209, 191, 204, 187, 187, 187, 187, 187, 188, 188, 188, 188, 188, 188, 182, 183, 184, 185, 186, 206, 204, 207, 509, 510, 389, 511, 513, 514, 515, 516, 517, 518, 519, 520, 1053, 162, 163, 164, 189, 165, 166, 167, 160, 168, 169, 170, 172, 203, 205, 208, 230, 233, 236, 238, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 266, 267, 300, 301, 302, 394, 395, 396, 554, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 174, 225, 175, 192, 193, 194, 231, 182, 183, 184, 185, 186, 206, 1053, 195, 176, 177, 178, 196, 192, 179, 232, 197, 159, 198, 226, 180, 199, 227, 228, 181, 229, 200, 201, 202, 807, 611, 611, 804, 274, 274, 274, 274, 976, 973, 974, 592, 626, 1147, 594, 594, 571, 533, 526, 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1250, 1250, 986, 328, 812, 1027, 860, 855, 856, 869, 1026, 813, 857, 810, 858, 859, 811, 803, 1251, 1251, 805, 1250, 863, 912, 936, 910, 910, 908, 910, 687, 363, 526, 1003, 1004, 533, 1253, 525, 945, 940, 1251, 542, 543, 820, 1148, 838, 370, 552, 832, 308, 288, 819, 573, 864, 795, 865, 1200, 1200, 785, 587, 588, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1151, 1151, 1151, 968, 1149, 1208, 1209, 968, 968, 472, 968, 968, 968, 779, 968, 968, 968, 1232, 1232, 1232, 1232, 1151, 1151, 1151, 1151, 1151, 785, 21, 785, 546, 1198, 1198, 1151, 1151, 1151, 1198, 1198, 1198, 1198, 1198, 1198, 1198, 1198, 1198, 1198, 523, 523, 523, 325, 876, 512, 512, 392, 877, 582, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 1240, 427, 906, 906, 906, 906, 387, 387, 387, 387, 602, 604, 427, 900, 907, 540, 904, 379, 662, 688, 606, 608, 933, 5, 627, 6, 539, 645, 649, 947, 653, 661, 943, 586, 377, 378, 800, 1019, 570, 635, 666, 636, 359, 381, 382, 383, 453, 646, 652, 652, 384, 658, 1017, 454, 323, 580, 595, 598, 599, 600, 601, 619, 620, 621, 665, 527, 537, 1267, 450, 1225, 1226, 527, 545, 537, 800, 628, 362, 1101, 528, 434, 521, 521, 521, 521, 1227, 1228, 1132, 890, 405, 575, 1133, 1136, 891, 1137, 444, 553, 445, 419, 419, 419, 830, 329, 330, 1258, 1259, 1222, 1222, 1222, 622, 623, 431, 637, 638, 1211, 439, 439, 550, 585, 881, 1041, 797, 319, 833, 821, 991, 439, 590, 995, 825, 828, 369, 822, 952, 1234, 1234, 1234, 1234, 915, 648, 824, 955, 632, 931, 473, 691, 474, 992, 818, 1144, 451, 996, 480, 0, 834, 1036, 1218, 917, 0, 0, 1143, 0, 905, 0, 0, 0, 0, 0, 528, 0, 0, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 800, 419, 1034, 837, 0, 0, 0, 994, 0, 0, 1220, 1220, 994, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 270, 524, 524, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 950, 950); - protected $gotoCheck = array(41, 41, 71, 6, 8, 64, 64, 105, 105, 105, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 57, 57, 57, 57, 57, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 14, 107, 107, 25, 22, 22, 22, 22, 107, 107, 107, 54, 54, 19, 99, 99, 114, 74, 74, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 164, 164, 111, 88, 14, 128, 14, 14, 14, 14, 128, 14, 14, 14, 14, 14, 14, 24, 165, 165, 26, 164, 14, 48, 24, 24, 24, 24, 24, 24, 74, 74, 14, 14, 74, 164, 24, 24, 24, 165, 74, 74, 34, 19, 44, 74, 74, 34, 151, 151, 34, 74, 63, 19, 63, 152, 152, 11, 74, 74, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 71, 71, 71, 71, 19, 19, 19, 71, 71, 74, 71, 71, 71, 5, 71, 71, 71, 8, 8, 8, 8, 71, 71, 71, 71, 71, 11, 74, 11, 154, 153, 153, 71, 71, 71, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 18, 18, 18, 161, 71, 155, 155, 12, 71, 12, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 163, 18, 18, 18, 18, 18, 23, 23, 23, 23, 81, 81, 18, 18, 18, 47, 84, 84, 84, 47, 47, 47, 94, 45, 47, 45, 8, 47, 47, 47, 47, 47, 47, 8, 78, 78, 21, 7, 7, 78, 7, 78, 60, 78, 78, 78, 80, 78, 7, 7, 78, 7, 7, 80, 78, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 8, 8, 13, 158, 158, 158, 8, 95, 8, 21, 62, 8, 135, 13, 8, 98, 98, 98, 98, 160, 160, 76, 76, 103, 98, 76, 76, 76, 76, 8, 8, 8, 22, 22, 22, 8, 88, 88, 8, 8, 114, 114, 114, 82, 82, 79, 82, 82, 13, 133, 133, 2, 2, 16, 16, 17, 28, 15, 15, 15, 133, 16, 15, 38, 8, 27, 36, 16, 114, 114, 114, 114, 15, 13, 16, 101, 16, 16, 139, 90, 139, 113, 16, 144, 141, 116, 139, -1, 40, 131, 114, 87, -1, -1, 16, -1, 15, -1, -1, -1, -1, -1, 13, -1, -1, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 21, 22, 15, 15, -1, -1, -1, 114, -1, -1, 114, 114, 114, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 23, 23, 23, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 98, 98); - protected $gotoBase = array(0, 0, -232, 0, 0, 249, -6, 351, -16, 0, 0, -37, -11, 79, -167, 22, 1, 148, 40, -188, 0, 83, 158, 324, 199, 156, 202, 132, 162, 0, 0, 0, 0, 0, -118, 0, 131, 0, 142, 0, 66, -1, 0, 0, 211, -347, 0, -332, 192, 0, 0, 0, 0, 0, 130, 0, 0, -23, 0, 0, 323, 0, 161, 225, -229, 0, 0, 0, 0, 0, 0, -5, 0, 0, -198, 0, 30, 42, -109, 157, -77, -122, -246, 0, 53, 0, 0, 67, -267, 0, 89, 0, 0, 0, 312, 352, 0, 0, 375, -63, 0, 116, 0, 140, 0, -264, 0, -110, 0, 0, 0, 186, 0, 118, 165, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -74, 0, 0, 64, 0, 405, 0, 135, 0, 0, 0, -4, 0, 63, 0, 0, 65, 0, 0, 0, 0, 0, 0, -71, 7, 52, 258, 72, 0, 0, 99, 0, 58, 276, 0, 292, -101, -83, 0, 0); - protected $gotoDefault = array(-32768, 485, 695, 4, 696, 769, 777, 569, 503, 664, 324, 596, 390, 331, 862, 1040, 551, 796, 1160, 1168, 428, 799, 312, 326, 844, 845, 846, 366, 351, 357, 364, 617, 597, 467, 831, 422, 823, 459, 826, 421, 835, 158, 386, 483, 839, 3, 841, 530, 872, 352, 849, 353, 641, 851, 536, 853, 854, 360, 367, 368, 1045, 544, 593, 866, 237, 538, 867, 350, 868, 875, 355, 358, 650, 438, 478, 380, 1021, 579, 614, 416, 447, 591, 603, 589, 902, 460, 436, 916, 327, 924, 693, 1052, 609, 462, 932, 610, 939, 942, 504, 505, 452, 954, 268, 463, 981, 633, 634, 966, 612, 979, 446, 985, 423, 993, 1204, 426, 997, 260, 1000, 269, 385, 401, 1005, 1006, 7, 1011, 656, 657, 10, 265, 482, 1035, 651, 420, 1051, 406, 1120, 1122, 532, 464, 1140, 1139, 644, 479, 1145, 1207, 417, 506, 448, 299, 507, 287, 315, 296, 522, 278, 316, 508, 449, 1213, 1221, 313, 29, 1241, 1252, 322, 548, 584); - protected $ruleToNonTerminal = array(0, 1, 3, 3, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 8, 9, 10, 10, 10, 11, 11, 12, 12, 13, 14, 14, 15, 15, 16, 16, 17, 17, 20, 20, 21, 22, 22, 23, 23, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 28, 28, 29, 29, 31, 33, 33, 27, 35, 35, 32, 37, 37, 34, 34, 36, 36, 38, 38, 30, 39, 39, 40, 42, 43, 43, 44, 45, 45, 47, 46, 46, 46, 46, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 24, 24, 67, 67, 70, 70, 69, 68, 68, 61, 73, 73, 74, 74, 75, 75, 76, 76, 25, 25, 26, 26, 26, 26, 79, 79, 79, 80, 80, 83, 83, 81, 81, 84, 85, 85, 55, 55, 63, 63, 66, 66, 66, 65, 86, 86, 87, 56, 56, 56, 56, 88, 88, 89, 89, 90, 90, 91, 92, 92, 93, 93, 94, 94, 53, 53, 49, 49, 96, 51, 51, 97, 50, 50, 52, 52, 62, 62, 62, 62, 77, 77, 100, 100, 102, 102, 102, 102, 101, 101, 101, 104, 104, 104, 105, 105, 107, 107, 107, 106, 106, 108, 108, 109, 109, 109, 103, 103, 78, 78, 78, 19, 19, 110, 110, 111, 111, 111, 111, 58, 112, 112, 113, 59, 115, 115, 116, 116, 117, 117, 82, 118, 118, 118, 118, 118, 123, 123, 124, 124, 125, 125, 125, 125, 125, 126, 127, 127, 122, 122, 119, 119, 121, 121, 129, 129, 128, 128, 128, 128, 128, 128, 120, 130, 130, 132, 131, 131, 60, 95, 133, 133, 54, 54, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 140, 134, 134, 139, 139, 142, 143, 143, 144, 145, 145, 145, 18, 18, 71, 71, 71, 71, 135, 135, 135, 135, 147, 147, 136, 136, 138, 138, 138, 141, 141, 152, 152, 152, 152, 152, 152, 152, 152, 152, 153, 153, 99, 155, 155, 155, 155, 137, 137, 137, 137, 137, 137, 137, 137, 57, 57, 150, 150, 150, 150, 156, 156, 146, 146, 146, 157, 157, 157, 157, 157, 157, 72, 72, 64, 64, 64, 64, 114, 114, 114, 114, 160, 159, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 158, 158, 158, 158, 98, 154, 162, 162, 161, 161, 163, 163, 163, 163, 163, 163, 163, 163, 151, 151, 151, 151, 165, 166, 164, 164, 164, 164, 164, 164, 164, 164, 167, 167, 167, 167); - protected $ruleToLength = array(1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 2, 1, 3, 4, 1, 2, 0, 1, 1, 1, 1, 1, 3, 5, 4, 3, 4, 2, 3, 1, 1, 7, 6, 2, 3, 1, 2, 3, 1, 2, 3, 1, 1, 3, 1, 3, 1, 2, 2, 3, 1, 3, 2, 3, 1, 3, 2, 0, 1, 1, 1, 1, 1, 3, 7, 10, 5, 7, 9, 5, 3, 3, 3, 3, 3, 3, 1, 2, 5, 7, 9, 6, 5, 6, 3, 2, 1, 1, 1, 0, 2, 1, 3, 8, 0, 4, 2, 1, 3, 0, 1, 0, 1, 3, 1, 8, 9, 7, 8, 7, 6, 1, 2, 2, 0, 2, 0, 2, 0, 2, 2, 1, 3, 1, 4, 1, 4, 1, 1, 4, 2, 1, 3, 3, 3, 4, 4, 5, 0, 2, 4, 3, 1, 1, 7, 0, 2, 1, 3, 3, 4, 1, 4, 0, 2, 5, 0, 2, 6, 0, 2, 0, 3, 1, 2, 1, 1, 2, 0, 1, 3, 0, 1, 1, 1, 6, 8, 6, 1, 2, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 1, 2, 1, 0, 1, 0, 2, 2, 2, 4, 1, 3, 1, 2, 2, 3, 2, 3, 1, 1, 2, 3, 1, 1, 3, 2, 0, 1, 5, 5, 10, 3, 1, 1, 3, 0, 2, 4, 5, 4, 4, 4, 3, 1, 1, 1, 1, 1, 1, 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 3, 1, 1, 3, 2, 2, 3, 1, 0, 1, 1, 3, 3, 3, 4, 1, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 3, 4, 4, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 2, 1, 2, 4, 2, 2, 8, 9, 8, 9, 9, 10, 9, 10, 8, 3, 2, 0, 4, 2, 1, 3, 2, 2, 2, 4, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 0, 3, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 4, 1, 1, 3, 1, 1, 1, 1, 1, 3, 2, 3, 0, 1, 1, 3, 1, 1, 1, 1, 1, 3, 1, 1, 4, 4, 1, 4, 4, 0, 1, 1, 1, 3, 3, 1, 4, 2, 2, 1, 3, 1, 4, 4, 3, 3, 3, 3, 1, 3, 1, 1, 3, 1, 1, 4, 1, 1, 1, 3, 1, 1, 2, 1, 3, 4, 3, 2, 0, 2, 2, 1, 2, 1, 1, 1, 4, 3, 3, 3, 3, 6, 3, 1, 1, 2, 1); - protected function initReduceCallbacks() - { - $this->reduceCallbacks = [0 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 1 => function ($stackPos) { - $this->semValue = $this->handleNamespaces($this->semStack[$stackPos - (1 - 1)]); - }, 2 => function ($stackPos) { - if (\is_array($this->semStack[$stackPos - (2 - 2)])) { - $this->semValue = \array_merge($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); - } else { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - } - }, 3 => function ($stackPos) { - $this->semValue = array(); - }, 4 => function ($stackPos) { - $startAttributes = $this->lookaheadStartAttributes; - if (isset($startAttributes['comments'])) { - $nop = new \PHPUnit\PhpParser\Node\Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); - } else { - $nop = null; - } - if ($nop !== null) { - $this->semStack[$stackPos - (1 - 1)][] = $nop; - } - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 5 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 6 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 7 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 8 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 9 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 10 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 11 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 12 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 13 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 14 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 15 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 16 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 17 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 18 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 19 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 20 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 21 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 22 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 23 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 24 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 25 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 26 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 27 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 28 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 29 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 30 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 31 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 32 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 33 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 34 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 35 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 36 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 37 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 38 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 39 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 40 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 41 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 42 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 43 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 44 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 45 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 46 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 47 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 48 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 49 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 50 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 51 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 52 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 53 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 54 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 55 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 56 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 57 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 58 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 59 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 60 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 61 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 62 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 63 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 64 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 65 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 66 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 67 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 68 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 69 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 70 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 71 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 72 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 73 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 74 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 75 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 76 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 77 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 78 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 79 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 80 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 81 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 82 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 83 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 84 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 85 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 86 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 87 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 88 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 89 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 90 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 91 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 92 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 93 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 94 => function ($stackPos) { - /* nothing */ - }, 95 => function ($stackPos) { - /* nothing */ - }, 96 => function ($stackPos) { - /* nothing */ - }, 97 => function ($stackPos) { - $this->emitError(new \PHPUnit\PhpParser\Error('A trailing comma is not allowed here', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes)); - }, 98 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 99 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 100 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Attribute($this->semStack[$stackPos - (1 - 1)], [], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 101 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Attribute($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 102 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 103 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 104 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\AttributeGroup($this->semStack[$stackPos - (4 - 2)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 105 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 106 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 107 => function ($stackPos) { - $this->semValue = []; - }, 108 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 109 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 110 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 111 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 112 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\HaltCompiler($this->lexer->handleHaltCompiler(), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 113 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Namespace_($this->semStack[$stackPos - (3 - 2)], null, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - $this->semValue->setAttribute('kind', \PHPUnit\PhpParser\Node\Stmt\Namespace_::KIND_SEMICOLON); - $this->checkNamespace($this->semValue); - }, 114 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Namespace_($this->semStack[$stackPos - (5 - 2)], $this->semStack[$stackPos - (5 - 4)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - $this->semValue->setAttribute('kind', \PHPUnit\PhpParser\Node\Stmt\Namespace_::KIND_BRACED); - $this->checkNamespace($this->semValue); - }, 115 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Namespace_(null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - $this->semValue->setAttribute('kind', \PHPUnit\PhpParser\Node\Stmt\Namespace_::KIND_BRACED); - $this->checkNamespace($this->semValue); - }, 116 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Use_($this->semStack[$stackPos - (3 - 2)], \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_NORMAL, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 117 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Use_($this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 2)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 118 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 119 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Const_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 120 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_FUNCTION; - }, 121 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_CONSTANT; - }, 122 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\GroupUse($this->semStack[$stackPos - (7 - 3)], $this->semStack[$stackPos - (7 - 6)], $this->semStack[$stackPos - (7 - 2)], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - }, 123 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\GroupUse($this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 5)], \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 124 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 125 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 126 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 127 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 128 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 129 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 130 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 131 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 132 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 133 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\UseUse($this->semStack[$stackPos - (1 - 1)], null, \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - $this->checkUseUse($this->semValue, $stackPos - (1 - 1)); - }, 134 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\UseUse($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - $this->checkUseUse($this->semValue, $stackPos - (3 - 3)); - }, 135 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\UseUse($this->semStack[$stackPos - (1 - 1)], null, \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - $this->checkUseUse($this->semValue, $stackPos - (1 - 1)); - }, 136 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\UseUse($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - $this->checkUseUse($this->semValue, $stackPos - (3 - 3)); - }, 137 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - $this->semValue->type = \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_NORMAL; - }, 138 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue->type = $this->semStack[$stackPos - (2 - 1)]; - }, 139 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 140 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 141 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 142 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Const_($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 143 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 144 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 145 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 146 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Const_($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 147 => function ($stackPos) { - if (\is_array($this->semStack[$stackPos - (2 - 2)])) { - $this->semValue = \array_merge($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); - } else { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - } - }, 148 => function ($stackPos) { - $this->semValue = array(); - }, 149 => function ($stackPos) { - $startAttributes = $this->lookaheadStartAttributes; - if (isset($startAttributes['comments'])) { - $nop = new \PHPUnit\PhpParser\Node\Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); - } else { - $nop = null; - } - if ($nop !== null) { - $this->semStack[$stackPos - (1 - 1)][] = $nop; - } - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 150 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 151 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 152 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 153 => function ($stackPos) { - throw new \PHPUnit\PhpParser\Error('__HALT_COMPILER() can only be used from the outermost scope', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 154 => function ($stackPos) { - if ($this->semStack[$stackPos - (3 - 2)]) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - $attrs = $this->startAttributeStack[$stackPos - (3 - 1)]; - $stmts = $this->semValue; - if (!empty($attrs['comments'])) { - $stmts[0]->setAttribute('comments', \array_merge($attrs['comments'], $stmts[0]->getAttribute('comments', []))); - } - } else { - $startAttributes = $this->startAttributeStack[$stackPos - (3 - 1)]; - if (isset($startAttributes['comments'])) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Nop($startAttributes + $this->endAttributes); - } else { - $this->semValue = null; - } - if (null === $this->semValue) { - $this->semValue = array(); - } - } - }, 155 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\If_($this->semStack[$stackPos - (7 - 3)], ['stmts' => \is_array($this->semStack[$stackPos - (7 - 5)]) ? $this->semStack[$stackPos - (7 - 5)] : array($this->semStack[$stackPos - (7 - 5)]), 'elseifs' => $this->semStack[$stackPos - (7 - 6)], 'else' => $this->semStack[$stackPos - (7 - 7)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - }, 156 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\If_($this->semStack[$stackPos - (10 - 3)], ['stmts' => $this->semStack[$stackPos - (10 - 6)], 'elseifs' => $this->semStack[$stackPos - (10 - 7)], 'else' => $this->semStack[$stackPos - (10 - 8)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); - }, 157 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\While_($this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 158 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Do_($this->semStack[$stackPos - (7 - 5)], \is_array($this->semStack[$stackPos - (7 - 2)]) ? $this->semStack[$stackPos - (7 - 2)] : array($this->semStack[$stackPos - (7 - 2)]), $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - }, 159 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\For_(['init' => $this->semStack[$stackPos - (9 - 3)], 'cond' => $this->semStack[$stackPos - (9 - 5)], 'loop' => $this->semStack[$stackPos - (9 - 7)], 'stmts' => $this->semStack[$stackPos - (9 - 9)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 160 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Switch_($this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 161 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Break_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 162 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Continue_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 163 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Return_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 164 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Global_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 165 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Static_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 166 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Echo_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 167 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\InlineHTML($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 168 => function ($stackPos) { - $e = $this->semStack[$stackPos - (2 - 1)]; - if ($e instanceof \PHPUnit\PhpParser\Node\Expr\Throw_) { - // For backwards-compatibility reasons, convert throw in statement position into - // Stmt\Throw_ rather than Stmt\Expression(Expr\Throw_). - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Throw_($e->expr, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - } else { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Expression($e, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); +namespace PHPUnit\SebastianBergmann\Diff; + +final class Chunk +{ + /** + * @var int + */ + private $start; + /** + * @var int + */ + private $startRange; + /** + * @var int + */ + private $end; + /** + * @var int + */ + private $endRange; + /** + * @var Line[] + */ + private $lines; + public function __construct(int $start = 0, int $startRange = 1, int $end = 0, int $endRange = 1, array $lines = []) + { + $this->start = $start; + $this->startRange = $startRange; + $this->end = $end; + $this->endRange = $endRange; + $this->lines = $lines; + } + public function getStart() : int + { + return $this->start; + } + public function getStartRange() : int + { + return $this->startRange; + } + public function getEnd() : int + { + return $this->end; + } + public function getEndRange() : int + { + return $this->endRange; + } + /** + * @return Line[] + */ + public function getLines() : array + { + return $this->lines; + } + /** + * @param Line[] $lines + */ + public function setLines(array $lines) : void + { + foreach ($lines as $line) { + if (!$line instanceof Line) { + throw new InvalidArgumentException(); } - }, 169 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Unset_($this->semStack[$stackPos - (5 - 3)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 170 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Foreach_($this->semStack[$stackPos - (7 - 3)], $this->semStack[$stackPos - (7 - 5)][0], ['keyVar' => null, 'byRef' => $this->semStack[$stackPos - (7 - 5)][1], 'stmts' => $this->semStack[$stackPos - (7 - 7)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - }, 171 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Foreach_($this->semStack[$stackPos - (9 - 3)], $this->semStack[$stackPos - (9 - 7)][0], ['keyVar' => $this->semStack[$stackPos - (9 - 5)], 'byRef' => $this->semStack[$stackPos - (9 - 7)][1], 'stmts' => $this->semStack[$stackPos - (9 - 9)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 172 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Foreach_($this->semStack[$stackPos - (6 - 3)], new \PHPUnit\PhpParser\Node\Expr\Error($this->startAttributeStack[$stackPos - (6 - 4)] + $this->endAttributeStack[$stackPos - (6 - 4)]), ['stmts' => $this->semStack[$stackPos - (6 - 6)]], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 173 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Declare_($this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 174 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\TryCatch($this->semStack[$stackPos - (6 - 3)], $this->semStack[$stackPos - (6 - 5)], $this->semStack[$stackPos - (6 - 6)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - $this->checkTryCatch($this->semValue); - }, 175 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Goto_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 176 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Label($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 177 => function ($stackPos) { - $this->semValue = array(); - /* means: no statement */ - }, 178 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 179 => function ($stackPos) { - $startAttributes = $this->startAttributeStack[$stackPos - (1 - 1)]; - if (isset($startAttributes['comments'])) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Nop($startAttributes + $this->endAttributes); - } else { - $this->semValue = null; + } + $this->lines = $lines; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Diff; + +final class Diff +{ + /** + * @var string + */ + private $from; + /** + * @var string + */ + private $to; + /** + * @var Chunk[] + */ + private $chunks; + /** + * @param Chunk[] $chunks + */ + public function __construct(string $from, string $to, array $chunks = []) + { + $this->from = $from; + $this->to = $to; + $this->chunks = $chunks; + } + public function getFrom() : string + { + return $this->from; + } + public function getTo() : string + { + return $this->to; + } + /** + * @return Chunk[] + */ + public function getChunks() : array + { + return $this->chunks; + } + /** + * @param Chunk[] $chunks + */ + public function setChunks(array $chunks) : void + { + $this->chunks = $chunks; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Diff; + +interface LongestCommonSubsequenceCalculator +{ + /** + * Calculates the longest common subsequence of two arrays. + */ + public function calculate(array $from, array $to) : array; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Diff; + +use const PHP_INT_SIZE; +use const PREG_SPLIT_DELIM_CAPTURE; +use const PREG_SPLIT_NO_EMPTY; +use function array_shift; +use function array_unshift; +use function array_values; +use function count; +use function current; +use function end; +use function get_class; +use function gettype; +use function is_array; +use function is_object; +use function is_string; +use function key; +use function min; +use function preg_split; +use function prev; +use function reset; +use function sprintf; +use function substr; +use PHPUnit\SebastianBergmann\Diff\Output\DiffOutputBuilderInterface; +use PHPUnit\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; +final class Differ +{ + public const OLD = 0; + public const ADDED = 1; + public const REMOVED = 2; + public const DIFF_LINE_END_WARNING = 3; + public const NO_LINE_END_EOF_WARNING = 4; + /** + * @var DiffOutputBuilderInterface + */ + private $outputBuilder; + /** + * @param DiffOutputBuilderInterface $outputBuilder + * + * @throws InvalidArgumentException + */ + public function __construct($outputBuilder = null) + { + if ($outputBuilder instanceof DiffOutputBuilderInterface) { + $this->outputBuilder = $outputBuilder; + } elseif (null === $outputBuilder) { + $this->outputBuilder = new UnifiedDiffOutputBuilder(); + } elseif (is_string($outputBuilder)) { + // PHPUnit 6.1.4, 6.2.0, 6.2.1, 6.2.2, and 6.2.3 support + // @see https://github.com/sebastianbergmann/phpunit/issues/2734#issuecomment-314514056 + // @deprecated + $this->outputBuilder = new UnifiedDiffOutputBuilder($outputBuilder); + } else { + throw new InvalidArgumentException(sprintf('Expected builder to be an instance of DiffOutputBuilderInterface, or a string, got %s.', is_object($outputBuilder) ? 'instance of "' . get_class($outputBuilder) . '"' : gettype($outputBuilder) . ' "' . $outputBuilder . '"')); + } + } + /** + * Returns the diff between two arrays or strings as string. + * + * @param array|string $from + * @param array|string $to + */ + public function diff($from, $to, LongestCommonSubsequenceCalculator $lcs = null) : string + { + $diff = $this->diffToArray($this->normalizeDiffInput($from), $this->normalizeDiffInput($to), $lcs); + return $this->outputBuilder->getDiff($diff); + } + /** + * Returns the diff between two arrays or strings as array. + * + * Each array element contains two elements: + * - [0] => mixed $token + * - [1] => 2|1|0 + * + * - 2: REMOVED: $token was removed from $from + * - 1: ADDED: $token was added to $from + * - 0: OLD: $token is not changed in $to + * + * @param array|string $from + * @param array|string $to + * @param LongestCommonSubsequenceCalculator $lcs + */ + public function diffToArray($from, $to, LongestCommonSubsequenceCalculator $lcs = null) : array + { + if (is_string($from)) { + $from = $this->splitStringByLines($from); + } elseif (!is_array($from)) { + throw new InvalidArgumentException('"from" must be an array or string.'); + } + if (is_string($to)) { + $to = $this->splitStringByLines($to); + } elseif (!is_array($to)) { + throw new InvalidArgumentException('"to" must be an array or string.'); + } + [$from, $to, $start, $end] = self::getArrayDiffParted($from, $to); + if ($lcs === null) { + $lcs = $this->selectLcsImplementation($from, $to); + } + $common = $lcs->calculate(array_values($from), array_values($to)); + $diff = []; + foreach ($start as $token) { + $diff[] = [$token, self::OLD]; + } + reset($from); + reset($to); + foreach ($common as $token) { + while (($fromToken = reset($from)) !== $token) { + $diff[] = [array_shift($from), self::REMOVED]; } - if ($this->semValue === null) { - $this->semValue = array(); + while (($toToken = reset($to)) !== $token) { + $diff[] = [array_shift($to), self::ADDED]; } - /* means: no statement */ - }, 180 => function ($stackPos) { - $this->semValue = array(); - }, 181 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 182 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 183 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 184 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Catch_($this->semStack[$stackPos - (8 - 3)], $this->semStack[$stackPos - (8 - 4)], $this->semStack[$stackPos - (8 - 7)], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); - }, 185 => function ($stackPos) { - $this->semValue = null; - }, 186 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Finally_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 187 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 188 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 189 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 190 => function ($stackPos) { - $this->semValue = \false; - }, 191 => function ($stackPos) { - $this->semValue = \true; - }, 192 => function ($stackPos) { - $this->semValue = \false; - }, 193 => function ($stackPos) { - $this->semValue = \true; - }, 194 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 195 => function ($stackPos) { - $this->semValue = []; - }, 196 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Function_($this->semStack[$stackPos - (8 - 3)], ['byRef' => $this->semStack[$stackPos - (8 - 2)], 'params' => $this->semStack[$stackPos - (8 - 5)], 'returnType' => $this->semStack[$stackPos - (8 - 7)], 'stmts' => $this->semStack[$stackPos - (8 - 8)], 'attrGroups' => []], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); - }, 197 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Function_($this->semStack[$stackPos - (9 - 4)], ['byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 6)], 'returnType' => $this->semStack[$stackPos - (9 - 8)], 'stmts' => $this->semStack[$stackPos - (9 - 9)], 'attrGroups' => $this->semStack[$stackPos - (9 - 1)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 198 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Class_($this->semStack[$stackPos - (7 - 2)], ['type' => $this->semStack[$stackPos - (7 - 1)], 'extends' => $this->semStack[$stackPos - (7 - 3)], 'implements' => $this->semStack[$stackPos - (7 - 4)], 'stmts' => $this->semStack[$stackPos - (7 - 6)], 'attrGroups' => []], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - $this->checkClass($this->semValue, $stackPos - (7 - 2)); - }, 199 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Class_($this->semStack[$stackPos - (8 - 3)], ['type' => $this->semStack[$stackPos - (8 - 2)], 'extends' => $this->semStack[$stackPos - (8 - 4)], 'implements' => $this->semStack[$stackPos - (8 - 5)], 'stmts' => $this->semStack[$stackPos - (8 - 7)], 'attrGroups' => $this->semStack[$stackPos - (8 - 1)]], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); - $this->checkClass($this->semValue, $stackPos - (8 - 3)); - }, 200 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Interface_($this->semStack[$stackPos - (7 - 3)], ['extends' => $this->semStack[$stackPos - (7 - 4)], 'stmts' => $this->semStack[$stackPos - (7 - 6)], 'attrGroups' => $this->semStack[$stackPos - (7 - 1)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - $this->checkInterface($this->semValue, $stackPos - (7 - 3)); - }, 201 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Trait_($this->semStack[$stackPos - (6 - 3)], ['stmts' => $this->semStack[$stackPos - (6 - 5)], 'attrGroups' => $this->semStack[$stackPos - (6 - 1)]], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 202 => function ($stackPos) { - $this->semValue = 0; - }, 203 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_ABSTRACT; - }, 204 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_FINAL; - }, 205 => function ($stackPos) { - $this->semValue = null; - }, 206 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 207 => function ($stackPos) { - $this->semValue = array(); - }, 208 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 209 => function ($stackPos) { - $this->semValue = array(); - }, 210 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 211 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 212 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 213 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 214 => function ($stackPos) { - $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); - }, 215 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 216 => function ($stackPos) { - $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); - }, 217 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 218 => function ($stackPos) { - $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); - }, 219 => function ($stackPos) { - $this->semValue = null; - }, 220 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 221 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 222 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 223 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 224 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\DeclareDeclare($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 225 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 226 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 3)]; - }, 227 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 228 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (5 - 3)]; - }, 229 => function ($stackPos) { - $this->semValue = array(); - }, 230 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 231 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Case_($this->semStack[$stackPos - (4 - 2)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 232 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Case_(null, $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 233 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 234 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 235 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Match_($this->semStack[$stackPos - (7 - 3)], $this->semStack[$stackPos - (7 - 6)], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - }, 236 => function ($stackPos) { - $this->semValue = []; - }, 237 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 238 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 239 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 240 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\MatchArm($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 241 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\MatchArm(null, $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 242 => function ($stackPos) { - $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); - }, 243 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 244 => function ($stackPos) { - $this->semValue = array(); - }, 245 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 246 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\ElseIf_($this->semStack[$stackPos - (5 - 3)], \is_array($this->semStack[$stackPos - (5 - 5)]) ? $this->semStack[$stackPos - (5 - 5)] : array($this->semStack[$stackPos - (5 - 5)]), $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 247 => function ($stackPos) { - $this->semValue = array(); - }, 248 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 249 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\ElseIf_($this->semStack[$stackPos - (6 - 3)], $this->semStack[$stackPos - (6 - 6)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 250 => function ($stackPos) { - $this->semValue = null; - }, 251 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Else_(\is_array($this->semStack[$stackPos - (2 - 2)]) ? $this->semStack[$stackPos - (2 - 2)] : array($this->semStack[$stackPos - (2 - 2)]), $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 252 => function ($stackPos) { - $this->semValue = null; - }, 253 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Else_($this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 254 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)], \false); - }, 255 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (2 - 2)], \true); - }, 256 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)], \false); - }, 257 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)], \false); - }, 258 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 259 => function ($stackPos) { - $this->semValue = array(); - }, 260 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 261 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 262 => function ($stackPos) { - $this->semValue = 0; - }, 263 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC; - }, 264 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED; - }, 265 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE; - }, 266 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Param($this->semStack[$stackPos - (6 - 6)], null, $this->semStack[$stackPos - (6 - 3)], $this->semStack[$stackPos - (6 - 4)], $this->semStack[$stackPos - (6 - 5)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 1)]); - $this->checkParam($this->semValue); - }, 267 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Param($this->semStack[$stackPos - (8 - 6)], $this->semStack[$stackPos - (8 - 8)], $this->semStack[$stackPos - (8 - 3)], $this->semStack[$stackPos - (8 - 4)], $this->semStack[$stackPos - (8 - 5)], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (8 - 2)], $this->semStack[$stackPos - (8 - 1)]); - $this->checkParam($this->semValue); - }, 268 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Param(new \PHPUnit\PhpParser\Node\Expr\Error($this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes), null, $this->semStack[$stackPos - (6 - 3)], $this->semStack[$stackPos - (6 - 4)], $this->semStack[$stackPos - (6 - 5)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 1)]); - }, 269 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 270 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\NullableType($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 271 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\UnionType($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 272 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 273 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name('static', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 274 => function ($stackPos) { - $this->semValue = $this->handleBuiltinTypes($this->semStack[$stackPos - (1 - 1)]); - }, 275 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Identifier('array', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 276 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Identifier('callable', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 277 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)]); - }, 278 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 279 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)]); - }, 280 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 281 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 282 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\NullableType($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 283 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\UnionType($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 284 => function ($stackPos) { - $this->semValue = null; - }, 285 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 286 => function ($stackPos) { - $this->semValue = null; - }, 287 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 288 => function ($stackPos) { - $this->semValue = null; - }, 289 => function ($stackPos) { - $this->semValue = array(); - }, 290 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 291 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 292 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 293 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Arg($this->semStack[$stackPos - (1 - 1)], \false, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 294 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Arg($this->semStack[$stackPos - (2 - 2)], \true, \false, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 295 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Arg($this->semStack[$stackPos - (2 - 2)], \false, \true, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 296 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Arg($this->semStack[$stackPos - (3 - 3)], \false, \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (3 - 1)]); - }, 297 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 298 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 299 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 300 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 301 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 302 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 303 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 304 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\StaticVar($this->semStack[$stackPos - (1 - 1)], null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 305 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\StaticVar($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 306 => function ($stackPos) { - if ($this->semStack[$stackPos - (2 - 2)] !== null) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + $diff[] = [$token, self::OLD]; + array_shift($from); + array_shift($to); + } + while (($token = array_shift($from)) !== null) { + $diff[] = [$token, self::REMOVED]; + } + while (($token = array_shift($to)) !== null) { + $diff[] = [$token, self::ADDED]; + } + foreach ($end as $token) { + $diff[] = [$token, self::OLD]; + } + if ($this->detectUnmatchedLineEndings($diff)) { + array_unshift($diff, ["#Warning: Strings contain different line endings!\n", self::DIFF_LINE_END_WARNING]); + } + return $diff; + } + /** + * Casts variable to string if it is not a string or array. + * + * @return array|string + */ + private function normalizeDiffInput($input) + { + if (!is_array($input) && !is_string($input)) { + return (string) $input; + } + return $input; + } + /** + * Checks if input is string, if so it will split it line-by-line. + */ + private function splitStringByLines(string $input) : array + { + return preg_split('/(.*\\R)/', $input, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY); + } + private function selectLcsImplementation(array $from, array $to) : LongestCommonSubsequenceCalculator + { + // We do not want to use the time-efficient implementation if its memory + // footprint will probably exceed this value. Note that the footprint + // calculation is only an estimation for the matrix and the LCS method + // will typically allocate a bit more memory than this. + $memoryLimit = 100 * 1024 * 1024; + if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) { + return new MemoryEfficientLongestCommonSubsequenceCalculator(); + } + return new TimeEfficientLongestCommonSubsequenceCalculator(); + } + /** + * Calculates the estimated memory footprint for the DP-based method. + * + * @return float|int + */ + private function calculateEstimatedFootprint(array $from, array $to) + { + $itemSize = \PHP_INT_SIZE === 4 ? 76 : 144; + return $itemSize * min(count($from), count($to)) ** 2; + } + /** + * Returns true if line ends don't match in a diff. + */ + private function detectUnmatchedLineEndings(array $diff) : bool + { + $newLineBreaks = ['' => \true]; + $oldLineBreaks = ['' => \true]; + foreach ($diff as $entry) { + if (self::OLD === $entry[1]) { + $ln = $this->getLinebreak($entry[0]); + $oldLineBreaks[$ln] = \true; + $newLineBreaks[$ln] = \true; + } elseif (self::ADDED === $entry[1]) { + $newLineBreaks[$this->getLinebreak($entry[0])] = \true; + } elseif (self::REMOVED === $entry[1]) { + $oldLineBreaks[$this->getLinebreak($entry[0])] = \true; } - }, 307 => function ($stackPos) { - $this->semValue = array(); - }, 308 => function ($stackPos) { - $startAttributes = $this->lookaheadStartAttributes; - if (isset($startAttributes['comments'])) { - $nop = new \PHPUnit\PhpParser\Node\Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); + } + // if either input or output is a single line without breaks than no warning should be raised + if (['' => \true] === $newLineBreaks || ['' => \true] === $oldLineBreaks) { + return \false; + } + // two way compare + foreach ($newLineBreaks as $break => $set) { + if (!isset($oldLineBreaks[$break])) { + return \true; + } + } + foreach ($oldLineBreaks as $break => $set) { + if (!isset($newLineBreaks[$break])) { + return \true; + } + } + return \false; + } + private function getLinebreak($line) : string + { + if (!is_string($line)) { + return ''; + } + $lc = substr($line, -1); + if ("\r" === $lc) { + return "\r"; + } + if ("\n" !== $lc) { + return ''; + } + if ("\r\n" === substr($line, -2)) { + return "\r\n"; + } + return "\n"; + } + private static function getArrayDiffParted(array &$from, array &$to) : array + { + $start = []; + $end = []; + reset($to); + foreach ($from as $k => $v) { + $toK = key($to); + if ($toK === $k && $v === $to[$k]) { + $start[$k] = $v; + unset($from[$k], $to[$k]); } else { - $nop = null; + break; } - if ($nop !== null) { - $this->semStack[$stackPos - (1 - 1)][] = $nop; + } + end($from); + end($to); + do { + $fromK = key($from); + $toK = key($to); + if (null === $fromK || null === $toK || current($from) !== current($to)) { + break; } - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 309 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Property($this->semStack[$stackPos - (5 - 2)], $this->semStack[$stackPos - (5 - 4)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 1)]); - $this->checkProperty($this->semValue, $stackPos - (5 - 2)); - }, 310 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\ClassConst($this->semStack[$stackPos - (5 - 4)], $this->semStack[$stackPos - (5 - 2)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes, $this->semStack[$stackPos - (5 - 1)]); - $this->checkClassConst($this->semValue, $stackPos - (5 - 2)); - }, 311 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\ClassMethod($this->semStack[$stackPos - (10 - 5)], ['type' => $this->semStack[$stackPos - (10 - 2)], 'byRef' => $this->semStack[$stackPos - (10 - 4)], 'params' => $this->semStack[$stackPos - (10 - 7)], 'returnType' => $this->semStack[$stackPos - (10 - 9)], 'stmts' => $this->semStack[$stackPos - (10 - 10)], 'attrGroups' => $this->semStack[$stackPos - (10 - 1)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); - $this->checkClassMethod($this->semValue, $stackPos - (10 - 2)); - }, 312 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\TraitUse($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 313 => function ($stackPos) { - $this->semValue = null; - /* will be skipped */ - }, 314 => function ($stackPos) { - $this->semValue = array(); - }, 315 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 316 => function ($stackPos) { - $this->semValue = array(); - }, 317 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 318 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\TraitUseAdaptation\Precedence($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 319 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (5 - 1)][0], $this->semStack[$stackPos - (5 - 1)][1], $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 4)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 320 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], $this->semStack[$stackPos - (4 - 3)], null, $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 321 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 322 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 323 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)]); - }, 324 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 325 => function ($stackPos) { - $this->semValue = array(null, $this->semStack[$stackPos - (1 - 1)]); - }, 326 => function ($stackPos) { - $this->semValue = null; - }, 327 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 328 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 329 => function ($stackPos) { - $this->semValue = 0; - }, 330 => function ($stackPos) { - $this->semValue = 0; - }, 331 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 332 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 333 => function ($stackPos) { - $this->checkModifier($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $stackPos - (2 - 2)); - $this->semValue = $this->semStack[$stackPos - (2 - 1)] | $this->semStack[$stackPos - (2 - 2)]; - }, 334 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC; - }, 335 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED; - }, 336 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE; - }, 337 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_STATIC; - }, 338 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_ABSTRACT; - }, 339 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_FINAL; - }, 340 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 341 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 342 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 343 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\VarLikeIdentifier(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 344 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\PropertyProperty($this->semStack[$stackPos - (1 - 1)], null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 345 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\PropertyProperty($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 346 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 347 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 348 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 349 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 350 => function ($stackPos) { - $this->semValue = array(); - }, 351 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 352 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 353 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Assign($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 354 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Assign($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 355 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Assign($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 356 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignRef($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 357 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 358 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 359 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Clone_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 360 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\Plus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 361 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\Minus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 362 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\Mul($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 363 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\Div($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 364 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\Concat($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 365 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\Mod($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 366 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 367 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 368 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 369 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\ShiftLeft($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 370 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\ShiftRight($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 371 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\Pow($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 372 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\Coalesce($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 373 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\PostInc($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 374 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\PreInc($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 375 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\PostDec($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 376 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\PreDec($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 377 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 378 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 379 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 380 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 381 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 382 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 383 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 384 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 385 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Concat($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 386 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Plus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 387 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Minus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 388 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Mul($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 389 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Div($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 390 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Mod($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 391 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\ShiftLeft($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 392 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\ShiftRight($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 393 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Pow($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 394 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\UnaryPlus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 395 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\UnaryMinus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 396 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BooleanNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 397 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BitwiseNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 398 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Identical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 399 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\NotIdentical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 400 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Equal($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 401 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\NotEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 402 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Spaceship($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 403 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Smaller($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 404 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\SmallerOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 405 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Greater($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 406 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\GreaterOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 407 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Instanceof_($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 408 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 409 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Ternary($this->semStack[$stackPos - (5 - 1)], $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 410 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Ternary($this->semStack[$stackPos - (4 - 1)], null, $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 411 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Coalesce($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 412 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Isset_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 413 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Empty_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 414 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Include_($this->semStack[$stackPos - (2 - 2)], \PHPUnit\PhpParser\Node\Expr\Include_::TYPE_INCLUDE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 415 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Include_($this->semStack[$stackPos - (2 - 2)], \PHPUnit\PhpParser\Node\Expr\Include_::TYPE_INCLUDE_ONCE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 416 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Eval_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 417 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Include_($this->semStack[$stackPos - (2 - 2)], \PHPUnit\PhpParser\Node\Expr\Include_::TYPE_REQUIRE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 418 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Include_($this->semStack[$stackPos - (2 - 2)], \PHPUnit\PhpParser\Node\Expr\Include_::TYPE_REQUIRE_ONCE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 419 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Cast\Int_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 420 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes; - $attrs['kind'] = $this->getFloatCastKind($this->semStack[$stackPos - (2 - 1)]); - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Cast\Double($this->semStack[$stackPos - (2 - 2)], $attrs); - }, 421 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Cast\String_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 422 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Cast\Array_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 423 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Cast\Object_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 424 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Cast\Bool_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 425 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Cast\Unset_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 426 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes; - $attrs['kind'] = \strtolower($this->semStack[$stackPos - (2 - 1)]) === 'exit' ? \PHPUnit\PhpParser\Node\Expr\Exit_::KIND_EXIT : \PHPUnit\PhpParser\Node\Expr\Exit_::KIND_DIE; - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Exit_($this->semStack[$stackPos - (2 - 2)], $attrs); - }, 427 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ErrorSuppress($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 428 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 429 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ShellExec($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 430 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Print_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 431 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Yield_(null, null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 432 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Yield_($this->semStack[$stackPos - (2 - 2)], null, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 433 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Yield_($this->semStack[$stackPos - (4 - 4)], $this->semStack[$stackPos - (4 - 2)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 434 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\YieldFrom($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 435 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Throw_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 436 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrowFunction(['static' => \false, 'byRef' => $this->semStack[$stackPos - (8 - 2)], 'params' => $this->semStack[$stackPos - (8 - 4)], 'returnType' => $this->semStack[$stackPos - (8 - 6)], 'expr' => $this->semStack[$stackPos - (8 - 8)], 'attrGroups' => []], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); - }, 437 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrowFunction(['static' => \true, 'byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 5)], 'returnType' => $this->semStack[$stackPos - (9 - 7)], 'expr' => $this->semStack[$stackPos - (9 - 9)], 'attrGroups' => []], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 438 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Closure(['static' => \false, 'byRef' => $this->semStack[$stackPos - (8 - 2)], 'params' => $this->semStack[$stackPos - (8 - 4)], 'uses' => $this->semStack[$stackPos - (8 - 6)], 'returnType' => $this->semStack[$stackPos - (8 - 7)], 'stmts' => $this->semStack[$stackPos - (8 - 8)], 'attrGroups' => []], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); - }, 439 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Closure(['static' => \true, 'byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 5)], 'uses' => $this->semStack[$stackPos - (9 - 7)], 'returnType' => $this->semStack[$stackPos - (9 - 8)], 'stmts' => $this->semStack[$stackPos - (9 - 9)], 'attrGroups' => []], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 440 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrowFunction(['static' => \false, 'byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 5)], 'returnType' => $this->semStack[$stackPos - (9 - 7)], 'expr' => $this->semStack[$stackPos - (9 - 9)], 'attrGroups' => $this->semStack[$stackPos - (9 - 1)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 441 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrowFunction(['static' => \true, 'byRef' => $this->semStack[$stackPos - (10 - 4)], 'params' => $this->semStack[$stackPos - (10 - 6)], 'returnType' => $this->semStack[$stackPos - (10 - 8)], 'expr' => $this->semStack[$stackPos - (10 - 10)], 'attrGroups' => $this->semStack[$stackPos - (10 - 1)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); - }, 442 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Closure(['static' => \false, 'byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 5)], 'uses' => $this->semStack[$stackPos - (9 - 7)], 'returnType' => $this->semStack[$stackPos - (9 - 8)], 'stmts' => $this->semStack[$stackPos - (9 - 9)], 'attrGroups' => $this->semStack[$stackPos - (9 - 1)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 443 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Closure(['static' => \true, 'byRef' => $this->semStack[$stackPos - (10 - 4)], 'params' => $this->semStack[$stackPos - (10 - 6)], 'uses' => $this->semStack[$stackPos - (10 - 8)], 'returnType' => $this->semStack[$stackPos - (10 - 9)], 'stmts' => $this->semStack[$stackPos - (10 - 10)], 'attrGroups' => $this->semStack[$stackPos - (10 - 1)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); - }, 444 => function ($stackPos) { - $this->semValue = array(new \PHPUnit\PhpParser\Node\Stmt\Class_(null, ['type' => 0, 'extends' => $this->semStack[$stackPos - (8 - 4)], 'implements' => $this->semStack[$stackPos - (8 - 5)], 'stmts' => $this->semStack[$stackPos - (8 - 7)], 'attrGroups' => $this->semStack[$stackPos - (8 - 1)]], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes), $this->semStack[$stackPos - (8 - 3)]); - $this->checkClass($this->semValue[0], -1); - }, 445 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\New_($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 446 => function ($stackPos) { - list($class, $ctorArgs) = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\New_($class, $ctorArgs, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 447 => function ($stackPos) { - $this->semValue = array(); - }, 448 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 3)]; - }, 449 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 450 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 451 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 452 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ClosureUse($this->semStack[$stackPos - (2 - 2)], $this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 453 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\FuncCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 454 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\FuncCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 455 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\StaticCall($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 456 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 457 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 458 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 459 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 460 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name\FullyQualified(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 461 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name\Relative(\substr($this->semStack[$stackPos - (1 - 1)], 10), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 462 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 463 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 464 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 465 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Error($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - $this->errorState = 2; - }, 466 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 467 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 468 => function ($stackPos) { - $this->semValue = null; - }, 469 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 470 => function ($stackPos) { - $this->semValue = array(); - }, 471 => function ($stackPos) { - $this->semValue = array(new \PHPUnit\PhpParser\Node\Scalar\EncapsedStringPart(\PHPUnit\PhpParser\Node\Scalar\String_::parseEscapeSequences($this->semStack[$stackPos - (1 - 1)], '`'), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes)); - }, 472 => function ($stackPos) { - foreach ($this->semStack[$stackPos - (1 - 1)] as $s) { - if ($s instanceof \PHPUnit\PhpParser\Node\Scalar\EncapsedStringPart) { - $s->value = \PHPUnit\PhpParser\Node\Scalar\String_::parseEscapeSequences($s->value, '`', \true); + prev($from); + prev($to); + $end = [$fromK => $from[$fromK]] + $end; + unset($from[$fromK], $to[$toK]); + } while (\true); + return [$from, $to, $start, $end]; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Diff; + +use function array_pop; +use function count; +use function max; +use function preg_match; +use function preg_split; +/** + * Unified diff parser. + */ +final class Parser +{ + /** + * @return Diff[] + */ + public function parse(string $string) : array + { + $lines = preg_split('(\\r\\n|\\r|\\n)', $string); + if (!empty($lines) && $lines[count($lines) - 1] === '') { + array_pop($lines); + } + $lineCount = count($lines); + $diffs = []; + $diff = null; + $collected = []; + for ($i = 0; $i < $lineCount; ++$i) { + if (preg_match('#^---\\h+"?(?P[^\\v\\t"]+)#', $lines[$i], $fromMatch) && preg_match('#^\\+\\+\\+\\h+"?(?P[^\\v\\t"]+)#', $lines[$i + 1], $toMatch)) { + if ($diff !== null) { + $this->parseFileDiff($diff, $collected); + $diffs[] = $diff; + $collected = []; } - } - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 473 => function ($stackPos) { - $this->semValue = array(); - }, 474 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 475 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ConstFetch($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 476 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\MagicConst\Line($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 477 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\MagicConst\File($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 478 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\MagicConst\Dir($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 479 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\MagicConst\Class_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 480 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\MagicConst\Trait_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 481 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\MagicConst\Method($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 482 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\MagicConst\Function_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 483 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\MagicConst\Namespace_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 484 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ClassConstFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 485 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ClassConstFetch($this->semStack[$stackPos - (3 - 1)], new \PHPUnit\PhpParser\Node\Expr\Error($this->startAttributeStack[$stackPos - (3 - 3)] + $this->endAttributeStack[$stackPos - (3 - 3)]), $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - $this->errorState = 2; - }, 486 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes; - $attrs['kind'] = \PHPUnit\PhpParser\Node\Expr\Array_::KIND_SHORT; - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Array_($this->semStack[$stackPos - (3 - 2)], $attrs); - }, 487 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes; - $attrs['kind'] = \PHPUnit\PhpParser\Node\Expr\Array_::KIND_LONG; - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Array_($this->semStack[$stackPos - (4 - 3)], $attrs); - }, 488 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 489 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes; - $attrs['kind'] = $this->semStack[$stackPos - (1 - 1)][0] === "'" || $this->semStack[$stackPos - (1 - 1)][1] === "'" && ($this->semStack[$stackPos - (1 - 1)][0] === 'b' || $this->semStack[$stackPos - (1 - 1)][0] === 'B') ? \PHPUnit\PhpParser\Node\Scalar\String_::KIND_SINGLE_QUOTED : \PHPUnit\PhpParser\Node\Scalar\String_::KIND_DOUBLE_QUOTED; - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\String_(\PHPUnit\PhpParser\Node\Scalar\String_::parse($this->semStack[$stackPos - (1 - 1)]), $attrs); - }, 490 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes; - $attrs['kind'] = \PHPUnit\PhpParser\Node\Scalar\String_::KIND_DOUBLE_QUOTED; - foreach ($this->semStack[$stackPos - (3 - 2)] as $s) { - if ($s instanceof \PHPUnit\PhpParser\Node\Scalar\EncapsedStringPart) { - $s->value = \PHPUnit\PhpParser\Node\Scalar\String_::parseEscapeSequences($s->value, '"', \true); + $diff = new Diff($fromMatch['file'], $toMatch['file']); + ++$i; + } else { + if (preg_match('/^(?:diff --git |index [\\da-f\\.]+|[+-]{3} [ab])/', $lines[$i])) { + continue; } + $collected[] = $lines[$i]; } - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\Encapsed($this->semStack[$stackPos - (3 - 2)], $attrs); - }, 491 => function ($stackPos) { - $this->semValue = $this->parseLNumber($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 492 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\DNumber(\PHPUnit\PhpParser\Node\Scalar\DNumber::parse($this->semStack[$stackPos - (1 - 1)]), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 493 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 494 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 495 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 496 => function ($stackPos) { - $this->semValue = $this->parseDocString($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (3 - 3)] + $this->endAttributeStack[$stackPos - (3 - 3)], \true); - }, 497 => function ($stackPos) { - $this->semValue = $this->parseDocString($this->semStack[$stackPos - (2 - 1)], '', $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (2 - 2)] + $this->endAttributeStack[$stackPos - (2 - 2)], \true); - }, 498 => function ($stackPos) { - $this->semValue = $this->parseDocString($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (3 - 3)] + $this->endAttributeStack[$stackPos - (3 - 3)], \true); - }, 499 => function ($stackPos) { - $this->semValue = null; - }, 500 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 501 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 502 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 503 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 504 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 505 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 506 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 507 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 508 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 509 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 510 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 511 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 512 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 513 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 514 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\MethodCall($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 515 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\NullsafeMethodCall($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 516 => function ($stackPos) { - $this->semValue = null; - }, 517 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 518 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 519 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 520 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 521 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\NullsafePropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 522 => function ($stackPos) { - $this->semValue = \substr($this->semStack[$stackPos - (1 - 1)], 1); - }, 523 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 3)]; - }, 524 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 525 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Error($this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - $this->errorState = 2; - }, 526 => function ($stackPos) { - $var = $this->semStack[$stackPos - (1 - 1)]; - $this->semValue = \is_string($var) ? new \PHPUnit\PhpParser\Node\VarLikeIdentifier($var, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes) : $var; - }, 527 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\StaticPropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 528 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 529 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 530 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 531 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 532 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\NullsafePropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 533 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\StaticPropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 534 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\StaticPropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 535 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 536 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 537 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 538 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 539 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 540 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 541 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Error($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - $this->errorState = 2; - }, 542 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\List_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 543 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - $end = \count($this->semValue) - 1; - if ($this->semValue[$end] === null) { - \array_pop($this->semValue); + } + if ($diff !== null && count($collected)) { + $this->parseFileDiff($diff, $collected); + $diffs[] = $diff; + } + return $diffs; + } + private function parseFileDiff(Diff $diff, array $lines) : void + { + $chunks = []; + $chunk = null; + $diffLines = []; + foreach ($lines as $line) { + if (preg_match('/^@@\\s+-(?P\\d+)(?:,\\s*(?P\\d+))?\\s+\\+(?P\\d+)(?:,\\s*(?P\\d+))?\\s+@@/', $line, $match)) { + $chunk = new Chunk((int) $match['start'], isset($match['startrange']) ? max(1, (int) $match['startrange']) : 1, (int) $match['end'], isset($match['endrange']) ? max(1, (int) $match['endrange']) : 1); + $chunks[] = $chunk; + $diffLines = []; + continue; + } + if (preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { + $type = Line::UNCHANGED; + if ($match['type'] === '+') { + $type = Line::ADDED; + } elseif ($match['type'] === '-') { + $type = Line::REMOVED; + } + $diffLines[] = new Line($type, $match['line']); + if (null !== $chunk) { + $chunk->setLines($diffLines); + } } - }, 544 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 545 => function ($stackPos) { - /* do nothing -- prevent default action of $$=$this->semStack[$1]. See $551. */ - }, 546 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 547 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 548 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 549 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayItem($this->semStack[$stackPos - (2 - 2)], null, \true, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 550 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 551 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayItem($this->semStack[$stackPos - (3 - 3)], $this->semStack[$stackPos - (3 - 1)], \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 552 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayItem($this->semStack[$stackPos - (4 - 4)], $this->semStack[$stackPos - (4 - 1)], \true, $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 553 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayItem($this->semStack[$stackPos - (3 - 3)], $this->semStack[$stackPos - (3 - 1)], \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 554 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayItem($this->semStack[$stackPos - (2 - 2)], null, \false, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes, \true, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 555 => function ($stackPos) { - $this->semValue = null; - }, 556 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 557 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 558 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 559 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); - }, 560 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\EncapsedStringPart($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 561 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 562 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 563 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 564 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 565 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\NullsafePropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 566 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 567 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 568 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 4)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 569 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 570 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\String_($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 571 => function ($stackPos) { - $this->semValue = $this->parseNumString($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 572 => function ($stackPos) { - $this->semValue = $this->parseNumString('-' . $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 573 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }]; + } + $diff->setChunks($chunks); } } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\Diff; -/* GENERATED file based on grammar/tokens.y */ -final class Tokens +final class Line { - const YYERRTOK = 256; - const T_THROW = 257; - const T_INCLUDE = 258; - const T_INCLUDE_ONCE = 259; - const T_EVAL = 260; - const T_REQUIRE = 261; - const T_REQUIRE_ONCE = 262; - const T_LOGICAL_OR = 263; - const T_LOGICAL_XOR = 264; - const T_LOGICAL_AND = 265; - const T_PRINT = 266; - const T_YIELD = 267; - const T_DOUBLE_ARROW = 268; - const T_YIELD_FROM = 269; - const T_PLUS_EQUAL = 270; - const T_MINUS_EQUAL = 271; - const T_MUL_EQUAL = 272; - const T_DIV_EQUAL = 273; - const T_CONCAT_EQUAL = 274; - const T_MOD_EQUAL = 275; - const T_AND_EQUAL = 276; - const T_OR_EQUAL = 277; - const T_XOR_EQUAL = 278; - const T_SL_EQUAL = 279; - const T_SR_EQUAL = 280; - const T_POW_EQUAL = 281; - const T_COALESCE_EQUAL = 282; - const T_COALESCE = 283; - const T_BOOLEAN_OR = 284; - const T_BOOLEAN_AND = 285; - const T_IS_EQUAL = 286; - const T_IS_NOT_EQUAL = 287; - const T_IS_IDENTICAL = 288; - const T_IS_NOT_IDENTICAL = 289; - const T_SPACESHIP = 290; - const T_IS_SMALLER_OR_EQUAL = 291; - const T_IS_GREATER_OR_EQUAL = 292; - const T_SL = 293; - const T_SR = 294; - const T_INSTANCEOF = 295; - const T_INC = 296; - const T_DEC = 297; - const T_INT_CAST = 298; - const T_DOUBLE_CAST = 299; - const T_STRING_CAST = 300; - const T_ARRAY_CAST = 301; - const T_OBJECT_CAST = 302; - const T_BOOL_CAST = 303; - const T_UNSET_CAST = 304; - const T_POW = 305; - const T_NEW = 306; - const T_CLONE = 307; - const T_EXIT = 308; - const T_IF = 309; - const T_ELSEIF = 310; - const T_ELSE = 311; - const T_ENDIF = 312; - const T_LNUMBER = 313; - const T_DNUMBER = 314; - const T_STRING = 315; - const T_STRING_VARNAME = 316; - const T_VARIABLE = 317; - const T_NUM_STRING = 318; - const T_INLINE_HTML = 319; - const T_ENCAPSED_AND_WHITESPACE = 320; - const T_CONSTANT_ENCAPSED_STRING = 321; - const T_ECHO = 322; - const T_DO = 323; - const T_WHILE = 324; - const T_ENDWHILE = 325; - const T_FOR = 326; - const T_ENDFOR = 327; - const T_FOREACH = 328; - const T_ENDFOREACH = 329; - const T_DECLARE = 330; - const T_ENDDECLARE = 331; - const T_AS = 332; - const T_SWITCH = 333; - const T_MATCH = 334; - const T_ENDSWITCH = 335; - const T_CASE = 336; - const T_DEFAULT = 337; - const T_BREAK = 338; - const T_CONTINUE = 339; - const T_GOTO = 340; - const T_FUNCTION = 341; - const T_FN = 342; - const T_CONST = 343; - const T_RETURN = 344; - const T_TRY = 345; - const T_CATCH = 346; - const T_FINALLY = 347; - const T_USE = 348; - const T_INSTEADOF = 349; - const T_GLOBAL = 350; - const T_STATIC = 351; - const T_ABSTRACT = 352; - const T_FINAL = 353; - const T_PRIVATE = 354; - const T_PROTECTED = 355; - const T_PUBLIC = 356; - const T_VAR = 357; - const T_UNSET = 358; - const T_ISSET = 359; - const T_EMPTY = 360; - const T_HALT_COMPILER = 361; - const T_CLASS = 362; - const T_TRAIT = 363; - const T_INTERFACE = 364; - const T_EXTENDS = 365; - const T_IMPLEMENTS = 366; - const T_OBJECT_OPERATOR = 367; - const T_NULLSAFE_OBJECT_OPERATOR = 368; - const T_LIST = 369; - const T_ARRAY = 370; - const T_CALLABLE = 371; - const T_CLASS_C = 372; - const T_TRAIT_C = 373; - const T_METHOD_C = 374; - const T_FUNC_C = 375; - const T_LINE = 376; - const T_FILE = 377; - const T_START_HEREDOC = 378; - const T_END_HEREDOC = 379; - const T_DOLLAR_OPEN_CURLY_BRACES = 380; - const T_CURLY_OPEN = 381; - const T_PAAMAYIM_NEKUDOTAYIM = 382; - const T_NAMESPACE = 383; - const T_NS_C = 384; - const T_DIR = 385; - const T_NS_SEPARATOR = 386; - const T_ELLIPSIS = 387; - const T_NAME_FULLY_QUALIFIED = 388; - const T_NAME_QUALIFIED = 389; - const T_NAME_RELATIVE = 390; - const T_ATTRIBUTE = 391; + public const ADDED = 1; + public const REMOVED = 2; + public const UNCHANGED = 3; + /** + * @var int + */ + private $type; + /** + * @var string + */ + private $content; + public function __construct(int $type = self::UNCHANGED, string $content = '') + { + $this->type = $type; + $this->content = $content; + } + public function getContent() : string + { + return $this->content; + } + public function getType() : int + { + return $this->type; + } } +sebastian/diff + +Copyright (c) 2002-2020, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. '", "T_IS_GREATER_OR_EQUAL", "T_SL", "T_SR", "'+'", "'-'", "'.'", "'*'", "'/'", "'%'", "'!'", "T_INSTANCEOF", "'~'", "T_INC", "T_DEC", "T_INT_CAST", "T_DOUBLE_CAST", "T_STRING_CAST", "T_ARRAY_CAST", "T_OBJECT_CAST", "T_BOOL_CAST", "T_UNSET_CAST", "'@'", "T_POW", "'['", "T_NEW", "T_CLONE", "T_EXIT", "T_IF", "T_ELSEIF", "T_ELSE", "T_ENDIF", "T_LNUMBER", "T_DNUMBER", "T_STRING", "T_STRING_VARNAME", "T_VARIABLE", "T_NUM_STRING", "T_INLINE_HTML", "T_ENCAPSED_AND_WHITESPACE", "T_CONSTANT_ENCAPSED_STRING", "T_ECHO", "T_DO", "T_WHILE", "T_ENDWHILE", "T_FOR", "T_ENDFOR", "T_FOREACH", "T_ENDFOREACH", "T_DECLARE", "T_ENDDECLARE", "T_AS", "T_SWITCH", "T_MATCH", "T_ENDSWITCH", "T_CASE", "T_DEFAULT", "T_BREAK", "T_CONTINUE", "T_GOTO", "T_FUNCTION", "T_FN", "T_CONST", "T_RETURN", "T_TRY", "T_CATCH", "T_FINALLY", "T_USE", "T_INSTEADOF", "T_GLOBAL", "T_STATIC", "T_ABSTRACT", "T_FINAL", "T_PRIVATE", "T_PROTECTED", "T_PUBLIC", "T_VAR", "T_UNSET", "T_ISSET", "T_EMPTY", "T_HALT_COMPILER", "T_CLASS", "T_TRAIT", "T_INTERFACE", "T_EXTENDS", "T_IMPLEMENTS", "T_OBJECT_OPERATOR", "T_LIST", "T_ARRAY", "T_CALLABLE", "T_CLASS_C", "T_TRAIT_C", "T_METHOD_C", "T_FUNC_C", "T_LINE", "T_FILE", "T_START_HEREDOC", "T_END_HEREDOC", "T_DOLLAR_OPEN_CURLY_BRACES", "T_CURLY_OPEN", "T_PAAMAYIM_NEKUDOTAYIM", "T_NAMESPACE", "T_NS_C", "T_DIR", "T_NS_SEPARATOR", "T_ELLIPSIS", "T_NAME_FULLY_QUALIFIED", "T_NAME_QUALIFIED", "T_NAME_RELATIVE", "';'", "'{'", "'}'", "'('", "')'", "'\$'", "'`'", "']'", "'\"'", "T_NULLSAFE_OBJECT_OPERATOR", "T_ATTRIBUTE"); - protected $tokenToSymbol = array(0, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 55, 162, 165, 159, 54, 37, 165, 157, 158, 52, 49, 8, 50, 51, 53, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 31, 154, 43, 16, 45, 30, 67, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 69, 165, 161, 36, 165, 160, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 155, 35, 156, 57, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 38, 39, 40, 41, 42, 44, 46, 47, 48, 56, 58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 163, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 164); - protected $action = array(693, 663, 664, 665, 666, 667, 282, 668, 669, 670, 706, 707, 221, 222, 223, 224, 225, 226, 227, 228, 229, 0, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32767, -32767, -32767, -32767, 27, 242, 243, -32766, -32766, -32766, -32766, -32766, 671, -32766, 333, -32766, -32766, -32766, -32766, -32766, -32766, -32767, -32767, -32767, -32767, -32767, 672, 673, 674, 675, 676, 677, 678, 1034, 816, 740, 941, 942, 943, 940, 939, 938, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 709, 732, 710, 711, 712, 713, 701, 702, 703, 731, 704, 705, 690, 691, 692, 694, 695, 696, 734, 735, 736, 737, 738, 739, 697, 698, 699, 700, 730, 721, 719, 720, 716, 717, 437, 708, 714, 715, 722, 723, 725, 724, 726, 727, 55, 56, 417, 57, 58, 718, 729, 728, 28, 59, 60, -220, 61, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, -32766, 36, -32767, -32767, -32767, -32767, 1034, 35, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, -32766, -32766, -32766, -32766, 62, 63, 1034, 125, 285, 292, 64, 748, 65, 290, 291, 66, 67, 68, 69, 70, 71, 72, 73, 763, 25, 298, 74, 409, 973, 975, 294, 294, 1086, 1087, 1064, 796, 748, 218, 219, 220, 465, -32766, -32766, -32766, 742, 864, 817, 54, 807, 9, -32766, -32766, -32766, 760, 320, 761, 410, 10, 202, 246, 428, 209, -32766, 933, -32766, -32766, -32766, -32766, -32766, -32766, 488, -32766, 438, -32766, -32766, -32766, -32766, -32766, 473, 474, 941, 942, 943, 940, 939, 938, -32766, 475, 476, 337, 1092, 1093, 1094, 1095, 1089, 1090, 315, 1214, -255, 747, 1215, -505, 1096, 1091, 888, 889, 1066, 1065, 1067, 218, 219, 220, 41, 414, 337, 330, 895, 332, 418, -126, -126, -126, 75, 52, 464, -4, 817, 54, 805, -224, 202, 40, 21, 419, -126, 466, -126, 467, -126, 468, -126, 359, 420, 128, 128, 748, 1171, 31, 32, 421, 422, 1034, 894, 33, 469, -32766, -32766, -32766, 1186, 351, 352, 470, 471, -32766, -32766, -32766, 309, 472, 865, 323, 788, 835, 423, 424, -32767, -32767, -32767, -32767, 97, 98, 99, 100, 101, 615, -32766, 313, -32766, -32766, -32766, -32766, 354, 1185, 1171, 218, 219, 220, 475, 748, 418, 819, 629, -126, 297, 915, 464, 817, 54, -32766, 805, 124, 748, 40, 21, 419, 202, 466, 48, 467, 534, 468, 129, 429, 420, 337, 341, 888, 889, 31, 32, 421, 422, 416, 405, 33, 469, -32766, -32766, 311, 298, 351, 352, 470, 471, -32766, -32766, -32766, 748, 472, 412, 748, 752, 835, 423, 424, 338, 1066, 1065, 1067, 219, 220, 919, 1136, 296, 20, -32766, 576, -32766, -32766, -32766, 742, 341, 342, 413, 429, 1064, 337, 512, 418, 202, 819, 629, -4, 1034, 464, 817, 54, 49, 805, 337, 762, 40, 21, 419, 51, 466, 1034, 467, 475, 468, 340, 748, 420, 120, -205, -205, -205, 31, 32, 421, 422, 1062, -32766, 33, 469, -32766, -32766, -32766, 744, 351, 352, 470, 471, 429, 1098, 337, 429, 472, 337, 1034, 788, 835, 423, 424, 415, 1098, -32766, 802, -32766, -32766, 102, 103, 104, 1137, 303, 202, 130, 1066, 1065, 1067, 337, 123, 239, 240, 241, 748, 105, 418, 1205, 819, 629, -205, 440, 464, -32766, -32766, -32766, 805, 242, 243, 40, 21, 419, 121, 466, 126, 467, 429, 468, 337, 122, 420, 1052, -204, -204, -204, 31, 32, 421, 422, 1034, 745, 33, 469, 220, 759, 817, 54, 351, 352, 470, 471, 218, 219, 220, 119, 472, 244, 127, 788, 835, 423, 424, 202, -32766, -32766, -32766, 30, 293, 803, 79, 80, 81, 202, 798, 210, 632, 99, 100, 101, 236, 237, 238, 817, 54, -32766, 211, 800, 819, 629, -204, 34, 1034, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 286, 303, 418, 1034, 817, 54, -32766, -32766, 464, 218, 219, 220, 805, 105, 914, 40, 21, 419, 78, 466, 212, 467, 337, 468, 133, 247, 420, 295, 567, 248, 202, 31, 32, 421, 633, 242, 243, 33, 469, 418, 249, 817, 54, 351, 352, 464, 760, -84, 761, 805, 310, 472, 40, 21, 419, -32766, 466, 640, 467, 643, 468, 447, 22, 420, 815, 452, 584, 132, 31, 32, 421, 637, 134, 364, 33, 469, 418, 303, 817, 54, 351, 352, 464, 819, 629, 828, 805, 43, 472, 40, 21, 419, 44, 466, 45, 467, 46, 468, 591, 592, 420, 753, 635, 930, 649, 31, 32, 421, 641, 918, 657, 33, 469, 418, 105, 817, 54, 351, 352, 464, 819, 629, 47, 805, 50, 472, 40, 21, 419, 53, 466, 131, 467, 298, 468, 599, 742, 420, -32766, -274, 516, 570, 31, 32, 421, 646, 748, 946, 33, 469, 418, 589, 436, -32766, 351, 352, 464, 819, 629, 623, 805, 836, 472, 40, 21, 419, 611, 466, -82, 467, 603, 468, 11, 573, 420, 439, 456, 281, 318, 31, 32, 421, 588, 432, 321, 33, 469, 418, -414, 458, 322, 351, 352, 464, 851, 629, 837, 805, -505, 472, 40, 21, 419, 654, 466, 38, 467, 24, 468, 0, 0, 420, 319, 0, -405, 0, 31, 32, 421, 245, 312, 314, 33, 469, -506, 0, 0, 1097, 351, 352, 1143, 819, 629, 0, 0, 527, 472, 213, 214, 6, 7, 12, 14, 215, 363, 216, -415, 558, 789, -221, 830, 0, 0, 747, 0, 0, 0, 207, 39, 652, 653, 758, 806, 814, 793, 1086, 1087, 808, 819, 629, 213, 214, 867, 1088, 858, 859, 215, 791, 216, 852, 849, 847, 925, 926, 923, 813, 797, 799, 801, 804, 207, 922, 756, 757, 924, 287, 78, 331, 1086, 1087, 353, 630, 634, 636, 638, 639, 1088, 642, 644, 645, 647, 648, 631, 1142, 1211, 1213, 755, 834, 754, 833, 1212, 554, 832, 1092, 1093, 1094, 1095, 1089, 1090, 388, 1048, 824, 1036, 831, 1037, 1096, 1091, 822, 931, 856, 857, 451, 1210, 1179, 0, 217, 1177, 1162, 1175, 1077, 906, 1183, 1173, 0, 554, 26, 1092, 1093, 1094, 1095, 1089, 1090, 388, 29, 37, 42, 76, 77, 1096, 1091, 208, 284, 288, 289, 304, 305, 306, 307, 217, 335, 406, 408, 0, -220, 16, 17, 18, 383, 448, 455, 457, 462, 548, 620, 1039, 1042, 896, 1102, 1038, 1014, 559, 1013, 1079, 0, 0, -424, 1032, 0, 1043, 1045, 1044, 1047, 1046, 1061, 1176, 1161, 1157, 1174, 1076, 1208, 1103, 1156, 595); - protected $actionCheck = array(2, 3, 4, 5, 6, 7, 14, 9, 10, 11, 12, 13, 33, 34, 35, 36, 37, 38, 39, 40, 41, 0, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 9, 10, 11, 33, 34, 35, 36, 37, 38, 39, 40, 41, 8, 68, 69, 33, 34, 35, 36, 37, 56, 30, 8, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 70, 71, 72, 73, 74, 75, 76, 13, 1, 79, 115, 116, 117, 118, 119, 120, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 31, 132, 133, 134, 135, 136, 137, 138, 139, 140, 3, 4, 5, 6, 7, 146, 147, 148, 8, 12, 13, 158, 15, 33, 34, 35, 36, 37, 38, 39, 40, 41, 14, 43, 44, 45, 46, 13, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 33, 34, 35, 36, 49, 50, 13, 8, 8, 37, 55, 81, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 156, 69, 70, 71, 72, 58, 59, 37, 37, 77, 78, 79, 154, 81, 9, 10, 11, 85, 9, 10, 11, 79, 31, 1, 2, 154, 107, 9, 10, 11, 105, 112, 107, 126, 8, 30, 31, 105, 8, 30, 121, 32, 33, 34, 35, 36, 37, 115, 30, 155, 32, 33, 34, 35, 36, 123, 124, 115, 116, 117, 118, 119, 120, 115, 132, 133, 159, 135, 136, 137, 138, 139, 140, 141, 79, 156, 151, 82, 131, 147, 148, 133, 134, 151, 152, 153, 9, 10, 11, 157, 8, 159, 160, 158, 162, 73, 74, 75, 76, 150, 69, 79, 0, 1, 2, 83, 158, 30, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 8, 97, 150, 150, 81, 81, 102, 103, 104, 105, 13, 158, 108, 109, 9, 10, 11, 158, 114, 115, 116, 117, 9, 10, 11, 8, 122, 154, 8, 125, 126, 127, 128, 43, 44, 45, 46, 47, 48, 49, 50, 51, 79, 30, 131, 32, 33, 34, 35, 8, 1, 81, 9, 10, 11, 132, 81, 73, 154, 155, 156, 37, 154, 79, 1, 2, 115, 83, 155, 81, 86, 87, 88, 30, 90, 69, 92, 80, 94, 155, 157, 97, 159, 159, 133, 134, 102, 103, 104, 105, 8, 107, 108, 109, 9, 10, 112, 70, 114, 115, 116, 117, 9, 10, 11, 81, 122, 8, 81, 125, 126, 127, 128, 8, 151, 152, 153, 10, 11, 156, 161, 8, 158, 30, 84, 32, 33, 34, 79, 159, 146, 8, 157, 79, 159, 84, 73, 30, 154, 155, 156, 13, 79, 1, 2, 69, 83, 159, 156, 86, 87, 88, 69, 90, 13, 92, 132, 94, 69, 81, 97, 155, 99, 100, 101, 102, 103, 104, 105, 115, 9, 108, 109, 9, 10, 11, 79, 114, 115, 116, 117, 157, 142, 159, 157, 122, 159, 13, 125, 126, 127, 128, 8, 142, 30, 154, 32, 33, 52, 53, 54, 158, 56, 30, 155, 151, 152, 153, 159, 14, 52, 53, 54, 81, 68, 73, 84, 154, 155, 156, 131, 79, 33, 34, 35, 83, 68, 69, 86, 87, 88, 155, 90, 155, 92, 157, 94, 159, 155, 97, 158, 99, 100, 101, 102, 103, 104, 105, 13, 152, 108, 109, 11, 154, 1, 2, 114, 115, 116, 117, 9, 10, 11, 16, 122, 14, 31, 125, 126, 127, 128, 30, 9, 10, 11, 143, 144, 154, 9, 10, 11, 30, 154, 16, 31, 49, 50, 51, 49, 50, 51, 1, 2, 30, 16, 154, 154, 155, 156, 30, 13, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 37, 56, 73, 13, 1, 2, 33, 34, 79, 9, 10, 11, 83, 68, 154, 86, 87, 88, 155, 90, 16, 92, 159, 94, 155, 16, 97, 37, 159, 16, 30, 102, 103, 104, 31, 68, 69, 108, 109, 73, 16, 1, 2, 114, 115, 79, 105, 31, 107, 83, 31, 122, 86, 87, 88, 33, 90, 31, 92, 31, 94, 74, 75, 97, 31, 74, 75, 31, 102, 103, 104, 31, 100, 101, 108, 109, 73, 56, 1, 2, 114, 115, 79, 154, 155, 37, 83, 69, 122, 86, 87, 88, 69, 90, 69, 92, 69, 94, 110, 111, 97, 154, 155, 154, 155, 102, 103, 104, 31, 154, 155, 108, 109, 73, 68, 1, 2, 114, 115, 79, 154, 155, 69, 83, 69, 122, 86, 87, 88, 69, 90, 69, 92, 70, 94, 76, 79, 97, 84, 81, 84, 89, 102, 103, 104, 31, 81, 81, 108, 109, 73, 112, 88, 115, 114, 115, 79, 154, 155, 91, 83, 126, 122, 86, 87, 88, 93, 90, 96, 92, 95, 94, 96, 99, 97, 96, 96, 96, 129, 102, 103, 104, 99, 105, 113, 108, 109, 73, 145, 105, 129, 114, 115, 79, 154, 155, 126, 83, 131, 122, 86, 87, 88, 156, 90, 154, 92, 157, 94, -1, -1, 97, 130, -1, 145, -1, 102, 103, 104, 31, 131, 131, 108, 109, 131, -1, -1, 142, 114, 115, 142, 154, 155, -1, -1, 149, 122, 49, 50, 145, 145, 145, 145, 55, 145, 57, 145, 149, 156, 158, 150, -1, -1, 151, -1, -1, -1, 69, 154, 154, 154, 154, 154, 154, 154, 77, 78, 154, 154, 155, 49, 50, 154, 85, 154, 154, 55, 154, 57, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 69, 154, 154, 154, 154, 159, 155, 155, 77, 78, 155, 155, 155, 155, 155, 155, 85, 155, 155, 155, 155, 155, 155, 162, 156, 156, 156, 156, 156, 156, 156, 133, 156, 135, 136, 137, 138, 139, 140, 141, 156, 156, 156, 156, 156, 147, 148, 156, 156, 156, 156, 156, 156, 156, -1, 157, 156, 156, 156, 156, 156, 156, 156, -1, 133, 157, 135, 136, 137, 138, 139, 140, 141, 157, 157, 157, 157, 157, 147, 148, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, -1, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, -1, -1, 160, 160, -1, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161); - protected $actionBase = array(0, 226, 306, 385, 464, 285, 246, 246, 786, -2, -2, 146, -2, -2, -2, 649, 723, 760, 723, 575, 686, 612, 612, 612, 175, 153, 153, 153, 174, 890, 319, 62, 450, 463, 557, 609, 636, 496, 496, 496, 496, 136, 136, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 195, 75, 777, 517, 147, 778, 779, 780, 886, 727, 887, 832, 833, 682, 836, 837, 838, 839, 840, 831, 841, 907, 842, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 483, 573, 365, 209, 281, 407, 646, 646, 646, 646, 646, 646, 646, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 429, 834, 585, 585, 585, 563, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 495, 486, -21, -21, 415, 668, 335, 619, 222, 511, 213, 25, 25, 25, 25, 25, 148, 16, 4, 4, 4, 4, 151, 312, 312, 312, 312, 119, 119, 119, 119, 346, 346, 123, 245, 245, 349, 400, 297, 297, 297, 297, 297, 297, 297, 297, 297, 297, 111, 558, 558, 561, 561, 310, 152, 152, 152, 152, 704, 273, 273, 129, 371, 371, 371, 373, 734, 797, 376, 376, 376, 376, 376, 376, 468, 468, 468, 480, 480, 480, 702, 587, 454, 587, 454, 684, 748, 509, 748, 700, 199, 515, 803, 398, 720, 829, 729, 830, 601, 747, 235, 782, 724, 419, 782, 633, 637, 634, 419, 419, 715, 98, 863, 292, 195, 595, 405, 667, 781, 421, 732, 784, 363, 445, 411, 593, 328, 286, 744, 785, 888, 889, 181, 739, 667, 667, 667, 139, 362, 328, -8, 613, 613, 613, 613, 48, 613, 613, 613, 613, 314, 230, 506, 404, 783, 703, 703, 712, 694, 852, 696, 696, 703, 711, 703, 712, 694, 854, 854, 854, 854, 703, 694, 703, 703, 703, 696, 696, 694, 709, 696, 38, 694, 695, 707, 707, 854, 751, 752, 703, 703, 728, 696, 696, 696, 728, 694, 854, 685, 746, 234, 696, 854, 665, 711, 665, 703, 685, 694, 665, 711, 711, 665, 21, 662, 664, 853, 855, 869, 792, 681, 716, 861, 862, 856, 860, 844, 679, 753, 754, 569, 669, 671, 673, 699, 740, 701, 735, 724, 692, 692, 692, 713, 741, 713, 692, 692, 692, 692, 692, 692, 692, 692, 893, 689, 745, 736, 710, 755, 589, 600, 793, 731, 738, 882, 875, 891, 892, 863, 880, 713, 894, 697, 180, 650, 864, 693, 788, 713, 865, 713, 794, 713, 883, 804, 708, 805, 806, 692, 884, 895, 896, 897, 898, 899, 900, 901, 902, 706, 903, 756, 698, 876, 339, 859, 715, 742, 725, 791, 759, 807, 342, 904, 808, 713, 713, 795, 787, 713, 796, 764, 750, 872, 766, 877, 905, 731, 726, 878, 713, 730, 809, 906, 342, 672, 705, 737, 721, 767, 870, 885, 868, 798, 655, 659, 810, 812, 820, 674, 769, 873, 874, 871, 771, 799, 670, 800, 719, 821, 801, 866, 772, 822, 823, 881, 718, 743, 717, 722, 714, 802, 824, 879, 773, 774, 775, 827, 776, 828, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 136, 136, 136, -2, -2, -2, -2, 0, 0, -2, 0, 0, 0, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 0, 0, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 591, -21, -21, -21, -21, 591, -21, -21, -21, -21, -21, -21, -21, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, -21, 376, 591, 591, 591, -21, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, -21, 591, 0, 0, 591, -21, 591, -21, 591, -21, 591, 591, 591, 591, 591, 591, -21, -21, -21, -21, -21, -21, 0, 468, 468, 468, 468, -21, -21, -21, -21, 376, 376, -37, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 468, 468, 480, 480, 376, 376, 376, 376, 376, -37, 376, 376, 419, 711, 711, 711, 454, 454, 454, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 454, 419, 0, 419, 0, 376, 419, 711, 419, 454, 711, 711, 419, 696, 618, 618, 618, 618, 342, 328, 0, 711, 711, 0, 711, 0, 0, 0, 0, 0, 696, 0, 703, 0, 0, 0, 0, 692, 180, 0, 725, 427, 0, 0, 0, 0, 0, 0, 725, 427, 435, 435, 0, 706, 692, 692, 692, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 342); - protected $actionDefault = array(3, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 534, 534, 489, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 293, 293, 293, 32767, 32767, 32767, 522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 32767, 32767, 32767, 32767, 32767, 32767, 376, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 382, 539, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 357, 358, 360, 361, 292, 542, 523, 241, 383, 538, 291, 243, 321, 493, 32767, 32767, 32767, 323, 120, 252, 197, 492, 123, 290, 228, 375, 377, 322, 297, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 296, 449, 32767, 354, 353, 352, 451, 486, 486, 489, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 450, 319, 477, 476, 320, 447, 324, 448, 326, 452, 325, 342, 343, 340, 341, 344, 454, 453, 470, 471, 468, 469, 295, 345, 346, 347, 348, 472, 473, 474, 475, 32767, 32767, 276, 533, 533, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 333, 334, 461, 462, 32767, 232, 232, 232, 232, 277, 232, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 328, 329, 327, 456, 457, 455, 423, 32767, 32767, 32767, 425, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 494, 32767, 32767, 32767, 32767, 32767, 507, 412, 32767, 404, 32767, 32767, 216, 218, 165, 32767, 32767, 480, 32767, 32767, 32767, 32767, 32767, 512, 338, 32767, 32767, 114, 32767, 32767, 32767, 549, 32767, 507, 32767, 114, 32767, 32767, 32767, 32767, 351, 330, 331, 332, 32767, 32767, 511, 505, 464, 465, 466, 467, 32767, 458, 459, 460, 463, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 169, 420, 426, 426, 32767, 32767, 32767, 32767, 169, 32767, 32767, 32767, 32767, 32767, 169, 32767, 32767, 32767, 510, 509, 169, 32767, 405, 488, 169, 182, 180, 180, 32767, 202, 202, 32767, 32767, 184, 481, 500, 32767, 184, 169, 32767, 393, 171, 488, 32767, 32767, 234, 32767, 234, 32767, 393, 169, 234, 32767, 32767, 234, 32767, 406, 430, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 372, 373, 483, 496, 32767, 497, 32767, 404, 336, 337, 339, 316, 32767, 318, 362, 363, 364, 365, 366, 367, 368, 370, 32767, 410, 32767, 413, 32767, 32767, 32767, 251, 32767, 547, 32767, 32767, 300, 547, 32767, 32767, 32767, 541, 32767, 32767, 294, 32767, 32767, 32767, 32767, 247, 32767, 167, 32767, 531, 32767, 548, 32767, 505, 32767, 335, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 506, 32767, 32767, 32767, 32767, 223, 32767, 443, 32767, 114, 32767, 32767, 32767, 183, 32767, 32767, 298, 242, 32767, 32767, 540, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 112, 32767, 168, 32767, 32767, 32767, 185, 32767, 32767, 505, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 289, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 505, 32767, 32767, 227, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 406, 32767, 270, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 125, 125, 3, 125, 125, 254, 3, 254, 125, 254, 254, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 210, 213, 202, 202, 162, 125, 125, 262); - protected $goto = array(165, 139, 139, 139, 165, 143, 146, 140, 141, 142, 148, 186, 167, 162, 162, 162, 162, 143, 143, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 137, 158, 159, 160, 161, 183, 138, 184, 489, 490, 367, 491, 495, 496, 497, 498, 499, 500, 501, 502, 959, 163, 144, 145, 147, 170, 175, 185, 203, 251, 254, 256, 258, 260, 261, 262, 263, 264, 265, 273, 274, 275, 276, 299, 300, 324, 325, 326, 384, 385, 386, 538, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 149, 150, 151, 166, 152, 168, 153, 204, 169, 154, 155, 156, 205, 157, 135, 616, 556, 574, 578, 622, 624, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 1099, 515, 345, 571, 600, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 1099, 504, 1202, 1202, 1075, 1074, 504, 540, 541, 542, 543, 544, 545, 546, 547, 549, 582, 3, 4, 173, 1202, 844, 844, 844, 844, 839, 845, 176, 177, 178, 391, 392, 393, 394, 172, 201, 206, 250, 255, 257, 259, 266, 267, 268, 269, 270, 271, 277, 278, 279, 280, 301, 302, 327, 328, 329, 396, 397, 398, 399, 174, 179, 252, 253, 180, 181, 182, 493, 493, 750, 493, 493, 493, 493, 493, 493, 493, 493, 493, 493, 493, 493, 493, 493, 505, 929, 442, 444, 627, 505, 751, 779, 1100, 610, 927, 880, 880, 765, 1190, 1190, 1168, 555, 775, 764, 743, 1168, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 390, 602, 746, 532, 532, 564, 528, 530, 530, 492, 494, 520, 536, 565, 568, 579, 586, 810, 606, 506, 346, 347, 609, 850, 506, 365, 537, 746, 533, 746, 563, 430, 430, 375, 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, 1063, 581, 957, 596, 597, 1063, 887, 887, 887, 887, 1160, 887, 887, 1182, 1182, 1182, 376, 376, 376, 749, 1063, 1063, 1063, 1063, 1063, 1063, 334, 1056, 317, 374, 374, 374, 866, 848, 846, 848, 650, 461, 507, 875, 870, 376, 1194, 368, 374, 389, 374, 898, 374, 1080, 583, 348, 404, 374, 1216, 590, 601, 1017, 19, 15, 361, 1148, 1187, 525, 936, 904, 510, 526, 904, 651, 551, 381, 1201, 1201, 587, 1007, 550, 877, 607, 608, 873, 612, 613, 619, 621, 626, 628, 23, 884, 937, 1201, 336, 598, 1059, 1060, 1204, 378, 1056, 557, 539, 893, 768, 766, 379, 514, 902, 509, 524, 655, 1057, 1159, 1057, 776, 509, 1167, 524, 514, 514, 1058, 1167, 1049, 907, 508, 1054, 511, 433, 434, 510, 1184, 1184, 1184, 854, 445, 945, 569, 1145, 459, 362, 0, 0, 773, 1209, 0, 518, 0, 519, 0, 529, 0, 0, 0, 0, 0, 1166, 0, 0, 0, 771, 0, 0, 0, 449, 0, 0, 0, 0, 0, 0, 605, 0, 0, 0, 0, 13, 1055, 614); - protected $gotoCheck = array(42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 56, 66, 59, 59, 59, 8, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 124, 99, 69, 39, 39, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 66, 140, 140, 122, 122, 66, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 29, 29, 26, 140, 66, 66, 66, 66, 66, 66, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 115, 115, 14, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 7, 7, 7, 7, 115, 15, 28, 7, 7, 7, 74, 74, 22, 74, 74, 116, 56, 22, 22, 5, 116, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 50, 50, 10, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 49, 60, 120, 69, 69, 60, 32, 120, 60, 2, 10, 107, 10, 2, 56, 56, 10, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 64, 99, 64, 64, 56, 56, 56, 56, 56, 79, 56, 56, 8, 8, 8, 121, 121, 121, 13, 56, 56, 56, 56, 56, 56, 123, 79, 123, 12, 12, 12, 13, 13, 13, 13, 13, 56, 13, 13, 13, 121, 138, 45, 12, 121, 12, 81, 12, 33, 67, 67, 67, 12, 12, 125, 48, 33, 33, 33, 33, 129, 136, 8, 95, 12, 12, 31, 12, 31, 31, 47, 139, 139, 31, 100, 33, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 76, 95, 139, 17, 33, 79, 79, 139, 11, 79, 11, 46, 78, 24, 23, 16, 46, 82, 8, 8, 71, 79, 79, 79, 25, 8, 117, 8, 46, 46, 79, 117, 111, 83, 8, 113, 8, 8, 8, 12, 117, 117, 117, 68, 62, 97, 63, 128, 106, 57, -1, -1, 8, 8, -1, 57, -1, 99, -1, 57, -1, -1, -1, -1, -1, 117, -1, -1, -1, 8, -1, -1, -1, 57, -1, -1, -1, -1, -1, -1, 12, -1, -1, -1, -1, 57, 12, 12); - protected $gotoBase = array(0, 0, -249, 0, 0, 300, 0, 287, 105, 0, 47, 164, 118, 421, 274, 295, 171, 184, 0, 0, 0, 0, -49, 168, 172, 104, 24, 0, 288, -431, 0, -159, 359, 44, 0, 0, 0, 0, 0, 125, 0, 0, -24, 0, 0, 407, 479, 186, 178, 355, 75, 0, 0, 0, 0, 0, 106, 119, 0, -192, -81, 0, 101, 93, -231, 0, -90, 135, 121, -276, 0, 148, 0, 0, 21, 0, 183, 0, 194, 71, 0, 423, 155, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 185, 0, 122, 0, 120, 176, 0, 0, 0, 0, 0, 83, 358, 170, 0, 0, 113, 0, 111, 0, -7, 9, 220, 0, 0, 77, 108, -102, 100, -42, 251, 0, 0, 89, 256, 0, 0, 0, 0, 0, 0, 181, 0, 419, 160, -107, 0, 0); - protected $gotoDefault = array(-32768, 463, 659, 2, 660, 733, 741, 593, 477, 625, 577, 370, 1178, 785, 786, 787, 371, 358, 478, 369, 400, 395, 774, 767, 769, 777, 171, 401, 780, 1, 782, 513, 818, 1008, 355, 790, 356, 585, 792, 522, 794, 795, 136, 372, 373, 523, 479, 380, 572, 809, 272, 377, 811, 357, 812, 821, 360, 460, 454, 552, 604, 425, 441, 566, 560, 531, 1072, 561, 853, 344, 861, 656, 869, 872, 480, 553, 883, 446, 891, 1085, 387, 897, 903, 908, 283, 911, 407, 402, 580, 916, 917, 5, 921, 617, 618, 8, 308, 944, 594, 958, 411, 1027, 1029, 481, 482, 517, 453, 503, 521, 483, 1050, 435, 403, 1053, 484, 485, 426, 427, 1069, 350, 1153, 349, 443, 316, 1140, 575, 1104, 450, 1193, 1149, 343, 486, 487, 366, 1172, 382, 1188, 431, 1195, 1203, 339, 535, 562); - protected $ruleToNonTerminal = array(0, 1, 3, 3, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 8, 9, 10, 10, 11, 11, 12, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 17, 17, 18, 18, 20, 20, 16, 16, 21, 21, 22, 22, 23, 23, 24, 24, 19, 19, 25, 27, 27, 28, 29, 29, 31, 30, 30, 30, 30, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 13, 13, 53, 53, 55, 54, 54, 47, 47, 57, 57, 58, 58, 14, 15, 15, 15, 61, 61, 61, 62, 62, 65, 65, 63, 63, 67, 67, 40, 40, 49, 49, 52, 52, 52, 51, 51, 68, 41, 41, 41, 41, 69, 69, 70, 70, 71, 71, 38, 38, 34, 34, 72, 36, 36, 73, 35, 35, 37, 37, 48, 48, 48, 59, 59, 75, 75, 76, 76, 78, 78, 78, 77, 77, 60, 60, 79, 79, 79, 80, 80, 81, 81, 81, 43, 43, 82, 82, 82, 44, 44, 83, 83, 84, 84, 64, 85, 85, 85, 85, 90, 90, 91, 91, 92, 92, 92, 92, 92, 93, 94, 94, 89, 89, 86, 86, 88, 88, 96, 96, 95, 95, 95, 95, 95, 95, 87, 87, 98, 97, 97, 45, 45, 39, 39, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 33, 33, 46, 46, 103, 103, 104, 104, 104, 104, 110, 99, 99, 106, 106, 112, 112, 113, 114, 114, 114, 114, 114, 114, 66, 66, 56, 56, 56, 56, 100, 100, 118, 118, 115, 115, 119, 119, 119, 119, 101, 101, 101, 105, 105, 105, 111, 111, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 26, 26, 26, 26, 26, 26, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 109, 109, 102, 102, 102, 102, 125, 125, 128, 128, 127, 127, 129, 129, 50, 50, 50, 50, 131, 131, 130, 130, 130, 130, 130, 132, 132, 117, 117, 120, 120, 116, 116, 134, 133, 133, 133, 133, 121, 121, 121, 121, 108, 108, 122, 122, 122, 122, 74, 135, 135, 136, 136, 136, 107, 107, 137, 137, 138, 138, 138, 138, 138, 123, 123, 123, 123, 140, 141, 139, 139, 139, 139, 139, 139, 139, 142, 142, 142); - protected $ruleToLength = array(1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 4, 3, 4, 2, 3, 1, 1, 7, 6, 3, 1, 3, 1, 3, 1, 1, 3, 1, 3, 1, 2, 3, 1, 3, 3, 1, 3, 2, 0, 1, 1, 1, 1, 1, 3, 5, 8, 3, 5, 9, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 1, 2, 2, 5, 7, 9, 5, 6, 3, 3, 2, 2, 1, 1, 1, 0, 2, 8, 0, 4, 1, 3, 0, 1, 0, 1, 10, 7, 6, 5, 1, 2, 2, 0, 2, 0, 2, 0, 2, 1, 3, 1, 4, 1, 4, 1, 1, 4, 1, 3, 3, 3, 4, 4, 5, 0, 2, 4, 3, 1, 1, 1, 4, 0, 2, 3, 0, 2, 4, 0, 2, 0, 3, 1, 2, 1, 1, 0, 1, 3, 4, 6, 1, 1, 1, 0, 1, 0, 2, 2, 3, 3, 1, 3, 1, 2, 2, 3, 1, 1, 2, 4, 3, 1, 1, 3, 2, 0, 1, 3, 3, 9, 3, 1, 3, 0, 2, 4, 5, 4, 4, 4, 3, 1, 1, 1, 3, 1, 1, 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3, 3, 1, 0, 1, 1, 3, 3, 4, 4, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 5, 4, 3, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 3, 2, 1, 2, 10, 11, 3, 3, 2, 4, 4, 3, 4, 4, 4, 4, 7, 3, 2, 0, 4, 1, 3, 2, 2, 4, 6, 2, 2, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 4, 4, 0, 2, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 3, 1, 4, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 4, 3, 1, 3, 1, 1, 3, 3, 0, 2, 0, 1, 3, 1, 3, 1, 1, 1, 1, 1, 6, 4, 3, 4, 2, 4, 4, 1, 3, 1, 2, 1, 1, 4, 1, 1, 3, 6, 4, 4, 4, 4, 1, 4, 0, 1, 1, 3, 1, 1, 4, 3, 1, 1, 1, 0, 0, 2, 3, 1, 3, 1, 4, 2, 2, 2, 2, 1, 2, 1, 1, 1, 4, 3, 3, 3, 6, 3, 1, 1, 1); - protected function initReduceCallbacks() + /** @var int */ + private $line; + /** @var string */ + private $name; + /** @var string */ + private $value; + /** + * Token constructor. + */ + public function __construct(int $line, string $name, string $value) { - $this->reduceCallbacks = [0 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 1 => function ($stackPos) { - $this->semValue = $this->handleNamespaces($this->semStack[$stackPos - (1 - 1)]); - }, 2 => function ($stackPos) { - if (\is_array($this->semStack[$stackPos - (2 - 2)])) { - $this->semValue = \array_merge($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); - } else { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - } - }, 3 => function ($stackPos) { - $this->semValue = array(); - }, 4 => function ($stackPos) { - $startAttributes = $this->lookaheadStartAttributes; - if (isset($startAttributes['comments'])) { - $nop = new \PHPUnit\PhpParser\Node\Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); - } else { - $nop = null; - } - if ($nop !== null) { - $this->semStack[$stackPos - (1 - 1)][] = $nop; - } - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 5 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 6 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 7 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 8 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 9 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 10 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 11 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 12 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 13 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 14 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 15 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 16 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 17 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 18 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 19 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 20 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 21 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 22 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 23 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 24 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 25 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 26 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 27 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 28 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 29 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 30 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 31 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 32 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 33 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 34 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 35 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 36 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 37 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 38 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 39 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 40 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 41 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 42 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 43 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 44 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 45 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 46 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 47 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 48 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 49 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 50 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 51 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 52 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 53 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 54 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 55 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 56 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 57 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 58 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 59 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 60 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 61 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 62 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 63 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 64 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 65 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 66 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 67 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 68 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 69 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 70 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 71 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 72 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 73 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 74 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 75 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 76 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 77 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 78 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 79 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 80 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 81 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 82 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 83 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 84 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 85 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Identifier($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 86 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 87 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 88 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 89 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 90 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 91 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 92 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 93 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 94 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\HaltCompiler($this->lexer->handleHaltCompiler(), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 95 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Namespace_($this->semStack[$stackPos - (3 - 2)], null, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - $this->semValue->setAttribute('kind', \PHPUnit\PhpParser\Node\Stmt\Namespace_::KIND_SEMICOLON); - $this->checkNamespace($this->semValue); - }, 96 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Namespace_($this->semStack[$stackPos - (5 - 2)], $this->semStack[$stackPos - (5 - 4)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - $this->semValue->setAttribute('kind', \PHPUnit\PhpParser\Node\Stmt\Namespace_::KIND_BRACED); - $this->checkNamespace($this->semValue); - }, 97 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Namespace_(null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - $this->semValue->setAttribute('kind', \PHPUnit\PhpParser\Node\Stmt\Namespace_::KIND_BRACED); - $this->checkNamespace($this->semValue); - }, 98 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Use_($this->semStack[$stackPos - (3 - 2)], \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_NORMAL, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 99 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Use_($this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 2)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 100 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 101 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Const_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 102 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_FUNCTION; - }, 103 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_CONSTANT; - }, 104 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\GroupUse($this->semStack[$stackPos - (7 - 3)], $this->semStack[$stackPos - (7 - 6)], $this->semStack[$stackPos - (7 - 2)], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - }, 105 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\GroupUse($this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 5)], \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 106 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 107 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 108 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 109 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 110 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 111 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 112 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\UseUse($this->semStack[$stackPos - (1 - 1)], null, \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - $this->checkUseUse($this->semValue, $stackPos - (1 - 1)); - }, 113 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\UseUse($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - $this->checkUseUse($this->semValue, $stackPos - (3 - 3)); - }, 114 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\UseUse($this->semStack[$stackPos - (1 - 1)], null, \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - $this->checkUseUse($this->semValue, $stackPos - (1 - 1)); - }, 115 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\UseUse($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - $this->checkUseUse($this->semValue, $stackPos - (3 - 3)); - }, 116 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - $this->semValue->type = \PHPUnit\PhpParser\Node\Stmt\Use_::TYPE_NORMAL; - }, 117 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue->type = $this->semStack[$stackPos - (2 - 1)]; - }, 118 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 119 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 120 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Const_($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 121 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 122 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 123 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Const_($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 124 => function ($stackPos) { - if (\is_array($this->semStack[$stackPos - (2 - 2)])) { - $this->semValue = \array_merge($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); - } else { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - } - }, 125 => function ($stackPos) { - $this->semValue = array(); - }, 126 => function ($stackPos) { - $startAttributes = $this->lookaheadStartAttributes; - if (isset($startAttributes['comments'])) { - $nop = new \PHPUnit\PhpParser\Node\Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); - } else { - $nop = null; - } - if ($nop !== null) { - $this->semStack[$stackPos - (1 - 1)][] = $nop; - } - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 127 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 128 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 129 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 130 => function ($stackPos) { - throw new \PHPUnit\PhpParser\Error('__HALT_COMPILER() can only be used from the outermost scope', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 131 => function ($stackPos) { - if ($this->semStack[$stackPos - (3 - 2)]) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - $attrs = $this->startAttributeStack[$stackPos - (3 - 1)]; - $stmts = $this->semValue; - if (!empty($attrs['comments'])) { - $stmts[0]->setAttribute('comments', \array_merge($attrs['comments'], $stmts[0]->getAttribute('comments', []))); - } - } else { - $startAttributes = $this->startAttributeStack[$stackPos - (3 - 1)]; - if (isset($startAttributes['comments'])) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Nop($startAttributes + $this->endAttributes); - } else { - $this->semValue = null; - } - if (null === $this->semValue) { - $this->semValue = array(); - } + $this->line = $line; + $this->name = $name; + $this->value = $value; + } + public function getLine() : int + { + return $this->line; + } + public function getName() : string + { + return $this->name; + } + public function getValue() : string + { + return $this->value; + } +} + 'T_OPEN_BRACKET', ')' => 'T_CLOSE_BRACKET', '[' => 'T_OPEN_SQUARE', ']' => 'T_CLOSE_SQUARE', '{' => 'T_OPEN_CURLY', '}' => 'T_CLOSE_CURLY', ';' => 'T_SEMICOLON', '.' => 'T_DOT', ',' => 'T_COMMA', '=' => 'T_EQUAL', '<' => 'T_LT', '>' => 'T_GT', '+' => 'T_PLUS', '-' => 'T_MINUS', '*' => 'T_MULT', '/' => 'T_DIV', '?' => 'T_QUESTION_MARK', '!' => 'T_EXCLAMATION_MARK', ':' => 'T_COLON', '"' => 'T_DOUBLE_QUOTES', '@' => 'T_AT', '&' => 'T_AMPERSAND', '%' => 'T_PERCENT', '|' => 'T_PIPE', '$' => 'T_DOLLAR', '^' => 'T_CARET', '~' => 'T_TILDE', '`' => 'T_BACKTICK']; + public function parse(string $source) : TokenCollection + { + $result = new TokenCollection(); + if ($source === '') { + return $result; + } + $tokens = \token_get_all($source); + $lastToken = new Token($tokens[0][2], 'Placeholder', ''); + foreach ($tokens as $pos => $tok) { + if (\is_string($tok)) { + $token = new Token($lastToken->getLine(), $this->map[$tok], $tok); + $result->addToken($token); + $lastToken = $token; + continue; } - }, 132 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\If_($this->semStack[$stackPos - (5 - 2)], ['stmts' => \is_array($this->semStack[$stackPos - (5 - 3)]) ? $this->semStack[$stackPos - (5 - 3)] : array($this->semStack[$stackPos - (5 - 3)]), 'elseifs' => $this->semStack[$stackPos - (5 - 4)], 'else' => $this->semStack[$stackPos - (5 - 5)]], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 133 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\If_($this->semStack[$stackPos - (8 - 2)], ['stmts' => $this->semStack[$stackPos - (8 - 4)], 'elseifs' => $this->semStack[$stackPos - (8 - 5)], 'else' => $this->semStack[$stackPos - (8 - 6)]], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); - }, 134 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\While_($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 135 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Do_($this->semStack[$stackPos - (5 - 4)], \is_array($this->semStack[$stackPos - (5 - 2)]) ? $this->semStack[$stackPos - (5 - 2)] : array($this->semStack[$stackPos - (5 - 2)]), $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 136 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\For_(['init' => $this->semStack[$stackPos - (9 - 3)], 'cond' => $this->semStack[$stackPos - (9 - 5)], 'loop' => $this->semStack[$stackPos - (9 - 7)], 'stmts' => $this->semStack[$stackPos - (9 - 9)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 137 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Switch_($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 138 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Break_(null, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 139 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Break_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 140 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Continue_(null, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 141 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Continue_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 142 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Return_(null, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 143 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Return_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 144 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Global_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 145 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Static_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 146 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Echo_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 147 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\InlineHTML($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 148 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Expression($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 149 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Expression($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 150 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Unset_($this->semStack[$stackPos - (5 - 3)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 151 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Foreach_($this->semStack[$stackPos - (7 - 3)], $this->semStack[$stackPos - (7 - 5)][0], ['keyVar' => null, 'byRef' => $this->semStack[$stackPos - (7 - 5)][1], 'stmts' => $this->semStack[$stackPos - (7 - 7)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - }, 152 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Foreach_($this->semStack[$stackPos - (9 - 3)], $this->semStack[$stackPos - (9 - 7)][0], ['keyVar' => $this->semStack[$stackPos - (9 - 5)], 'byRef' => $this->semStack[$stackPos - (9 - 7)][1], 'stmts' => $this->semStack[$stackPos - (9 - 9)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - }, 153 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Declare_($this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 154 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\TryCatch($this->semStack[$stackPos - (6 - 3)], $this->semStack[$stackPos - (6 - 5)], $this->semStack[$stackPos - (6 - 6)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - $this->checkTryCatch($this->semValue); - }, 155 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Throw_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 156 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Goto_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 157 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Label($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 158 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Expression($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 159 => function ($stackPos) { - $this->semValue = array(); - /* means: no statement */ - }, 160 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 161 => function ($stackPos) { - $startAttributes = $this->startAttributeStack[$stackPos - (1 - 1)]; - if (isset($startAttributes['comments'])) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Nop($startAttributes + $this->endAttributes); - } else { - $this->semValue = null; + $line = $tok[2]; + $values = \preg_split('/\\R+/Uu', $tok[1]); + foreach ($values as $v) { + $token = new Token($line, \token_name($tok[0]), $v); + $lastToken = $token; + $line++; + if ($v === '') { + continue; + } + $result->addToken($token); } - if ($this->semValue === null) { - $this->semValue = array(); + } + return $this->fillBlanks($result, $lastToken->getLine()); + } + private function fillBlanks(TokenCollection $tokens, int $maxLine) : TokenCollection + { + $prev = new Token(0, 'Placeholder', ''); + $final = new TokenCollection(); + foreach ($tokens as $token) { + if ($prev === null) { + $final->addToken($token); + $prev = $token; + continue; } - /* means: no statement */ - }, 162 => function ($stackPos) { - $this->semValue = array(); - }, 163 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 164 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Catch_(array($this->semStack[$stackPos - (8 - 3)]), $this->semStack[$stackPos - (8 - 4)], $this->semStack[$stackPos - (8 - 7)], $this->startAttributeStack[$stackPos - (8 - 1)] + $this->endAttributes); - }, 165 => function ($stackPos) { - $this->semValue = null; - }, 166 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Finally_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 167 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 168 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 169 => function ($stackPos) { - $this->semValue = \false; - }, 170 => function ($stackPos) { - $this->semValue = \true; - }, 171 => function ($stackPos) { - $this->semValue = \false; - }, 172 => function ($stackPos) { - $this->semValue = \true; - }, 173 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Function_($this->semStack[$stackPos - (10 - 3)], ['byRef' => $this->semStack[$stackPos - (10 - 2)], 'params' => $this->semStack[$stackPos - (10 - 5)], 'returnType' => $this->semStack[$stackPos - (10 - 7)], 'stmts' => $this->semStack[$stackPos - (10 - 9)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); - }, 174 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Class_($this->semStack[$stackPos - (7 - 2)], ['type' => $this->semStack[$stackPos - (7 - 1)], 'extends' => $this->semStack[$stackPos - (7 - 3)], 'implements' => $this->semStack[$stackPos - (7 - 4)], 'stmts' => $this->semStack[$stackPos - (7 - 6)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes); - $this->checkClass($this->semValue, $stackPos - (7 - 2)); - }, 175 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Interface_($this->semStack[$stackPos - (6 - 2)], ['extends' => $this->semStack[$stackPos - (6 - 3)], 'stmts' => $this->semStack[$stackPos - (6 - 5)]], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - $this->checkInterface($this->semValue, $stackPos - (6 - 2)); - }, 176 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Trait_($this->semStack[$stackPos - (5 - 2)], ['stmts' => $this->semStack[$stackPos - (5 - 4)]], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 177 => function ($stackPos) { - $this->semValue = 0; - }, 178 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_ABSTRACT; - }, 179 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_FINAL; - }, 180 => function ($stackPos) { - $this->semValue = null; - }, 181 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 182 => function ($stackPos) { - $this->semValue = array(); - }, 183 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 184 => function ($stackPos) { - $this->semValue = array(); - }, 185 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 186 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 187 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 188 => function ($stackPos) { - $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); - }, 189 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 190 => function ($stackPos) { - $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); - }, 191 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 192 => function ($stackPos) { - $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); - }, 193 => function ($stackPos) { - $this->semValue = null; - }, 194 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 195 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 196 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 197 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\DeclareDeclare($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 198 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 199 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 3)]; - }, 200 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 201 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (5 - 3)]; - }, 202 => function ($stackPos) { - $this->semValue = array(); - }, 203 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 204 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Case_($this->semStack[$stackPos - (4 - 2)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 205 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Case_(null, $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 206 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 207 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 208 => function ($stackPos) { - $this->semValue = \is_array($this->semStack[$stackPos - (1 - 1)]) ? $this->semStack[$stackPos - (1 - 1)] : array($this->semStack[$stackPos - (1 - 1)]); - }, 209 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 2)]; - }, 210 => function ($stackPos) { - $this->semValue = array(); - }, 211 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 212 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\ElseIf_($this->semStack[$stackPos - (3 - 2)], \is_array($this->semStack[$stackPos - (3 - 3)]) ? $this->semStack[$stackPos - (3 - 3)] : array($this->semStack[$stackPos - (3 - 3)]), $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 213 => function ($stackPos) { - $this->semValue = array(); - }, 214 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 215 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\ElseIf_($this->semStack[$stackPos - (4 - 2)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 216 => function ($stackPos) { - $this->semValue = null; - }, 217 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Else_(\is_array($this->semStack[$stackPos - (2 - 2)]) ? $this->semStack[$stackPos - (2 - 2)] : array($this->semStack[$stackPos - (2 - 2)]), $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 218 => function ($stackPos) { - $this->semValue = null; - }, 219 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Else_($this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 220 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)], \false); - }, 221 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (2 - 2)], \true); - }, 222 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)], \false); - }, 223 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 224 => function ($stackPos) { - $this->semValue = array(); - }, 225 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 226 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 227 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Param($this->semStack[$stackPos - (4 - 4)], null, $this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 2)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - $this->checkParam($this->semValue); - }, 228 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Param($this->semStack[$stackPos - (6 - 4)], $this->semStack[$stackPos - (6 - 6)], $this->semStack[$stackPos - (6 - 1)], $this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 3)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - $this->checkParam($this->semValue); - }, 229 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 230 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Identifier('array', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 231 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Identifier('callable', $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 232 => function ($stackPos) { - $this->semValue = null; - }, 233 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 234 => function ($stackPos) { - $this->semValue = null; - }, 235 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 2)]; - }, 236 => function ($stackPos) { - $this->semValue = array(); - }, 237 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 238 => function ($stackPos) { - $this->semValue = array(new \PHPUnit\PhpParser\Node\Arg($this->semStack[$stackPos - (3 - 2)], \false, \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes)); - }, 239 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 240 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 241 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Arg($this->semStack[$stackPos - (1 - 1)], \false, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 242 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Arg($this->semStack[$stackPos - (2 - 2)], \true, \false, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 243 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Arg($this->semStack[$stackPos - (2 - 2)], \false, \true, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 244 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 245 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 246 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 247 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 248 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 249 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 250 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 251 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\StaticVar($this->semStack[$stackPos - (1 - 1)], null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 252 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\StaticVar($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 253 => function ($stackPos) { - if ($this->semStack[$stackPos - (2 - 2)] !== null) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; + $gap = $token->getLine() - $prev->getLine(); + while ($gap > 1) { + $linebreak = new Token($prev->getLine() + 1, 'T_WHITESPACE', ''); + $final->addToken($linebreak); + $prev = $linebreak; + $gap--; } - }, 254 => function ($stackPos) { - $this->semValue = array(); - }, 255 => function ($stackPos) { - $startAttributes = $this->lookaheadStartAttributes; - if (isset($startAttributes['comments'])) { - $nop = new \PHPUnit\PhpParser\Node\Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); - } else { - $nop = null; + $final->addToken($token); + $prev = $token; + } + $gap = $maxLine - $prev->getLine(); + while ($gap > 0) { + $linebreak = new Token($prev->getLine() + 1, 'T_WHITESPACE', ''); + $final->addToken($linebreak); + $prev = $linebreak; + $gap--; + } + return $final; + } +} +xmlns = $xmlns; + } + public function toDom(TokenCollection $tokens) : DOMDocument + { + $dom = new DOMDocument(); + $dom->preserveWhiteSpace = \false; + $dom->loadXML($this->toXML($tokens)); + return $dom; + } + public function toXML(TokenCollection $tokens) : string + { + $this->writer = new \XMLWriter(); + $this->writer->openMemory(); + $this->writer->setIndent(\true); + $this->writer->startDocument(); + $this->writer->startElement('source'); + $this->writer->writeAttribute('xmlns', $this->xmlns->asString()); + if (\count($tokens) > 0) { + $this->writer->startElement('line'); + $this->writer->writeAttribute('no', '1'); + $this->previousToken = $tokens[0]; + foreach ($tokens as $token) { + $this->addToken($token); } - if ($nop !== null) { - $this->semStack[$stackPos - (1 - 1)][] = $nop; + } + $this->writer->endElement(); + $this->writer->endElement(); + $this->writer->endDocument(); + return $this->writer->outputMemory(); + } + private function addToken(Token $token) : void + { + if ($this->previousToken->getLine() < $token->getLine()) { + $this->writer->endElement(); + $this->writer->startElement('line'); + $this->writer->writeAttribute('no', (string) $token->getLine()); + $this->previousToken = $token; + } + if ($token->getValue() !== '') { + $this->writer->startElement('token'); + $this->writer->writeAttribute('name', $token->getName()); + $this->writer->writeRaw(\htmlspecialchars($token->getValue(), \ENT_NOQUOTES | \ENT_DISALLOWED | \ENT_XML1)); + $this->writer->endElement(); + } + } +} +ensureValidUri($value); + $this->value = $value; + } + public function asString() : string + { + return $this->value; + } + private function ensureValidUri($value) : void + { + if (\strpos($value, ':') === \false) { + throw new NamespaceUriException(\sprintf("Namespace URI '%s' must contain at least one colon", $value)); + } + } +} +Tokenizer + +Copyright (c) 2017 Arne Blankerts and contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of Arne Blankerts nor the names of contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +tokens[] = $token; + } + public function current() : Token + { + return \current($this->tokens); + } + public function key() : int + { + return \key($this->tokens); + } + public function next() : void + { + \next($this->tokens); + $this->pos++; + } + public function valid() : bool + { + return $this->count() > $this->pos; + } + public function rewind() : void + { + \reset($this->tokens); + $this->pos = 0; + } + public function count() : int + { + return \count($this->tokens); + } + public function offsetExists($offset) : bool + { + return isset($this->tokens[$offset]); + } + /** + * @throws TokenCollectionException + */ + public function offsetGet($offset) : Token + { + if (!$this->offsetExists($offset)) { + throw new TokenCollectionException(\sprintf('No Token at offest %s', $offset)); + } + return $this->tokens[$offset]; + } + /** + * @param Token $value + * + * @throws TokenCollectionException + */ + public function offsetSet($offset, $value) : void + { + if (!\is_int($offset)) { + $type = \gettype($offset); + throw new TokenCollectionException(\sprintf('Offset must be of type integer, %s given', $type === 'object' ? \get_class($value) : $type)); + } + if (!$value instanceof Token) { + $type = \gettype($value); + throw new TokenCollectionException(\sprintf('Value must be of type %s, %s given', Token::class, $type === 'object' ? \get_class($value) : $type)); + } + $this->tokens[$offset] = $value; + } + public function offsetUnset($offset) : void + { + unset($this->tokens[$offset]); + } +} +Object Enumerator + +Copyright (c) 2016-2020, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\SebastianBergmann\CodeUnitReverseLookup; + +use function array_merge; +use function assert; +use function get_declared_classes; +use function get_declared_traits; +use function get_defined_functions; +use function is_array; +use function range; +use ReflectionClass; +use ReflectionFunction; +use ReflectionFunctionAbstract; +use ReflectionMethod; +/** + * @since Class available since Release 1.0.0 + */ +class Wizard +{ + /** + * @var array + */ + private $lookupTable = []; + /** + * @var array + */ + private $processedClasses = []; + /** + * @var array + */ + private $processedFunctions = []; + /** + * @param string $filename + * @param int $lineNumber + * + * @return string + */ + public function lookup($filename, $lineNumber) + { + if (!isset($this->lookupTable[$filename][$lineNumber])) { + $this->updateLookupTable(); + } + if (isset($this->lookupTable[$filename][$lineNumber])) { + return $this->lookupTable[$filename][$lineNumber]; + } + return $filename . ':' . $lineNumber; + } + private function updateLookupTable() : void + { + $this->processClassesAndTraits(); + $this->processFunctions(); + } + private function processClassesAndTraits() : void + { + $classes = get_declared_classes(); + $traits = get_declared_traits(); + assert(is_array($classes)); + assert(is_array($traits)); + foreach (array_merge($classes, $traits) as $classOrTrait) { + if (isset($this->processedClasses[$classOrTrait])) { + continue; } - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 256 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\Property($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - $this->checkProperty($this->semValue, $stackPos - (3 - 1)); - }, 257 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\ClassConst($this->semStack[$stackPos - (3 - 2)], 0, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 258 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\ClassMethod($this->semStack[$stackPos - (9 - 4)], ['type' => $this->semStack[$stackPos - (9 - 1)], 'byRef' => $this->semStack[$stackPos - (9 - 3)], 'params' => $this->semStack[$stackPos - (9 - 6)], 'returnType' => $this->semStack[$stackPos - (9 - 8)], 'stmts' => $this->semStack[$stackPos - (9 - 9)]], $this->startAttributeStack[$stackPos - (9 - 1)] + $this->endAttributes); - $this->checkClassMethod($this->semValue, $stackPos - (9 - 1)); - }, 259 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\TraitUse($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 260 => function ($stackPos) { - $this->semValue = array(); - }, 261 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 262 => function ($stackPos) { - $this->semValue = array(); - }, 263 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 264 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\TraitUseAdaptation\Precedence($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 265 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (5 - 1)][0], $this->semStack[$stackPos - (5 - 1)][1], $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 4)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 266 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], $this->semStack[$stackPos - (4 - 3)], null, $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 267 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 268 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos - (4 - 1)][0], $this->semStack[$stackPos - (4 - 1)][1], null, $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 269 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)]); - }, 270 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 271 => function ($stackPos) { - $this->semValue = array(null, $this->semStack[$stackPos - (1 - 1)]); - }, 272 => function ($stackPos) { - $this->semValue = null; - }, 273 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 274 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 275 => function ($stackPos) { - $this->semValue = 0; - }, 276 => function ($stackPos) { - $this->semValue = 0; - }, 277 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 278 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 279 => function ($stackPos) { - $this->checkModifier($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $stackPos - (2 - 2)); - $this->semValue = $this->semStack[$stackPos - (2 - 1)] | $this->semStack[$stackPos - (2 - 2)]; - }, 280 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC; - }, 281 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED; - }, 282 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE; - }, 283 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_STATIC; - }, 284 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_ABSTRACT; - }, 285 => function ($stackPos) { - $this->semValue = \PHPUnit\PhpParser\Node\Stmt\Class_::MODIFIER_FINAL; - }, 286 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 287 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 288 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\VarLikeIdentifier(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 289 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\PropertyProperty($this->semStack[$stackPos - (1 - 1)], null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 290 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Stmt\PropertyProperty($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 291 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 292 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 293 => function ($stackPos) { - $this->semValue = array(); - }, 294 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 295 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 296 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Assign($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 297 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Assign($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 298 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignRef($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 299 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignRef($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 300 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 301 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Clone_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 302 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\Plus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 303 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\Minus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 304 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\Mul($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 305 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\Div($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 306 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\Concat($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 307 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\Mod($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 308 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 309 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 310 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\BitwiseXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 311 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\ShiftLeft($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 312 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\ShiftRight($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 313 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\Pow($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 314 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\AssignOp\Coalesce($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 315 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\PostInc($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 316 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\PreInc($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 317 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\PostDec($this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 318 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\PreDec($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 319 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 320 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 321 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 322 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 323 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 324 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 325 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 326 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 327 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Concat($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 328 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Plus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 329 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Minus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 330 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Mul($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 331 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Div($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 332 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Mod($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 333 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\ShiftLeft($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 334 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\ShiftRight($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 335 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Pow($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 336 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\UnaryPlus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 337 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\UnaryMinus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 338 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BooleanNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 339 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BitwiseNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 340 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Identical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 341 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\NotIdentical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 342 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Equal($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 343 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\NotEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 344 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Spaceship($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 345 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Smaller($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 346 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\SmallerOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 347 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Greater($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 348 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\GreaterOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 349 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Instanceof_($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 350 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 351 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 352 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Ternary($this->semStack[$stackPos - (5 - 1)], $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 353 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Ternary($this->semStack[$stackPos - (4 - 1)], null, $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 354 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Coalesce($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 355 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Isset_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 356 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Empty_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 357 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Include_($this->semStack[$stackPos - (2 - 2)], \PHPUnit\PhpParser\Node\Expr\Include_::TYPE_INCLUDE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 358 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Include_($this->semStack[$stackPos - (2 - 2)], \PHPUnit\PhpParser\Node\Expr\Include_::TYPE_INCLUDE_ONCE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 359 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Eval_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 360 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Include_($this->semStack[$stackPos - (2 - 2)], \PHPUnit\PhpParser\Node\Expr\Include_::TYPE_REQUIRE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 361 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Include_($this->semStack[$stackPos - (2 - 2)], \PHPUnit\PhpParser\Node\Expr\Include_::TYPE_REQUIRE_ONCE, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 362 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Cast\Int_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 363 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes; - $attrs['kind'] = $this->getFloatCastKind($this->semStack[$stackPos - (2 - 1)]); - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Cast\Double($this->semStack[$stackPos - (2 - 2)], $attrs); - }, 364 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Cast\String_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 365 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Cast\Array_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 366 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Cast\Object_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 367 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Cast\Bool_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 368 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Cast\Unset_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 369 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes; - $attrs['kind'] = \strtolower($this->semStack[$stackPos - (2 - 1)]) === 'exit' ? \PHPUnit\PhpParser\Node\Expr\Exit_::KIND_EXIT : \PHPUnit\PhpParser\Node\Expr\Exit_::KIND_DIE; - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Exit_($this->semStack[$stackPos - (2 - 2)], $attrs); - }, 370 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ErrorSuppress($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 371 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 372 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 373 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 374 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ShellExec($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 375 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Print_($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 376 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Yield_(null, null, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 377 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\YieldFrom($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 378 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Closure(['static' => \false, 'byRef' => $this->semStack[$stackPos - (10 - 2)], 'params' => $this->semStack[$stackPos - (10 - 4)], 'uses' => $this->semStack[$stackPos - (10 - 6)], 'returnType' => $this->semStack[$stackPos - (10 - 7)], 'stmts' => $this->semStack[$stackPos - (10 - 9)]], $this->startAttributeStack[$stackPos - (10 - 1)] + $this->endAttributes); - }, 379 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Closure(['static' => \true, 'byRef' => $this->semStack[$stackPos - (11 - 3)], 'params' => $this->semStack[$stackPos - (11 - 5)], 'uses' => $this->semStack[$stackPos - (11 - 7)], 'returnType' => $this->semStack[$stackPos - (11 - 8)], 'stmts' => $this->semStack[$stackPos - (11 - 10)]], $this->startAttributeStack[$stackPos - (11 - 1)] + $this->endAttributes); - }, 380 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 381 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 382 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Yield_($this->semStack[$stackPos - (2 - 2)], null, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 383 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Yield_($this->semStack[$stackPos - (4 - 4)], $this->semStack[$stackPos - (4 - 2)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 384 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes; - $attrs['kind'] = \PHPUnit\PhpParser\Node\Expr\Array_::KIND_LONG; - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Array_($this->semStack[$stackPos - (4 - 3)], $attrs); - }, 385 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes; - $attrs['kind'] = \PHPUnit\PhpParser\Node\Expr\Array_::KIND_SHORT; - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Array_($this->semStack[$stackPos - (3 - 2)], $attrs); - }, 386 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 387 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes; - $attrs['kind'] = $this->semStack[$stackPos - (4 - 1)][0] === "'" || $this->semStack[$stackPos - (4 - 1)][1] === "'" && ($this->semStack[$stackPos - (4 - 1)][0] === 'b' || $this->semStack[$stackPos - (4 - 1)][0] === 'B') ? \PHPUnit\PhpParser\Node\Scalar\String_::KIND_SINGLE_QUOTED : \PHPUnit\PhpParser\Node\Scalar\String_::KIND_DOUBLE_QUOTED; - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch(new \PHPUnit\PhpParser\Node\Scalar\String_(\PHPUnit\PhpParser\Node\Scalar\String_::parse($this->semStack[$stackPos - (4 - 1)]), $attrs), $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 388 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 389 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 390 => function ($stackPos) { - $this->semValue = array(new \PHPUnit\PhpParser\Node\Stmt\Class_(null, ['type' => 0, 'extends' => $this->semStack[$stackPos - (7 - 3)], 'implements' => $this->semStack[$stackPos - (7 - 4)], 'stmts' => $this->semStack[$stackPos - (7 - 6)]], $this->startAttributeStack[$stackPos - (7 - 1)] + $this->endAttributes), $this->semStack[$stackPos - (7 - 2)]); - $this->checkClass($this->semValue[0], -1); - }, 391 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\New_($this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 392 => function ($stackPos) { - list($class, $ctorArgs) = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\New_($class, $ctorArgs, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 393 => function ($stackPos) { - $this->semValue = array(); - }, 394 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (4 - 3)]; - }, 395 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 396 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 397 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ClosureUse($this->semStack[$stackPos - (2 - 2)], $this->semStack[$stackPos - (2 - 1)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 398 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\FuncCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 399 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\StaticCall($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 400 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\StaticCall($this->semStack[$stackPos - (6 - 1)], $this->semStack[$stackPos - (6 - 4)], $this->semStack[$stackPos - (6 - 6)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 401 => function ($stackPos) { - $this->semValue = $this->fixupPhp5StaticPropCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 402 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\FuncCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 403 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 404 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 405 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 406 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 407 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 408 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name\FullyQualified(\substr($this->semStack[$stackPos - (1 - 1)], 1), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 409 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Name\Relative(\substr($this->semStack[$stackPos - (1 - 1)], 10), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 410 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 411 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 412 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 413 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 414 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 415 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 416 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 417 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 418 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 419 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 420 => function ($stackPos) { - $this->semValue = null; - }, 421 => function ($stackPos) { - $this->semValue = null; - }, 422 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 423 => function ($stackPos) { - $this->semValue = array(); - }, 424 => function ($stackPos) { - $this->semValue = array(new \PHPUnit\PhpParser\Node\Scalar\EncapsedStringPart(\PHPUnit\PhpParser\Node\Scalar\String_::parseEscapeSequences($this->semStack[$stackPos - (1 - 1)], '`', \false), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes)); - }, 425 => function ($stackPos) { - foreach ($this->semStack[$stackPos - (1 - 1)] as $s) { - if ($s instanceof \PHPUnit\PhpParser\Node\Scalar\EncapsedStringPart) { - $s->value = \PHPUnit\PhpParser\Node\Scalar\String_::parseEscapeSequences($s->value, '`', \false); - } + $reflector = new ReflectionClass($classOrTrait); + foreach ($reflector->getMethods() as $method) { + $this->processFunctionOrMethod($method); } - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 426 => function ($stackPos) { - $this->semValue = array(); - }, 427 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 428 => function ($stackPos) { - $this->semValue = $this->parseLNumber($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes, \true); - }, 429 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\DNumber(\PHPUnit\PhpParser\Node\Scalar\DNumber::parse($this->semStack[$stackPos - (1 - 1)]), $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 430 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes; - $attrs['kind'] = $this->semStack[$stackPos - (1 - 1)][0] === "'" || $this->semStack[$stackPos - (1 - 1)][1] === "'" && ($this->semStack[$stackPos - (1 - 1)][0] === 'b' || $this->semStack[$stackPos - (1 - 1)][0] === 'B') ? \PHPUnit\PhpParser\Node\Scalar\String_::KIND_SINGLE_QUOTED : \PHPUnit\PhpParser\Node\Scalar\String_::KIND_DOUBLE_QUOTED; - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\String_(\PHPUnit\PhpParser\Node\Scalar\String_::parse($this->semStack[$stackPos - (1 - 1)], \false), $attrs); - }, 431 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\MagicConst\Line($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 432 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\MagicConst\File($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 433 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\MagicConst\Dir($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 434 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\MagicConst\Class_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 435 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\MagicConst\Trait_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 436 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\MagicConst\Method($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 437 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\MagicConst\Function_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 438 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\MagicConst\Namespace_($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 439 => function ($stackPos) { - $this->semValue = $this->parseDocString($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (3 - 3)] + $this->endAttributeStack[$stackPos - (3 - 3)], \false); - }, 440 => function ($stackPos) { - $this->semValue = $this->parseDocString($this->semStack[$stackPos - (2 - 1)], '', $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (2 - 2)] + $this->endAttributeStack[$stackPos - (2 - 2)], \false); - }, 441 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 442 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ClassConstFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 443 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ConstFetch($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 444 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Array_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 445 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Array_($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 446 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 447 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 448 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\BooleanAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 449 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 450 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 451 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\LogicalXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 452 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseOr($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 453 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 454 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\BitwiseXor($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 455 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Concat($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 456 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Plus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 457 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Minus($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 458 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Mul($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 459 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Div($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 460 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Mod($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 461 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\ShiftLeft($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 462 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\ShiftRight($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 463 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Pow($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 464 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\UnaryPlus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 465 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\UnaryMinus($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 466 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BooleanNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 467 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BitwiseNot($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 468 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Identical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 469 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\NotIdentical($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 470 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Equal($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 471 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\NotEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 472 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Smaller($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 473 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\SmallerOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 474 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\Greater($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 475 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\BinaryOp\GreaterOrEqual($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 476 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Ternary($this->semStack[$stackPos - (5 - 1)], $this->semStack[$stackPos - (5 - 3)], $this->semStack[$stackPos - (5 - 5)], $this->startAttributeStack[$stackPos - (5 - 1)] + $this->endAttributes); - }, 477 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Ternary($this->semStack[$stackPos - (4 - 1)], null, $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 478 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 479 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 480 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ConstFetch($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 481 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ClassConstFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 482 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 483 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 484 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes; - $attrs['kind'] = \PHPUnit\PhpParser\Node\Scalar\String_::KIND_DOUBLE_QUOTED; - foreach ($this->semStack[$stackPos - (3 - 2)] as $s) { - if ($s instanceof \PHPUnit\PhpParser\Node\Scalar\EncapsedStringPart) { - $s->value = \PHPUnit\PhpParser\Node\Scalar\String_::parseEscapeSequences($s->value, '"', \true); - } + $this->processedClasses[$classOrTrait] = \true; + } + } + private function processFunctions() : void + { + foreach (get_defined_functions()['user'] as $function) { + if (isset($this->processedFunctions[$function])) { + continue; } - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\Encapsed($this->semStack[$stackPos - (3 - 2)], $attrs); - }, 485 => function ($stackPos) { - $this->semValue = $this->parseDocString($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 2)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes, $this->startAttributeStack[$stackPos - (3 - 3)] + $this->endAttributeStack[$stackPos - (3 - 3)], \true); - }, 486 => function ($stackPos) { - $this->semValue = array(); - }, 487 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 488 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 489 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; - }, 490 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 491 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 492 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayItem($this->semStack[$stackPos - (3 - 3)], $this->semStack[$stackPos - (3 - 1)], \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 493 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 494 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 495 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 496 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 497 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 498 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 5)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 499 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 500 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 501 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\MethodCall($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 502 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\FuncCall($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 503 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 504 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 505 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 506 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 507 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 508 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable($this->semStack[$stackPos - (2 - 2)], $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 509 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 510 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 511 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\StaticPropertyFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 4)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 512 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 513 => function ($stackPos) { - $var = \substr($this->semStack[$stackPos - (1 - 1)], 1); - $this->semValue = \is_string($var) ? new \PHPUnit\PhpParser\Node\VarLikeIdentifier($var, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes) : $var; - }, 514 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\StaticPropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 515 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\StaticPropertyFetch($this->semStack[$stackPos - (6 - 1)], $this->semStack[$stackPos - (6 - 5)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 516 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 517 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 518 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 519 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 520 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 521 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 522 => function ($stackPos) { - $this->semValue = null; - }, 523 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 524 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 525 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 526 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 527 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Error($this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - $this->errorState = 2; - }, 528 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\List_($this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 529 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 530 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 531 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 532 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 533 => function ($stackPos) { - $this->semValue = null; - }, 534 => function ($stackPos) { - $this->semValue = array(); - }, 535 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 536 => function ($stackPos) { - $this->semStack[$stackPos - (3 - 1)][] = $this->semStack[$stackPos - (3 - 3)]; - $this->semValue = $this->semStack[$stackPos - (3 - 1)]; - }, 537 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 538 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayItem($this->semStack[$stackPos - (3 - 3)], $this->semStack[$stackPos - (3 - 1)], \false, $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 539 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayItem($this->semStack[$stackPos - (1 - 1)], null, \false, $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 540 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayItem($this->semStack[$stackPos - (4 - 4)], $this->semStack[$stackPos - (4 - 1)], \true, $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 541 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayItem($this->semStack[$stackPos - (2 - 2)], null, \true, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 542 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayItem($this->semStack[$stackPos - (2 - 2)], null, \false, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes, \true, $this->startAttributeStack[$stackPos - (2 - 1)] + $this->endAttributes); - }, 543 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 544 => function ($stackPos) { - $this->semStack[$stackPos - (2 - 1)][] = $this->semStack[$stackPos - (2 - 2)]; - $this->semValue = $this->semStack[$stackPos - (2 - 1)]; - }, 545 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (1 - 1)]); - }, 546 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos - (2 - 1)], $this->semStack[$stackPos - (2 - 2)]); - }, 547 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\EncapsedStringPart($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 548 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 549 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }, 550 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (4 - 1)], $this->semStack[$stackPos - (4 - 3)], $this->startAttributeStack[$stackPos - (4 - 1)] + $this->endAttributes); - }, 551 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\PropertyFetch($this->semStack[$stackPos - (3 - 1)], $this->semStack[$stackPos - (3 - 3)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 552 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 553 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\Variable($this->semStack[$stackPos - (3 - 2)], $this->startAttributeStack[$stackPos - (3 - 1)] + $this->endAttributes); - }, 554 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Expr\ArrayDimFetch($this->semStack[$stackPos - (6 - 2)], $this->semStack[$stackPos - (6 - 4)], $this->startAttributeStack[$stackPos - (6 - 1)] + $this->endAttributes); - }, 555 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (3 - 2)]; - }, 556 => function ($stackPos) { - $this->semValue = new \PHPUnit\PhpParser\Node\Scalar\String_($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 557 => function ($stackPos) { - $this->semValue = $this->parseNumString($this->semStack[$stackPos - (1 - 1)], $this->startAttributeStack[$stackPos - (1 - 1)] + $this->endAttributes); - }, 558 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos - (1 - 1)]; - }]; + $this->processFunctionOrMethod(new ReflectionFunction($function)); + $this->processedFunctions[$function] = \true; + } + } + private function processFunctionOrMethod(ReflectionFunctionAbstract $functionOrMethod) : void + { + if ($functionOrMethod->isInternal()) { + return; + } + $name = $functionOrMethod->getName(); + if ($functionOrMethod instanceof ReflectionMethod) { + $name = $functionOrMethod->getDeclaringClass()->getName() . '::' . $name; + } + if (!isset($this->lookupTable[$functionOrMethod->getFileName()])) { + $this->lookupTable[$functionOrMethod->getFileName()] = []; + } + foreach (range($functionOrMethod->getStartLine(), $functionOrMethod->getEndLine()) as $line) { + $this->lookupTable[$functionOrMethod->getFileName()][$line] = $name; + } } } -. All rights reserved. Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\RecursionContext; + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. -use Throwable; -interface Exception extends \Throwable -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\RecursionContext; + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. -final class InvalidArgumentException extends \InvalidArgumentException implements \PHPUnit\SebastianBergmann\RecursionContext\Exception -{ -} +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. arrays = []; - $this->objects = new \SplObjectStorage(); + $this->objects = new SplObjectStorage(); } /** * @codeCoverageIgnore @@ -91679,9 +96415,9 @@ final class Context public function __destruct() { foreach ($this->arrays as &$array) { - if (\is_array($array)) { - \array_pop($array); - \array_pop($array); + if (is_array($array)) { + array_pop($array); + array_pop($array); } } } @@ -91700,13 +96436,13 @@ final class Context */ public function add(&$value) { - if (\is_array($value)) { + if (is_array($value)) { return $this->addArray($value); } - if (\is_object($value)) { + if (is_object($value)) { return $this->addObject($value); } - throw new \PHPUnit\SebastianBergmann\RecursionContext\InvalidArgumentException('Only arrays and objects are supported'); + throw new InvalidArgumentException('Only arrays and objects are supported'); } /** * Checks if the given value exists within the context. @@ -91723,13 +96459,13 @@ final class Context */ public function contains(&$value) { - if (\is_array($value)) { + if (is_array($value)) { return $this->containsArray($value); } - if (\is_object($value)) { + if (is_object($value)) { return $this->containsObject($value); } - throw new \PHPUnit\SebastianBergmann\RecursionContext\InvalidArgumentException('Only arrays and objects are supported'); + throw new InvalidArgumentException('Only arrays and objects are supported'); } /** * @return bool|int @@ -91740,7 +96476,7 @@ final class Context if ($key !== \false) { return $key; } - $key = \count($this->arrays); + $key = count($this->arrays); $this->arrays[] =& $array; if (!isset($array[\PHP_INT_MAX]) && !isset($array[\PHP_INT_MAX - 1])) { $array[] = $key; @@ -91748,11 +96484,11 @@ final class Context } else { /* cover the improbable case too */ do { - $key = \random_int(\PHP_INT_MIN, \PHP_INT_MAX); + $key = random_int(\PHP_INT_MIN, \PHP_INT_MAX); } while (isset($array[$key])); $array[$key] = $key; do { - $key = \random_int(\PHP_INT_MIN, \PHP_INT_MAX); + $key = random_int(\PHP_INT_MIN, \PHP_INT_MAX); } while (isset($array[$key])); $array[$key] = $this->objects; } @@ -91766,14 +96502,14 @@ final class Context if (!$this->objects->contains($object)) { $this->objects->attach($object); } - return \spl_object_hash($object); + return spl_object_hash($object); } /** * @return false|int */ private function containsArray(array &$array) { - $end = \array_slice($array, -2); + $end = array_slice($array, -2); return isset($end[1]) && $end[1] === $this->objects ? $end[0] : \false; } /** @@ -91784,334 +96520,31 @@ final class Context private function containsObject($value) { if ($this->objects->contains($value)) { - return \spl_object_hash($value); + return spl_object_hash($value); } return \false; } } -Recursion Context - -Copyright (c) 2002-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\LinesOfCode; - -use function substr_count; -use PHPUnit\PhpParser\Error; -use PHPUnit\PhpParser\Lexer; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\NodeTraverser; -use PHPUnit\PhpParser\Parser; -use PHPUnit\PhpParser\ParserFactory; -final class Counter -{ - /** - * @throws RuntimeException - */ - public function countInSourceFile(string $sourceFile) : \PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode - { - return $this->countInSourceString(\file_get_contents($sourceFile)); - } - /** - * @throws RuntimeException - */ - public function countInSourceString(string $source) : \PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode - { - $linesOfCode = \substr_count($source, "\n"); - if ($linesOfCode === 0 && !empty($source)) { - $linesOfCode = 1; - } - try { - $nodes = $this->parser()->parse($source); - \assert($nodes !== null); - return $this->countInAbstractSyntaxTree($linesOfCode, $nodes); - // @codeCoverageIgnoreStart - } catch (\PHPUnit\PhpParser\Error $error) { - throw new \PHPUnit\SebastianBergmann\LinesOfCode\RuntimeException($error->getMessage(), (int) $error->getCode(), $error); - } - // @codeCoverageIgnoreEnd - } - /** - * @param Node[] $nodes - * - * @throws RuntimeException - */ - public function countInAbstractSyntaxTree(int $linesOfCode, array $nodes) : \PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode - { - $traverser = new \PHPUnit\PhpParser\NodeTraverser(); - $visitor = new \PHPUnit\SebastianBergmann\LinesOfCode\LineCountingVisitor($linesOfCode); - $traverser->addVisitor($visitor); - try { - /* @noinspection UnusedFunctionResultInspection */ - $traverser->traverse($nodes); - // @codeCoverageIgnoreStart - } catch (\PHPUnit\PhpParser\Error $error) { - throw new \PHPUnit\SebastianBergmann\LinesOfCode\RuntimeException($error->getMessage(), (int) $error->getCode(), $error); - } - // @codeCoverageIgnoreEnd - return $visitor->result(); - } - private function parser() : \PHPUnit\PhpParser\Parser - { - return (new \PHPUnit\PhpParser\ParserFactory())->create(\PHPUnit\PhpParser\ParserFactory::PREFER_PHP7, new \PHPUnit\PhpParser\Lexer()); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\LinesOfCode; - -/** - * @psalm-immutable - */ -final class LinesOfCode -{ - /** - * @var int - */ - private $linesOfCode; - /** - * @var int - */ - private $commentLinesOfCode; - /** - * @var int - */ - private $nonCommentLinesOfCode; - /** - * @var int - */ - private $logicalLinesOfCode; - /** - * @throws IllogicalValuesException - * @throws NegativeValueException - */ - public function __construct(int $linesOfCode, int $commentLinesOfCode, int $nonCommentLinesOfCode, int $logicalLinesOfCode) - { - if ($linesOfCode < 0) { - throw new \PHPUnit\SebastianBergmann\LinesOfCode\NegativeValueException('$linesOfCode must not be negative'); - } - if ($commentLinesOfCode < 0) { - throw new \PHPUnit\SebastianBergmann\LinesOfCode\NegativeValueException('$commentLinesOfCode must not be negative'); - } - if ($nonCommentLinesOfCode < 0) { - throw new \PHPUnit\SebastianBergmann\LinesOfCode\NegativeValueException('$nonCommentLinesOfCode must not be negative'); - } - if ($logicalLinesOfCode < 0) { - throw new \PHPUnit\SebastianBergmann\LinesOfCode\NegativeValueException('$logicalLinesOfCode must not be negative'); - } - if ($linesOfCode - $commentLinesOfCode !== $nonCommentLinesOfCode) { - throw new \PHPUnit\SebastianBergmann\LinesOfCode\IllogicalValuesException('$linesOfCode !== $commentLinesOfCode + $nonCommentLinesOfCode'); - } - $this->linesOfCode = $linesOfCode; - $this->commentLinesOfCode = $commentLinesOfCode; - $this->nonCommentLinesOfCode = $nonCommentLinesOfCode; - $this->logicalLinesOfCode = $logicalLinesOfCode; - } - public function linesOfCode() : int - { - return $this->linesOfCode; - } - public function commentLinesOfCode() : int - { - return $this->commentLinesOfCode; - } - public function nonCommentLinesOfCode() : int - { - return $this->nonCommentLinesOfCode; - } - public function logicalLinesOfCode() : int - { - return $this->logicalLinesOfCode; - } - public function plus(self $other) : self - { - return new self($this->linesOfCode() + $other->linesOfCode(), $this->commentLinesOfCode() + $other->commentLinesOfCode(), $this->nonCommentLinesOfCode() + $other->nonCommentLinesOfCode(), $this->logicalLinesOfCode() + $other->logicalLinesOfCode()); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\LinesOfCode; - -use InvalidArgumentException; -final class NegativeValueException extends \InvalidArgumentException implements \PHPUnit\SebastianBergmann\LinesOfCode\Exception -{ -} * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\LinesOfCode; +namespace PHPUnit\SebastianBergmann\RecursionContext; use Throwable; -interface Exception extends \Throwable -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\LinesOfCode; - -use LogicException; -final class IllogicalValuesException extends \LogicException implements \PHPUnit\SebastianBergmann\LinesOfCode\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\LinesOfCode; - -final class RuntimeException extends \RuntimeException implements \PHPUnit\SebastianBergmann\LinesOfCode\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\SebastianBergmann\LinesOfCode; - -use function array_merge; -use function array_unique; -use function count; -use PHPUnit\PhpParser\Comment; -use PHPUnit\PhpParser\Node; -use PHPUnit\PhpParser\Node\Expr; -use PHPUnit\PhpParser\NodeVisitorAbstract; -final class LineCountingVisitor extends \PHPUnit\PhpParser\NodeVisitorAbstract +interface Exception extends Throwable { - /** - * @var int - */ - private $linesOfCode; - /** - * @var Comment[] - */ - private $comments = []; - /** - * @var int[] - */ - private $linesWithStatements = []; - public function __construct(int $linesOfCode) - { - $this->linesOfCode = $linesOfCode; - } - public function enterNode(\PHPUnit\PhpParser\Node $node) : void - { - $this->comments = \array_merge($this->comments, $node->getComments()); - if (!$node instanceof \PHPUnit\PhpParser\Node\Expr) { - return; - } - $this->linesWithStatements[] = $node->getStartLine(); - } - public function result() : \PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode - { - $commentLinesOfCode = 0; - foreach ($this->comments() as $comment) { - $commentLinesOfCode += $comment->getEndLine() - $comment->getStartLine() + 1; - } - return new \PHPUnit\SebastianBergmann\LinesOfCode\LinesOfCode($this->linesOfCode, $commentLinesOfCode, $this->linesOfCode - $commentLinesOfCode, \count(\array_unique($this->linesWithStatements))); - } - /** - * @return Comment[] - */ - private function comments() : array - { - $comments = []; - foreach ($this->comments as $comment) { - $comments[$comment->getStartLine() . '_' . $comment->getStartTokenPos() . '_' . $comment->getEndLine() . '_' . $comment->getEndTokenPos()] = $comment; - } - return $comments; - } } -sebastian/lines-of-code +Recursion Context -Copyright (c) 2020, Sebastian Bergmann . +Copyright (c) 2002-2020, Sebastian Bergmann . All rights reserved. Redistribution and use in source and binary forms, with or without @@ -92146,139 +96579,16 @@ POSSIBILITY OF SUCH DAMAGE. declare (strict_types=1); /* - * This file is part of sebastian/code-unit-reverse-lookup. + * This file is part of the Recursion Context package. * * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\SebastianBergmann\CodeUnitReverseLookup; +namespace PHPUnit\SebastianBergmann\RecursionContext; -use function array_merge; -use function assert; -use function get_declared_classes; -use function get_declared_traits; -use function get_defined_functions; -use function is_array; -use function range; -use ReflectionClass; -use ReflectionFunction; -use ReflectionFunctionAbstract; -use ReflectionMethod; -/** - * @since Class available since Release 1.0.0 - */ -class Wizard +final class InvalidArgumentException extends \InvalidArgumentException implements Exception { - /** - * @var array - */ - private $lookupTable = []; - /** - * @var array - */ - private $processedClasses = []; - /** - * @var array - */ - private $processedFunctions = []; - /** - * @param string $filename - * @param int $lineNumber - * - * @return string - */ - public function lookup($filename, $lineNumber) - { - if (!isset($this->lookupTable[$filename][$lineNumber])) { - $this->updateLookupTable(); - } - if (isset($this->lookupTable[$filename][$lineNumber])) { - return $this->lookupTable[$filename][$lineNumber]; - } - return $filename . ':' . $lineNumber; - } - private function updateLookupTable() : void - { - $this->processClassesAndTraits(); - $this->processFunctions(); - } - private function processClassesAndTraits() : void - { - $classes = \get_declared_classes(); - $traits = \get_declared_traits(); - \assert(\is_array($classes)); - \assert(\is_array($traits)); - foreach (\array_merge($classes, $traits) as $classOrTrait) { - if (isset($this->processedClasses[$classOrTrait])) { - continue; - } - $reflector = new \ReflectionClass($classOrTrait); - foreach ($reflector->getMethods() as $method) { - $this->processFunctionOrMethod($method); - } - $this->processedClasses[$classOrTrait] = \true; - } - } - private function processFunctions() : void - { - foreach (\get_defined_functions()['user'] as $function) { - if (isset($this->processedFunctions[$function])) { - continue; - } - $this->processFunctionOrMethod(new \ReflectionFunction($function)); - $this->processedFunctions[$function] = \true; - } - } - private function processFunctionOrMethod(\ReflectionFunctionAbstract $functionOrMethod) : void - { - if ($functionOrMethod->isInternal()) { - return; - } - $name = $functionOrMethod->getName(); - if ($functionOrMethod instanceof \ReflectionMethod) { - $name = $functionOrMethod->getDeclaringClass()->getName() . '::' . $name; - } - if (!isset($this->lookupTable[$functionOrMethod->getFileName()])) { - $this->lookupTable[$functionOrMethod->getFileName()] = []; - } - foreach (\range($functionOrMethod->getStartLine(), $functionOrMethod->getEndLine()) as $line) { - $this->lookupTable[$functionOrMethod->getFileName()][$line] = $name; - } - } } -code-unit-reverse-lookup - -Copyright (c) 2016-2020, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -"UÔG(S{AGBMB \ No newline at end of file +^5xB7C=7^HzSU/NrXÙZ_P͡-frijqwcNW=CPh4GBMB \ No newline at end of file diff --git a/tools/psalm.phar b/tools/psalm.phar index f91f119..f9ccfd5 100755 Binary files a/tools/psalm.phar and b/tools/psalm.phar differ