diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f897f23..434e590 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,7 +16,7 @@ updates: - dependency-type: "development" ignore: - dependency-name: "phpunit/phpunit" - versions: [ "^9.0", "^10.0" ] + versions: [ "^10.0" ] - dependency-name: "symfony/yaml" - dependency-name: "typo3/cms-*" versioning-strategy: "increase" diff --git a/Tests/Unit/Controller/TeaControllerTest.php b/Tests/Unit/Controller/TeaControllerTest.php index c20da1d..dea9227 100644 --- a/Tests/Unit/Controller/TeaControllerTest.php +++ b/Tests/Unit/Controller/TeaControllerTest.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace TTN\Tea\Tests\Unit\Controller; use PHPUnit\Framework\MockObject\MockObject; +use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\ProphecySubjectInterface; use TTN\Tea\Controller\TeaController; @@ -22,6 +23,8 @@ use TYPO3\TestingFramework\Core\Unit\UnitTestCase; */ class TeaControllerTest extends UnitTestCase { + use ProphecyTrait; + /** * @var TeaController&MockObject&AccessibleObjectInterface */ diff --git a/Tests/Unit/Domain/Repository/Product/TeaRepositoryTest.php b/Tests/Unit/Domain/Repository/Product/TeaRepositoryTest.php index f48753f..120383a 100644 --- a/Tests/Unit/Domain/Repository/Product/TeaRepositoryTest.php +++ b/Tests/Unit/Domain/Repository/Product/TeaRepositoryTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace TTN\Tea\Tests\Unit\Domain\Repository\Product; +use Prophecy\PhpUnit\ProphecyTrait; use TTN\Tea\Domain\Repository\Product\TeaRepository; use TYPO3\CMS\Extbase\Object\ObjectManagerInterface; use TYPO3\CMS\Extbase\Persistence\Repository; @@ -14,6 +15,8 @@ use TYPO3\TestingFramework\Core\Unit\UnitTestCase; */ class TeaRepositoryTest extends UnitTestCase { + use ProphecyTrait; + private TeaRepository $subject; protected function setUp(): void diff --git a/composer.json b/composer.json index 920929a..a06c762 100644 --- a/composer.json +++ b/composer.json @@ -42,11 +42,12 @@ "jangregor/phpstan-prophecy": "^1.0.0", "php-coveralls/php-coveralls": "^2.5.3", "phpspec/prophecy": "^1.15.0", + "phpspec/prophecy-phpunit": "2.0.1", "phpstan/extension-installer": "^1.1.0", "phpstan/phpstan": "^1.8.8", "phpstan/phpstan-phpunit": "^1.1.1", "phpstan/phpstan-strict-rules": "^1.4.4", - "phpunit/phpunit": "^8.5.30", + "phpunit/phpunit": "^9.5.25", "saschaegerer/phpstan-typo3": "^1.1.2", "seld/jsonlint": "^1.9.0", "squizlabs/php_codesniffer": "^3.7.1", diff --git a/phive.xml b/phive.xml index aaa7595..7ec6a97 100644 --- a/phive.xml +++ b/phive.xml @@ -2,5 +2,5 @@ - + diff --git a/tools/phpcov b/tools/phpcov index edb8087..642af5d 100755 --- a/tools/phpcov +++ b/tools/phpcov @@ -8,7 +8,7 @@ if ($_SERVER['SCRIPT_NAME'] != '-') { } define('__PHPCOV_PHAR__', str_replace(DIRECTORY_SEPARATOR, '/', $phar)); -define('__PHPCOV_PHAR_ROOT__', 'phar://phpcov-6.0.1.phar'); +define('__PHPCOV_PHAR_ROOT__', 'phar://phpcov-8.2.1.phar'); spl_autoload_register( function ($class) @@ -17,241 +17,329 @@ spl_autoload_register( if ($classes === NULL) { $classes = array( - 'jsonexception' => '/symfony/polyfill-php73/Resources/stubs/JsonException.php', - 'php_token' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_abstract' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_ampersand' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_and_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_array' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_array_cast' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_as' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_at' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_backtick' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_bad_character' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_bool_cast' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_boolean_and' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_boolean_or' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_break' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_callable' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_caret' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_case' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_catch' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_character' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_class' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_class_c' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_class_name_constant' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_clone' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_close_bracket' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_close_curly' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_close_square' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_close_tag' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_coalesce' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_coalesce_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_colon' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_comma' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_comment' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_concat_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_const' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_constant_encapsed_string' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_continue' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_curly_open' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_dec' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_declare' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_default' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_dir' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_div' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_div_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_dnumber' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_do' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_doc_comment' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_dollar' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_dollar_open_curly_braces' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_dot' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_double_arrow' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_double_cast' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_double_colon' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_double_quotes' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_echo' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_ellipsis' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_else' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_elseif' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_empty' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_encapsed_and_whitespace' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_end_heredoc' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_enddeclare' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_endfor' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_endforeach' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_endif' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_endswitch' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_endwhile' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_eval' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_exclamation_mark' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_exit' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_extends' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_file' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_final' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_finally' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_fn' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_for' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_foreach' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_func_c' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_function' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_global' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_goto' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_gt' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_halt_compiler' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_if' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_implements' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_inc' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_include' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_include_once' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_includes' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_inline_html' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_instanceof' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_insteadof' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_int_cast' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_interface' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_is_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_is_greater_or_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_is_identical' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_is_not_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_is_not_identical' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_is_smaller_or_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_isset' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_line' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_list' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_lnumber' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_logical_and' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_logical_or' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_logical_xor' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_lt' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_method_c' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_minus' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_minus_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_mod_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_mul_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_mult' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_namespace' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_new' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_ns_c' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_ns_separator' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_num_string' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_object_cast' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_object_operator' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_open_bracket' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_open_curly' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_open_square' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_open_tag' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_open_tag_with_echo' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_or_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_paamayim_nekudotayim' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_percent' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_pipe' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_plus' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_plus_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_pow' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_pow_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_print' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_private' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_protected' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_public' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_question_mark' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_require' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_require_once' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_return' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_semicolon' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_sl' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_sl_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_spaceship' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_sr' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_sr_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_start_heredoc' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_static' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_stream' => '/phpunit/php-token-stream/src/Token/Stream.php', - 'php_token_stream_cachingfactory' => '/phpunit/php-token-stream/src/Token/Stream/CachingFactory.php', - 'php_token_string' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_string_cast' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_string_varname' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_switch' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_throw' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_tilde' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_trait' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_trait_c' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_try' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_unset' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_unset_cast' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_use' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_use_function' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_util' => '/phpunit/php-token-stream/src/Token/Util.php', - 'php_token_var' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_variable' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_while' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_whitespace' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_xor_equal' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_yield' => '/phpunit/php-token-stream/src/Token.php', - 'php_token_yield_from' => '/phpunit/php-token-stream/src/Token.php', - 'php_tokenwithscope' => '/phpunit/php-token-stream/src/Token.php', - 'php_tokenwithscopeandvisibility' => '/phpunit/php-token-stream/src/Token.php', + 'phpparser\\builder' => '/nikic/PhpParser/Builder.php', + 'phpparser\\builder\\class_' => '/nikic/PhpParser/Builder/Class_.php', + 'phpparser\\builder\\classconst' => '/nikic/PhpParser/Builder/ClassConst.php', + 'phpparser\\builder\\declaration' => '/nikic/PhpParser/Builder/Declaration.php', + 'phpparser\\builder\\enum_' => '/nikic/PhpParser/Builder/Enum_.php', + 'phpparser\\builder\\enumcase' => '/nikic/PhpParser/Builder/EnumCase.php', + 'phpparser\\builder\\function_' => '/nikic/PhpParser/Builder/Function_.php', + 'phpparser\\builder\\functionlike' => '/nikic/PhpParser/Builder/FunctionLike.php', + 'phpparser\\builder\\interface_' => '/nikic/PhpParser/Builder/Interface_.php', + 'phpparser\\builder\\method' => '/nikic/PhpParser/Builder/Method.php', + 'phpparser\\builder\\namespace_' => '/nikic/PhpParser/Builder/Namespace_.php', + 'phpparser\\builder\\param' => '/nikic/PhpParser/Builder/Param.php', + 'phpparser\\builder\\property' => '/nikic/PhpParser/Builder/Property.php', + 'phpparser\\builder\\trait_' => '/nikic/PhpParser/Builder/Trait_.php', + 'phpparser\\builder\\traituse' => '/nikic/PhpParser/Builder/TraitUse.php', + 'phpparser\\builder\\traituseadaptation' => '/nikic/PhpParser/Builder/TraitUseAdaptation.php', + 'phpparser\\builder\\use_' => '/nikic/PhpParser/Builder/Use_.php', + 'phpparser\\builderfactory' => '/nikic/PhpParser/BuilderFactory.php', + 'phpparser\\builderhelpers' => '/nikic/PhpParser/BuilderHelpers.php', + 'phpparser\\comment' => '/nikic/PhpParser/Comment.php', + 'phpparser\\comment\\doc' => '/nikic/PhpParser/Comment/Doc.php', + 'phpparser\\constexprevaluationexception' => '/nikic/PhpParser/ConstExprEvaluationException.php', + 'phpparser\\constexprevaluator' => '/nikic/PhpParser/ConstExprEvaluator.php', + 'phpparser\\error' => '/nikic/PhpParser/Error.php', + 'phpparser\\errorhandler' => '/nikic/PhpParser/ErrorHandler.php', + 'phpparser\\errorhandler\\collecting' => '/nikic/PhpParser/ErrorHandler/Collecting.php', + 'phpparser\\errorhandler\\throwing' => '/nikic/PhpParser/ErrorHandler/Throwing.php', + 'phpparser\\internal\\diffelem' => '/nikic/PhpParser/Internal/DiffElem.php', + 'phpparser\\internal\\differ' => '/nikic/PhpParser/Internal/Differ.php', + 'phpparser\\internal\\printablenewanonclassnode' => '/nikic/PhpParser/Internal/PrintableNewAnonClassNode.php', + 'phpparser\\internal\\tokenstream' => '/nikic/PhpParser/Internal/TokenStream.php', + 'phpparser\\jsondecoder' => '/nikic/PhpParser/JsonDecoder.php', + 'phpparser\\lexer' => '/nikic/PhpParser/Lexer.php', + 'phpparser\\lexer\\emulative' => '/nikic/PhpParser/Lexer/Emulative.php', + 'phpparser\\lexer\\tokenemulator\\attributeemulator' => '/nikic/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php', + 'phpparser\\lexer\\tokenemulator\\coaleseequaltokenemulator' => '/nikic/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php', + 'phpparser\\lexer\\tokenemulator\\enumtokenemulator' => '/nikic/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php', + 'phpparser\\lexer\\tokenemulator\\explicitoctalemulator' => '/nikic/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php', + 'phpparser\\lexer\\tokenemulator\\flexibledocstringemulator' => '/nikic/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.php', + 'phpparser\\lexer\\tokenemulator\\fntokenemulator' => '/nikic/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php', + 'phpparser\\lexer\\tokenemulator\\keywordemulator' => '/nikic/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php', + 'phpparser\\lexer\\tokenemulator\\matchtokenemulator' => '/nikic/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php', + 'phpparser\\lexer\\tokenemulator\\nullsafetokenemulator' => '/nikic/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php', + 'phpparser\\lexer\\tokenemulator\\numericliteralseparatoremulator' => '/nikic/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php', + 'phpparser\\lexer\\tokenemulator\\readonlytokenemulator' => '/nikic/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php', + 'phpparser\\lexer\\tokenemulator\\reverseemulator' => '/nikic/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php', + 'phpparser\\lexer\\tokenemulator\\tokenemulator' => '/nikic/PhpParser/Lexer/TokenEmulator/TokenEmulator.php', + 'phpparser\\namecontext' => '/nikic/PhpParser/NameContext.php', + 'phpparser\\node' => '/nikic/PhpParser/Node.php', + 'phpparser\\node\\arg' => '/nikic/PhpParser/Node/Arg.php', + 'phpparser\\node\\attribute' => '/nikic/PhpParser/Node/Attribute.php', + 'phpparser\\node\\attributegroup' => '/nikic/PhpParser/Node/AttributeGroup.php', + 'phpparser\\node\\complextype' => '/nikic/PhpParser/Node/ComplexType.php', + 'phpparser\\node\\const_' => '/nikic/PhpParser/Node/Const_.php', + 'phpparser\\node\\expr' => '/nikic/PhpParser/Node/Expr.php', + 'phpparser\\node\\expr\\array_' => '/nikic/PhpParser/Node/Expr/Array_.php', + 'phpparser\\node\\expr\\arraydimfetch' => '/nikic/PhpParser/Node/Expr/ArrayDimFetch.php', + 'phpparser\\node\\expr\\arrayitem' => '/nikic/PhpParser/Node/Expr/ArrayItem.php', + 'phpparser\\node\\expr\\arrowfunction' => '/nikic/PhpParser/Node/Expr/ArrowFunction.php', + 'phpparser\\node\\expr\\assign' => '/nikic/PhpParser/Node/Expr/Assign.php', + 'phpparser\\node\\expr\\assignop' => '/nikic/PhpParser/Node/Expr/AssignOp.php', + 'phpparser\\node\\expr\\assignop\\bitwiseand' => '/nikic/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php', + 'phpparser\\node\\expr\\assignop\\bitwiseor' => '/nikic/PhpParser/Node/Expr/AssignOp/BitwiseOr.php', + 'phpparser\\node\\expr\\assignop\\bitwisexor' => '/nikic/PhpParser/Node/Expr/AssignOp/BitwiseXor.php', + 'phpparser\\node\\expr\\assignop\\coalesce' => '/nikic/PhpParser/Node/Expr/AssignOp/Coalesce.php', + 'phpparser\\node\\expr\\assignop\\concat' => '/nikic/PhpParser/Node/Expr/AssignOp/Concat.php', + 'phpparser\\node\\expr\\assignop\\div' => '/nikic/PhpParser/Node/Expr/AssignOp/Div.php', + 'phpparser\\node\\expr\\assignop\\minus' => '/nikic/PhpParser/Node/Expr/AssignOp/Minus.php', + 'phpparser\\node\\expr\\assignop\\mod' => '/nikic/PhpParser/Node/Expr/AssignOp/Mod.php', + 'phpparser\\node\\expr\\assignop\\mul' => '/nikic/PhpParser/Node/Expr/AssignOp/Mul.php', + 'phpparser\\node\\expr\\assignop\\plus' => '/nikic/PhpParser/Node/Expr/AssignOp/Plus.php', + 'phpparser\\node\\expr\\assignop\\pow' => '/nikic/PhpParser/Node/Expr/AssignOp/Pow.php', + 'phpparser\\node\\expr\\assignop\\shiftleft' => '/nikic/PhpParser/Node/Expr/AssignOp/ShiftLeft.php', + 'phpparser\\node\\expr\\assignop\\shiftright' => '/nikic/PhpParser/Node/Expr/AssignOp/ShiftRight.php', + 'phpparser\\node\\expr\\assignref' => '/nikic/PhpParser/Node/Expr/AssignRef.php', + 'phpparser\\node\\expr\\binaryop' => '/nikic/PhpParser/Node/Expr/BinaryOp.php', + 'phpparser\\node\\expr\\binaryop\\bitwiseand' => '/nikic/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php', + 'phpparser\\node\\expr\\binaryop\\bitwiseor' => '/nikic/PhpParser/Node/Expr/BinaryOp/BitwiseOr.php', + 'phpparser\\node\\expr\\binaryop\\bitwisexor' => '/nikic/PhpParser/Node/Expr/BinaryOp/BitwiseXor.php', + 'phpparser\\node\\expr\\binaryop\\booleanand' => '/nikic/PhpParser/Node/Expr/BinaryOp/BooleanAnd.php', + 'phpparser\\node\\expr\\binaryop\\booleanor' => '/nikic/PhpParser/Node/Expr/BinaryOp/BooleanOr.php', + 'phpparser\\node\\expr\\binaryop\\coalesce' => '/nikic/PhpParser/Node/Expr/BinaryOp/Coalesce.php', + 'phpparser\\node\\expr\\binaryop\\concat' => '/nikic/PhpParser/Node/Expr/BinaryOp/Concat.php', + 'phpparser\\node\\expr\\binaryop\\div' => '/nikic/PhpParser/Node/Expr/BinaryOp/Div.php', + 'phpparser\\node\\expr\\binaryop\\equal' => '/nikic/PhpParser/Node/Expr/BinaryOp/Equal.php', + 'phpparser\\node\\expr\\binaryop\\greater' => '/nikic/PhpParser/Node/Expr/BinaryOp/Greater.php', + 'phpparser\\node\\expr\\binaryop\\greaterorequal' => '/nikic/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php', + 'phpparser\\node\\expr\\binaryop\\identical' => '/nikic/PhpParser/Node/Expr/BinaryOp/Identical.php', + 'phpparser\\node\\expr\\binaryop\\logicaland' => '/nikic/PhpParser/Node/Expr/BinaryOp/LogicalAnd.php', + 'phpparser\\node\\expr\\binaryop\\logicalor' => '/nikic/PhpParser/Node/Expr/BinaryOp/LogicalOr.php', + 'phpparser\\node\\expr\\binaryop\\logicalxor' => '/nikic/PhpParser/Node/Expr/BinaryOp/LogicalXor.php', + 'phpparser\\node\\expr\\binaryop\\minus' => '/nikic/PhpParser/Node/Expr/BinaryOp/Minus.php', + 'phpparser\\node\\expr\\binaryop\\mod' => '/nikic/PhpParser/Node/Expr/BinaryOp/Mod.php', + 'phpparser\\node\\expr\\binaryop\\mul' => '/nikic/PhpParser/Node/Expr/BinaryOp/Mul.php', + 'phpparser\\node\\expr\\binaryop\\notequal' => '/nikic/PhpParser/Node/Expr/BinaryOp/NotEqual.php', + 'phpparser\\node\\expr\\binaryop\\notidentical' => '/nikic/PhpParser/Node/Expr/BinaryOp/NotIdentical.php', + 'phpparser\\node\\expr\\binaryop\\plus' => '/nikic/PhpParser/Node/Expr/BinaryOp/Plus.php', + 'phpparser\\node\\expr\\binaryop\\pow' => '/nikic/PhpParser/Node/Expr/BinaryOp/Pow.php', + 'phpparser\\node\\expr\\binaryop\\shiftleft' => '/nikic/PhpParser/Node/Expr/BinaryOp/ShiftLeft.php', + 'phpparser\\node\\expr\\binaryop\\shiftright' => '/nikic/PhpParser/Node/Expr/BinaryOp/ShiftRight.php', + 'phpparser\\node\\expr\\binaryop\\smaller' => '/nikic/PhpParser/Node/Expr/BinaryOp/Smaller.php', + 'phpparser\\node\\expr\\binaryop\\smallerorequal' => '/nikic/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php', + 'phpparser\\node\\expr\\binaryop\\spaceship' => '/nikic/PhpParser/Node/Expr/BinaryOp/Spaceship.php', + 'phpparser\\node\\expr\\bitwisenot' => '/nikic/PhpParser/Node/Expr/BitwiseNot.php', + 'phpparser\\node\\expr\\booleannot' => '/nikic/PhpParser/Node/Expr/BooleanNot.php', + 'phpparser\\node\\expr\\calllike' => '/nikic/PhpParser/Node/Expr/CallLike.php', + 'phpparser\\node\\expr\\cast' => '/nikic/PhpParser/Node/Expr/Cast.php', + 'phpparser\\node\\expr\\cast\\array_' => '/nikic/PhpParser/Node/Expr/Cast/Array_.php', + 'phpparser\\node\\expr\\cast\\bool_' => '/nikic/PhpParser/Node/Expr/Cast/Bool_.php', + 'phpparser\\node\\expr\\cast\\double' => '/nikic/PhpParser/Node/Expr/Cast/Double.php', + 'phpparser\\node\\expr\\cast\\int_' => '/nikic/PhpParser/Node/Expr/Cast/Int_.php', + 'phpparser\\node\\expr\\cast\\object_' => '/nikic/PhpParser/Node/Expr/Cast/Object_.php', + 'phpparser\\node\\expr\\cast\\string_' => '/nikic/PhpParser/Node/Expr/Cast/String_.php', + 'phpparser\\node\\expr\\cast\\unset_' => '/nikic/PhpParser/Node/Expr/Cast/Unset_.php', + 'phpparser\\node\\expr\\classconstfetch' => '/nikic/PhpParser/Node/Expr/ClassConstFetch.php', + 'phpparser\\node\\expr\\clone_' => '/nikic/PhpParser/Node/Expr/Clone_.php', + 'phpparser\\node\\expr\\closure' => '/nikic/PhpParser/Node/Expr/Closure.php', + 'phpparser\\node\\expr\\closureuse' => '/nikic/PhpParser/Node/Expr/ClosureUse.php', + 'phpparser\\node\\expr\\constfetch' => '/nikic/PhpParser/Node/Expr/ConstFetch.php', + 'phpparser\\node\\expr\\empty_' => '/nikic/PhpParser/Node/Expr/Empty_.php', + 'phpparser\\node\\expr\\error' => '/nikic/PhpParser/Node/Expr/Error.php', + 'phpparser\\node\\expr\\errorsuppress' => '/nikic/PhpParser/Node/Expr/ErrorSuppress.php', + 'phpparser\\node\\expr\\eval_' => '/nikic/PhpParser/Node/Expr/Eval_.php', + 'phpparser\\node\\expr\\exit_' => '/nikic/PhpParser/Node/Expr/Exit_.php', + 'phpparser\\node\\expr\\funccall' => '/nikic/PhpParser/Node/Expr/FuncCall.php', + 'phpparser\\node\\expr\\include_' => '/nikic/PhpParser/Node/Expr/Include_.php', + 'phpparser\\node\\expr\\instanceof_' => '/nikic/PhpParser/Node/Expr/Instanceof_.php', + 'phpparser\\node\\expr\\isset_' => '/nikic/PhpParser/Node/Expr/Isset_.php', + 'phpparser\\node\\expr\\list_' => '/nikic/PhpParser/Node/Expr/List_.php', + 'phpparser\\node\\expr\\match_' => '/nikic/PhpParser/Node/Expr/Match_.php', + 'phpparser\\node\\expr\\methodcall' => '/nikic/PhpParser/Node/Expr/MethodCall.php', + 'phpparser\\node\\expr\\new_' => '/nikic/PhpParser/Node/Expr/New_.php', + 'phpparser\\node\\expr\\nullsafemethodcall' => '/nikic/PhpParser/Node/Expr/NullsafeMethodCall.php', + 'phpparser\\node\\expr\\nullsafepropertyfetch' => '/nikic/PhpParser/Node/Expr/NullsafePropertyFetch.php', + 'phpparser\\node\\expr\\postdec' => '/nikic/PhpParser/Node/Expr/PostDec.php', + 'phpparser\\node\\expr\\postinc' => '/nikic/PhpParser/Node/Expr/PostInc.php', + 'phpparser\\node\\expr\\predec' => '/nikic/PhpParser/Node/Expr/PreDec.php', + 'phpparser\\node\\expr\\preinc' => '/nikic/PhpParser/Node/Expr/PreInc.php', + 'phpparser\\node\\expr\\print_' => '/nikic/PhpParser/Node/Expr/Print_.php', + 'phpparser\\node\\expr\\propertyfetch' => '/nikic/PhpParser/Node/Expr/PropertyFetch.php', + 'phpparser\\node\\expr\\shellexec' => '/nikic/PhpParser/Node/Expr/ShellExec.php', + 'phpparser\\node\\expr\\staticcall' => '/nikic/PhpParser/Node/Expr/StaticCall.php', + 'phpparser\\node\\expr\\staticpropertyfetch' => '/nikic/PhpParser/Node/Expr/StaticPropertyFetch.php', + 'phpparser\\node\\expr\\ternary' => '/nikic/PhpParser/Node/Expr/Ternary.php', + 'phpparser\\node\\expr\\throw_' => '/nikic/PhpParser/Node/Expr/Throw_.php', + 'phpparser\\node\\expr\\unaryminus' => '/nikic/PhpParser/Node/Expr/UnaryMinus.php', + 'phpparser\\node\\expr\\unaryplus' => '/nikic/PhpParser/Node/Expr/UnaryPlus.php', + 'phpparser\\node\\expr\\variable' => '/nikic/PhpParser/Node/Expr/Variable.php', + 'phpparser\\node\\expr\\yield_' => '/nikic/PhpParser/Node/Expr/Yield_.php', + 'phpparser\\node\\expr\\yieldfrom' => '/nikic/PhpParser/Node/Expr/YieldFrom.php', + 'phpparser\\node\\functionlike' => '/nikic/PhpParser/Node/FunctionLike.php', + 'phpparser\\node\\identifier' => '/nikic/PhpParser/Node/Identifier.php', + 'phpparser\\node\\intersectiontype' => '/nikic/PhpParser/Node/IntersectionType.php', + 'phpparser\\node\\matcharm' => '/nikic/PhpParser/Node/MatchArm.php', + 'phpparser\\node\\name' => '/nikic/PhpParser/Node/Name.php', + 'phpparser\\node\\name\\fullyqualified' => '/nikic/PhpParser/Node/Name/FullyQualified.php', + 'phpparser\\node\\name\\relative' => '/nikic/PhpParser/Node/Name/Relative.php', + 'phpparser\\node\\nullabletype' => '/nikic/PhpParser/Node/NullableType.php', + 'phpparser\\node\\param' => '/nikic/PhpParser/Node/Param.php', + 'phpparser\\node\\scalar' => '/nikic/PhpParser/Node/Scalar.php', + 'phpparser\\node\\scalar\\dnumber' => '/nikic/PhpParser/Node/Scalar/DNumber.php', + 'phpparser\\node\\scalar\\encapsed' => '/nikic/PhpParser/Node/Scalar/Encapsed.php', + 'phpparser\\node\\scalar\\encapsedstringpart' => '/nikic/PhpParser/Node/Scalar/EncapsedStringPart.php', + 'phpparser\\node\\scalar\\lnumber' => '/nikic/PhpParser/Node/Scalar/LNumber.php', + 'phpparser\\node\\scalar\\magicconst' => '/nikic/PhpParser/Node/Scalar/MagicConst.php', + 'phpparser\\node\\scalar\\magicconst\\class_' => '/nikic/PhpParser/Node/Scalar/MagicConst/Class_.php', + 'phpparser\\node\\scalar\\magicconst\\dir' => '/nikic/PhpParser/Node/Scalar/MagicConst/Dir.php', + 'phpparser\\node\\scalar\\magicconst\\file' => '/nikic/PhpParser/Node/Scalar/MagicConst/File.php', + 'phpparser\\node\\scalar\\magicconst\\function_' => '/nikic/PhpParser/Node/Scalar/MagicConst/Function_.php', + 'phpparser\\node\\scalar\\magicconst\\line' => '/nikic/PhpParser/Node/Scalar/MagicConst/Line.php', + 'phpparser\\node\\scalar\\magicconst\\method' => '/nikic/PhpParser/Node/Scalar/MagicConst/Method.php', + 'phpparser\\node\\scalar\\magicconst\\namespace_' => '/nikic/PhpParser/Node/Scalar/MagicConst/Namespace_.php', + 'phpparser\\node\\scalar\\magicconst\\trait_' => '/nikic/PhpParser/Node/Scalar/MagicConst/Trait_.php', + 'phpparser\\node\\scalar\\string_' => '/nikic/PhpParser/Node/Scalar/String_.php', + 'phpparser\\node\\stmt' => '/nikic/PhpParser/Node/Stmt.php', + 'phpparser\\node\\stmt\\break_' => '/nikic/PhpParser/Node/Stmt/Break_.php', + 'phpparser\\node\\stmt\\case_' => '/nikic/PhpParser/Node/Stmt/Case_.php', + 'phpparser\\node\\stmt\\catch_' => '/nikic/PhpParser/Node/Stmt/Catch_.php', + 'phpparser\\node\\stmt\\class_' => '/nikic/PhpParser/Node/Stmt/Class_.php', + 'phpparser\\node\\stmt\\classconst' => '/nikic/PhpParser/Node/Stmt/ClassConst.php', + 'phpparser\\node\\stmt\\classlike' => '/nikic/PhpParser/Node/Stmt/ClassLike.php', + 'phpparser\\node\\stmt\\classmethod' => '/nikic/PhpParser/Node/Stmt/ClassMethod.php', + 'phpparser\\node\\stmt\\const_' => '/nikic/PhpParser/Node/Stmt/Const_.php', + 'phpparser\\node\\stmt\\continue_' => '/nikic/PhpParser/Node/Stmt/Continue_.php', + 'phpparser\\node\\stmt\\declare_' => '/nikic/PhpParser/Node/Stmt/Declare_.php', + 'phpparser\\node\\stmt\\declaredeclare' => '/nikic/PhpParser/Node/Stmt/DeclareDeclare.php', + 'phpparser\\node\\stmt\\do_' => '/nikic/PhpParser/Node/Stmt/Do_.php', + 'phpparser\\node\\stmt\\echo_' => '/nikic/PhpParser/Node/Stmt/Echo_.php', + 'phpparser\\node\\stmt\\else_' => '/nikic/PhpParser/Node/Stmt/Else_.php', + 'phpparser\\node\\stmt\\elseif_' => '/nikic/PhpParser/Node/Stmt/ElseIf_.php', + 'phpparser\\node\\stmt\\enum_' => '/nikic/PhpParser/Node/Stmt/Enum_.php', + 'phpparser\\node\\stmt\\enumcase' => '/nikic/PhpParser/Node/Stmt/EnumCase.php', + 'phpparser\\node\\stmt\\expression' => '/nikic/PhpParser/Node/Stmt/Expression.php', + 'phpparser\\node\\stmt\\finally_' => '/nikic/PhpParser/Node/Stmt/Finally_.php', + 'phpparser\\node\\stmt\\for_' => '/nikic/PhpParser/Node/Stmt/For_.php', + 'phpparser\\node\\stmt\\foreach_' => '/nikic/PhpParser/Node/Stmt/Foreach_.php', + 'phpparser\\node\\stmt\\function_' => '/nikic/PhpParser/Node/Stmt/Function_.php', + 'phpparser\\node\\stmt\\global_' => '/nikic/PhpParser/Node/Stmt/Global_.php', + 'phpparser\\node\\stmt\\goto_' => '/nikic/PhpParser/Node/Stmt/Goto_.php', + 'phpparser\\node\\stmt\\groupuse' => '/nikic/PhpParser/Node/Stmt/GroupUse.php', + 'phpparser\\node\\stmt\\haltcompiler' => '/nikic/PhpParser/Node/Stmt/HaltCompiler.php', + 'phpparser\\node\\stmt\\if_' => '/nikic/PhpParser/Node/Stmt/If_.php', + 'phpparser\\node\\stmt\\inlinehtml' => '/nikic/PhpParser/Node/Stmt/InlineHTML.php', + 'phpparser\\node\\stmt\\interface_' => '/nikic/PhpParser/Node/Stmt/Interface_.php', + 'phpparser\\node\\stmt\\label' => '/nikic/PhpParser/Node/Stmt/Label.php', + 'phpparser\\node\\stmt\\namespace_' => '/nikic/PhpParser/Node/Stmt/Namespace_.php', + 'phpparser\\node\\stmt\\nop' => '/nikic/PhpParser/Node/Stmt/Nop.php', + 'phpparser\\node\\stmt\\property' => '/nikic/PhpParser/Node/Stmt/Property.php', + 'phpparser\\node\\stmt\\propertyproperty' => '/nikic/PhpParser/Node/Stmt/PropertyProperty.php', + 'phpparser\\node\\stmt\\return_' => '/nikic/PhpParser/Node/Stmt/Return_.php', + 'phpparser\\node\\stmt\\static_' => '/nikic/PhpParser/Node/Stmt/Static_.php', + 'phpparser\\node\\stmt\\staticvar' => '/nikic/PhpParser/Node/Stmt/StaticVar.php', + 'phpparser\\node\\stmt\\switch_' => '/nikic/PhpParser/Node/Stmt/Switch_.php', + 'phpparser\\node\\stmt\\throw_' => '/nikic/PhpParser/Node/Stmt/Throw_.php', + 'phpparser\\node\\stmt\\trait_' => '/nikic/PhpParser/Node/Stmt/Trait_.php', + 'phpparser\\node\\stmt\\traituse' => '/nikic/PhpParser/Node/Stmt/TraitUse.php', + 'phpparser\\node\\stmt\\traituseadaptation' => '/nikic/PhpParser/Node/Stmt/TraitUseAdaptation.php', + 'phpparser\\node\\stmt\\traituseadaptation\\alias' => '/nikic/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php', + 'phpparser\\node\\stmt\\traituseadaptation\\precedence' => '/nikic/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php', + 'phpparser\\node\\stmt\\trycatch' => '/nikic/PhpParser/Node/Stmt/TryCatch.php', + 'phpparser\\node\\stmt\\unset_' => '/nikic/PhpParser/Node/Stmt/Unset_.php', + 'phpparser\\node\\stmt\\use_' => '/nikic/PhpParser/Node/Stmt/Use_.php', + 'phpparser\\node\\stmt\\useuse' => '/nikic/PhpParser/Node/Stmt/UseUse.php', + 'phpparser\\node\\stmt\\while_' => '/nikic/PhpParser/Node/Stmt/While_.php', + 'phpparser\\node\\uniontype' => '/nikic/PhpParser/Node/UnionType.php', + 'phpparser\\node\\variadicplaceholder' => '/nikic/PhpParser/Node/VariadicPlaceholder.php', + 'phpparser\\node\\varlikeidentifier' => '/nikic/PhpParser/Node/VarLikeIdentifier.php', + 'phpparser\\nodeabstract' => '/nikic/PhpParser/NodeAbstract.php', + 'phpparser\\nodedumper' => '/nikic/PhpParser/NodeDumper.php', + 'phpparser\\nodefinder' => '/nikic/PhpParser/NodeFinder.php', + 'phpparser\\nodetraverser' => '/nikic/PhpParser/NodeTraverser.php', + 'phpparser\\nodetraverserinterface' => '/nikic/PhpParser/NodeTraverserInterface.php', + 'phpparser\\nodevisitor' => '/nikic/PhpParser/NodeVisitor.php', + 'phpparser\\nodevisitor\\cloningvisitor' => '/nikic/PhpParser/NodeVisitor/CloningVisitor.php', + 'phpparser\\nodevisitor\\findingvisitor' => '/nikic/PhpParser/NodeVisitor/FindingVisitor.php', + 'phpparser\\nodevisitor\\firstfindingvisitor' => '/nikic/PhpParser/NodeVisitor/FirstFindingVisitor.php', + 'phpparser\\nodevisitor\\nameresolver' => '/nikic/PhpParser/NodeVisitor/NameResolver.php', + 'phpparser\\nodevisitor\\nodeconnectingvisitor' => '/nikic/PhpParser/NodeVisitor/NodeConnectingVisitor.php', + 'phpparser\\nodevisitor\\parentconnectingvisitor' => '/nikic/PhpParser/NodeVisitor/ParentConnectingVisitor.php', + 'phpparser\\nodevisitorabstract' => '/nikic/PhpParser/NodeVisitorAbstract.php', + 'phpparser\\parser' => '/nikic/PhpParser/Parser.php', + 'phpparser\\parser\\multiple' => '/nikic/PhpParser/Parser/Multiple.php', + 'phpparser\\parser\\php5' => '/nikic/PhpParser/Parser/Php5.php', + 'phpparser\\parser\\php7' => '/nikic/PhpParser/Parser/Php7.php', + 'phpparser\\parser\\tokens' => '/nikic/PhpParser/Parser/Tokens.php', + 'phpparser\\parserabstract' => '/nikic/PhpParser/ParserAbstract.php', + 'phpparser\\parserfactory' => '/nikic/PhpParser/ParserFactory.php', + 'phpparser\\prettyprinter\\standard' => '/nikic/PhpParser/PrettyPrinter/Standard.php', + 'phpparser\\prettyprinterabstract' => '/nikic/PhpParser/PrettyPrinterAbstract.php', 'phpunit\\exception' => '/phpunit/phpunit/src/Exception.php', + 'phpunit\\framework\\actualvalueisnotanobjectexception' => '/phpunit/phpunit/src/Framework/Exception/ActualValueIsNotAnObjectException.php', 'phpunit\\framework\\assert' => '/phpunit/phpunit/src/Framework/Assert.php', 'phpunit\\framework\\assertionfailederror' => '/phpunit/phpunit/src/Framework/Exception/AssertionFailedError.php', 'phpunit\\framework\\codecoverageexception' => '/phpunit/phpunit/src/Framework/Exception/CodeCoverageException.php', - 'phpunit\\framework\\constraint\\arrayhaskey' => '/phpunit/phpunit/src/Framework/Constraint/ArrayHasKey.php', - 'phpunit\\framework\\constraint\\arraysubset' => '/phpunit/phpunit/src/Framework/Constraint/ArraySubset.php', - 'phpunit\\framework\\constraint\\attribute' => '/phpunit/phpunit/src/Framework/Constraint/Attribute.php', + 'phpunit\\framework\\comparisonmethoddoesnotacceptparametertypeexception' => '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.php', + 'phpunit\\framework\\comparisonmethoddoesnotdeclareboolreturntypeexception' => '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php', + 'phpunit\\framework\\comparisonmethoddoesnotdeclareexactlyoneparameterexception' => '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php', + 'phpunit\\framework\\comparisonmethoddoesnotdeclareparametertypeexception' => '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareParameterTypeException.php', + 'phpunit\\framework\\comparisonmethoddoesnotexistexception' => '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotExistException.php', + 'phpunit\\framework\\constraint\\arrayhaskey' => '/phpunit/phpunit/src/Framework/Constraint/Traversable/ArrayHasKey.php', + 'phpunit\\framework\\constraint\\binaryoperator' => '/phpunit/phpunit/src/Framework/Constraint/Operator/BinaryOperator.php', 'phpunit\\framework\\constraint\\callback' => '/phpunit/phpunit/src/Framework/Constraint/Callback.php', - 'phpunit\\framework\\constraint\\classhasattribute' => '/phpunit/phpunit/src/Framework/Constraint/ClassHasAttribute.php', - 'phpunit\\framework\\constraint\\classhasstaticattribute' => '/phpunit/phpunit/src/Framework/Constraint/ClassHasStaticAttribute.php', - 'phpunit\\framework\\constraint\\composite' => '/phpunit/phpunit/src/Framework/Constraint/Composite.php', + 'phpunit\\framework\\constraint\\classhasattribute' => '/phpunit/phpunit/src/Framework/Constraint/Object/ClassHasAttribute.php', + 'phpunit\\framework\\constraint\\classhasstaticattribute' => '/phpunit/phpunit/src/Framework/Constraint/Object/ClassHasStaticAttribute.php', 'phpunit\\framework\\constraint\\constraint' => '/phpunit/phpunit/src/Framework/Constraint/Constraint.php', - 'phpunit\\framework\\constraint\\count' => '/phpunit/phpunit/src/Framework/Constraint/Count.php', - 'phpunit\\framework\\constraint\\directoryexists' => '/phpunit/phpunit/src/Framework/Constraint/DirectoryExists.php', - 'phpunit\\framework\\constraint\\exception' => '/phpunit/phpunit/src/Framework/Constraint/Exception.php', - 'phpunit\\framework\\constraint\\exceptioncode' => '/phpunit/phpunit/src/Framework/Constraint/ExceptionCode.php', - 'phpunit\\framework\\constraint\\exceptionmessage' => '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessage.php', - 'phpunit\\framework\\constraint\\exceptionmessageregularexpression' => '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessageRegularExpression.php', - 'phpunit\\framework\\constraint\\fileexists' => '/phpunit/phpunit/src/Framework/Constraint/FileExists.php', - 'phpunit\\framework\\constraint\\greaterthan' => '/phpunit/phpunit/src/Framework/Constraint/GreaterThan.php', + 'phpunit\\framework\\constraint\\count' => '/phpunit/phpunit/src/Framework/Constraint/Cardinality/Count.php', + 'phpunit\\framework\\constraint\\directoryexists' => '/phpunit/phpunit/src/Framework/Constraint/Filesystem/DirectoryExists.php', + 'phpunit\\framework\\constraint\\exception' => '/phpunit/phpunit/src/Framework/Constraint/Exception/Exception.php', + 'phpunit\\framework\\constraint\\exceptioncode' => '/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionCode.php', + 'phpunit\\framework\\constraint\\exceptionmessage' => '/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionMessage.php', + 'phpunit\\framework\\constraint\\exceptionmessageregularexpression' => '/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionMessageRegularExpression.php', + 'phpunit\\framework\\constraint\\fileexists' => '/phpunit/phpunit/src/Framework/Constraint/Filesystem/FileExists.php', + 'phpunit\\framework\\constraint\\greaterthan' => '/phpunit/phpunit/src/Framework/Constraint/Cardinality/GreaterThan.php', 'phpunit\\framework\\constraint\\isanything' => '/phpunit/phpunit/src/Framework/Constraint/IsAnything.php', - 'phpunit\\framework\\constraint\\isempty' => '/phpunit/phpunit/src/Framework/Constraint/IsEmpty.php', - 'phpunit\\framework\\constraint\\isequal' => '/phpunit/phpunit/src/Framework/Constraint/IsEqual.php', - 'phpunit\\framework\\constraint\\isfalse' => '/phpunit/phpunit/src/Framework/Constraint/IsFalse.php', - 'phpunit\\framework\\constraint\\isfinite' => '/phpunit/phpunit/src/Framework/Constraint/IsFinite.php', + 'phpunit\\framework\\constraint\\isempty' => '/phpunit/phpunit/src/Framework/Constraint/Cardinality/IsEmpty.php', + 'phpunit\\framework\\constraint\\isequal' => '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqual.php', + 'phpunit\\framework\\constraint\\isequalcanonicalizing' => '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualCanonicalizing.php', + 'phpunit\\framework\\constraint\\isequalignoringcase' => '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualIgnoringCase.php', + 'phpunit\\framework\\constraint\\isequalwithdelta' => '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualWithDelta.php', + 'phpunit\\framework\\constraint\\isfalse' => '/phpunit/phpunit/src/Framework/Constraint/Boolean/IsFalse.php', + 'phpunit\\framework\\constraint\\isfinite' => '/phpunit/phpunit/src/Framework/Constraint/Math/IsFinite.php', 'phpunit\\framework\\constraint\\isidentical' => '/phpunit/phpunit/src/Framework/Constraint/IsIdentical.php', - 'phpunit\\framework\\constraint\\isinfinite' => '/phpunit/phpunit/src/Framework/Constraint/IsInfinite.php', - 'phpunit\\framework\\constraint\\isinstanceof' => '/phpunit/phpunit/src/Framework/Constraint/IsInstanceOf.php', - 'phpunit\\framework\\constraint\\isjson' => '/phpunit/phpunit/src/Framework/Constraint/IsJson.php', - 'phpunit\\framework\\constraint\\isnan' => '/phpunit/phpunit/src/Framework/Constraint/IsNan.php', - 'phpunit\\framework\\constraint\\isnull' => '/phpunit/phpunit/src/Framework/Constraint/IsNull.php', - 'phpunit\\framework\\constraint\\isreadable' => '/phpunit/phpunit/src/Framework/Constraint/IsReadable.php', - 'phpunit\\framework\\constraint\\istrue' => '/phpunit/phpunit/src/Framework/Constraint/IsTrue.php', - 'phpunit\\framework\\constraint\\istype' => '/phpunit/phpunit/src/Framework/Constraint/IsType.php', - 'phpunit\\framework\\constraint\\iswritable' => '/phpunit/phpunit/src/Framework/Constraint/IsWritable.php', + 'phpunit\\framework\\constraint\\isinfinite' => '/phpunit/phpunit/src/Framework/Constraint/Math/IsInfinite.php', + 'phpunit\\framework\\constraint\\isinstanceof' => '/phpunit/phpunit/src/Framework/Constraint/Type/IsInstanceOf.php', + 'phpunit\\framework\\constraint\\isjson' => '/phpunit/phpunit/src/Framework/Constraint/String/IsJson.php', + 'phpunit\\framework\\constraint\\isnan' => '/phpunit/phpunit/src/Framework/Constraint/Math/IsNan.php', + 'phpunit\\framework\\constraint\\isnull' => '/phpunit/phpunit/src/Framework/Constraint/Type/IsNull.php', + 'phpunit\\framework\\constraint\\isreadable' => '/phpunit/phpunit/src/Framework/Constraint/Filesystem/IsReadable.php', + 'phpunit\\framework\\constraint\\istrue' => '/phpunit/phpunit/src/Framework/Constraint/Boolean/IsTrue.php', + 'phpunit\\framework\\constraint\\istype' => '/phpunit/phpunit/src/Framework/Constraint/Type/IsType.php', + 'phpunit\\framework\\constraint\\iswritable' => '/phpunit/phpunit/src/Framework/Constraint/Filesystem/IsWritable.php', 'phpunit\\framework\\constraint\\jsonmatches' => '/phpunit/phpunit/src/Framework/Constraint/JsonMatches.php', 'phpunit\\framework\\constraint\\jsonmatcheserrormessageprovider' => '/phpunit/phpunit/src/Framework/Constraint/JsonMatchesErrorMessageProvider.php', - 'phpunit\\framework\\constraint\\lessthan' => '/phpunit/phpunit/src/Framework/Constraint/LessThan.php', - 'phpunit\\framework\\constraint\\logicaland' => '/phpunit/phpunit/src/Framework/Constraint/LogicalAnd.php', - 'phpunit\\framework\\constraint\\logicalnot' => '/phpunit/phpunit/src/Framework/Constraint/LogicalNot.php', - 'phpunit\\framework\\constraint\\logicalor' => '/phpunit/phpunit/src/Framework/Constraint/LogicalOr.php', - 'phpunit\\framework\\constraint\\logicalxor' => '/phpunit/phpunit/src/Framework/Constraint/LogicalXor.php', - 'phpunit\\framework\\constraint\\objecthasattribute' => '/phpunit/phpunit/src/Framework/Constraint/ObjectHasAttribute.php', - 'phpunit\\framework\\constraint\\regularexpression' => '/phpunit/phpunit/src/Framework/Constraint/RegularExpression.php', - 'phpunit\\framework\\constraint\\samesize' => '/phpunit/phpunit/src/Framework/Constraint/SameSize.php', - 'phpunit\\framework\\constraint\\stringcontains' => '/phpunit/phpunit/src/Framework/Constraint/StringContains.php', - 'phpunit\\framework\\constraint\\stringendswith' => '/phpunit/phpunit/src/Framework/Constraint/StringEndsWith.php', - 'phpunit\\framework\\constraint\\stringmatchesformatdescription' => '/phpunit/phpunit/src/Framework/Constraint/StringMatchesFormatDescription.php', - 'phpunit\\framework\\constraint\\stringstartswith' => '/phpunit/phpunit/src/Framework/Constraint/StringStartsWith.php', - 'phpunit\\framework\\constraint\\traversablecontains' => '/phpunit/phpunit/src/Framework/Constraint/TraversableContains.php', - 'phpunit\\framework\\constraint\\traversablecontainsonly' => '/phpunit/phpunit/src/Framework/Constraint/TraversableContainsOnly.php', + 'phpunit\\framework\\constraint\\lessthan' => '/phpunit/phpunit/src/Framework/Constraint/Cardinality/LessThan.php', + 'phpunit\\framework\\constraint\\logicaland' => '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalAnd.php', + 'phpunit\\framework\\constraint\\logicalnot' => '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalNot.php', + 'phpunit\\framework\\constraint\\logicalor' => '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalOr.php', + 'phpunit\\framework\\constraint\\logicalxor' => '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalXor.php', + 'phpunit\\framework\\constraint\\objectequals' => '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectEquals.php', + 'phpunit\\framework\\constraint\\objecthasattribute' => '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectHasAttribute.php', + 'phpunit\\framework\\constraint\\operator' => '/phpunit/phpunit/src/Framework/Constraint/Operator/Operator.php', + 'phpunit\\framework\\constraint\\regularexpression' => '/phpunit/phpunit/src/Framework/Constraint/String/RegularExpression.php', + 'phpunit\\framework\\constraint\\samesize' => '/phpunit/phpunit/src/Framework/Constraint/Cardinality/SameSize.php', + 'phpunit\\framework\\constraint\\stringcontains' => '/phpunit/phpunit/src/Framework/Constraint/String/StringContains.php', + 'phpunit\\framework\\constraint\\stringendswith' => '/phpunit/phpunit/src/Framework/Constraint/String/StringEndsWith.php', + 'phpunit\\framework\\constraint\\stringmatchesformatdescription' => '/phpunit/phpunit/src/Framework/Constraint/String/StringMatchesFormatDescription.php', + 'phpunit\\framework\\constraint\\stringstartswith' => '/phpunit/phpunit/src/Framework/Constraint/String/StringStartsWith.php', + 'phpunit\\framework\\constraint\\traversablecontains' => '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContains.php', + 'phpunit\\framework\\constraint\\traversablecontainsequal' => '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsEqual.php', + 'phpunit\\framework\\constraint\\traversablecontainsidentical' => '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsIdentical.php', + 'phpunit\\framework\\constraint\\traversablecontainsonly' => '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsOnly.php', + 'phpunit\\framework\\constraint\\unaryoperator' => '/phpunit/phpunit/src/Framework/Constraint/Operator/UnaryOperator.php', 'phpunit\\framework\\coveredcodenotexecutedexception' => '/phpunit/phpunit/src/Framework/Exception/CoveredCodeNotExecutedException.php', 'phpunit\\framework\\dataprovidertestsuite' => '/phpunit/phpunit/src/Framework/DataProviderTestSuite.php', + 'phpunit\\framework\\error' => '/phpunit/phpunit/src/Framework/Exception/Error.php', 'phpunit\\framework\\error\\deprecated' => '/phpunit/phpunit/src/Framework/Error/Deprecated.php', 'phpunit\\framework\\error\\error' => '/phpunit/phpunit/src/Framework/Error/Error.php', 'phpunit\\framework\\error\\notice' => '/phpunit/phpunit/src/Framework/Error/Notice.php', 'phpunit\\framework\\error\\warning' => '/phpunit/phpunit/src/Framework/Error/Warning.php', + 'phpunit\\framework\\errortestcase' => '/phpunit/phpunit/src/Framework/ErrorTestCase.php', 'phpunit\\framework\\exception' => '/phpunit/phpunit/src/Framework/Exception/Exception.php', 'phpunit\\framework\\exceptionwrapper' => '/phpunit/phpunit/src/Framework/ExceptionWrapper.php', + 'phpunit\\framework\\executionorderdependency' => '/phpunit/phpunit/src/Framework/ExecutionOrderDependency.php', 'phpunit\\framework\\expectationfailedexception' => '/phpunit/phpunit/src/Framework/Exception/ExpectationFailedException.php', 'phpunit\\framework\\incompletetest' => '/phpunit/phpunit/src/Framework/IncompleteTest.php', 'phpunit\\framework\\incompletetestcase' => '/phpunit/phpunit/src/Framework/IncompleteTestCase.php', @@ -266,20 +354,31 @@ spl_autoload_register( 'phpunit\\framework\\mockobject\\builder\\identity' => '/phpunit/phpunit/src/Framework/MockObject/Builder/Identity.php', 'phpunit\\framework\\mockobject\\builder\\invocationmocker' => '/phpunit/phpunit/src/Framework/MockObject/Builder/InvocationMocker.php', 'phpunit\\framework\\mockobject\\builder\\invocationstubber' => '/phpunit/phpunit/src/Framework/MockObject/Builder/InvocationStubber.php', - 'phpunit\\framework\\mockobject\\builder\\match' => '/phpunit/phpunit/src/Framework/MockObject/Builder/Match.php', 'phpunit\\framework\\mockobject\\builder\\methodnamematch' => '/phpunit/phpunit/src/Framework/MockObject/Builder/MethodNameMatch.php', 'phpunit\\framework\\mockobject\\builder\\parametersmatch' => '/phpunit/phpunit/src/Framework/MockObject/Builder/ParametersMatch.php', 'phpunit\\framework\\mockobject\\builder\\stub' => '/phpunit/phpunit/src/Framework/MockObject/Builder/Stub.php', + 'phpunit\\framework\\mockobject\\cannotuseaddmethodsexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/CannotUseAddMethodsException.php', + 'phpunit\\framework\\mockobject\\cannotuseonlymethodsexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/CannotUseOnlyMethodsException.php', + 'phpunit\\framework\\mockobject\\classalreadyexistsexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/ClassAlreadyExistsException.php', + 'phpunit\\framework\\mockobject\\classisfinalexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/ClassIsFinalException.php', 'phpunit\\framework\\mockobject\\configurablemethod' => '/phpunit/phpunit/src/Framework/MockObject/ConfigurableMethod.php', 'phpunit\\framework\\mockobject\\configurablemethodsalreadyinitializedexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php', + 'phpunit\\framework\\mockobject\\duplicatemethodexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/DuplicateMethodException.php', 'phpunit\\framework\\mockobject\\exception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/Exception.php', 'phpunit\\framework\\mockobject\\generator' => '/phpunit/phpunit/src/Framework/MockObject/Generator.php', 'phpunit\\framework\\mockobject\\incompatiblereturnvalueexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/IncompatibleReturnValueException.php', + 'phpunit\\framework\\mockobject\\invalidmethodnameexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/InvalidMethodNameException.php', 'phpunit\\framework\\mockobject\\invocation' => '/phpunit/phpunit/src/Framework/MockObject/Invocation.php', 'phpunit\\framework\\mockobject\\invocationhandler' => '/phpunit/phpunit/src/Framework/MockObject/InvocationHandler.php', + 'phpunit\\framework\\mockobject\\matchbuildernotfoundexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/MatchBuilderNotFoundException.php', 'phpunit\\framework\\mockobject\\matcher' => '/phpunit/phpunit/src/Framework/MockObject/Matcher.php', + 'phpunit\\framework\\mockobject\\matcheralreadyregisteredexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.php', 'phpunit\\framework\\mockobject\\method' => '/phpunit/phpunit/src/Framework/MockObject/Api/Method.php', + 'phpunit\\framework\\mockobject\\methodcannotbeconfiguredexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodCannotBeConfiguredException.php', + 'phpunit\\framework\\mockobject\\methodnamealreadyconfiguredexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.php', 'phpunit\\framework\\mockobject\\methodnameconstraint' => '/phpunit/phpunit/src/Framework/MockObject/MethodNameConstraint.php', + 'phpunit\\framework\\mockobject\\methodnamenotconfiguredexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodNameNotConfiguredException.php', + 'phpunit\\framework\\mockobject\\methodparametersalreadyconfiguredexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.php', 'phpunit\\framework\\mockobject\\mockbuilder' => '/phpunit/phpunit/src/Framework/MockObject/MockBuilder.php', 'phpunit\\framework\\mockobject\\mockclass' => '/phpunit/phpunit/src/Framework/MockObject/MockClass.php', 'phpunit\\framework\\mockobject\\mockedclonemethod' => '/phpunit/phpunit/src/Framework/MockObject/Api/MockedCloneMethod.php', @@ -288,6 +387,9 @@ spl_autoload_register( 'phpunit\\framework\\mockobject\\mockobject' => '/phpunit/phpunit/src/Framework/MockObject/MockObject.php', 'phpunit\\framework\\mockobject\\mocktrait' => '/phpunit/phpunit/src/Framework/MockObject/MockTrait.php', 'phpunit\\framework\\mockobject\\mocktype' => '/phpunit/phpunit/src/Framework/MockObject/MockType.php', + 'phpunit\\framework\\mockobject\\originalconstructorinvocationrequiredexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/OriginalConstructorInvocationRequiredException.php', + 'phpunit\\framework\\mockobject\\reflectionexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/ReflectionException.php', + 'phpunit\\framework\\mockobject\\returnvaluenotconfiguredexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php', 'phpunit\\framework\\mockobject\\rule\\anyinvokedcount' => '/phpunit/phpunit/src/Framework/MockObject/Rule/AnyInvokedCount.php', 'phpunit\\framework\\mockobject\\rule\\anyparameters' => '/phpunit/phpunit/src/Framework/MockObject/Rule/AnyParameters.php', 'phpunit\\framework\\mockobject\\rule\\consecutiveparameters' => '/phpunit/phpunit/src/Framework/MockObject/Rule/ConsecutiveParameters.php', @@ -301,6 +403,7 @@ spl_autoload_register( 'phpunit\\framework\\mockobject\\rule\\parameters' => '/phpunit/phpunit/src/Framework/MockObject/Rule/Parameters.php', 'phpunit\\framework\\mockobject\\rule\\parametersrule' => '/phpunit/phpunit/src/Framework/MockObject/Rule/ParametersRule.php', 'phpunit\\framework\\mockobject\\runtimeexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/RuntimeException.php', + 'phpunit\\framework\\mockobject\\soapextensionnotavailableexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/SoapExtensionNotAvailableException.php', 'phpunit\\framework\\mockobject\\stub' => '/phpunit/phpunit/src/Framework/MockObject/Stub.php', 'phpunit\\framework\\mockobject\\stub\\consecutivecalls' => '/phpunit/phpunit/src/Framework/MockObject/Stub/ConsecutiveCalls.php', 'phpunit\\framework\\mockobject\\stub\\exception' => '/phpunit/phpunit/src/Framework/MockObject/Stub/Exception.php', @@ -311,10 +414,15 @@ spl_autoload_register( 'phpunit\\framework\\mockobject\\stub\\returnstub' => '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnStub.php', 'phpunit\\framework\\mockobject\\stub\\returnvaluemap' => '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnValueMap.php', 'phpunit\\framework\\mockobject\\stub\\stub' => '/phpunit/phpunit/src/Framework/MockObject/Stub/Stub.php', + 'phpunit\\framework\\mockobject\\unknownclassexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownClassException.php', + 'phpunit\\framework\\mockobject\\unknowntraitexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownTraitException.php', + 'phpunit\\framework\\mockobject\\unknowntypeexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownTypeException.php', 'phpunit\\framework\\mockobject\\unmockedclonemethod' => '/phpunit/phpunit/src/Framework/MockObject/Api/UnmockedCloneMethod.php', 'phpunit\\framework\\mockobject\\verifiable' => '/phpunit/phpunit/src/Framework/MockObject/Verifiable.php', + 'phpunit\\framework\\nochildtestsuiteexception' => '/phpunit/phpunit/src/Framework/Exception/NoChildTestSuiteException.php', 'phpunit\\framework\\outputerror' => '/phpunit/phpunit/src/Framework/Exception/OutputError.php', 'phpunit\\framework\\phptassertionfailederror' => '/phpunit/phpunit/src/Framework/Exception/PHPTAssertionFailedError.php', + 'phpunit\\framework\\reorderable' => '/phpunit/phpunit/src/Framework/Reorderable.php', 'phpunit\\framework\\riskytesterror' => '/phpunit/phpunit/src/Framework/Exception/RiskyTestError.php', 'phpunit\\framework\\selfdescribing' => '/phpunit/phpunit/src/Framework/SelfDescribing.php', 'phpunit\\framework\\skippedtest' => '/phpunit/phpunit/src/Framework/SkippedTest.php', @@ -332,7 +440,6 @@ spl_autoload_register( 'phpunit\\framework\\testresult' => '/phpunit/phpunit/src/Framework/TestResult.php', 'phpunit\\framework\\testsuite' => '/phpunit/phpunit/src/Framework/TestSuite.php', 'phpunit\\framework\\testsuiteiterator' => '/phpunit/phpunit/src/Framework/TestSuiteIterator.php', - 'phpunit\\framework\\unexpectedvalueexception' => '/phpunit/phpunit/src/Framework/Exception/UnexpectedValueException.php', 'phpunit\\framework\\unintentionallycoveredcodeerror' => '/phpunit/phpunit/src/Framework/Exception/UnintentionallyCoveredCodeError.php', 'phpunit\\framework\\warning' => '/phpunit/phpunit/src/Framework/Exception/Warning.php', 'phpunit\\framework\\warningtestcase' => '/phpunit/phpunit/src/Framework/WarningTestCase.php', @@ -350,6 +457,8 @@ spl_autoload_register( 'phpunit\\runner\\beforetesthook' => '/phpunit/phpunit/src/Runner/Hook/BeforeTestHook.php', 'phpunit\\runner\\defaulttestresultcache' => '/phpunit/phpunit/src/Runner/DefaultTestResultCache.php', 'phpunit\\runner\\exception' => '/phpunit/phpunit/src/Runner/Exception.php', + 'phpunit\\runner\\extension\\extensionhandler' => '/phpunit/phpunit/src/Runner/Extension/ExtensionHandler.php', + 'phpunit\\runner\\extension\\pharloader' => '/phpunit/phpunit/src/Runner/Extension/PharLoader.php', 'phpunit\\runner\\filter\\excludegroupfilteriterator' => '/phpunit/phpunit/src/Runner/Filter/ExcludeGroupFilterIterator.php', 'phpunit\\runner\\filter\\factory' => '/phpunit/phpunit/src/Runner/Filter/Factory.php', 'phpunit\\runner\\filter\\groupfilteriterator' => '/phpunit/phpunit/src/Runner/Filter/GroupFilterIterator.php', @@ -366,24 +475,112 @@ spl_autoload_register( 'phpunit\\runner\\testsuiteloader' => '/phpunit/phpunit/src/Runner/TestSuiteLoader.php', 'phpunit\\runner\\testsuitesorter' => '/phpunit/phpunit/src/Runner/TestSuiteSorter.php', 'phpunit\\runner\\version' => '/phpunit/phpunit/src/Runner/Version.php', + 'phpunit\\textui\\cliarguments\\builder' => '/phpunit/phpunit/src/TextUI/CliArguments/Builder.php', + 'phpunit\\textui\\cliarguments\\configuration' => '/phpunit/phpunit/src/TextUI/CliArguments/Configuration.php', + 'phpunit\\textui\\cliarguments\\exception' => '/phpunit/phpunit/src/TextUI/CliArguments/Exception.php', + 'phpunit\\textui\\cliarguments\\mapper' => '/phpunit/phpunit/src/TextUI/CliArguments/Mapper.php', 'phpunit\\textui\\command' => '/phpunit/phpunit/src/TextUI/Command.php', - 'phpunit\\textui\\exception' => '/phpunit/phpunit/src/TextUI/Exception.php', + 'phpunit\\textui\\defaultresultprinter' => '/phpunit/phpunit/src/TextUI/DefaultResultPrinter.php', + 'phpunit\\textui\\exception' => '/phpunit/phpunit/src/TextUI/Exception/Exception.php', 'phpunit\\textui\\help' => '/phpunit/phpunit/src/TextUI/Help.php', + 'phpunit\\textui\\reflectionexception' => '/phpunit/phpunit/src/TextUI/Exception/ReflectionException.php', 'phpunit\\textui\\resultprinter' => '/phpunit/phpunit/src/TextUI/ResultPrinter.php', + 'phpunit\\textui\\runtimeexception' => '/phpunit/phpunit/src/TextUI/Exception/RuntimeException.php', + 'phpunit\\textui\\testdirectorynotfoundexception' => '/phpunit/phpunit/src/TextUI/Exception/TestDirectoryNotFoundException.php', + 'phpunit\\textui\\testfilenotfoundexception' => '/phpunit/phpunit/src/TextUI/Exception/TestFileNotFoundException.php', 'phpunit\\textui\\testrunner' => '/phpunit/phpunit/src/TextUI/TestRunner.php', + 'phpunit\\textui\\testsuitemapper' => '/phpunit/phpunit/src/TextUI/TestSuiteMapper.php', + 'phpunit\\textui\\xmlconfiguration\\codecoverage\\codecoverage' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.php', + 'phpunit\\textui\\xmlconfiguration\\codecoverage\\filter\\directory' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/Directory.php', + 'phpunit\\textui\\xmlconfiguration\\codecoverage\\filter\\directorycollection' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollection.php', + 'phpunit\\textui\\xmlconfiguration\\codecoverage\\filter\\directorycollectioniterator' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.php', + 'phpunit\\textui\\xmlconfiguration\\codecoverage\\filtermapper' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/FilterMapper.php', + 'phpunit\\textui\\xmlconfiguration\\codecoverage\\report\\clover' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Clover.php', + 'phpunit\\textui\\xmlconfiguration\\codecoverage\\report\\cobertura' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.php', + 'phpunit\\textui\\xmlconfiguration\\codecoverage\\report\\crap4j' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.php', + 'phpunit\\textui\\xmlconfiguration\\codecoverage\\report\\html' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Html.php', + 'phpunit\\textui\\xmlconfiguration\\codecoverage\\report\\php' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Php.php', + 'phpunit\\textui\\xmlconfiguration\\codecoverage\\report\\text' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Text.php', + 'phpunit\\textui\\xmlconfiguration\\codecoverage\\report\\xml' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Xml.php', + 'phpunit\\textui\\xmlconfiguration\\configuration' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Configuration.php', + 'phpunit\\textui\\xmlconfiguration\\constant' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Constant.php', + 'phpunit\\textui\\xmlconfiguration\\constantcollection' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/ConstantCollection.php', + 'phpunit\\textui\\xmlconfiguration\\constantcollectioniterator' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/ConstantCollectionIterator.php', + 'phpunit\\textui\\xmlconfiguration\\convertlogtypes' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/ConvertLogTypes.php', + 'phpunit\\textui\\xmlconfiguration\\coverageclovertoreport' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageCloverToReport.php', + 'phpunit\\textui\\xmlconfiguration\\coveragecrap4jtoreport' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageCrap4jToReport.php', + 'phpunit\\textui\\xmlconfiguration\\coveragehtmltoreport' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageHtmlToReport.php', + 'phpunit\\textui\\xmlconfiguration\\coveragephptoreport' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoveragePhpToReport.php', + 'phpunit\\textui\\xmlconfiguration\\coveragetexttoreport' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageTextToReport.php', + 'phpunit\\textui\\xmlconfiguration\\coveragexmltoreport' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageXmlToReport.php', + 'phpunit\\textui\\xmlconfiguration\\directory' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/Directory.php', + 'phpunit\\textui\\xmlconfiguration\\directorycollection' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/DirectoryCollection.php', + 'phpunit\\textui\\xmlconfiguration\\directorycollectioniterator' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/DirectoryCollectionIterator.php', + 'phpunit\\textui\\xmlconfiguration\\exception' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Exception.php', + 'phpunit\\textui\\xmlconfiguration\\extension' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/Extension.php', + 'phpunit\\textui\\xmlconfiguration\\extensioncollection' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/ExtensionCollection.php', + 'phpunit\\textui\\xmlconfiguration\\extensioncollectioniterator' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/ExtensionCollectionIterator.php', + 'phpunit\\textui\\xmlconfiguration\\file' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/File.php', + 'phpunit\\textui\\xmlconfiguration\\filecollection' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/FileCollection.php', + 'phpunit\\textui\\xmlconfiguration\\filecollectioniterator' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/FileCollectionIterator.php', + 'phpunit\\textui\\xmlconfiguration\\generator' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Generator.php', + 'phpunit\\textui\\xmlconfiguration\\group' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/Group.php', + 'phpunit\\textui\\xmlconfiguration\\groupcollection' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/GroupCollection.php', + 'phpunit\\textui\\xmlconfiguration\\groupcollectioniterator' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/GroupCollectionIterator.php', + 'phpunit\\textui\\xmlconfiguration\\groups' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/Groups.php', + 'phpunit\\textui\\xmlconfiguration\\inisetting' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSetting.php', + 'phpunit\\textui\\xmlconfiguration\\inisettingcollection' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSettingCollection.php', + 'phpunit\\textui\\xmlconfiguration\\inisettingcollectioniterator' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSettingCollectionIterator.php', + 'phpunit\\textui\\xmlconfiguration\\introducecoverageelement' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/IntroduceCoverageElement.php', + 'phpunit\\textui\\xmlconfiguration\\loader' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Loader.php', + 'phpunit\\textui\\xmlconfiguration\\logging\\junit' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Junit.php', + 'phpunit\\textui\\xmlconfiguration\\logging\\logging' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Logging.php', + 'phpunit\\textui\\xmlconfiguration\\logging\\teamcity' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TeamCity.php', + 'phpunit\\textui\\xmlconfiguration\\logging\\testdox\\html' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Html.php', + 'phpunit\\textui\\xmlconfiguration\\logging\\testdox\\text' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Text.php', + 'phpunit\\textui\\xmlconfiguration\\logging\\testdox\\xml' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Xml.php', + 'phpunit\\textui\\xmlconfiguration\\logging\\text' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Text.php', + 'phpunit\\textui\\xmlconfiguration\\logtoreportmigration' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/LogToReportMigration.php', + 'phpunit\\textui\\xmlconfiguration\\migration' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/Migration.php', + 'phpunit\\textui\\xmlconfiguration\\migrationbuilder' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationBuilder.php', + 'phpunit\\textui\\xmlconfiguration\\migrationbuilderexception' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationBuilderException.php', + 'phpunit\\textui\\xmlconfiguration\\migrationexception' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationException.php', + 'phpunit\\textui\\xmlconfiguration\\migrator' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrator.php', + 'phpunit\\textui\\xmlconfiguration\\moveattributesfromfilterwhitelisttocoverage' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php', + 'phpunit\\textui\\xmlconfiguration\\moveattributesfromroottocoverage' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.php', + 'phpunit\\textui\\xmlconfiguration\\movewhitelistdirectoriestocoverage' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistDirectoriesToCoverage.php', + 'phpunit\\textui\\xmlconfiguration\\movewhitelistexcludestocoverage' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistExcludesToCoverage.php', + 'phpunit\\textui\\xmlconfiguration\\php' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Php.php', + 'phpunit\\textui\\xmlconfiguration\\phphandler' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/PhpHandler.php', + 'phpunit\\textui\\xmlconfiguration\\phpunit' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/PHPUnit.php', + 'phpunit\\textui\\xmlconfiguration\\removecachetokensattribute' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveCacheTokensAttribute.php', + 'phpunit\\textui\\xmlconfiguration\\removeemptyfilter' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveEmptyFilter.php', + 'phpunit\\textui\\xmlconfiguration\\removelogtypes' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveLogTypes.php', + 'phpunit\\textui\\xmlconfiguration\\testdirectory' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectory.php', + 'phpunit\\textui\\xmlconfiguration\\testdirectorycollection' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollection.php', + 'phpunit\\textui\\xmlconfiguration\\testdirectorycollectioniterator' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollectionIterator.php', + 'phpunit\\textui\\xmlconfiguration\\testfile' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFile.php', + 'phpunit\\textui\\xmlconfiguration\\testfilecollection' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFileCollection.php', + 'phpunit\\textui\\xmlconfiguration\\testfilecollectioniterator' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFileCollectionIterator.php', + 'phpunit\\textui\\xmlconfiguration\\testsuite' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuite.php', + 'phpunit\\textui\\xmlconfiguration\\testsuitecollection' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuiteCollection.php', + 'phpunit\\textui\\xmlconfiguration\\testsuitecollectioniterator' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuiteCollectionIterator.php', + 'phpunit\\textui\\xmlconfiguration\\updateschemalocationto93' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/UpdateSchemaLocationTo93.php', + 'phpunit\\textui\\xmlconfiguration\\variable' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Variable.php', + 'phpunit\\textui\\xmlconfiguration\\variablecollection' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/VariableCollection.php', + 'phpunit\\textui\\xmlconfiguration\\variablecollectioniterator' => '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.php', 'phpunit\\util\\annotation\\docblock' => '/phpunit/phpunit/src/Util/Annotation/DocBlock.php', 'phpunit\\util\\annotation\\registry' => '/phpunit/phpunit/src/Util/Annotation/Registry.php', 'phpunit\\util\\blacklist' => '/phpunit/phpunit/src/Util/Blacklist.php', 'phpunit\\util\\color' => '/phpunit/phpunit/src/Util/Color.php', - 'phpunit\\util\\configuration' => '/phpunit/phpunit/src/Util/Configuration.php', - 'phpunit\\util\\configurationgenerator' => '/phpunit/phpunit/src/Util/ConfigurationGenerator.php', 'phpunit\\util\\errorhandler' => '/phpunit/phpunit/src/Util/ErrorHandler.php', 'phpunit\\util\\exception' => '/phpunit/phpunit/src/Util/Exception.php', + 'phpunit\\util\\excludelist' => '/phpunit/phpunit/src/Util/ExcludeList.php', 'phpunit\\util\\fileloader' => '/phpunit/phpunit/src/Util/FileLoader.php', 'phpunit\\util\\filesystem' => '/phpunit/phpunit/src/Util/Filesystem.php', 'phpunit\\util\\filter' => '/phpunit/phpunit/src/Util/Filter.php', - 'phpunit\\util\\getopt' => '/phpunit/phpunit/src/Util/Getopt.php', 'phpunit\\util\\globalstate' => '/phpunit/phpunit/src/Util/GlobalState.php', + 'phpunit\\util\\invaliddatasetexception' => '/phpunit/phpunit/src/Util/InvalidDataSetException.php', 'phpunit\\util\\json' => '/phpunit/phpunit/src/Util/Json.php', 'phpunit\\util\\log\\junit' => '/phpunit/phpunit/src/Util/Log/JUnit.php', 'phpunit\\util\\log\\teamcity' => '/phpunit/phpunit/src/Util/Log/TeamCity.php', @@ -402,25 +599,60 @@ spl_autoload_register( 'phpunit\\util\\testdox\\xmlresultprinter' => '/phpunit/phpunit/src/Util/TestDox/XmlResultPrinter.php', 'phpunit\\util\\texttestlistrenderer' => '/phpunit/phpunit/src/Util/TextTestListRenderer.php', 'phpunit\\util\\type' => '/phpunit/phpunit/src/Util/Type.php', + 'phpunit\\util\\versioncomparisonoperator' => '/phpunit/phpunit/src/Util/VersionComparisonOperator.php', 'phpunit\\util\\xdebugfilterscriptgenerator' => '/phpunit/phpunit/src/Util/XdebugFilterScriptGenerator.php', 'phpunit\\util\\xml' => '/phpunit/phpunit/src/Util/Xml.php', + 'phpunit\\util\\xml\\exception' => '/phpunit/phpunit/src/Util/Xml/Exception.php', + 'phpunit\\util\\xml\\failedschemadetectionresult' => '/phpunit/phpunit/src/Util/Xml/FailedSchemaDetectionResult.php', + 'phpunit\\util\\xml\\loader' => '/phpunit/phpunit/src/Util/Xml/Loader.php', + 'phpunit\\util\\xml\\schemadetectionresult' => '/phpunit/phpunit/src/Util/Xml/SchemaDetectionResult.php', + 'phpunit\\util\\xml\\schemadetector' => '/phpunit/phpunit/src/Util/Xml/SchemaDetector.php', + 'phpunit\\util\\xml\\schemafinder' => '/phpunit/phpunit/src/Util/Xml/SchemaFinder.php', + 'phpunit\\util\\xml\\snapshotnodelist' => '/phpunit/phpunit/src/Util/Xml/SnapshotNodeList.php', + 'phpunit\\util\\xml\\successfulschemadetectionresult' => '/phpunit/phpunit/src/Util/Xml/SuccessfulSchemaDetectionResult.php', + 'phpunit\\util\\xml\\validationresult' => '/phpunit/phpunit/src/Util/Xml/ValidationResult.php', + 'phpunit\\util\\xml\\validator' => '/phpunit/phpunit/src/Util/Xml/Validator.php', 'phpunit\\util\\xmltestlistrenderer' => '/phpunit/phpunit/src/Util/XmlTestListRenderer.php', + 'sebastianbergmann\\cliparser\\ambiguousoptionexception' => '/sebastian/cli-parser/src/exceptions/AmbiguousOptionException.php', + 'sebastianbergmann\\cliparser\\exception' => '/sebastian/cli-parser/src/exceptions/Exception.php', + 'sebastianbergmann\\cliparser\\optiondoesnotallowargumentexception' => '/sebastian/cli-parser/src/exceptions/OptionDoesNotAllowArgumentException.php', + 'sebastianbergmann\\cliparser\\parser' => '/sebastian/cli-parser/src/Parser.php', + 'sebastianbergmann\\cliparser\\requiredoptionargumentmissingexception' => '/sebastian/cli-parser/src/exceptions/RequiredOptionArgumentMissingException.php', + 'sebastianbergmann\\cliparser\\unknownoptionexception' => '/sebastian/cli-parser/src/exceptions/UnknownOptionException.php', + 'sebastianbergmann\\codecoverage\\branchandpathcoveragenotsupportedexception' => '/phpunit/php-code-coverage/src/Exception/BranchAndPathCoverageNotSupportedException.php', 'sebastianbergmann\\codecoverage\\codecoverage' => '/phpunit/php-code-coverage/src/CodeCoverage.php', - 'sebastianbergmann\\codecoverage\\coveredcodenotexecutedexception' => '/phpunit/php-code-coverage/src/Exception/CoveredCodeNotExecutedException.php', + 'sebastianbergmann\\codecoverage\\deadcodedetectionnotsupportedexception' => '/phpunit/php-code-coverage/src/Exception/DeadCodeDetectionNotSupportedException.php', 'sebastianbergmann\\codecoverage\\driver\\driver' => '/phpunit/php-code-coverage/src/Driver/Driver.php', - 'sebastianbergmann\\codecoverage\\driver\\pcov' => '/phpunit/php-code-coverage/src/Driver/PCOV.php', - 'sebastianbergmann\\codecoverage\\driver\\phpdbg' => '/phpunit/php-code-coverage/src/Driver/PHPDBG.php', - 'sebastianbergmann\\codecoverage\\driver\\xdebug' => '/phpunit/php-code-coverage/src/Driver/Xdebug.php', + 'sebastianbergmann\\codecoverage\\driver\\pathexistsbutisnotdirectoryexception' => '/phpunit/php-code-coverage/src/Exception/PathExistsButIsNotDirectoryException.php', + 'sebastianbergmann\\codecoverage\\driver\\pcovdriver' => '/phpunit/php-code-coverage/src/Driver/PcovDriver.php', + 'sebastianbergmann\\codecoverage\\driver\\pcovnotavailableexception' => '/phpunit/php-code-coverage/src/Exception/PcovNotAvailableException.php', + 'sebastianbergmann\\codecoverage\\driver\\phpdbgdriver' => '/phpunit/php-code-coverage/src/Driver/PhpdbgDriver.php', + 'sebastianbergmann\\codecoverage\\driver\\phpdbgnotavailableexception' => '/phpunit/php-code-coverage/src/Exception/PhpdbgNotAvailableException.php', + 'sebastianbergmann\\codecoverage\\driver\\selector' => '/phpunit/php-code-coverage/src/Driver/Selector.php', + 'sebastianbergmann\\codecoverage\\driver\\writeoperationfailedexception' => '/phpunit/php-code-coverage/src/Exception/WriteOperationFailedException.php', + 'sebastianbergmann\\codecoverage\\driver\\wrongxdebugversionexception' => '/phpunit/php-code-coverage/src/Exception/WrongXdebugVersionException.php', + 'sebastianbergmann\\codecoverage\\driver\\xdebug2driver' => '/phpunit/php-code-coverage/src/Driver/Xdebug2Driver.php', + 'sebastianbergmann\\codecoverage\\driver\\xdebug2notenabledexception' => '/phpunit/php-code-coverage/src/Exception/Xdebug2NotEnabledException.php', + 'sebastianbergmann\\codecoverage\\driver\\xdebug3driver' => '/phpunit/php-code-coverage/src/Driver/Xdebug3Driver.php', + 'sebastianbergmann\\codecoverage\\driver\\xdebug3notenabledexception' => '/phpunit/php-code-coverage/src/Exception/Xdebug3NotEnabledException.php', + 'sebastianbergmann\\codecoverage\\driver\\xdebugnotavailableexception' => '/phpunit/php-code-coverage/src/Exception/XdebugNotAvailableException.php', 'sebastianbergmann\\codecoverage\\exception' => '/phpunit/php-code-coverage/src/Exception/Exception.php', 'sebastianbergmann\\codecoverage\\filter' => '/phpunit/php-code-coverage/src/Filter.php', 'sebastianbergmann\\codecoverage\\invalidargumentexception' => '/phpunit/php-code-coverage/src/Exception/InvalidArgumentException.php', - 'sebastianbergmann\\codecoverage\\missingcoversannotationexception' => '/phpunit/php-code-coverage/src/Exception/MissingCoversAnnotationException.php', + 'sebastianbergmann\\codecoverage\\nocodecoveragedriveravailableexception' => '/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverAvailableException.php', + 'sebastianbergmann\\codecoverage\\nocodecoveragedriverwithpathcoveragesupportavailableexception' => '/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.php', 'sebastianbergmann\\codecoverage\\node\\abstractnode' => '/phpunit/php-code-coverage/src/Node/AbstractNode.php', 'sebastianbergmann\\codecoverage\\node\\builder' => '/phpunit/php-code-coverage/src/Node/Builder.php', + 'sebastianbergmann\\codecoverage\\node\\crapindex' => '/phpunit/php-code-coverage/src/Node/CrapIndex.php', 'sebastianbergmann\\codecoverage\\node\\directory' => '/phpunit/php-code-coverage/src/Node/Directory.php', 'sebastianbergmann\\codecoverage\\node\\file' => '/phpunit/php-code-coverage/src/Node/File.php', 'sebastianbergmann\\codecoverage\\node\\iterator' => '/phpunit/php-code-coverage/src/Node/Iterator.php', + 'sebastianbergmann\\codecoverage\\parserexception' => '/phpunit/php-code-coverage/src/Exception/ParserException.php', + 'sebastianbergmann\\codecoverage\\processedcodecoveragedata' => '/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.php', + 'sebastianbergmann\\codecoverage\\rawcodecoveragedata' => '/phpunit/php-code-coverage/src/RawCodeCoverageData.php', + 'sebastianbergmann\\codecoverage\\reflectionexception' => '/phpunit/php-code-coverage/src/Exception/ReflectionException.php', 'sebastianbergmann\\codecoverage\\report\\clover' => '/phpunit/php-code-coverage/src/Report/Clover.php', + 'sebastianbergmann\\codecoverage\\report\\cobertura' => '/phpunit/php-code-coverage/src/Report/Cobertura.php', 'sebastianbergmann\\codecoverage\\report\\crap4j' => '/phpunit/php-code-coverage/src/Report/Crap4j.php', 'sebastianbergmann\\codecoverage\\report\\html\\dashboard' => '/phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.php', 'sebastianbergmann\\codecoverage\\report\\html\\directory' => '/phpunit/php-code-coverage/src/Report/Html/Renderer/Directory.php', @@ -442,10 +674,37 @@ spl_autoload_register( 'sebastianbergmann\\codecoverage\\report\\xml\\tests' => '/phpunit/php-code-coverage/src/Report/Xml/Tests.php', 'sebastianbergmann\\codecoverage\\report\\xml\\totals' => '/phpunit/php-code-coverage/src/Report/Xml/Totals.php', 'sebastianbergmann\\codecoverage\\report\\xml\\unit' => '/phpunit/php-code-coverage/src/Report/Xml/Unit.php', - 'sebastianbergmann\\codecoverage\\runtimeexception' => '/phpunit/php-code-coverage/src/Exception/RuntimeException.php', + 'sebastianbergmann\\codecoverage\\reportalreadyfinalizedexception' => '/phpunit/php-code-coverage/src/Exception/ReportAlreadyFinalizedException.php', + 'sebastianbergmann\\codecoverage\\staticanalysis\\cachewarmer' => '/phpunit/php-code-coverage/src/StaticAnalysis/CacheWarmer.php', + 'sebastianbergmann\\codecoverage\\staticanalysis\\cachingfileanalyser' => '/phpunit/php-code-coverage/src/StaticAnalysis/CachingFileAnalyser.php', + 'sebastianbergmann\\codecoverage\\staticanalysis\\codeunitfindingvisitor' => '/phpunit/php-code-coverage/src/StaticAnalysis/CodeUnitFindingVisitor.php', + 'sebastianbergmann\\codecoverage\\staticanalysis\\executablelinesfindingvisitor' => '/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php', + 'sebastianbergmann\\codecoverage\\staticanalysis\\fileanalyser' => '/phpunit/php-code-coverage/src/StaticAnalysis/FileAnalyser.php', + 'sebastianbergmann\\codecoverage\\staticanalysis\\ignoredlinesfindingvisitor' => '/phpunit/php-code-coverage/src/StaticAnalysis/IgnoredLinesFindingVisitor.php', + 'sebastianbergmann\\codecoverage\\staticanalysis\\parsingfileanalyser' => '/phpunit/php-code-coverage/src/StaticAnalysis/ParsingFileAnalyser.php', + 'sebastianbergmann\\codecoverage\\staticanalysiscachenotconfiguredexception' => '/phpunit/php-code-coverage/src/Exception/StaticAnalysisCacheNotConfiguredException.php', + 'sebastianbergmann\\codecoverage\\testidmissingexception' => '/phpunit/php-code-coverage/src/Exception/TestIdMissingException.php', 'sebastianbergmann\\codecoverage\\unintentionallycoveredcodeexception' => '/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php', - 'sebastianbergmann\\codecoverage\\util' => '/phpunit/php-code-coverage/src/Util.php', + 'sebastianbergmann\\codecoverage\\util\\directorycouldnotbecreatedexception' => '/phpunit/php-code-coverage/src/Exception/DirectoryCouldNotBeCreatedException.php', + 'sebastianbergmann\\codecoverage\\util\\filesystem' => '/phpunit/php-code-coverage/src/Util/Filesystem.php', + 'sebastianbergmann\\codecoverage\\util\\percentage' => '/phpunit/php-code-coverage/src/Util/Percentage.php', 'sebastianbergmann\\codecoverage\\version' => '/phpunit/php-code-coverage/src/Version.php', + 'sebastianbergmann\\codecoverage\\xmlexception' => '/phpunit/php-code-coverage/src/Exception/XmlException.php', + 'sebastianbergmann\\codeunit\\classmethodunit' => '/sebastian/code-unit/src/ClassMethodUnit.php', + 'sebastianbergmann\\codeunit\\classunit' => '/sebastian/code-unit/src/ClassUnit.php', + 'sebastianbergmann\\codeunit\\codeunit' => '/sebastian/code-unit/src/CodeUnit.php', + 'sebastianbergmann\\codeunit\\codeunitcollection' => '/sebastian/code-unit/src/CodeUnitCollection.php', + 'sebastianbergmann\\codeunit\\codeunitcollectioniterator' => '/sebastian/code-unit/src/CodeUnitCollectionIterator.php', + 'sebastianbergmann\\codeunit\\exception' => '/sebastian/code-unit/src/exceptions/Exception.php', + 'sebastianbergmann\\codeunit\\functionunit' => '/sebastian/code-unit/src/FunctionUnit.php', + 'sebastianbergmann\\codeunit\\interfacemethodunit' => '/sebastian/code-unit/src/InterfaceMethodUnit.php', + 'sebastianbergmann\\codeunit\\interfaceunit' => '/sebastian/code-unit/src/InterfaceUnit.php', + 'sebastianbergmann\\codeunit\\invalidcodeunitexception' => '/sebastian/code-unit/src/exceptions/InvalidCodeUnitException.php', + 'sebastianbergmann\\codeunit\\mapper' => '/sebastian/code-unit/src/Mapper.php', + 'sebastianbergmann\\codeunit\\notraitexception' => '/sebastian/code-unit/src/exceptions/NoTraitException.php', + 'sebastianbergmann\\codeunit\\reflectionexception' => '/sebastian/code-unit/src/exceptions/ReflectionException.php', + 'sebastianbergmann\\codeunit\\traitmethodunit' => '/sebastian/code-unit/src/TraitMethodUnit.php', + 'sebastianbergmann\\codeunit\\traitunit' => '/sebastian/code-unit/src/TraitUnit.php', 'sebastianbergmann\\codeunitreverselookup\\wizard' => '/sebastian/code-unit-reverse-lookup/src/Wizard.php', 'sebastianbergmann\\comparator\\arraycomparator' => '/sebastian/comparator/src/ArrayComparator.php', 'sebastianbergmann\\comparator\\comparator' => '/sebastian/comparator/src/Comparator.php', @@ -453,21 +712,39 @@ spl_autoload_register( 'sebastianbergmann\\comparator\\datetimecomparator' => '/sebastian/comparator/src/DateTimeComparator.php', 'sebastianbergmann\\comparator\\domnodecomparator' => '/sebastian/comparator/src/DOMNodeComparator.php', 'sebastianbergmann\\comparator\\doublecomparator' => '/sebastian/comparator/src/DoubleComparator.php', + 'sebastianbergmann\\comparator\\exception' => '/sebastian/comparator/src/exceptions/Exception.php', 'sebastianbergmann\\comparator\\exceptioncomparator' => '/sebastian/comparator/src/ExceptionComparator.php', 'sebastianbergmann\\comparator\\factory' => '/sebastian/comparator/src/Factory.php', 'sebastianbergmann\\comparator\\mockobjectcomparator' => '/sebastian/comparator/src/MockObjectComparator.php', 'sebastianbergmann\\comparator\\numericcomparator' => '/sebastian/comparator/src/NumericComparator.php', 'sebastianbergmann\\comparator\\objectcomparator' => '/sebastian/comparator/src/ObjectComparator.php', 'sebastianbergmann\\comparator\\resourcecomparator' => '/sebastian/comparator/src/ResourceComparator.php', + 'sebastianbergmann\\comparator\\runtimeexception' => '/sebastian/comparator/src/exceptions/RuntimeException.php', 'sebastianbergmann\\comparator\\scalarcomparator' => '/sebastian/comparator/src/ScalarComparator.php', 'sebastianbergmann\\comparator\\splobjectstoragecomparator' => '/sebastian/comparator/src/SplObjectStorageComparator.php', 'sebastianbergmann\\comparator\\typecomparator' => '/sebastian/comparator/src/TypeComparator.php', + 'sebastianbergmann\\complexity\\calculator' => '/sebastian/complexity/src/Calculator.php', + 'sebastianbergmann\\complexity\\complexity' => '/sebastian/complexity/src/Complexity/Complexity.php', + 'sebastianbergmann\\complexity\\complexitycalculatingvisitor' => '/sebastian/complexity/src/Visitor/ComplexityCalculatingVisitor.php', + 'sebastianbergmann\\complexity\\complexitycollection' => '/sebastian/complexity/src/Complexity/ComplexityCollection.php', + 'sebastianbergmann\\complexity\\complexitycollectioniterator' => '/sebastian/complexity/src/Complexity/ComplexityCollectionIterator.php', + 'sebastianbergmann\\complexity\\cyclomaticcomplexitycalculatingvisitor' => '/sebastian/complexity/src/Visitor/CyclomaticComplexityCalculatingVisitor.php', + 'sebastianbergmann\\complexity\\exception' => '/sebastian/complexity/src/Exception/Exception.php', + 'sebastianbergmann\\complexity\\runtimeexception' => '/sebastian/complexity/src/Exception/RuntimeException.php', 'sebastianbergmann\\diff\\chunk' => '/sebastian/diff/src/Chunk.php', + 'sebastianbergmann\\diff\\configurationexception' => '/sebastian/diff/src/Exception/ConfigurationException.php', 'sebastianbergmann\\diff\\diff' => '/sebastian/diff/src/Diff.php', 'sebastianbergmann\\diff\\differ' => '/sebastian/diff/src/Differ.php', + 'sebastianbergmann\\diff\\exception' => '/sebastian/diff/src/Exception/Exception.php', + 'sebastianbergmann\\diff\\invalidargumentexception' => '/sebastian/diff/src/Exception/InvalidArgumentException.php', 'sebastianbergmann\\diff\\line' => '/sebastian/diff/src/Line.php', 'sebastianbergmann\\diff\\longestcommonsubsequencecalculator' => '/sebastian/diff/src/LongestCommonSubsequenceCalculator.php', 'sebastianbergmann\\diff\\memoryefficientlongestcommonsubsequencecalculator' => '/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php', + 'sebastianbergmann\\diff\\output\\abstractchunkoutputbuilder' => '/sebastian/diff/src/Output/AbstractChunkOutputBuilder.php', + 'sebastianbergmann\\diff\\output\\diffonlyoutputbuilder' => '/sebastian/diff/src/Output/DiffOnlyOutputBuilder.php', + 'sebastianbergmann\\diff\\output\\diffoutputbuilderinterface' => '/sebastian/diff/src/Output/DiffOutputBuilderInterface.php', + 'sebastianbergmann\\diff\\output\\strictunifieddiffoutputbuilder' => '/sebastian/diff/src/Output/StrictUnifiedDiffOutputBuilder.php', + 'sebastianbergmann\\diff\\output\\unifieddiffoutputbuilder' => '/sebastian/diff/src/Output/UnifiedDiffOutputBuilder.php', 'sebastianbergmann\\diff\\parser' => '/sebastian/diff/src/Parser.php', 'sebastianbergmann\\diff\\timeefficientlongestcommonsubsequencecalculator' => '/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php', 'sebastianbergmann\\environment\\console' => '/sebastian/environment/src/Console.php', @@ -477,1173 +754,194 @@ spl_autoload_register( 'sebastianbergmann\\fileiterator\\facade' => '/phpunit/php-file-iterator/src/Facade.php', 'sebastianbergmann\\fileiterator\\factory' => '/phpunit/php-file-iterator/src/Factory.php', 'sebastianbergmann\\fileiterator\\iterator' => '/phpunit/php-file-iterator/src/Iterator.php', - 'sebastianbergmann\\finderfacade\\configuration' => '/sebastian/finder-facade/src/Configuration.php', - 'sebastianbergmann\\finderfacade\\finderfacade' => '/sebastian/finder-facade/src/FinderFacade.php', - 'sebastianbergmann\\globalstate\\blacklist' => '/sebastian/global-state/src/Blacklist.php', 'sebastianbergmann\\globalstate\\codeexporter' => '/sebastian/global-state/src/CodeExporter.php', + 'sebastianbergmann\\globalstate\\exception' => '/sebastian/global-state/src/exceptions/Exception.php', + 'sebastianbergmann\\globalstate\\excludelist' => '/sebastian/global-state/src/ExcludeList.php', 'sebastianbergmann\\globalstate\\restorer' => '/sebastian/global-state/src/Restorer.php', + 'sebastianbergmann\\globalstate\\runtimeexception' => '/sebastian/global-state/src/exceptions/RuntimeException.php', 'sebastianbergmann\\globalstate\\snapshot' => '/sebastian/global-state/src/Snapshot.php', + 'sebastianbergmann\\invoker\\exception' => '/phpunit/php-invoker/src/exceptions/Exception.php', + 'sebastianbergmann\\invoker\\invoker' => '/phpunit/php-invoker/src/Invoker.php', + 'sebastianbergmann\\invoker\\processcontrolextensionnotloadedexception' => '/phpunit/php-invoker/src/exceptions/ProcessControlExtensionNotLoadedException.php', + 'sebastianbergmann\\invoker\\timeoutexception' => '/phpunit/php-invoker/src/exceptions/TimeoutException.php', + 'sebastianbergmann\\linesofcode\\counter' => '/sebastian/lines-of-code/src/Counter.php', + 'sebastianbergmann\\linesofcode\\exception' => '/sebastian/lines-of-code/src/Exception/Exception.php', + 'sebastianbergmann\\linesofcode\\illogicalvaluesexception' => '/sebastian/lines-of-code/src/Exception/IllogicalValuesException.php', + 'sebastianbergmann\\linesofcode\\linecountingvisitor' => '/sebastian/lines-of-code/src/LineCountingVisitor.php', + 'sebastianbergmann\\linesofcode\\linesofcode' => '/sebastian/lines-of-code/src/LinesOfCode.php', + 'sebastianbergmann\\linesofcode\\negativevalueexception' => '/sebastian/lines-of-code/src/Exception/NegativeValueException.php', + 'sebastianbergmann\\linesofcode\\runtimeexception' => '/sebastian/lines-of-code/src/Exception/RuntimeException.php', 'sebastianbergmann\\objectenumerator\\enumerator' => '/sebastian/object-enumerator/src/Enumerator.php', 'sebastianbergmann\\objectenumerator\\exception' => '/sebastian/object-enumerator/src/Exception.php', 'sebastianbergmann\\objectenumerator\\invalidargumentexception' => '/sebastian/object-enumerator/src/InvalidArgumentException.php', 'sebastianbergmann\\objectreflector\\exception' => '/sebastian/object-reflector/src/Exception.php', 'sebastianbergmann\\objectreflector\\invalidargumentexception' => '/sebastian/object-reflector/src/InvalidArgumentException.php', 'sebastianbergmann\\objectreflector\\objectreflector' => '/sebastian/object-reflector/src/ObjectReflector.php', - 'sebastianbergmann\\phpcov\\application' => '/src/Application.php', - 'sebastianbergmann\\phpcov\\basecommand' => '/src/BaseCommand.php', - 'sebastianbergmann\\phpcov\\executecommand' => '/src/ExecuteCommand.php', - 'sebastianbergmann\\phpcov\\mergecommand' => '/src/MergeCommand.php', + 'sebastianbergmann\\phpcov\\application' => '/src/cli/Application.php', + 'sebastianbergmann\\phpcov\\arguments' => '/src/cli/Arguments.php', + 'sebastianbergmann\\phpcov\\argumentsbuilder' => '/src/cli/ArgumentsBuilder.php', + 'sebastianbergmann\\phpcov\\argumentsbuilderexception' => '/src/exceptions/ArgumentsBuilderException.php', + 'sebastianbergmann\\phpcov\\command' => '/src/cli/Command.php', + 'sebastianbergmann\\phpcov\\exception' => '/src/exceptions/Exception.php', + 'sebastianbergmann\\phpcov\\executecommand' => '/src/cli/ExecuteCommand.php', + 'sebastianbergmann\\phpcov\\helpcommand' => '/src/cli/HelpCommand.php', + 'sebastianbergmann\\phpcov\\mergecommand' => '/src/cli/MergeCommand.php', 'sebastianbergmann\\phpcov\\patchcoverage' => '/src/PatchCoverage.php', - 'sebastianbergmann\\phpcov\\patchcoveragecommand' => '/src/PatchCoverageCommand.php', + 'sebastianbergmann\\phpcov\\patchcoveragecommand' => '/src/cli/PatchCoverageCommand.php', + 'sebastianbergmann\\phpcov\\requiredargumentmissingexception' => '/src/exceptions/RequiredArgumentMissingException.php', 'sebastianbergmann\\recursioncontext\\context' => '/sebastian/recursion-context/src/Context.php', 'sebastianbergmann\\recursioncontext\\exception' => '/sebastian/recursion-context/src/Exception.php', 'sebastianbergmann\\recursioncontext\\invalidargumentexception' => '/sebastian/recursion-context/src/InvalidArgumentException.php', 'sebastianbergmann\\resourceoperations\\resourceoperations' => '/sebastian/resource-operations/src/ResourceOperations.php', - 'sebastianbergmann\\timer\\exception' => '/phpunit/php-timer/src/Exception.php', - 'sebastianbergmann\\timer\\runtimeexception' => '/phpunit/php-timer/src/RuntimeException.php', + 'sebastianbergmann\\template\\exception' => '/phpunit/php-text-template/src/exceptions/Exception.php', + 'sebastianbergmann\\template\\invalidargumentexception' => '/phpunit/php-text-template/src/exceptions/InvalidArgumentException.php', + 'sebastianbergmann\\template\\runtimeexception' => '/phpunit/php-text-template/src/exceptions/RuntimeException.php', + 'sebastianbergmann\\template\\template' => '/phpunit/php-text-template/src/Template.php', + 'sebastianbergmann\\timer\\duration' => '/phpunit/php-timer/src/Duration.php', + 'sebastianbergmann\\timer\\exception' => '/phpunit/php-timer/src/exceptions/Exception.php', + 'sebastianbergmann\\timer\\noactivetimerexception' => '/phpunit/php-timer/src/exceptions/NoActiveTimerException.php', + 'sebastianbergmann\\timer\\resourceusageformatter' => '/phpunit/php-timer/src/ResourceUsageFormatter.php', 'sebastianbergmann\\timer\\timer' => '/phpunit/php-timer/src/Timer.php', - 'sebastianbergmann\\type\\callabletype' => '/sebastian/type/src/CallableType.php', - 'sebastianbergmann\\type\\genericobjecttype' => '/sebastian/type/src/GenericObjectType.php', - 'sebastianbergmann\\type\\iterabletype' => '/sebastian/type/src/IterableType.php', - 'sebastianbergmann\\type\\nulltype' => '/sebastian/type/src/NullType.php', - 'sebastianbergmann\\type\\objecttype' => '/sebastian/type/src/ObjectType.php', - 'sebastianbergmann\\type\\simpletype' => '/sebastian/type/src/SimpleType.php', - 'sebastianbergmann\\type\\type' => '/sebastian/type/src/Type.php', + 'sebastianbergmann\\timer\\timesincestartofrequestnotavailableexception' => '/phpunit/php-timer/src/exceptions/TimeSinceStartOfRequestNotAvailableException.php', + 'sebastianbergmann\\type\\callabletype' => '/sebastian/type/src/type/CallableType.php', + 'sebastianbergmann\\type\\exception' => '/sebastian/type/src/exception/Exception.php', + 'sebastianbergmann\\type\\falsetype' => '/sebastian/type/src/type/FalseType.php', + 'sebastianbergmann\\type\\genericobjecttype' => '/sebastian/type/src/type/GenericObjectType.php', + 'sebastianbergmann\\type\\intersectiontype' => '/sebastian/type/src/type/IntersectionType.php', + 'sebastianbergmann\\type\\iterabletype' => '/sebastian/type/src/type/IterableType.php', + 'sebastianbergmann\\type\\mixedtype' => '/sebastian/type/src/type/MixedType.php', + 'sebastianbergmann\\type\\nevertype' => '/sebastian/type/src/type/NeverType.php', + 'sebastianbergmann\\type\\nulltype' => '/sebastian/type/src/type/NullType.php', + 'sebastianbergmann\\type\\objecttype' => '/sebastian/type/src/type/ObjectType.php', + 'sebastianbergmann\\type\\reflectionmapper' => '/sebastian/type/src/ReflectionMapper.php', + 'sebastianbergmann\\type\\runtimeexception' => '/sebastian/type/src/exception/RuntimeException.php', + 'sebastianbergmann\\type\\simpletype' => '/sebastian/type/src/type/SimpleType.php', + 'sebastianbergmann\\type\\statictype' => '/sebastian/type/src/type/StaticType.php', + 'sebastianbergmann\\type\\type' => '/sebastian/type/src/type/Type.php', 'sebastianbergmann\\type\\typename' => '/sebastian/type/src/TypeName.php', - 'sebastianbergmann\\type\\unknowntype' => '/sebastian/type/src/UnknownType.php', - 'sebastianbergmann\\type\\voidtype' => '/sebastian/type/src/VoidType.php', + 'sebastianbergmann\\type\\uniontype' => '/sebastian/type/src/type/UnionType.php', + 'sebastianbergmann\\type\\unknowntype' => '/sebastian/type/src/type/UnknownType.php', + 'sebastianbergmann\\type\\voidtype' => '/sebastian/type/src/type/VoidType.php', 'sebastianbergmann\\version' => '/sebastian/version/src/Version.php', - 'symfony\\component\\console\\application' => '/symfony/console/Application.php', - 'symfony\\component\\console\\command\\command' => '/symfony/console/Command/Command.php', - 'symfony\\component\\console\\command\\helpcommand' => '/symfony/console/Command/HelpCommand.php', - 'symfony\\component\\console\\command\\listcommand' => '/symfony/console/Command/ListCommand.php', - 'symfony\\component\\console\\command\\lockabletrait' => '/symfony/console/Command/LockableTrait.php', - 'symfony\\component\\console\\commandloader\\commandloaderinterface' => '/symfony/console/CommandLoader/CommandLoaderInterface.php', - 'symfony\\component\\console\\commandloader\\containercommandloader' => '/symfony/console/CommandLoader/ContainerCommandLoader.php', - 'symfony\\component\\console\\commandloader\\factorycommandloader' => '/symfony/console/CommandLoader/FactoryCommandLoader.php', - 'symfony\\component\\console\\consoleevents' => '/symfony/console/ConsoleEvents.php', - 'symfony\\component\\console\\dependencyinjection\\addconsolecommandpass' => '/symfony/console/DependencyInjection/AddConsoleCommandPass.php', - 'symfony\\component\\console\\descriptor\\applicationdescription' => '/symfony/console/Descriptor/ApplicationDescription.php', - 'symfony\\component\\console\\descriptor\\descriptor' => '/symfony/console/Descriptor/Descriptor.php', - 'symfony\\component\\console\\descriptor\\descriptorinterface' => '/symfony/console/Descriptor/DescriptorInterface.php', - 'symfony\\component\\console\\descriptor\\jsondescriptor' => '/symfony/console/Descriptor/JsonDescriptor.php', - 'symfony\\component\\console\\descriptor\\markdowndescriptor' => '/symfony/console/Descriptor/MarkdownDescriptor.php', - 'symfony\\component\\console\\descriptor\\textdescriptor' => '/symfony/console/Descriptor/TextDescriptor.php', - 'symfony\\component\\console\\descriptor\\xmldescriptor' => '/symfony/console/Descriptor/XmlDescriptor.php', - 'symfony\\component\\console\\event\\consolecommandevent' => '/symfony/console/Event/ConsoleCommandEvent.php', - 'symfony\\component\\console\\event\\consoleerrorevent' => '/symfony/console/Event/ConsoleErrorEvent.php', - 'symfony\\component\\console\\event\\consoleevent' => '/symfony/console/Event/ConsoleEvent.php', - 'symfony\\component\\console\\event\\consoleterminateevent' => '/symfony/console/Event/ConsoleTerminateEvent.php', - 'symfony\\component\\console\\eventlistener\\errorlistener' => '/symfony/console/EventListener/ErrorListener.php', - 'symfony\\component\\console\\exception\\commandnotfoundexception' => '/symfony/console/Exception/CommandNotFoundException.php', - 'symfony\\component\\console\\exception\\exceptioninterface' => '/symfony/console/Exception/ExceptionInterface.php', - 'symfony\\component\\console\\exception\\invalidargumentexception' => '/symfony/console/Exception/InvalidArgumentException.php', - 'symfony\\component\\console\\exception\\invalidoptionexception' => '/symfony/console/Exception/InvalidOptionException.php', - 'symfony\\component\\console\\exception\\logicexception' => '/symfony/console/Exception/LogicException.php', - 'symfony\\component\\console\\exception\\namespacenotfoundexception' => '/symfony/console/Exception/NamespaceNotFoundException.php', - 'symfony\\component\\console\\exception\\runtimeexception' => '/symfony/console/Exception/RuntimeException.php', - 'symfony\\component\\console\\formatter\\outputformatter' => '/symfony/console/Formatter/OutputFormatter.php', - 'symfony\\component\\console\\formatter\\outputformatterinterface' => '/symfony/console/Formatter/OutputFormatterInterface.php', - 'symfony\\component\\console\\formatter\\outputformatterstyle' => '/symfony/console/Formatter/OutputFormatterStyle.php', - 'symfony\\component\\console\\formatter\\outputformatterstyleinterface' => '/symfony/console/Formatter/OutputFormatterStyleInterface.php', - 'symfony\\component\\console\\formatter\\outputformatterstylestack' => '/symfony/console/Formatter/OutputFormatterStyleStack.php', - 'symfony\\component\\console\\formatter\\wrappableoutputformatterinterface' => '/symfony/console/Formatter/WrappableOutputFormatterInterface.php', - 'symfony\\component\\console\\helper\\debugformatterhelper' => '/symfony/console/Helper/DebugFormatterHelper.php', - 'symfony\\component\\console\\helper\\descriptorhelper' => '/symfony/console/Helper/DescriptorHelper.php', - 'symfony\\component\\console\\helper\\dumper' => '/symfony/console/Helper/Dumper.php', - 'symfony\\component\\console\\helper\\formatterhelper' => '/symfony/console/Helper/FormatterHelper.php', - 'symfony\\component\\console\\helper\\helper' => '/symfony/console/Helper/Helper.php', - 'symfony\\component\\console\\helper\\helperinterface' => '/symfony/console/Helper/HelperInterface.php', - 'symfony\\component\\console\\helper\\helperset' => '/symfony/console/Helper/HelperSet.php', - 'symfony\\component\\console\\helper\\inputawarehelper' => '/symfony/console/Helper/InputAwareHelper.php', - 'symfony\\component\\console\\helper\\processhelper' => '/symfony/console/Helper/ProcessHelper.php', - 'symfony\\component\\console\\helper\\progressbar' => '/symfony/console/Helper/ProgressBar.php', - 'symfony\\component\\console\\helper\\progressindicator' => '/symfony/console/Helper/ProgressIndicator.php', - 'symfony\\component\\console\\helper\\questionhelper' => '/symfony/console/Helper/QuestionHelper.php', - 'symfony\\component\\console\\helper\\symfonyquestionhelper' => '/symfony/console/Helper/SymfonyQuestionHelper.php', - 'symfony\\component\\console\\helper\\table' => '/symfony/console/Helper/Table.php', - 'symfony\\component\\console\\helper\\tablecell' => '/symfony/console/Helper/TableCell.php', - 'symfony\\component\\console\\helper\\tablerows' => '/symfony/console/Helper/TableRows.php', - 'symfony\\component\\console\\helper\\tableseparator' => '/symfony/console/Helper/TableSeparator.php', - 'symfony\\component\\console\\helper\\tablestyle' => '/symfony/console/Helper/TableStyle.php', - 'symfony\\component\\console\\input\\argvinput' => '/symfony/console/Input/ArgvInput.php', - 'symfony\\component\\console\\input\\arrayinput' => '/symfony/console/Input/ArrayInput.php', - 'symfony\\component\\console\\input\\input' => '/symfony/console/Input/Input.php', - 'symfony\\component\\console\\input\\inputargument' => '/symfony/console/Input/InputArgument.php', - 'symfony\\component\\console\\input\\inputawareinterface' => '/symfony/console/Input/InputAwareInterface.php', - 'symfony\\component\\console\\input\\inputdefinition' => '/symfony/console/Input/InputDefinition.php', - 'symfony\\component\\console\\input\\inputinterface' => '/symfony/console/Input/InputInterface.php', - 'symfony\\component\\console\\input\\inputoption' => '/symfony/console/Input/InputOption.php', - 'symfony\\component\\console\\input\\streamableinputinterface' => '/symfony/console/Input/StreamableInputInterface.php', - 'symfony\\component\\console\\input\\stringinput' => '/symfony/console/Input/StringInput.php', - 'symfony\\component\\console\\logger\\consolelogger' => '/symfony/console/Logger/ConsoleLogger.php', - 'symfony\\component\\console\\output\\bufferedoutput' => '/symfony/console/Output/BufferedOutput.php', - 'symfony\\component\\console\\output\\consoleoutput' => '/symfony/console/Output/ConsoleOutput.php', - 'symfony\\component\\console\\output\\consoleoutputinterface' => '/symfony/console/Output/ConsoleOutputInterface.php', - 'symfony\\component\\console\\output\\consolesectionoutput' => '/symfony/console/Output/ConsoleSectionOutput.php', - 'symfony\\component\\console\\output\\nulloutput' => '/symfony/console/Output/NullOutput.php', - 'symfony\\component\\console\\output\\output' => '/symfony/console/Output/Output.php', - 'symfony\\component\\console\\output\\outputinterface' => '/symfony/console/Output/OutputInterface.php', - 'symfony\\component\\console\\output\\streamoutput' => '/symfony/console/Output/StreamOutput.php', - 'symfony\\component\\console\\question\\choicequestion' => '/symfony/console/Question/ChoiceQuestion.php', - 'symfony\\component\\console\\question\\confirmationquestion' => '/symfony/console/Question/ConfirmationQuestion.php', - 'symfony\\component\\console\\question\\question' => '/symfony/console/Question/Question.php', - 'symfony\\component\\console\\style\\outputstyle' => '/symfony/console/Style/OutputStyle.php', - 'symfony\\component\\console\\style\\styleinterface' => '/symfony/console/Style/StyleInterface.php', - 'symfony\\component\\console\\style\\symfonystyle' => '/symfony/console/Style/SymfonyStyle.php', - 'symfony\\component\\console\\terminal' => '/symfony/console/Terminal.php', - 'symfony\\component\\console\\tester\\applicationtester' => '/symfony/console/Tester/ApplicationTester.php', - 'symfony\\component\\console\\tester\\commandtester' => '/symfony/console/Tester/CommandTester.php', - 'symfony\\component\\console\\tester\\testertrait' => '/symfony/console/Tester/TesterTrait.php', - 'symfony\\component\\finder\\comparator\\comparator' => '/symfony/finder/Comparator/Comparator.php', - 'symfony\\component\\finder\\comparator\\datecomparator' => '/symfony/finder/Comparator/DateComparator.php', - 'symfony\\component\\finder\\comparator\\numbercomparator' => '/symfony/finder/Comparator/NumberComparator.php', - 'symfony\\component\\finder\\exception\\accessdeniedexception' => '/symfony/finder/Exception/AccessDeniedException.php', - 'symfony\\component\\finder\\exception\\directorynotfoundexception' => '/symfony/finder/Exception/DirectoryNotFoundException.php', - 'symfony\\component\\finder\\finder' => '/symfony/finder/Finder.php', - 'symfony\\component\\finder\\gitignore' => '/symfony/finder/Gitignore.php', - 'symfony\\component\\finder\\glob' => '/symfony/finder/Glob.php', - 'symfony\\component\\finder\\iterator\\customfilteriterator' => '/symfony/finder/Iterator/CustomFilterIterator.php', - 'symfony\\component\\finder\\iterator\\daterangefilteriterator' => '/symfony/finder/Iterator/DateRangeFilterIterator.php', - 'symfony\\component\\finder\\iterator\\depthrangefilteriterator' => '/symfony/finder/Iterator/DepthRangeFilterIterator.php', - 'symfony\\component\\finder\\iterator\\excludedirectoryfilteriterator' => '/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php', - 'symfony\\component\\finder\\iterator\\filecontentfilteriterator' => '/symfony/finder/Iterator/FilecontentFilterIterator.php', - 'symfony\\component\\finder\\iterator\\filenamefilteriterator' => '/symfony/finder/Iterator/FilenameFilterIterator.php', - 'symfony\\component\\finder\\iterator\\filetypefilteriterator' => '/symfony/finder/Iterator/FileTypeFilterIterator.php', - 'symfony\\component\\finder\\iterator\\multiplepcrefilteriterator' => '/symfony/finder/Iterator/MultiplePcreFilterIterator.php', - 'symfony\\component\\finder\\iterator\\pathfilteriterator' => '/symfony/finder/Iterator/PathFilterIterator.php', - 'symfony\\component\\finder\\iterator\\recursivedirectoryiterator' => '/symfony/finder/Iterator/RecursiveDirectoryIterator.php', - 'symfony\\component\\finder\\iterator\\sizerangefilteriterator' => '/symfony/finder/Iterator/SizeRangeFilterIterator.php', - 'symfony\\component\\finder\\iterator\\sortableiterator' => '/symfony/finder/Iterator/SortableIterator.php', - 'symfony\\component\\finder\\splfileinfo' => '/symfony/finder/SplFileInfo.php', - 'symfony\\contracts\\service\\resetinterface' => '/symfony/service-contracts/ResetInterface.php', - 'symfony\\contracts\\service\\servicelocatortrait' => '/symfony/service-contracts/ServiceLocatorTrait.php', - 'symfony\\contracts\\service\\serviceproviderinterface' => '/symfony/service-contracts/ServiceProviderInterface.php', - 'symfony\\contracts\\service\\servicesubscriberinterface' => '/symfony/service-contracts/ServiceSubscriberInterface.php', - 'symfony\\contracts\\service\\servicesubscribertrait' => '/symfony/service-contracts/ServiceSubscriberTrait.php', - 'symfony\\contracts\\service\\test\\servicelocatortest' => '/symfony/service-contracts/Test/ServiceLocatorTest.php', - 'symfony\\polyfill\\ctype\\ctype' => '/symfony/polyfill-ctype/Ctype.php', - 'symfony\\polyfill\\mbstring\\mbstring' => '/symfony/polyfill-mbstring/Mbstring.php', - 'symfony\\polyfill\\php73\\php73' => '/symfony/polyfill-php73/Php73.php', - 'text_template' => '/phpunit/php-text-template/src/Template.php', - 'theseer\\fdom\\css\\dollarequalrule' => '/theseer-fdomdocument/css/DollarEqualRule.php', - 'theseer\\fdom\\css\\notrule' => '/theseer-fdomdocument/css/NotRule.php', - 'theseer\\fdom\\css\\nthchildrule' => '/theseer-fdomdocument/css/NthChildRule.php', - 'theseer\\fdom\\css\\regexrule' => '/theseer-fdomdocument/css/RegexRule.php', - 'theseer\\fdom\\css\\ruleinterface' => '/theseer-fdomdocument/css/RuleInterface.php', - 'theseer\\fdom\\css\\translator' => '/theseer-fdomdocument/css/Translator.php', - 'theseer\\fdom\\fdomdocument' => '/theseer-fdomdocument/fDOMDocument.php', - 'theseer\\fdom\\fdomdocumentfragment' => '/theseer-fdomdocument/fDOMDocumentFragment.php', - 'theseer\\fdom\\fdomelement' => '/theseer-fdomdocument/fDOMElement.php', - 'theseer\\fdom\\fdomexception' => '/theseer-fdomdocument/fDOMException.php', - 'theseer\\fdom\\fdomnode' => '/theseer-fdomdocument/fDOMNode.php', - 'theseer\\fdom\\fdomxpath' => '/theseer-fdomdocument/fDOMXPath.php', - 'theseer\\fdom\\xpathquery' => '/theseer-fdomdocument/XPathQuery.php', - 'theseer\\fdom\\xpathqueryexception' => '/theseer-fdomdocument/XPathQueryException.php', - 'theseer\\tokenizer\\exception' => '/theseer-tokenizer/Exception.php', - 'theseer\\tokenizer\\namespaceuri' => '/theseer-tokenizer/NamespaceUri.php', - 'theseer\\tokenizer\\namespaceuriexception' => '/theseer-tokenizer/NamespaceUriException.php', - 'theseer\\tokenizer\\token' => '/theseer-tokenizer/Token.php', - 'theseer\\tokenizer\\tokencollection' => '/theseer-tokenizer/TokenCollection.php', - 'theseer\\tokenizer\\tokencollectionexception' => '/theseer-tokenizer/TokenCollectionException.php', - 'theseer\\tokenizer\\tokenizer' => '/theseer-tokenizer/Tokenizer.php', - 'theseer\\tokenizer\\xmlserializer' => '/theseer-tokenizer/XMLSerializer.php' + 'theseer\\tokenizer\\exception' => '/theseer/tokenizer/src/Exception.php', + 'theseer\\tokenizer\\namespaceuri' => '/theseer/tokenizer/src/NamespaceUri.php', + 'theseer\\tokenizer\\namespaceuriexception' => '/theseer/tokenizer/src/NamespaceUriException.php', + 'theseer\\tokenizer\\token' => '/theseer/tokenizer/src/Token.php', + 'theseer\\tokenizer\\tokencollection' => '/theseer/tokenizer/src/TokenCollection.php', + 'theseer\\tokenizer\\tokencollectionexception' => '/theseer/tokenizer/src/TokenCollectionException.php', + 'theseer\\tokenizer\\tokenizer' => '/theseer/tokenizer/src/Tokenizer.php', + 'theseer\\tokenizer\\xmlserializer' => '/theseer/tokenizer/src/XMLSerializer.php' ); } $class = strtolower($class); if (isset($classes[$class])) { - require 'phar://phpcov-6.0.1.phar' . $classes[$class]; + require 'phar://phpcov-8.2.1.phar' . $classes[$class]; } } ); -Phar::mapPhar('phpcov-6.0.1.phar'); +Phar::mapPhar('phpcov-8.2.1.phar'); if (isset($_SERVER['argv'][1]) && $_SERVER['argv'][1] == '--manifest') { print file_get_contents(__PHPCOV_PHAR_ROOT__ . '/manifest.txt'); exit; } -$application = new SebastianBergmann\PHPCOV\Application; -$application->run(); +exit((new SebastianBergmann\PHPCOV\Application)->run($_SERVER['argv'])); __HALT_COMPILER(); ?> -phpcov-6.0.1.pharsrc/BaseCommand.php"].src/PatchCoverage.php "] csrc/Application.phpi"]iW{src/PatchCoverageCommand.php "] &37'src/ExecuteCommand.php"]#|src/MergeCommand.php "] +UyT manifest.txt["][|1<%theseer-tokenizer/TokenCollection.phpr -"]r -g9+theseer-tokenizer/NamespaceUriException.phpr"]r+Atheseer-tokenizer/Exception.phpg"]gլX"theseer-tokenizer/NamespaceUri.phpw"]w 'U#theseer-tokenizer/XMLSerializer.php "] (lٶ.theseer-tokenizer/TokenCollectionException.phpu"]uBKtheseer-tokenizer/Tokenizer.php3"]3atheseer-tokenizer/Token.phpq"]q?*phpunit/php-text-template/src/Template.php "] w4phpunit/php-timer/src/Timer.php "] :(#phpunit/php-timer/src/Exception.phpC"]C/u*phpunit/php-timer/src/RuntimeException.php{"]{%)phpunit/php-file-iterator/src/Factory.php"]l Ҷ*phpunit/php-file-iterator/src/Iterator.php "] %޶(phpunit/php-file-iterator/src/Facade.php "] k*̶+phpunit/php-token-stream/src/Token/Util.php"]T~Z˶<phpunit/php-token-stream/src/Token/Stream/CachingFactory.php"] \h-phpunit/php-token-stream/src/Token/Stream.php?"]?Qi&phpunit/php-token-stream/src/Token.phpa"]aP<.phpunit/php-code-coverage/src/CodeCoverage.phpr"]r8i)phpunit/php-code-coverage/src/Version.php"]OhU&phpunit/php-code-coverage/src/Util.phpf"]fQ9r3phpunit/php-code-coverage/src/Node/AbstractNode.phpN"]NH ʶ/phpunit/php-code-coverage/src/Node/Iterator.php:"]:_c.phpunit/php-code-coverage/src/Node/Builder.php"]a6Ҷ0phpunit/php-code-coverage/src/Node/Directory.phpq$"]q$nr)ö+phpunit/php-code-coverage/src/Node/File.php6@"]6@dö4phpunit/php-code-coverage/src/Report/Xml/Project.php= "]= \и3phpunit/php-code-coverage/src/Report/Xml/Source.php"]v]3phpunit/php-code-coverage/src/Report/Xml/Report.php "] ۈ|1phpunit/php-code-coverage/src/Report/Xml/Unit.php -"] -mlض1phpunit/php-code-coverage/src/Report/Xml/Node.phpf"]faD6phpunit/php-code-coverage/src/Report/Xml/Directory.phpp"]p h˶5phpunit/php-code-coverage/src/Report/Xml/Coverage.php"]*'mc3phpunit/php-code-coverage/src/Report/Xml/Method.php"]312phpunit/php-code-coverage/src/Report/Xml/Tests.php"]Ql3phpunit/php-code-coverage/src/Report/Xml/Facade.php "] )"3phpunit/php-code-coverage/src/Report/Xml/Totals.php0"]0Zh=phpunit/php-code-coverage/src/Report/Xml/BuildInformation.php "] X1phpunit/php-code-coverage/src/Report/Xml/File.php"]-ö6phpunit/php-code-coverage/src/Report/Html/Renderer.php!"]!\x?@phpunit/php-code-coverage/src/Report/Html/Renderer/Directory.php "] c~@phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.phpp%"]p%)Jphpunit/php-code-coverage/src/Report/Html/Renderer/Template/file.html.dist -"] -f "Tphpunit/php-code-coverage/src/Report/Html/Renderer/Template/directory_item.html.distA"]AdsQphpunit/php-code-coverage/src/Report/Html/Renderer/Template/method_item.html.dist"]s:Ophpunit/php-code-coverage/src/Report/Html/Renderer/Template/icons/file-code.svg0"]0QUUTphpunit/php-code-coverage/src/Report/Html/Renderer/Template/icons/file-directory.svg"]ZOphpunit/php-code-coverage/src/Report/Html/Renderer/Template/dashboard.html.distG"]GlRphpunit/php-code-coverage/src/Report/Html/Renderer/Template/coverage_bar.html.dist'"]'O}Ophpunit/php-code-coverage/src/Report/Html/Renderer/Template/file_item.html.distt"]tLphpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/octicons.cssX"]X'#Jphpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/custom.css"]Mphpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/nv.d3.min.cssX%"]X%0,Iphpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/style.css"]vQphpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/bootstrap.min.cssn`"]n`ӶOphpunit/php-code-coverage/src/Report/Html/Renderer/Template/directory.html.dist"]GMOphpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/bootstrap.min.js"]'^Lphpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/jquery.min.jsQX"]QXQ[Kphpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/nv.d3.min.jsR"]RܶOphpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.phpM"]M)5phpunit/php-code-coverage/src/Exception/Exception.php"]~Dphpunit/php-code-coverage/src/Exception/InvalidArgumentException.php"]eUuLphpunit/php-code-coverage/src/Exception/MissingCoversAnnotationException.php"]ʡKphpunit/php-code-coverage/src/Exception/CoveredCodeNotExecutedException.php"]X <phpunit/php-code-coverage/src/Exception/RuntimeException.php"]bPD/phpunit/php-code-coverage/src/Driver/Xdebug.php -"] -&" -phpunit/php-code-coverage/src/Driver/PCOV.php"]N=/phpunit/php-code-coverage/src/Driver/Driver.php"]N/phpunit/php-code-coverage/src/Driver/PHPDBG.php -"] -d(phpunit/php-code-coverage/src/Filter.phpE"]ER})phpunit/phpunit/src/TextUI/TestRunner.php"]e8#phpunit/phpunit/src/TextUI/Help.php0"]0U&phpunit/phpunit/src/TextUI/Command.php]"]]jf@(phpunit/phpunit/src/TextUI/Exception.php"]۶,phpunit/phpunit/src/TextUI/ResultPrinter.php3;"]3;ϐ !phpunit/phpunit/src/Exception.php"]Q.phpunit/phpunit/src/Util/RegularExpression.phpD"]Dnp[!phpunit/phpunit/src/Util/Test.phpv"]v(phpunit/phpunit/src/Util/GlobalState.phpJ"]J`:U6phpunit/phpunit/src/Util/TestDox/TextResultPrinter.php$"]$zi>3phpunit/phpunit/src/Util/TestDox/TestDoxPrinter.php)"])H -Ŷ6phpunit/phpunit/src/Util/TestDox/HtmlResultPrinter.php -"] -w6phpunit/phpunit/src/Util/TestDox/CliTestDoxPrinter.phpD*"]D*7|3phpunit/phpunit/src/Util/TestDox/NamePrettifier.php "] Xѩ5phpunit/phpunit/src/Util/TestDox/XmlResultPrinter.php"]1Ѷ2phpunit/phpunit/src/Util/TestDox/ResultPrinter.php"]Hw'phpunit/phpunit/src/Util/Filesystem.php"]~F$phpunit/phpunit/src/Util/Printer.php "] ̶'phpunit/phpunit/src/Util/FileLoader.php- "]- z0phpunit/phpunit/src/Util/Annotation/DocBlock.phphK"]hK-j;0phpunit/phpunit/src/Util/Annotation/Registry.phpt "]t [|2"phpunit/phpunit/src/Util/Color.php"] phpunit/phpunit/src/Util/Xml.php!"]!xӶ8phpunit/phpunit/src/Util/XdebugFilterScriptGenerator.php"]»P&phpunit/phpunit/src/Util/Log/JUnit.php,"],D()phpunit/phpunit/src/Util/Log/TeamCity.php("](8*ڶ!phpunit/phpunit/src/Util/Json.php" -"]" -. y&phpunit/phpunit/src/Util/Exception.php"]#!>3phpunit/phpunit/src/Util/PHP/AbstractPhpProcess.php("](DA2phpunit/phpunit/src/Util/PHP/WindowsPhpProcess.phpt"]t2K+phpunit/phpunit/src/Util/PHP/eval-stdin.php-"]--rX2phpunit/phpunit/src/Util/PHP/DefaultPhpProcess.php"]f¶*phpunit/phpunit/src/Util/Configuration.php6"]6pX3phpunit/phpunit/src/Util/ConfigurationGenerator.phpe"]e!phpunit/phpunit/src/Util/Type.php"]z#M#phpunit/phpunit/src/Util/Getopt.php"]g)phpunit/phpunit/src/Util/ErrorHandler.php"]8Y#0phpunit/phpunit/src/Util/XmlTestListRenderer.php% -"]% -`B1phpunit/phpunit/src/Util/TextTestListRenderer.php@"]@7&phpunit/phpunit/src/Util/Blacklist.php"]F%#phpunit/phpunit/src/Util/Filter.phph -"]h -O &phpunit/phpunit/src/Runner/Version.php"]I~9-phpunit/phpunit/src/Runner/BaseTestRunner.phpP"]P!O5phpunit/phpunit/src/Runner/DefaultTestResultCache.php"]?]׶-phpunit/phpunit/src/Runner/Filter/Factory.phpQ"]QW@phpunit/phpunit/src/Runner/Filter/ExcludeGroupFilterIterator.phpB"]Bċ8phpunit/phpunit/src/Runner/Filter/NameFilterIterator.phpl "]l ; -h9phpunit/phpunit/src/Runner/Filter/GroupFilterIterator.php"]/@phpunit/phpunit/src/Runner/Filter/IncludeGroupFilterIterator.phpA"]Aa(phpunit/phpunit/src/Runner/Exception.php"]m]3phpunit/phpunit/src/Runner/ResultCacheExtension.php* "]* .phpunit/phpunit/src/Runner/TestResultCache.php"]Y)ɶ.phpunit/phpunit/src/Runner/TestSuiteSorter.phpq3"]q3g58phpunit/phpunit/src/Runner/Hook/AfterTestWarningHook.php"]ֆ8phpunit/phpunit/src/Runner/Hook/AfterSkippedTestHook.php"]J (phpunit/phpunit/src/Runner/Hook/Hook.php+"]+kXM,phpunit/phpunit/src/Runner/Hook/TestHook.php<"]<xᤸ2phpunit/phpunit/src/Runner/Hook/BeforeTestHook.php"]zݶ6phpunit/phpunit/src/Runner/Hook/AfterTestErrorHook.php"]LXǶ7phpunit/phpunit/src/Runner/Hook/TestListenerAdapter.php"]>P7phpunit/phpunit/src/Runner/Hook/BeforeFirstTestHook.php{"]{.6phpunit/phpunit/src/Runner/Hook/AfterRiskyTestHook.php"]jYM5phpunit/phpunit/src/Runner/Hook/AfterLastTestHook.phpw"]w~F1phpunit/phpunit/src/Runner/Hook/AfterTestHook.phpU"]UfTx8phpunit/phpunit/src/Runner/Hook/AfterTestFailureHook.php"]zc;phpunit/phpunit/src/Runner/Hook/AfterSuccessfulTestHook.php"]j3;phpunit/phpunit/src/Runner/Hook/AfterIncompleteTestHook.php"]*K2phpunit/phpunit/src/Runner/NullTestResultCache.phpv"]vr.phpunit/phpunit/src/Runner/TestSuiteLoader.php8"]8+$˶6phpunit/phpunit/src/Runner/StandardTestSuiteLoader.php"]T*8+phpunit/phpunit/src/Runner/PhptTestCase.php=Q"]=QοW+phpunit/phpunit/src/Framework/TestSuite.phpS"]Sd"&phpunit/phpunit/src/Framework/Test.php"]8,phpunit/phpunit/src/Framework/TestResult.php{"]{&1phpunit/phpunit/src/Framework/SkippedTestCase.php"]-phpunit/phpunit/src/Framework/TestBuilder.php4"]4Tl(phpunit/phpunit/src/Framework/Assert.php["][kZ4phpunit/phpunit/src/Framework/IncompleteTestCase.php"])HM7phpunit/phpunit/src/Framework/DataProviderTestSuite.php"]sə$.phpunit/phpunit/src/Framework/TestListener.phpm"]m41NqAphpunit/phpunit/src/Framework/Exception/SkippedTestSuiteError.php"]f@phpunit/phpunit/src/Framework/Exception/AssertionFailedError.phpj"]jZY:phpunit/phpunit/src/Framework/Exception/RiskyTestError.php"]9d:phpunit/phpunit/src/Framework/Exception/SyntheticError.php&"]&iٶAphpunit/phpunit/src/Framework/Exception/CodeCoverageException.php"]<߆]7phpunit/phpunit/src/Framework/Exception/OutputError.php"]:t5phpunit/phpunit/src/Framework/Exception/Exception.phpD "]D |h`P<phpunit/phpunit/src/Framework/Exception/SkippedTestError.php"]=@Dphpunit/phpunit/src/Framework/Exception/PHPTAssertionFailedError.php "] >GDphpunit/phpunit/src/Framework/Exception/InvalidArgumentException.php,"],ԶDphpunit/phpunit/src/Framework/Exception/UnexpectedValueException.phpV"]V{Kphpunit/phpunit/src/Framework/Exception/UnintentionallyCoveredCodeError.php"]it?phpunit/phpunit/src/Framework/Exception/IncompleteTestError.php"])RLphpunit/phpunit/src/Framework/Exception/MissingCoversAnnotationException.php"]1ȶAphpunit/phpunit/src/Framework/Exception/SyntheticSkippedError.php"]ӏHphpunit/phpunit/src/Framework/Exception/InvalidCoversTargetException.php"]UKphpunit/phpunit/src/Framework/Exception/CoveredCodeNotExecutedException.php"]pFphpunit/phpunit/src/Framework/Exception/ExpectationFailedException.php"]ݘ3phpunit/phpunit/src/Framework/Exception/Warning.phpc"]c JHphpunit/phpunit/src/Framework/Exception/InvalidDataProviderException.php"]R6phpunit/phpunit/src/Framework/MockObject/Generator.php%~"]%~I5phpunit/phpunit/src/Framework/MockObject/MockType.php"]'58phpunit/phpunit/src/Framework/MockObject/MockBuilder.php+"]+wT7phpunit/phpunit/src/Framework/MockObject/MockObject.php -"] -r7phpunit/phpunit/src/Framework/MockObject/Api/Method.php"]8qͶDphpunit/phpunit/src/Framework/MockObject/Api/UnmockedCloneMethod.phpF"]F!O 4phpunit/phpunit/src/Framework/MockObject/Api/Api.php "] N쟶Bphpunit/phpunit/src/Framework/MockObject/Api/MockedCloneMethod.php("](ψꎶ>phpunit/phpunit/src/Framework/MockObject/InvocationHandler.php"]زu%Wphpunit/phpunit/src/Framework/MockObject/Exception/IncompatibleReturnValueException.php"]tMphpunit/phpunit/src/Framework/MockObject/Exception/BadMethodCallException.php"]iephpunit/phpunit/src/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php"]}-@phpunit/phpunit/src/Framework/MockObject/Exception/Exception.php"]濷Gphpunit/phpunit/src/Framework/MockObject/Exception/RuntimeException.php"]"bB7phpunit/phpunit/src/Framework/MockObject/Verifiable.php"]/v4phpunit/phpunit/src/Framework/MockObject/Matcher.php#"]#آAphpunit/phpunit/src/Framework/MockObject/MethodNameConstraint.php"]Ih@phpunit/phpunit/src/Framework/MockObject/Stub/ReturnValueMap.php"]Tw rBphpunit/phpunit/src/Framework/MockObject/Stub/ConsecutiveCalls.php"]emDŶ;phpunit/phpunit/src/Framework/MockObject/Stub/Exception.php"]JF6phpunit/phpunit/src/Framework/MockObject/Stub/Stub.php2"]27cAphpunit/phpunit/src/Framework/MockObject/Stub/ReturnReference.php"]G@phpunit/phpunit/src/Framework/MockObject/Stub/ReturnCallback.php"] -Ū@phpunit/phpunit/src/Framework/MockObject/Stub/ReturnArgument.php"] -<phpunit/phpunit/src/Framework/MockObject/Stub/ReturnSelf.php"] <phpunit/phpunit/src/Framework/MockObject/Stub/ReturnStub.php"]D1phpunit/phpunit/src/Framework/MockObject/Stub.php"]Pl?6phpunit/phpunit/src/Framework/MockObject/MockClass.phpy"]y67phpunit/phpunit/src/Framework/MockObject/Invocation.php"]#n9{?phpunit/phpunit/src/Framework/MockObject/ConfigurableMethod.php"] Aphpunit/phpunit/src/Framework/MockObject/Rule/AnyInvokedCount.phpD"]DMDphpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtLeastOnce.php&"]&WY<phpunit/phpunit/src/Framework/MockObject/Rule/Parameters.php"])3Ephpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtLeastCount.php"]@phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtIndex.php"]J@phpunit/phpunit/src/Framework/MockObject/Rule/ParametersRule.php"]B?phpunit/phpunit/src/Framework/MockObject/Rule/AnyParameters.php"];>phpunit/phpunit/src/Framework/MockObject/Rule/InvokedCount.php< -"]< -]i<phpunit/phpunit/src/Framework/MockObject/Rule/MethodName.php"]R -ĶAphpunit/phpunit/src/Framework/MockObject/Rule/InvocationOrder.php"]Dphpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtMostCount.php"]lGphpunit/phpunit/src/Framework/MockObject/Rule/ConsecutiveParameters.php'"]';Æ=phpunit/phpunit/src/Framework/MockObject/Builder/Identity.php"]}"9phpunit/phpunit/src/Framework/MockObject/Builder/Stub.php"]? :phpunit/phpunit/src/Framework/MockObject/Builder/Match.php"]EDphpunit/phpunit/src/Framework/MockObject/Builder/ParametersMatch.php"]+}Ephpunit/phpunit/src/Framework/MockObject/Builder/InvocationMocker.php|"]|'Fphpunit/phpunit/src/Framework/MockObject/Builder/InvocationStubber.php"]Dphpunit/phpunit/src/Framework/MockObject/Builder/MethodNameMatch.phpC"]C}k6phpunit/phpunit/src/Framework/MockObject/MockTrait.php"]l:phpunit/phpunit/src/Framework/MockObject/MockMethodSet.php"]<)7phpunit/phpunit/src/Framework/MockObject/MockMethod.php,"],R0phpunit/phpunit/src/Framework/SelfDescribing.php"]F0phpunit/phpunit/src/Framework/IncompleteTest.php"]#9d*phpunit/phpunit/src/Framework/TestCase.php)"]);-phpunit/phpunit/src/Framework/SkippedTest.php"])ً2phpunit/phpunit/src/Framework/ExceptionWrapper.php: "]: \FCphpunit/phpunit/src/Framework/TestListenerDefaultImplementation.php"]T]ꙶ7phpunit/phpunit/src/Framework/Constraint/LogicalXor.php@ "]@ 3n>phpunit/phpunit/src/Framework/Constraint/RegularExpression.php5"]5FgC4phpunit/phpunit/src/Framework/Constraint/IsEqual.php"]zζLphpunit/phpunit/src/Framework/Constraint/JsonMatchesErrorMessageProvider.php@"]@ 7phpunit/phpunit/src/Framework/Constraint/Constraint.php"]o7phpunit/phpunit/src/Framework/Constraint/IsInfinite.php>"]>;phpunit/phpunit/src/Framework/Constraint/StringContains.php"]N0T6phpunit/phpunit/src/Framework/Constraint/LogicalOr.php -"] -"0l5phpunit/phpunit/src/Framework/Constraint/Callback.php"]8phpunit/phpunit/src/Framework/Constraint/JsonMatches.php: "]: \F3phpunit/phpunit/src/Framework/Constraint/IsTrue.php-"]-2Đ7phpunit/phpunit/src/Framework/Constraint/LogicalNot.php"]?ʶ:phpunit/phpunit/src/Framework/Constraint/ExceptionCode.php;"];9phpunit/phpunit/src/Framework/Constraint/IsInstanceOf.php"] ]83phpunit/phpunit/src/Framework/Constraint/IsJson.php "] g8phpunit/phpunit/src/Framework/Constraint/ArrayHasKey.php"]~y;phpunit/phpunit/src/Framework/Constraint/StringEndsWith.phpO"]O -8phpunit/phpunit/src/Framework/Constraint/ArraySubset.php"]k3phpunit/phpunit/src/Framework/Constraint/IsNull.php-"]-ɭw6phpunit/phpunit/src/Framework/Constraint/Exception.php"]h_8phpunit/phpunit/src/Framework/Constraint/GreaterThan.php"]Ƕ5phpunit/phpunit/src/Framework/Constraint/LessThan.php"] -47phpunit/phpunit/src/Framework/Constraint/FileExists.php["][zEPe5phpunit/phpunit/src/Framework/Constraint/IsFinite.php6"]6{0jR8phpunit/phpunit/src/Framework/Constraint/IsIdentical.php"]㯳c4phpunit/phpunit/src/Framework/Constraint/IsEmpty.phpG"]G'YDphpunit/phpunit/src/Framework/Constraint/TraversableContainsOnly.php"]{( /=phpunit/phpunit/src/Framework/Constraint/ExceptionMessage.php6"]6 NDphpunit/phpunit/src/Framework/Constraint/ClassHasStaticAttribute.php"]?~7phpunit/phpunit/src/Framework/Constraint/IsWritable.phpd"]d_P6phpunit/phpunit/src/Framework/Constraint/Attribute.php"]?phpunit/phpunit/src/Framework/Constraint/ObjectHasAttribute.phpb"]bt`<phpunit/phpunit/src/Framework/Constraint/DirectoryExists.phpj"]j޶Kphpunit/phpunit/src/Framework/Constraint/StringMatchesFormatDescription.phpe -"]e -nG6phpunit/phpunit/src/Framework/Constraint/Composite.phpN"]N74phpunit/phpunit/src/Framework/Constraint/IsFalse.php1"]17phpunit/phpunit/src/Framework/Constraint/LogicalAnd.phpY "]Y 2/Nphpunit/phpunit/src/Framework/Constraint/ExceptionMessageRegularExpression.php]"]] 2phpunit/phpunit/src/Framework/Constraint/Count.php "] 3phpunit/phpunit/src/Framework/Constraint/IsType.php"]K@phpunit/phpunit/src/Framework/Constraint/TraversableContains.php` "]` \ m2phpunit/phpunit/src/Framework/Constraint/IsNan.php*"]*_h۶>phpunit/phpunit/src/Framework/Constraint/ClassHasAttribute.phpl"]l='7phpunit/phpunit/src/Framework/Constraint/IsReadable.phpd"]d=phpunit/phpunit/src/Framework/Constraint/StringStartsWith.php"]Fƶ5phpunit/phpunit/src/Framework/Constraint/SameSize.php"]i*7phpunit/phpunit/src/Framework/Constraint/IsAnything.php"]t-phpunit/phpunit/src/Framework/TestFailure.php "] 93phpunit/phpunit/src/Framework/TestSuiteIterator.php"]62phpunit/phpunit/src/Framework/Assert/Functions.php2"]2‹"-phpunit/phpunit/src/Framework/Error/Error.phpW"]W uͶ2phpunit/phpunit/src/Framework/Error/Deprecated.phpJ"]J.phpunit/phpunit/src/Framework/Error/Notice.phpF"]F"o/phpunit/phpunit/src/Framework/Error/Warning.phpG"]G}1phpunit/phpunit/src/Framework/WarningTestCase.phpE"]E$d@phpunit/phpunit/src/Framework/InvalidParameterGroupException.php"]}#"phpunit/phpunit/.phpstorm.meta.php"]G"theseer-fdomdocument/fDOMXPath.php"]!X̶$theseer-fdomdocument/fDOMElement.php8"]8o&theseer-fdomdocument/fDOMException.php"]dO,theseer-fdomdocument/XPathQueryException.php"]*#theseer-fdomdocument/XPathQuery.php"]߶!theseer-fdomdocument/autoload.php"]!H-theseer-fdomdocument/fDOMDocumentFragment.phpI"]I]~!theseer-fdomdocument/fDOMNode.php"]VGQ%theseer-fdomdocument/fDOMDocument.php"["]"[7h&$theseer-fdomdocument/css/NotRule.php7"]7ꙶ,theseer-fdomdocument/css/DollarEqualRule.php"]p&theseer-fdomdocument/css/RegexRule.php"]s'theseer-fdomdocument/css/Translator.php"]F]hX*theseer-fdomdocument/css/RuleInterface.php"],)theseer-fdomdocument/css/NthChildRule.php"][49symfony/polyfill-mbstring/Resources/unidata/lowerCase.phpI"]I(~9symfony/polyfill-mbstring/Resources/unidata/upperCase.phpI"]I?symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php9"]9>|zK'symfony/polyfill-mbstring/bootstrap.php"]&symfony/polyfill-mbstring/Mbstring.phpk"]k1D8symfony/service-contracts/ServiceSubscriberInterface.php"]SRܶ,symfony/service-contracts/ResetInterface.php"]v4symfony/service-contracts/ServiceSubscriberTrait.php"]1symfony/service-contracts/ServiceLocatorTrait.php "] .5symfony/service-contracts/Test/ServiceLocatorTest.phpO "]O !6symfony/service-contracts/ServiceProviderInterface.php"]mX¶7symfony/console/Formatter/OutputFormatterStyleStack.php "] ;2;symfony/console/Formatter/OutputFormatterStyleInterface.php<"]<Z?symfony/console/Formatter/WrappableOutputFormatterInterface.php"]`K>-symfony/console/Formatter/OutputFormatter.phpI"]IFG2symfony/console/Formatter/OutputFormatterStyle.php"]aԶ6symfony/console/Formatter/OutputFormatterInterface.php"]#7u(symfony/console/Input/InputInterface.php"]Έڶ'symfony/console/Input/InputArgument.phpN "]N 4t-symfony/console/Input/InputAwareInterface.php:"]: '$symfony/console/Input/ArrayInput.php"]kq>2symfony/console/Input/StreamableInputInterface.phpi"]i%symfony/console/Input/InputOption.php"]Ķ%symfony/console/Input/StringInput.phpL "]L l wsymfony/console/Input/Input.phpr"]rUjx)symfony/console/Input/InputDefinition.php+"]+Z>#symfony/console/Input/ArgvInput.php-"]-SG/symfony/console/Event/ConsoleTerminateEvent.php"]_+symfony/console/Event/ConsoleErrorEvent.php"]a-symfony/console/Event/ConsoleCommandEvent.php%"]%{˾&symfony/console/Event/ConsoleEvent.php"]xS*=symfony/console/DependencyInjection/AddConsoleCommandPass.php)"]) 8~8symfony/console/Exception/NamespaceNotFoundException.php"]BLH,symfony/console/Exception/LogicException.php"]SML6symfony/console/Exception/CommandNotFoundException.php"]N6symfony/console/Exception/InvalidArgumentException.php"]u i0symfony/console/Exception/ExceptionInterface.php"]l4symfony/console/Exception/InvalidOptionException.php"];.symfony/console/Exception/RuntimeException.php"]*b(symfony/console/Tester/CommandTester.phpg "]g x3 ,symfony/console/Tester/ApplicationTester.phpW"]W6[E&symfony/console/Tester/TesterTrait.phpk"]k1(symfony/console/Output/ConsoleOutput.php"]0%symfony/console/Output/NullOutput.phpn"]ntD!symfony/console/Output/Output.php"]\j1symfony/console/Output/ConsoleOutputInterface.php4"]4})symfony/console/Output/BufferedOutput.phpH"]H9Զ'symfony/console/Output/StreamOutput.php1"]1Fޑ/symfony/console/Output/ConsoleSectionOutput.phpD"]D0ǘ*symfony/console/Output/OutputInterface.php$ "]$ w;)symfony/console/Command/LockableTrait.php"]u#symfony/console/Command/Command.phpJ"]J#6'symfony/console/Command/ListCommand.php "] #'symfony/console/Command/HelpCommand.php? "]? '!symfony/console/ConsoleEvents.php,"],~HG$symfony/console/Helper/HelperSet.php "] 3C +symfony/console/Helper/DescriptorHelper.php "] )symfony/console/Helper/TableSeparator.php"]& %symfony/console/Helper/TableStyle.php<"]<b+symfony/console/Helper/InputAwareHelper.php"]/symfony/console/Helper/DebugFormatterHelper.phpH"]H]R`A symfony/console/Helper/Table.php"e"]"e ̶!symfony/console/Helper/Helper.php"]}Ku$symfony/console/Helper/TableCell.php"]B%*symfony/console/Helper/FormatterHelper.php "] *+!symfony/console/Helper/Dumper.php"]+'f*symfony/console/Helper/HelperInterface.phpp"]pn(symfony/console/Helper/ProcessHelper.php"]2C_,symfony/console/Helper/ProgressIndicator.php|"]|Dُж$symfony/console/Helper/TableRows.phpG"]Gy&symfony/console/Helper/ProgressBar.phpq@"]q@L2ж)symfony/console/Helper/QuestionHelper.php;"]; 0symfony/console/Helper/SymfonyQuestionHelper.php "] I靶(symfony/console/Logger/ConsoleLogger.php"]r9symfony/console/Terminal.phpW"]WEpR8symfony/console/CommandLoader/ContainerCommandLoader.php>"]>#*6symfony/console/CommandLoader/FactoryCommandLoader.php7"]7m_8symfony/console/CommandLoader/CommandLoaderInterface.php"]symfony/console/Application.phpA"]Az%symfony/console/Style/OutputStyle.php "] 1kz&symfony/console/Style/SymfonyStyle.php/"]/](symfony/console/Style/StyleInterface.php( "]( 5 /symfony/console/EventListener/ErrorListener.php -"] -e?߶-symfony/console/Descriptor/TextDescriptor.php,1"],1 g-5symfony/console/Descriptor/ApplicationDescription.phpm"]m]v2symfony/console/Descriptor/DescriptorInterface.php"]PZ)symfony/console/Descriptor/Descriptor.phpd "]d T-symfony/console/Descriptor/JsonDescriptor.php"],symfony/console/Descriptor/XmlDescriptor.php#"]#1symfony/console/Question/ConfirmationQuestion.php/"]/ƞ+symfony/console/Question/ChoiceQuestion.php"]/%symfony/console/Question/Question.php"]t.symfony/finder/Comparator/NumberComparator.php -"] -(symfony/finder/Comparator/Comparator.php"]~4ض,symfony/finder/Comparator/DateComparator.php"]is7symfony/finder/Exception/DirectoryNotFoundException.php"]RI2symfony/finder/Exception/AccessDeniedException.php"]cW޶symfony/finder/Gitignore.php{ "]{ Nmsymfony/finder/SplFileInfo.php"]L}symfony/finder/Finder.phpZ"]Z1K׶3symfony/finder/Iterator/SizeRangeFilterIterator.php"]=̶0symfony/finder/Iterator/CustomFilterIterator.php"]d)4symfony/finder/Iterator/DepthRangeFilterIterator.php"]h3symfony/finder/Iterator/DateRangeFilterIterator.php"])6symfony/finder/Iterator/MultiplePcreFilterIterator.php{ "]{ ӹ6symfony/finder/Iterator/RecursiveDirectoryIterator.php"]c.symfony/finder/Iterator/PathFilterIterator.php"]},symfony/finder/Iterator/SortableIterator.php "] 12symfony/finder/Iterator/FilenameFilterIterator.php"] p:symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php "] d!!5symfony/finder/Iterator/FilecontentFilterIterator.php"]r~2symfony/finder/Iterator/FileTypeFilterIterator.phpC"]CQNsymfony/finder/Glob.php"]{ symfony/polyfill-ctype/Ctype.php}"]}\谶$symfony/polyfill-ctype/bootstrap.phpS"]S78symfony/polyfill-php73/Resources/stubs/JsonException.php"])"]>))ɶ(sebastian/global-state/src/Blacklist.php -"] -=ŵ@#sebastian/exporter/src/Exporter.phpM#"]M#ғ2sebastian/object-reflector/src/ObjectReflector.php"] ",sebastian/object-reflector/src/Exception.phpN"]N ^;sebastian/object-reflector/src/InvalidArgumentException.php"]YJ!sebastian/type/src/ObjectType.php"]Ř1߶(sebastian/type/src/GenericObjectType.php"]"sebastian/type/src/UnknownType.phpE"]E sebastian/type/src/Type.php"]k5sebastian/type/src/NullType.php["][9osebastian/type/src/TypeName.php"]N>#sebastian/type/src/IterableType.phpQ"]Qq!sebastian/type/src/SimpleType.php"]g #sebastian/type/src/CallableType.php"]Ί]sebastian/type/src/VoidType.php["][)T!sebastian/version/src/Version.php"]N\Ƕ%sebastian/environment/src/Console.php"]Eܶ-sebastian/environment/src/OperatingSystem.php"]`%sebastian/environment/src/Runtime.phpd"]dHʱ-sebastian/recursion-context/src/Exception.phpJ"]J<sebastian/recursion-context/src/InvalidArgumentException.php"]mH+sebastian/recursion-context/src/Context.php{"]{-sebastian/object-enumerator/src/Exception.php6"]6n$*a<sebastian/object-enumerator/src/InvalidArgumentException.phpx"]x'.sebastian/object-enumerator/src/Enumerator.phpr"]rz\ - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\PHPCOV; - -use PHPUnit\Util\Configuration; -use SebastianBergmann\CodeCoverage\CodeCoverage; -use SebastianBergmann\CodeCoverage\Report\Clover as CloverReport; -use SebastianBergmann\CodeCoverage\Report\Crap4j as Crap4jReport; -use SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlReport; -use SebastianBergmann\CodeCoverage\Report\PHP as PhpReport; -use SebastianBergmann\CodeCoverage\Report\Text as TextReport; -use SebastianBergmann\CodeCoverage\Report\Xml\Facade as XmlReport; -use Symfony\Component\Console\Command\Command as AbstractCommand; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; - -abstract class BaseCommand extends AbstractCommand -{ - protected function handleConfiguration(CodeCoverage $coverage, InputInterface $input): void - { - $configuration = $input->getOption('configuration'); - - if (!$configuration) { - return; - } - - $filter = $coverage->filter(); - $configuration = Configuration::getInstance($configuration); - - $filterConfiguration = $configuration->getFilterConfiguration(); - - $coverage->setAddUncoveredFilesFromWhitelist( - $filterConfiguration['whitelist']['addUncoveredFilesFromWhitelist'] - ); - - $coverage->setProcessUncoveredFilesFromWhitelist( - $filterConfiguration['whitelist']['processUncoveredFilesFromWhitelist'] - ); - - foreach ($filterConfiguration['whitelist']['include']['directory'] as $dir) { - $filter->addDirectoryToWhitelist( - $dir['path'], - $dir['suffix'], - $dir['prefix'] - ); - } - - foreach ($filterConfiguration['whitelist']['include']['file'] as $file) { - $filter->addFileToWhitelist($file); - } - - foreach ($filterConfiguration['whitelist']['exclude']['directory'] as $dir) { - $filter->removeDirectoryFromWhitelist( - $dir['path'], - $dir['suffix'], - $dir['prefix'] - ); - } - - foreach ($filterConfiguration['whitelist']['exclude']['file'] as $file) { - $filter->removeFileFromWhitelist($file); - } - } - - protected function handleFilter(CodeCoverage $coverage, InputInterface $input): void - { - $filter = $coverage->filter(); - - $coverage->setAddUncoveredFilesFromWhitelist( - $input->getOption('add-uncovered') - ); - - $coverage->setProcessUncoveredFilesFromWhitelist( - $input->getOption('process-uncovered') - ); - - foreach ($input->getOption('whitelist') as $item) { - if (\is_dir($item)) { - $filter->addDirectoryToWhitelist($item); - } elseif (\is_file($item)) { - $filter->addFileToWhitelist($item); - } - } - } - - protected function handleReports(CodeCoverage $coverage, InputInterface $input, OutputInterface $output): void - { - if ($input->getOption('clover')) { - $output->write("\nGenerating code coverage report in Clover XML format ..."); - - $writer = new CloverReport; - $writer->process($coverage, $input->getOption('clover')); - - $output->write(" done\n"); - } - - if ($input->getOption('crap4j')) { - $output->write("\nGenerating code coverage report in Crap4J XML format..."); - - $writer = new Crap4jReport; - $writer->process($coverage, $input->getOption('crap4j')); - - $output->write(" done\n"); - } - - if ($input->getOption('html')) { - $output->write("\nGenerating code coverage report in HTML format ..."); - - $writer = new HtmlReport; - $writer->process($coverage, $input->getOption('html')); - - $output->write(" done\n"); - } - - if ($input->getOption('php')) { - $output->write("\nGenerating code coverage report in PHP format ..."); - - $writer = new PhpReport; - $writer->process($coverage, $input->getOption('php')); - - $output->write(" done\n"); - } - - if ($input->getOption('text')) { - $report = new TextReport; - - $color = false; - - if ($input->getOption('ansi')) { - $color = true; - } - - $output->write($report->process($coverage, $color)); - } - - if ($input->getOption('xml')) { - $output->write( - "\nGenerating code coverage report in PHP format ..." - ); - - $writer = new XmlReport('unknown'); - $writer->process($coverage, $input->getOption('xml')); - - $output->write(" done\n"); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\PHPCOV; - -use SebastianBergmann\Diff\Line; -use SebastianBergmann\Diff\Parser as DiffParser; - -class PatchCoverage -{ - /** - * @param string $coverage - * @param string $patch - * @param string $prefix - * - * @return array - */ - public function execute($coverage, $patch, $prefix) - { - $result = [ - 'numChangedLinesThatAreExecutable' => 0, - 'numChangedLinesThatWereExecuted' => 0, - 'changedLinesThatWereNotExecuted' => [], - ]; - - if (\substr($prefix, -1, 1) != \DIRECTORY_SEPARATOR) { - $prefix .= \DIRECTORY_SEPARATOR; - } - - $coverage = include($coverage); - $coverage = $coverage->getData(); - $parser = new DiffParser; - $patch = $parser->parse(\file_get_contents($patch)); - $changes = []; - - foreach ($patch as $diff) { - $file = \substr($diff->getTo(), 2); - $changes[$file] = []; - - foreach ($diff->getChunks() as $chunk) { - $lineNr = $chunk->getEnd(); - - foreach ($chunk->getLines() as $line) { - if ($line->getType() == Line::ADDED) { - $changes[$file][] = $lineNr; - } - - if ($line->getType() != Line::REMOVED) { - $lineNr++; - } - } - } - } - - foreach ($changes as $file => $lines) { - $key = $prefix . $file; - - foreach ($lines as $line) { - if (isset($coverage[$key][$line]) && - \is_array($coverage[$key][$line])) { - $result['numChangedLinesThatAreExecutable']++; - - if (empty($coverage[$key][$line])) { - if (!isset($result['changedLinesThatWereNotExecuted'][$file])) { - $result['changedLinesThatWereNotExecuted'][$file] = []; - } - - $result['changedLinesThatWereNotExecuted'][$file][] = $line; - } else { - $result['numChangedLinesThatWereExecuted']++; - } - } - } - } - - return $result; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\PHPCOV; - -use SebastianBergmann\Version; -use Symfony\Component\Console\Application as AbstractApplication; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * TextUI frontend for php-code-coverage. - */ -class Application extends AbstractApplication -{ - public function __construct() - { - $version = new Version('6.0.1', \dirname(__DIR__)); - parent::__construct('phpcov', $version->getVersion()); - - $this->add(new ExecuteCommand); - $this->add(new MergeCommand); - $this->add(new PatchCoverageCommand); - } - - /** - * Runs the current application. - * - * @param InputInterface $input An Input instance - * @param OutputInterface $output An Output instance - * - * @return int 0 if everything went fine, or an error code - */ - public function doRun(InputInterface $input, OutputInterface $output) - { - if (!$input->hasParameterOption('--quiet')) { - $output->write( - \sprintf( - "phpcov %s by Sebastian Bergmann.\n\n", - $this->getVersion() - ) - ); - } - - if ($input->hasParameterOption('--version') || - $input->hasParameterOption('-V')) { - exit; - } - - parent::doRun($input, $output); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\PHPCOV; - -use SebastianBergmann\CodeCoverage\Util; -use Symfony\Component\Console\Command\Command as AbstractCommand; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; - -class PatchCoverageCommand extends AbstractCommand -{ - /** - * Configures the current command. - */ - protected function configure(): void - { - $this->setName('patch-coverage') - ->addArgument( - 'coverage', - InputArgument::REQUIRED, - 'Exported code coverage object' - ) - ->addOption( - 'patch', - null, - InputOption::VALUE_REQUIRED, - 'Unified diff to be analysed for patch coverage' - ) - ->addOption( - 'path-prefix', - null, - InputOption::VALUE_REQUIRED, - 'Prefix that needs to be stripped from paths in the diff' - ); - } - - /** - * Executes the current command. - * - * @param InputInterface $input An InputInterface instance - * @param OutputInterface $output An OutputInterface instance - * - * @return null|int null or 0 if everything went fine, or an error code - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $pc = new PatchCoverage; - $pc = $pc->execute( - $input->getArgument('coverage'), - $input->getOption('patch'), - $input->getOption('path-prefix') - ); - - $output->writeln( - \sprintf( - '%d / %d changed executable lines covered (%s)', - $pc['numChangedLinesThatWereExecuted'], - $pc['numChangedLinesThatAreExecutable'], - Util::percent( - $pc['numChangedLinesThatWereExecuted'], - $pc['numChangedLinesThatAreExecutable'], - true - ) - ) - ); - - if (!empty($pc['changedLinesThatWereNotExecuted'])) { - $output->writeln("\nChanged executable lines that are not covered:\n"); - - foreach ($pc['changedLinesThatWereNotExecuted'] as $file => $lines) { - foreach ($lines as $line) { - $output->writeln( - \sprintf( - ' %s:%d', - $file, - $line - ) - ); - } - } - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\PHPCOV; - -use SebastianBergmann\CodeCoverage\CodeCoverage; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; - -class ExecuteCommand extends BaseCommand -{ - /** - * Configures the current command. - */ - protected function configure(): void - { - $this->setName('execute') - ->addArgument( - 'script', - InputArgument::REQUIRED, - 'Script to execute' - ) - ->addOption( - 'configuration', - null, - InputOption::VALUE_REQUIRED, - 'Read configuration from XML file' - ) - ->addOption( - 'whitelist', - null, - InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, - 'Add directory or file to the whitelist' - ) - ->addOption( - 'add-uncovered', - null, - InputOption::VALUE_NONE, - 'Add whitelisted files that are not covered' - ) - ->addOption( - 'process-uncovered', - null, - InputOption::VALUE_NONE, - 'Process whitelisted files that are not covered' - ) - ->addOption( - 'clover', - null, - InputOption::VALUE_REQUIRED, - 'Generate code coverage report in Clover XML format' - ) - ->addOption( - 'crap4j', - null, - InputOption::VALUE_REQUIRED, - 'Generate code coverage report in Crap4J XML format' - ) - ->addOption( - 'html', - null, - InputOption::VALUE_REQUIRED, - 'Generate code coverage report in HTML format' - ) - ->addOption( - 'php', - null, - InputOption::VALUE_REQUIRED, - 'Export code coverage object to file' - ) - ->addOption( - 'text', - null, - InputOption::VALUE_NONE, - 'Write code coverage report in text format to STDOUT' - ) - ->addOption( - 'xml', - null, - InputOption::VALUE_REQUIRED, - 'Generate code coverage report in PHPUnit XML format' - ); - } - - /** - * Executes the current command. - * - * @param InputInterface $input An InputInterface instance - * @param OutputInterface $output An OutputInterface instance - * - * @return null|int null or 0 if everything went fine, or an error code - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $coverage = new CodeCoverage; - - $this->handleConfiguration($coverage, $input); - $this->handleFilter($coverage, $input); - - $coverage->start('phpcov'); - - require $input->getArgument('script'); - - $coverage->stop(); - - $this->handleReports($coverage, $input, $output); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\PHPCOV; - -use SebastianBergmann\CodeCoverage\CodeCoverage; -use SebastianBergmann\FinderFacade\FinderFacade; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; - -class MergeCommand extends BaseCommand -{ - /** - * @var string[] - */ - protected $mergeErrors; - - /** - * Configures the current command. - */ - protected function configure(): void - { - $this->setName('merge') - ->addArgument( - 'directory', - InputArgument::REQUIRED, - 'Directory to scan for exported code coverage objects stored in .cov files' - ) - ->addOption( - 'clover', - null, - InputOption::VALUE_REQUIRED, - 'Generate code coverage report in Clover XML format' - ) - ->addOption( - 'crap4j', - null, - InputOption::VALUE_REQUIRED, - 'Generate code coverage report in Crap4J XML format' - ) - ->addOption( - 'html', - null, - InputOption::VALUE_REQUIRED, - 'Generate code coverage report in HTML format' - ) - ->addOption( - 'php', - null, - InputOption::VALUE_REQUIRED, - 'Export code coverage object to file' - ) - ->addOption( - 'text', - null, - InputOption::VALUE_NONE, - 'Generate code coverage report in text format to STDOUT' - ) - ->addOption( - 'xml', - null, - InputOption::VALUE_REQUIRED, - 'Generate code coverage report in PHPUnit XML format' - ); - } - - /** - * Executes the current command. - * - * @param InputInterface $input An InputInterface instance - * @param OutputInterface $output An OutputInterface instance - * - * @return null|int null or 0 if everything went fine, or an error code - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $mergedCoverage = new CodeCoverage; - - $finder = new FinderFacade( - [$input->getArgument('directory')], - [], - ['*.cov'] - ); - - foreach ($finder->findFiles() as $file) { - $_coverage = include($file); - - if (!($_coverage instanceof CodeCoverage)) { - $this->mergeErrors[] = $file; - unset($_coverage); - - continue; - } - - $mergedCoverage->merge($_coverage); - unset($_coverage); - } - - $this->handleReports($mergedCoverage, $input, $output); - $this->outputMergeErrors($output); - } - - protected function outputMergeErrors(OutputInterface $output): void - { - if (empty($this->mergeErrors)) { - return; - } - - foreach ($this->mergeErrors as $mergeError) { - $output->write('Failed to merge: ' . $mergeError, true); - } - } -} -phpunit/phpcov: 6.0.1 -doctrine/instantiator: 1.3.0 -myclabs/deep-copy: 1.9.3 -phar-io/manifest: 1.0.3 -phar-io/version: 2.0.1 -phpdocumentor/reflection-common: 2.0.0 -phpdocumentor/reflection-docblock: 4.3.2 -phpdocumentor/type-resolver: 1.0.1 -phpspec/prophecy: 1.9.0 -phpunit/php-code-coverage: 7.0.8 -phpunit/php-file-iterator: 2.0.2 -phpunit/php-text-template: 1.2.1 -phpunit/php-timer: 2.1.2 -phpunit/php-token-stream: 3.1.1 -phpunit/phpunit: 8.4.3 -psr/container: 1.0.0 -sebastian/code-unit-reverse-lookup: 1.0.1 -sebastian/comparator: 3.0.2 -sebastian/diff: 3.0.2 -sebastian/environment: 4.2.2 -sebastian/exporter: 3.1.2 -sebastian/finder-facade: 1.2.2 -sebastian/global-state: 3.0.0 -sebastian/object-enumerator: 3.0.3 -sebastian/object-reflector: 1.1.1 -sebastian/recursion-context: 3.0.0 -sebastian/resource-operations: 2.0.1 -sebastian/type: 1.1.3 -sebastian/version: 2.0.1 -symfony/console: v4.3.8 -symfony/finder: v4.3.8 -symfony/polyfill-ctype: v1.12.0 -symfony/polyfill-mbstring: v1.12.0 -symfony/polyfill-php73: v1.12.0 -symfony/service-contracts: v1.1.8 -theseer/fdomdocument: 1.6.6 -theseer/tokenizer: 1.1.3 -webmozart/assert: 1.5.0 -j#theseer/tokenizer/src/Tokenizer.phpm ^%nikic/PhpParser/Node/Stmt/Global_.php^^.Jo1nikic/PhpParser/Node/Expr/BinaryOp/LogicalAnd.php?^^ /nikic/PhpParser/Node/Expr/BinaryOp/Coalesce.php:^P,nikic/PhpParser/Node/VariadicPlaceholder.php}^Gphpunit/php-code-coverage/src/Exception/WrongXdebugVersionException.php^6phpunit/php-code-coverage/src/Driver/Xdebug2Driver.php ^^`Kphpunit/php-code-coverage/src/StaticAnalysis/IgnoredLinesFindingVisitor.php: ^&phpunit/phpunit/src/Util/Blacklist.php^7פGphpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectory.phpr^^&Ⱥ_phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.phpB^phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/PhpHandler.php^phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSetting.phpH^G_phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.php^Aphpunit/phpunit/src/Framework/Exception/SyntheticSkippedError.php^phpunit/phpunit/src/Framework/MockObject/Rule/InvokedCount.phpP +^phpunit/phpunit/src/Framework/MockObject/InvocationHandler.php^phpunit/phpunit/src/Framework/Constraint/Operator/Operator.php^7phpunit/phpunit/src/Framework/Constraint/Math/IsNan.php^phpunit/phpunit/src/Framework/Constraint/Cardinality/Count.php^phpunit/phpunit/src/Framework/Constraint/Type/IsInstanceOf.php^^b&-phpunit/phpunit/src/Framework/Error/Error.phpl^f9sebastian/diff/src/Exception/InvalidArgumentException.php^Dsebastian/diff/src/Differ.php$^sebastian/cli-parser/src/exceptions/UnknownOptionException.phpz^tokens[] = $token; - } - - /** - * @return Token - */ - public function current(): Token { - return current($this->tokens); - } - - /** - * @return int - */ - public function key(): int { - return key($this->tokens); - } - - /** - * @return void - */ - public function next() { - next($this->tokens); - $this->pos++; - } - - /** - * @return bool - */ - public function valid(): bool { - return $this->count() > $this->pos; - } - - /** - * @return void - */ - public function rewind() { - reset($this->tokens); - $this->pos = 0; - } - - /** - * @return int - */ - public function count(): int { - return count($this->tokens); - } - - /** - * @param mixed $offset - * - * @return bool - */ - public function offsetExists($offset): bool { - return isset($this->tokens[$offset]); - } - - /** - * @param mixed $offset - * - * @return Token - * @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 mixed $offset - * @param Token $value - * - * @throws TokenCollectionException - */ - public function offsetSet($offset, $value) { - 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; - } - - /** - * @param mixed $offset - */ - public function offsetUnset($offset) { - unset($this->tokens[$offset]); - } - -} -ensureValidUri($value); + public function __construct(int $line, string $name, string $value) { + $this->line = $line; + $this->name = $name; $this->value = $value; } - public function asString(): string { + public function getLine(): int { + return $this->line; + } + + public function getName(): string { + return $this->name; + } + + public function getValue(): string { return $this->value; } - - private function ensureValidUri($value) { - if (strpos($value, ':') === false) { - throw new NamespaceUriException( - sprintf("Namespace URI '%s' must contain at least one colon", $value) - ); - } - } -} -xmlns = $xmlns; - } - - /** - * @param TokenCollection $tokens - * - * @return DOMDocument - */ - public function toDom(TokenCollection $tokens): DOMDocument { - $dom = new DOMDocument(); - $dom->preserveWhiteSpace = false; - $dom->loadXML($this->toXML($tokens)); - - return $dom; - } - - /** - * @param TokenCollection $tokens - * - * @return string - */ - 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); - } - } - - $this->writer->endElement(); - $this->writer->endElement(); - $this->writer->endDocument(); - - return $this->writer->outputMemory(); - } - - /** - * @param Token $token - */ - private function addToken(Token $token) { - 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(); - } - } -} - $tok) { - if (is_string($tok)) { + if (\is_string($tok)) { $token = new Token( $lastToken->getLine(), $this->map[$tok], @@ -1710,165 +1008,22499 @@ class Tokenizer { ); $result->addToken($token); $lastToken = $token; + continue; } $line = $tok[2]; - $values = preg_split('/\R+/Uu', $tok[1]); + $values = \preg_split('/\R+/Uu', $tok[1]); foreach ($values as $v) { $token = new Token( $line, - token_name($tok[0]), + \token_name($tok[0]), $v ); - $result->addToken($token); - $line++; $lastToken = $token; + $line++; + + if ($v === '') { + continue; + } + + $result->addToken($token); } } + 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; + } + + $gap = $token->getLine() - $prev->getLine(); + + while ($gap > 1) { + $linebreak = new Token( + $prev->getLine() + 1, + 'T_WHITESPACE', + '' + ); + $final->addToken($linebreak); + $prev = $linebreak; + $gap--; + } + + $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); + } + } + + $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) + ); + } + } +} +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]); + } +} +phpunit/phpcov: 8.2.1 +doctrine/instantiator: 1.4.1 +myclabs/deep-copy: 1.11.0 +nikic/php-parser: v4.13.2 +phar-io/manifest: 2.0.3 +phar-io/version: 3.2.1 +phpdocumentor/reflection-common: 2.2.0 +phpdocumentor/reflection-docblock: 5.3.0 +phpdocumentor/type-resolver: 1.6.0 +phpspec/prophecy: v1.15.0 +phpunit/php-code-coverage: 9.2.15 +phpunit/php-file-iterator: 3.0.6 +phpunit/php-invoker: 3.1.1 +phpunit/php-text-template: 2.0.4 +phpunit/php-timer: 5.0.3 +phpunit/phpunit: 9.5.19 +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.4 +sebastian/global-state: 5.0.5 +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: 3.0.0 +sebastian/version: 3.0.2 +symfony/polyfill-ctype: v1.25.0 +theseer/tokenizer: 1.2.1 +webmozart/assert: 1.10.0 +dumpComments = !empty($options['dumpComments']); + $this->dumpPositions = !empty($options['dumpPositions']); + } + + /** + * 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 + */ + public function dump($node, string $code = null) : string { + $this->code = $code; + return $this->dumpRecursive($node); + } + + 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) { + $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 . ')'; + } + + /** + * 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 + */ + protected function dumpPosition(Node $node) { + 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]"; + } + + // 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; + } +} +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 haveParens(int $startPos, int $endPos) : bool { + return $this->haveTokenImmediatelyBefore($startPos, '(') + && $this->haveTokenImmediatelyAfter($endPos, ')'); + } + + /** + * Whether the given position is immediately surrounded by braces. + * + * @param int $startPos Start position + * @param int $endPos End position + * + * @return bool + */ + public function haveBraces(int $startPos, int $endPos) : bool { + 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; + } + + /** + * 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 + * + * @return bool Whether the expected token was found + */ + public function haveTokenImmediatelyAfter(int $pos, $expectedTokenType) : bool { + $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); + } + + /** + * Return first non-whitespace token position smaller or equal to passed position. + * + * @param int $pos Token position + * @return int Non-whitespace token position + */ + public function skipLeftWhitespace(int $pos) { + $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; + } + + /** + * Return first non-whitespace position greater or equal to passed position. + * + * @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. + * + * @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) { + $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, '}'); + } + + /** + * Get indentation before token position. + * + * @param int $pos Token position + * + * @return int Indentation depth (in spaces) + */ + public function getIndentationBefore(int $pos) : int { + return $this->indentMap[$pos]; + } + + /** + * Get the code corresponding to a token offset range, optionally adjusted for indentation. + * + * @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) + * + * @return string Code corresponding to token range, adjusted for indentation + */ + public function getTokenCode(int $from, int $to, int $indent) : string { + $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; + } + + /** + * Precalculate the indentation at every token position. + * + * @return int[] Token position to indentation map + */ + private function calcIndentMap() { + $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; + } + } + } + + // Add a sentinel for one past end of the file + $indentMap[] = $indent; + + return $indentMap; + } +} +type = $type; + $this->old = $old; + $this->new = $new; + } +} +isEqual = $isEqual; + } + + /** + * Calculate diff (edit script) from $old to $new. + * + * @param array $old Original array + * @param array $new New array + * + * @return DiffElem[] Diff (edit script) + */ + public function diff(array $old, array $new) { + list($trace, $x, $y) = $this->calculateTrace($old, $new); + return $this->extractDiff($trace, $x, $y, $old, $new); + } + + /** + * Calculate diff, including "replace" operations. + * + * If a sequence of remove operations is followed by the same number of add operations, these + * will be coalesced into replace operations. + * + * @param array $old Original array + * @param array $new New array + * + * @return DiffElem[] Diff (edit script), including replace operations + */ + public function diffWithReplacements(array $old, array $new) { + 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]; + } + } + } + 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--; + } + } + return array_reverse($result); + } + + /** + * Coalesce equal-length sequences of remove+add into a replace operation. + * + * @param DiffElem[] $diff + * @return DiffElem[] + */ + private function coalesceReplacements(array $diff) { + $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; + } +} +attrGroups = $attrGroups; + $this->args = $args; + $this->extends = $extends; + $this->implements = $implements; + $this->stmts = $stmts; + } + + public static function fromNewNode(Expr\New_ $newNode) { + $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 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. + * + * @param array $options Dictionary of formatting options + */ + public function __construct(array $options = []) { + $this->docStringEndToken = '_DOC_STRING_END_' . mt_rand(); + + $defaultOptions = ['shortArraySyntax' => false]; + $this->options = $options + $defaultOptions; + } + + /** + * Reset pretty printing state. + */ + protected function resetState() { + $this->indentLevel = 0; + $this->nl = "\n"; + $this->origTokens = null; + } + + /** + * Set indentation level + * + * @param int $level Level in number of spaces + */ + protected function setIndentLevel(int $level) { + $this->indentLevel = $level; + $this->nl = "\n" . \str_repeat(' ', $level); + } + + /** + * Increase indentation level. + */ + protected function indent() { + $this->indentLevel += 4; + $this->nl .= ' '; + } + + /** + * Decrease indentation level. + */ + protected function outdent() { + assert($this->indentLevel >= 4); + $this->indentLevel -= 4; + $this->nl = "\n" . str_repeat(' ', $this->indentLevel); + } + + /** + * Pretty prints an array of statements. + * + * @param Node[] $stmts Array of statements + * + * @return string Pretty printed statements + */ + public function prettyPrint(array $stmts) : string { + $this->resetState(); + $this->preprocessNodes($stmts); + + return ltrim($this->handleMagicTokens($this->pStmts($stmts, false))); + } + + /** + * Pretty prints an expression. + * + * @param Expr $node Expression node + * + * @return string Pretty printed node + */ + public function prettyPrintExpr(Expr $node) : string { + $this->resetState(); + return $this->handleMagicTokens($this->p($node)); + } + + /** + * Pretty prints a file of statements (includes the opening 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; + } + + /** + * Preprocesses the top-level nodes to initialize pretty printer state. + * + * @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 Stmt\Namespace_ && null === $node->name) { + $this->canUseSemicolonNamespaces = false; + break; + } + } + } + + /** + * Handles (and removes) no-indent and doc-string-end tokens. + * + * @param string $str + * @return string + */ + protected function handleMagicTokens(string $str) : string { + // 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; + } + + /** + * Pretty prints an array of nodes (statements) and indents them optionally. + * + * @param Node[] $nodes Array of nodes + * @param bool $indent Whether to indent the printed nodes + * + * @return string Pretty printed statements + */ + protected function pStmts(array $nodes, bool $indent = true) : string { + if ($indent) { + $this->indent(); + } + + $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); + } + + if ($indent) { + $this->outdent(); + } + return $result; } -} -line = $line; - $this->name = $name; + protected function pInfixOp(string $class, Node $leftNode, string $operatorString, Node $rightNode) : string { + list($precedence, $associativity) = $this->precedenceMap[$class]; + + return $this->pPrec($leftNode, $precedence, $associativity, -1) + . $operatorString + . $this->pPrec($rightNode, $precedence, $associativity, 1); + } + + /** + * Pretty-print a prefix operation while taking precedence into account. + * + * @param string $class Node class of operator + * @param string $operatorString String representation of the operator + * @param Node $node Node + * + * @return string Pretty printed prefix operation + */ + protected function pPrefixOp(string $class, string $operatorString, Node $node) : string { + list($precedence, $associativity) = $this->precedenceMap[$class]; + return $operatorString . $this->pPrec($node, $precedence, $associativity, 1); + } + + /** + * Pretty-print a postfix operation while taking precedence into account. + * + * @param string $class Node class of operator + * @param string $operatorString String representation of the operator + * @param Node $node Node + * + * @return string Pretty printed postfix operation + */ + protected function pPostfixOp(string $class, Node $node, string $operatorString) : string { + list($precedence, $associativity) = $this->precedenceMap[$class]; + return $this->pPrec($node, $precedence, $associativity, -1) . $operatorString; + } + + /** + * 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) + * + * @return string The pretty printed node + */ + protected function pPrec(Node $node, int $parentPrecedence, int $parentAssociativity, int $childPosition) : string { + $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 $this->p($node); + } + + /** + * Pretty prints an array of nodes and implodes the printed values. + * + * @param Node[] $nodes Array of Nodes to be printed + * @param string $glue Character to implode with + * + * @return string Imploded pretty printed nodes + */ + protected function pImplode(array $nodes, string $glue = '') : string { + $pNodes = []; + foreach ($nodes as $node) { + if (null === $node) { + $pNodes[] = ''; + } else { + $pNodes[] = $this->p($node); + } + } + + return implode($glue, $pNodes); + } + + /** + * Pretty prints an array of nodes and implodes the printed values with commas. + * + * @param Node[] $nodes Array of Nodes to be printed + * + * @return string Comma separated pretty printed nodes + */ + protected function pCommaSeparated(array $nodes) : string { + return $this->pImplode($nodes, ', '); + } + + /** + * 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 + */ + protected function pCommaSeparatedMultiline(array $nodes, bool $trailingComma) : string { + $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; + } + + /** + * Prints reformatted text of the passed comments. + * + * @param Comment[] $comments List of comments + * + * @return string Reformatted text of comments + */ + protected function pComments(array $comments) : string { + $formattedComments = []; + + foreach ($comments as $comment) { + $formattedComments[] = str_replace("\n", $this->nl, $comment->getReformattedText()); + } + + return implode($this->nl, $formattedComments); + } + + /** + * Perform a format-preserving pretty print of an AST. + * + * The format preservation is best effort. For some changes to the AST the formatting will not + * be preserved (at least not locally). + * + * 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. + * + * @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 + * + * @return string + */ + public function printFormatPreserving(array $stmts, array $origStmts, array $origTokens) : string { + $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)); + } + + protected function pFallback(Node $node) { + return $this->{'p' . $node->getType()}($node); + } + + /** + * 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 + * + * @return string Pretty printed node + */ + protected function p(Node $node, $parentFormatPreserved = false) : string { + // 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; + } + + /** + * Perform a format-preserving pretty print of an array. + * + * @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 + */ + protected function pArray( + array $nodes, array $origNodes, int &$pos, int $indentAdjustment, + string $parentNodeType, string $subNodeName, $fixup + ) { + $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; + } + + /** + * Print node with fixups. + * + * 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 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 string Result of fixed-up print of subnode + */ + protected function pFixup(int $fixup, Node $subNode, $parentClass, int $subStartPos, int $subEndPos) : string { + 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); + } + + /** + * 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 + */ + protected function safeAppend(string &$str, string $append) { + if ($str === "") { + $str = $append; + return; + } + + if ($append === "") { + return; + } + + if (!$this->labelCharMap[$append[0]] + || !$this->labelCharMap[$str[\strlen($str) - 1]]) { + $str .= $append; + } else { + $str .= " " . $append; + } + } + + /** + * Determines whether the LHS of a call must be wrapped in parenthesis. + * + * @param Node $node LHS of a call + * + * @return bool Whether parentheses are required + */ + protected function callLhsRequiresParens(Node $node) : bool { + return !($node instanceof Node\Name + || $node instanceof Expr\Variable + || $node instanceof Expr\ArrayDimFetch + || $node instanceof Expr\FuncCall + || $node instanceof Expr\MethodCall + || $node instanceof Expr\NullsafeMethodCall + || $node instanceof Expr\StaticCall + || $node instanceof Expr\Array_); + } + + /** + * Determines whether the LHS of a dereferencing operation must be wrapped in parenthesis. + * + * @param Node $node LHS of dereferencing operation + * + * @return bool Whether parentheses are required + */ + protected function dereferenceLhsRequiresParens(Node $node) : bool { + 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); + } + + /** + * Print modifiers, including trailing whitespace. + * + * @param int $modifiers Modifier mask to print + * + * @return string Printed modifiers + */ + protected function pModifiers(int $modifiers) { + 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 ' : ''); + } + + /** + * Determine whether a list of nodes uses multiline formatting. + * + * @param (Node|null)[] $nodes Node list + * + * @return bool Whether multiline formatting is used + */ + protected function isMultiline(array $nodes) : bool { + 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, // TODO: FIXUP_NEW_VARIABLE + ], + 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, + // 'Stmt_Case->cond': Replace with "default" + // 'Stmt_Class->name': Unclear what to do + // 'Stmt_Declare->stmts': Not a plain node + // 'Stmt_TraitUseAdaptation_Alias->newModifier': Not a plain node + ]; + } + + 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], + + // 'Expr_Exit->expr': Complicated due to optional () + // 'Stmt_Case->cond': Conversion from default to case + // 'Stmt_Class->name': Unclear + // 'Stmt_Declare->stmts': Not a proper node + // 'Stmt_TraitUseAdaptation_Alias->newModifier': Not a proper node + ]; + } + + 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' => ['(', '', ''], + + /* These cannot be empty to start with: + * Expr_Isset->vars + * Stmt_Catch->types + * Stmt_Const->consts + * Stmt_ClassConst->consts + * Stmt_Declare->declares + * Stmt_Echo->exprs + * Stmt_Global->vars + * Stmt_GroupUse->uses + * Stmt_Property->props + * Stmt_StaticVar->vars + * Stmt_TraitUse->traits + * Stmt_TraitUseAdaptation_Precedence->insteadof + * Stmt_Unset->vars + * Stmt_Use->uses + * UnionType->types + */ + + /* TODO + * Stmt_If->elseifs + * Stmt_TryCatch->catches + * Expr_Array->items + * Expr_List->items + * Stmt_For->init + * Stmt_For->cond + * Stmt_For->loop + */ + ]; + } + + 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, + //'Stmt_TraitUseAdaptation_Alias->newModifier' => 0, // TODO + ]; + + // List of integer subnodes that are not modifiers: + // Expr_Include->type + // Stmt_GroupUse->type + // Stmt_Use->type + // Stmt_UseUse->type + } +} +name = $name; + } + + /** + * Adds a statement. + * + * @param Node|PhpParser\Builder $stmt The statement to add + * + * @return $this The builder instance (for fluid interface) + */ + public function addStmt($stmt) { + $this->stmts[] = BuilderHelpers::normalizeStmt($stmt); + + return $this; + } + + /** + * Adds an attribute group. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) + */ + public function addAttribute($attribute) { + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + + return $this; + } + + /** + * Returns the built function node. + * + * @return Stmt\Function_ The built function node + */ + 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); + } +} +name = $name; + } + + /** + * Extends one or more interfaces. + * + * @param Name|string ...$interfaces Names of interfaces to extend + * + * @return $this The builder instance (for fluid interface) + */ + public function extend(...$interfaces) { + foreach ($interfaces as $interface) { + $this->extends[] = BuilderHelpers::normalizeName($interface); + } + + 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); + + 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 { + throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); + } + + return $this; + } + + /** + * Adds an attribute group. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) + */ + public function addAttribute($attribute) { + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + + return $this; + } + + /** + * Returns the built interface node. + * + * @return Stmt\Interface_ The built interface node + */ + public function getNode() : PhpParser\Node { + return new Stmt\Interface_($this->name, [ + 'extends' => $this->extends, + 'stmts' => array_merge($this->constants, $this->methods), + 'attrGroups' => $this->attributeGroups, + ], $this->attributes); + } +} +name = $name; + } + + /** + * Makes the method public. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePublic() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC); + + return $this; + } + + /** + * 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; + } + + /** + * Makes the method private. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePrivate() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE); + + return $this; + } + + /** + * Makes the method static. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeStatic() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC); + + return $this; + } + + /** + * Makes the method abstract. + * + * @return $this The builder instance (for fluid interface) + */ + 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; + } + + /** + * Makes the method final. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeFinal() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL); + + return $this; + } + + /** + * Adds a statement. + * + * @param Node|PhpParser\Builder $stmt The statement to add + * + * @return $this The builder instance (for fluid interface) + */ + 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; + } + + /** + * Adds an attribute group. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) + */ + public function addAttribute($attribute) { + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + + return $this; + } + + /** + * Returns the built method node. + * + * @return Stmt\ClassMethod The built method node + */ + public function getNode() : Node { + 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); + } +} +name = $name; + } + + /** + * 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->extends = BuilderHelpers::normalizeName($class); + + return $this; + } + + /** + * 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) { + foreach ($interfaces as $interface) { + $this->implements[] = BuilderHelpers::normalizeName($interface); + } + + return $this; + } + + /** + * Makes the class abstract. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeAbstract() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT); + + return $this; + } + + /** + * Makes the class final. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeFinal() { + $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())); + } + + $targets[$class][] = $stmt; + + return $this; + } + + /** + * Adds an attribute group. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) + */ + public function addAttribute($attribute) { + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + + return $this; + } + + /** + * Returns the built class node. + * + * @return Stmt\Class_ The built class node + */ + public function getNode() : PhpParser\Node { + 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); + } +} +name = $name; + } + + /** + * 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); + + 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 $this; + } + + /** + * Adds an attribute group. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) + */ + public function addAttribute($attribute) { + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + + return $this; + } + + /** + * Returns the built trait node. + * + * @return Stmt\Trait_ The built interface node + */ + public function getNode() : PhpParser\Node { + return new Stmt\Trait_( + $this->name, [ + 'stmts' => array_merge($this->uses, $this->properties, $this->methods), + 'attrGroups' => $this->attributeGroups, + ], $this->attributes + ); + } +} +returnByRef = true; + + return $this; + } + + /** + * Adds a parameter. + * + * @param Node\Param|Param $param The parameter to add + * + * @return $this The builder instance (for fluid interface) + */ + 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; + } + + /** + * Adds multiple parameters. + * + * @param array $params The parameters to add + * + * @return $this The builder instance (for fluid interface) + */ + public function addParams(array $params) { + foreach ($params as $param) { + $this->addParam($param); + } + + return $this; + } + + /** + * Sets the return type for PHP 7. + * + * @param string|Node\Name|Node\Identifier|Node\ComplexType $type + * + * @return $this The builder instance (for fluid interface) + */ + public function setReturnType($type) { + $this->returnType = BuilderHelpers::normalizeType($type); + + return $this; + } +} +and($trait); + } + } + + /** + * Adds used trait. + * + * @param Node\Name|string $trait Trait name + * + * @return $this The builder instance (for fluid interface) + */ + public function and($trait) { + $this->traits[] = BuilderHelpers::normalizeName($trait); + return $this; + } + + /** + * Adds trait adaptation. + * + * @param Stmt\TraitUseAdaptation|Builder\TraitUseAdaptation $adaptation Trait adaptation + * + * @return $this The builder instance (for fluid interface) + */ + 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; + } + + /** + * Returns the built node. + * + * @return Node The built node + */ + public function getNode() : Node { + return new Stmt\TraitUse($this->traits, $this->adaptations); + } +} +addStmt($stmt); + } + + return $this; + } + + /** + * Sets doc comment for the declaration. + * + * @param PhpParser\Comment\Doc|string $docComment Doc comment to set + * + * @return $this The builder instance (for fluid interface) + */ + public function setDocComment($docComment) { + $this->attributes['comments'] = [ + BuilderHelpers::normalizeDocComment($docComment) + ]; + + return $this; + } +} +name = $name; + } + + /** + * Sets the value. + * + * @param Node\Expr|string|int $value + * + * @return $this + */ + public function setValue($value) { + $this->value = BuilderHelpers::normalizeValue($value); + + return $this; + } + + /** + * 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 function setDocComment($docComment) { + $this->attributes = [ + 'comments' => [BuilderHelpers::normalizeDocComment($docComment)] + ]; + + return $this; + } + + /** + * Adds an attribute group. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) + */ + public function addAttribute($attribute) { + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + + return $this; + } + + /** + * Returns the built enum case node. + * + * @return Stmt\EnumCase The built constant node + */ + public function getNode(): PhpParser\Node { + return new Stmt\EnumCase( + $this->name, + $this->value, + $this->attributes, + $this->attributeGroups + ); + } +} +name = $name; + } + + /** + * Sets the scalar type. + * + * @param string|Identifier $type + * + * @return $this + */ + public function setScalarType($scalarType) { + $this->scalarType = BuilderHelpers::normalizeType($scalarType); + + return $this; + } + + /** + * 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) { + foreach ($interfaces as $interface) { + $this->implements[] = BuilderHelpers::normalizeName($interface); + } + + 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\EnumCase::class => &$this->enumCases, + Stmt\ClassConst::class => &$this->constants, + 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; + } + + /** + * Adds an attribute group. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) + */ + public function addAttribute($attribute) { + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + + return $this; + } + + /** + * Returns the built class node. + * + * @return Stmt\Enum_ The built enum node + */ + public function getNode() : PhpParser\Node { + return new Stmt\Enum_($this->name, [ + 'scalarType' => $this->scalarType, + 'implements' => $this->implements, + 'stmts' => array_merge($this->uses, $this->enumCases, $this->constants, $this->methods), + 'attrGroups' => $this->attributeGroups, + ], $this->attributes); + } +} +name = $name; + } + + /** + * Sets default value for the parameter. + * + * @param mixed $value Default value to use + * + * @return $this The builder instance (for fluid interface) + */ + public function setDefault($value) { + $this->default = BuilderHelpers::normalizeValue($value); + + return $this; + } + + /** + * Sets type for the parameter. + * + * @param string|Node\Name|Node\Identifier|Node\ComplexType $type Parameter type + * + * @return $this The builder instance (for fluid interface) + */ + public function setType($type) { + $this->type = BuilderHelpers::normalizeType($type); + if ($this->type == 'void') { + throw new \LogicException('Parameter type cannot be void'); + } + + return $this; + } + + /** + * 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 + */ + public function setTypeHint($type) { + return $this->setType($type); + } + + /** + * Make the parameter accept the value by reference. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeByRef() { + $this->byRef = true; + + return $this; + } + + /** + * Make the parameter variadic + * + * @return $this The builder instance (for fluid interface) + */ + public function makeVariadic() { + $this->variadic = true; + + return $this; + } + + /** + * Adds an attribute group. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) + */ + public function addAttribute($attribute) { + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + + return $this; + } + + /** + * Returns the built parameter node. + * + * @return Node\Param The built parameter node + */ + public function getNode() : Node { + return new Node\Param( + new Node\Expr\Variable($this->name), + $this->default, $this->type, $this->byRef, $this->variadic, [], 0, $this->attributeGroups + ); + } +} +name = BuilderHelpers::normalizeName($name); + $this->type = $type; + } + + /** + * Sets alias for used name. + * + * @param string $alias Alias to use (last component of full name by default) + * + * @return $this The builder instance (for fluid interface) + */ + public function as(string $alias) { + $this->alias = $alias; + return $this; + } + + /** + * Returns the built node. + * + * @return Stmt\Use_ The built node + */ + public function getNode() : Node { + return new Stmt\Use_([ + new Stmt\UseUse($this->name, $this->alias) + ], $this->type); + } +} +name = null !== $name ? BuilderHelpers::normalizeName($name) : null; + } + + /** + * Adds a statement. + * + * @param Node|PhpParser\Builder $stmt The statement to add + * + * @return $this The builder instance (for fluid interface) + */ + public function addStmt($stmt) { + $this->stmts[] = BuilderHelpers::normalizeStmt($stmt); + + return $this; + } + + /** + * Returns the built node. + * + * @return Stmt\Namespace_ The built node + */ + public function getNode() : Node { + return new Stmt\Namespace_($this->name, $this->stmts, $this->attributes); + } +} +constants = [new Const_($name, BuilderHelpers::normalizeValue($value))]; + } + + /** + * Add another constant to const group + * + * @param string|Identifier $name Name + * @param Node\Expr|bool|null|int|float|string|array $value Value + * + * @return $this The builder instance (for fluid interface) + */ + public function addConst($name, $value) { + $this->constants[] = new Const_($name, BuilderHelpers::normalizeValue($value)); + + return $this; + } + + /** + * Makes the constant public. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePublic() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC); + + return $this; + } + + /** + * Makes the constant protected. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeProtected() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED); + + return $this; + } + + /** + * Makes the constant private. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePrivate() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE); + + return $this; + } + + /** + * Makes the constant final. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeFinal() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL); + + return $this; + } + + /** + * 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 function setDocComment($docComment) { + $this->attributes = [ + 'comments' => [BuilderHelpers::normalizeDocComment($docComment)] + ]; + + return $this; + } + + /** + * Adds an attribute group. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) + */ + public function addAttribute($attribute) { + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + + return $this; + } + + /** + * Returns the built class node. + * + * @return Stmt\ClassConst The built constant node + */ + public function getNode(): PhpParser\Node { + return new Stmt\ClassConst( + $this->constants, + $this->flags, + $this->attributes, + $this->attributeGroups + ); + } +} +name = $name; + } + + /** + * Makes the property public. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePublic() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC); + + return $this; + } + + /** + * Makes the property protected. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeProtected() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED); + + return $this; + } + + /** + * Makes the property private. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePrivate() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE); + + return $this; + } + + /** + * Makes the property static. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeStatic() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC); + + return $this; + } + + /** + * Makes the property readonly. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeReadonly() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_READONLY); + + return $this; + } + + /** + * Sets default value for the property. + * + * @param mixed $value Default value to use + * + * @return $this The builder instance (for fluid interface) + */ + public function setDefault($value) { + $this->default = BuilderHelpers::normalizeValue($value); + + return $this; + } + + /** + * Sets doc comment for the property. + * + * @param PhpParser\Comment\Doc|string $docComment Doc comment to set + * + * @return $this The builder instance (for fluid interface) + */ + public function setDocComment($docComment) { + $this->attributes = [ + 'comments' => [BuilderHelpers::normalizeDocComment($docComment)] + ]; + + return $this; + } + + /** + * Sets the property type for PHP 7.4+. + * + * @param string|Name|Identifier|ComplexType $type + * + * @return $this + */ + public function setType($type) { + $this->type = BuilderHelpers::normalizeType($type); + + return $this; + } + + /** + * Adds an attribute group. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return $this The builder instance (for fluid interface) + */ + public function addAttribute($attribute) { + $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); + + return $this; + } + + /** + * Returns the built class node. + * + * @return Stmt\Property The built property node + */ + public function getNode() : PhpParser\Node { + 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 + ); + } +} +type = self::TYPE_UNDEFINED; + + $this->trait = is_null($trait)? null: BuilderHelpers::normalizeName($trait); + $this->method = BuilderHelpers::normalizeIdentifier($method); + } + + /** + * 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) { + 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; + } + + /** + * Sets adaptated method public. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePublic() { + $this->setModifier(Stmt\Class_::MODIFIER_PUBLIC); + return $this; + } + + /** + * Sets adaptated method protected. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeProtected() { + $this->setModifier(Stmt\Class_::MODIFIER_PROTECTED); + return $this; + } + + /** + * Sets adaptated method private. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePrivate() { + $this->setModifier(Stmt\Class_::MODIFIER_PRIVATE); + return $this; + } + + /** + * Adds overwritten traits. + * + * @param Node\Name|string ...$traits Traits for overwrite + * + * @return $this The builder instance (for fluid interface) + */ + public function insteadof(...$traits) { + 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; + } + + protected function setModifier(int $modifier) { + 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'); + } + } +} +rawMessage = $message; + if (is_array($attributes)) { + $this->attributes = $attributes; + } else { + $this->attributes = ['startLine' => $attributes]; + } + $this->updateMessage(); + } + + /** + * Gets the error message + * + * @return string Error message + */ + public function getRawMessage() : string { + return $this->rawMessage; + } + + /** + * Gets the line the error starts in. + * + * @return int Error start line + */ + public function getStartLine() : int { + return $this->attributes['startLine'] ?? -1; + } + + /** + * Gets the line the error ends in. + * + * @return int Error end line + */ + public function getEndLine() : int { + return $this->attributes['endLine'] ?? -1; + } + + /** + * Gets the attributes of the node/token the error occurred at. + * + * @return array + */ + public function getAttributes() : array { + return $this->attributes; + } + + /** + * Sets the attributes of the node/token the error occurred at. + * + * @param array $attributes + */ + public function setAttributes(array $attributes) { + $this->attributes = $attributes; + $this->updateMessage(); + } + + /** + * Sets the line of the PHP file the error occurred in. + * + * @param string $message Error message + */ + public function setRawMessage(string $message) { + $this->rawMessage = $message; + $this->updateMessage(); + } + + /** + * Sets the line the error starts in. + * + * @param int $line Error start line + */ + public function setStartLine(int $line) { + $this->attributes['startLine'] = $line; + $this->updateMessage(); + } + + /** + * Returns whether the error has start and end column information. + * + * For column information enable the startFilePos and endFilePos in the lexer options. + * + * @return bool + */ + public function hasColumnInfo() : bool { + return isset($this->attributes['startFilePos'], $this->attributes['endFilePos']); + } + + /** + * 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 (!$this->hasColumnInfo()) { + throw new \RuntimeException('Error does not have column information'); + } + + return $this->toColumn($code, $this->attributes['startFilePos']); + } + + /** + * Gets the end column (1-based) into the line where the error ended. + * + * @param string $code Source code of the file + * @return int + */ + public function getEndColumn(string $code) : int { + if (!$this->hasColumnInfo()) { + throw new \RuntimeException('Error does not have column information'); + } + + 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 + */ + public function getMessageWithColumnInfo(string $code) : string { + return sprintf( + '%s from %d:%d to %d:%d', $this->getRawMessage(), + $this->getStartLine(), $this->getStartColumn($code), + $this->getEndLine(), $this->getEndColumn($code) + ); + } + + /** + * 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) + */ + private function toColumn(string $code, int $pos) : int { + 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; + } + + /** + * Updates the exception message after a change to rawMessage or rawLine. + */ + protected function updateMessage() { + $this->message = $this->rawMessage; + + if (-1 === $this->getStartLine()) { + $this->message .= ' on unknown line'; + } else { + $this->message .= ' on line ' . $this->getStartLine(); + } + } +} +nameContext = new NameContext($errorHandler ?? new ErrorHandler\Throwing); + $this->preserveOriginalNames = $options['preserveOriginalNames'] ?? false; + $this->replaceNodes = $options['replaceNodes'] ?? true; + } + + /** + * Get name resolution context. + * + * @return NameContext + */ + public function getNameContext() : NameContext { + return $this->nameContext; + } + + public function beforeTraverse(array $nodes) { + $this->nameContext->startNamespace(); + return null; + } + + public function enterNode(Node $node) { + 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); + } + } 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; + } + + private function addAlias(Stmt\UseUse $use, $type, Name $prefix = null) { + // 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); + } + + private function resolveType($node) { + 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); + } + return $node; + } + return $node; + } + + /** + * Resolve name, according to name resolver options. + * + * @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 + */ + protected function resolveName(Name $name, int $type) : Name { + 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; + } + + protected function resolveClassName(Name $name) { + return $this->resolveName($name, Stmt\Use_::TYPE_NORMAL); + } + + protected function addNamespacedName(Node $node) { + $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); + } + } + } +} +$node->getAttribute('parent'). + */ +final class ParentConnectingVisitor extends NodeVisitorAbstract +{ + /** + * @var Node[] + */ + private $stack = []; + + public function beforeTraverse(array $nodes) + { + $this->stack = []; + } + + public function enterNode(Node $node) + { + if (!empty($this->stack)) { + $node->setAttribute('parent', $this->stack[count($this->stack) - 1]); + } + + $this->stack[] = $node; + } + + public function leaveNode(Node $node) + { + array_pop($this->stack); + } +} +setAttribute('origNode', $origNode); + return $node; + } +} +filterCallback = $filterCallback; + } + + /** + * Get found nodes satisfying the filter callback. + * + * Nodes are returned in pre-order. + * + * @return Node[] Found nodes + */ + public function getFoundNodes() : array { + return $this->foundNodes; + } + + public function beforeTraverse(array $nodes) { + $this->foundNodes = []; + + return null; + } + + public function enterNode(Node $node) { + $filterCallback = $this->filterCallback; + if ($filterCallback($node)) { + $this->foundNodes[] = $node; + } + + return null; + } +} +filterCallback = $filterCallback; + } + + /** + * Get found node satisfying the filter callback. + * + * Returns null if no node satisfies the filter callback. + * + * @return null|Node Found node (or null if not found) + */ + public function getFoundNode() { + return $this->foundNode; + } + + public function beforeTraverse(array $nodes) { + $this->foundNode = null; + + return null; + } + + public function enterNode(Node $node) { + $filterCallback = $this->filterCallback; + if ($filterCallback($node)) { + $this->foundNode = $node; + return NodeTraverser::STOP_TRAVERSAL; + } + + 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'). + */ +final class NodeConnectingVisitor extends NodeVisitorAbstract +{ + /** + * @var Node[] + */ + private $stack = []; + + /** + * @var ?Node + */ + private $previous; + + public function beforeTraverse(array $nodes) { + $this->stack = []; + $this->previous = null; + } + + public function enterNode(Node $node) { + if (!empty($this->stack)) { + $node->setAttribute('parent', $this->stack[count($this->stack) - 1]); + } + + if ($this->previous !== null && $this->previous->getAttribute('parent') === $node->getAttribute('parent')) { + $node->setAttribute('previous', $this->previous); + $this->previous->setAttribute('next', $node); + } + + $this->stack[] = $node; + } + + public function leaveNode(Node $node) { + $this->previous = $node; + + array_pop($this->stack); + } +} +fallbackEvaluator = $fallbackEvaluator ?? function(Expr $expr) { + throw new ConstExprEvaluationException( + "Expression of type {$expr->getType()} cannot be evaluated" + ); + }; + } + + /** + * 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 function evaluateSilently(Expr $expr) { + 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(); + } + } + + /** + * 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 evaluateDirectly(Expr $expr) { + return $this->evaluate($expr); + } + + private function evaluate(Expr $expr) { + if ($expr instanceof Scalar\LNumber + || $expr instanceof Scalar\DNumber + || $expr instanceof Scalar\String_ + ) { + return $expr->value; + } + + 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); + } elseif ($item->unpack) { + $array = array_merge($array, $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); + } +} +resolveIntegerOrFloatToken($tokens[$i + 1][1]); + array_splice($tokens, $i, 2, [ + [$tokenKind, '0' . $tokens[$i + 1][1], $tokens[$i][2]], + ]); + $c--; + } + } + return $tokens; + } + + private function resolveIntegerOrFloatToken(string $str): int + { + $str = substr($str, 1); + $str = str_replace('_', '', $str); + $num = octdec($str); + return is_float($num) ? \T_DNUMBER : \T_LNUMBER; + } + + public function reverseEmulate(string $code, array $tokens): array { + // Explicit octals were not legal code previously, don't bother. + return $tokens; + } +}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 { + return $this->emulator->emulate($code, $tokens); + } + + public function preprocessCode(string $code, array &$patches): string { + return $code; + } +}\h*)\2(?![a-zA-Z0-9_\x80-\xff])(?(?:;?[\r\n])?)/x +REGEX; + + public function getPhpVersion(): string + { + return 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; + } +} +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 $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; + } +} +getKeywordString()) !== false; + } + + protected function isKeywordContext(array $tokens, int $pos): bool + { + $previousNonSpaceToken = $this->getPreviousNonSpaceToken($tokens, $pos); + return $previousNonSpaceToken === null || $previousNonSpaceToken[0] !== \T_OBJECT_OPERATOR; + } + + 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 + && $this->isKeywordContext($tokens, $i)) { + $tokens[$i][0] = $this->getKeywordToken(); + } + } + + return $tokens; + } + + /** + * @param mixed[] $tokens + * @return mixed[]|null + */ + private function getPreviousNonSpaceToken(array $tokens, int $start) + { + for ($i = $start - 1; $i >= 0; --$i) { + if ($tokens[$i][0] === T_WHITESPACE) { + continue; + } + + return $tokens[$i]; + } + + return null; + } + + public function reverseEmulate(string $code, array $tokens): array + { + $keywordToken = $this->getKeywordToken(); + foreach ($tokens as $i => $token) { + if ($token[0] === $keywordToken) { + $tokens[$i][0] = \T_STRING; + } + } + + return $tokens; + } +} +') !== 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 ($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 $tokens; + } + + public function reverseEmulate(string $code, array $tokens): array + { + // ?-> was not valid code previously, don't bother. + return $tokens; + } +} +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); + } + } + } + + public function startLexing(string $code, ErrorHandler $errorHandler = null) { + $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; + } + + $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); + } + } + + 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() + { + // 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; + } + + // 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]; + } + + $pos += $len; + } + + // 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); + } + } +} + [aliasName => originalName]] */ + protected $aliases = []; + + /** @var Name[][] Same as $aliases but preserving original case */ + protected $origAliases = []; + + /** @var ErrorHandler Error handler */ + protected $errorHandler; + + /** + * Create a name context. + * + * @param ErrorHandler $errorHandler Error handling used to report errors + */ + public function __construct(ErrorHandler $errorHandler) { + $this->errorHandler = $errorHandler; + } + + /** + * Start a new namespace. + * + * This also resets the alias table. + * + * @param Name|null $namespace Null is the global namespace + */ + public function startNamespace(Name $namespace = null) { + $this->namespace = $namespace; + $this->origAliases = $this->aliases = [ + Stmt\Use_::TYPE_NORMAL => [], + Stmt\Use_::TYPE_FUNCTION => [], + Stmt\Use_::TYPE_CONSTANT => [], + ]; + } + + /** + * 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 addAlias(Name $name, string $aliasName, int $type, array $errorAttrs = []) { + // 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; + } + + /** + * Get current namespace. + * + * @return null|Name Namespace (or null if global namespace) + */ + public function getNamespace() { + return $this->namespace; + } + + /** + * 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(Name $name, int $type) { + // 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()); + } + + /** + * Get resolved class name. + * + * @param Name $name Class ame to resolve + * + * @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). + * + * @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 + */ + public function getPossibleNames(string $name, int $type) : array { + $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)]; + } + } + + // 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; + } + } + + // 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 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) : Name { + $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; + } + + 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) { + 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; + } + + private function normalizeConstName(string $name) { + $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; + } +} +text = $text; + $this->startLine = $startLine; + $this->startFilePos = $startFilePos; + $this->startTokenPos = $startTokenPos; + $this->endLine = $endLine; + $this->endFilePos = $endFilePos; + $this->endTokenPos = $endTokenPos; + } + + /** + * Gets the comment text. + * + * @return string The comment text (including comment delimiters like /*) + */ + public function getText() : string { + return $this->text; + } + + /** + * Gets the line number the comment started on. + * + * @return int Line number (or -1 if not available) + */ + public function getStartLine() : int { + return $this->startLine; + } + + /** + * Gets the file offset the comment started on. + * + * @return int File offset (or -1 if not available) + */ + public function getStartFilePos() : int { + return $this->startFilePos; + } + + /** + * Gets the token offset the comment started on. + * + * @return int Token offset (or -1 if not available) + */ + public function getStartTokenPos() : int { + return $this->startTokenPos; + } + + /** + * Gets the line number the comment ends on. + * + * @return int Line number (or -1 if not available) + */ + public function getEndLine() : int { + return $this->endLine; + } + + /** + * Gets the file offset the comment ends on. + * + * @return int File offset (or -1 if not available) + */ + public function getEndFilePos() : int { + return $this->endFilePos; + } + + /** + * Gets the token offset the comment ends on. + * + * @return int Token offset (or -1 if not available) + */ + public function getEndTokenPos() : int { + return $this->endTokenPos; + } + + /** + * Gets the line number the comment started on. + * + * @deprecated Use getStartLine() instead + * + * @return int Line number + */ + public function getLine() : int { + return $this->startLine; + } + + /** + * Gets the file offset the comment started on. + * + * @deprecated Use getStartFilePos() instead + * + * @return int File offset + */ + public function getFilePos() : int { + return $this->startFilePos; + } + + /** + * Gets the token offset the comment started on. + * + * @deprecated Use getStartTokenPos() instead + * + * @return int Token offset + */ + public function getTokenPos() : int { + return $this->startTokenPos; + } + + /** + * Gets the comment text. + * + * @return string The comment text (including comment delimiters like /*) + */ + public function __toString() : string { + return $this->text; + } + + /** + * 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 + */ + public function getReformattedText() { + $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; + } + + /** + * 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. + */ + private function getShortestWhitespacePrefixLen(string $str) : int { + $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; + } + + /** + * @return array + * @psalm-return array{nodeType:string, text:mixed, line:mixed, filePos:mixed} + */ + public function jsonSerialize() : array { + // 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, + ]; + } +} +lexer = $lexer; + + if (isset($options['throwOnError'])) { + throw new \LogicException( + '"throwOnError" is no longer supported, use "errorHandler" instead'); + } + + $this->initReduceCallbacks(); + } + + /** + * 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). + */ + public function parse(string $code, ErrorHandler $errorHandler = null) { + $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; + } + + 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 + )); + } + + // 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; + } + } + + throw new \RuntimeException('Reached end of parser loop'); + } + + protected function emitError(Error $error) { + $this->errorHandler->handleError($error); + } + + /** + * Format error message including expected tokens. + * + * @param int $symbol Unexpected symbol + * @param int $state State at time of error + * + * @return string Formatted error message + */ + protected function getErrorMessage(int $symbol, int $state) : string { + $expectedString = ''; + if ($expected = $this->getExpectedTokens($state)) { + $expectedString = ', expecting ' . implode(' or ', $expected); + } + + return 'Syntax error, unexpected ' . $this->symbolToName[$symbol] . $expectedString; + } + + /** + * Get limited number of expected tokens in given state. + * + * @param int $state State + * + * @return string[] Expected tokens. If too many, an empty array is returned. + */ + protected function getExpectedTokens(int $state) : array { + $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 $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 + */ + + /** + * Moves statements of semicolon-style namespaces into $ns->stmts and checks various error conditions. + * + * @param Node\Stmt[] $stmts + * @return Node\Stmt[] + */ + protected function handleNamespaces(array $stmts) : array { + $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 + } + } + 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; + } + } + + private function fixupNamespaceAttributes(Node\Stmt\Namespace_ $stmt) { + // 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)); + } + } + } + + /** + * Determine namespacing style (semicolon or brace) + * + * @param Node[] $stmts Top-level statements. + * + * @return null|string One of "semicolon", "brace" or null (no namespaces) + */ + private function getNamespacingStyle(array $stmts) { + $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() // Avoid marking the entire namespace as an error + )); + } + } elseif ($style !== $currentStyle) { + $this->emitError(new Error( + 'Cannot mix bracketed namespace declarations with unbracketed namespace declarations', + $stmt->getLine() // Avoid marking the entire namespace as an error + )); + // 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 $style; + } + + /** + * 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 + */ + protected function fixupPhp5StaticPropCall($prop, array $args, array $attributes) : Expr\StaticCall { + 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; + } + } + + 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)); + } + } + } + + 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()); + } + + /** + * Get combined start and end attributes at a stack location + * + * @param int $pos Stack location + * + * @return array Combined start and end attributes + */ + protected function getAttributesAt(int $pos) : array { + 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; + } + + 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); + } + } + + /** + * 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. + */ + protected function parseNumString(string $str, array $attributes) { + if (!preg_match('/^(?:0|-?[1-9][0-9]*)$/', $str)) { + return new String_($str, $attributes); + } + + $num = +$str; + if (!is_int($num)) { + return new String_($str, $attributes); + } + + return new LNumber($num, $attributes); + } + + protected function stripIndentation( + string $string, int $indentLen, string $indentChar, + bool $newlineAtStart, bool $newlineAtEnd, array $attributes + ) { + 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 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); + } + } + + /** + * Create attributes for a zero-length common-capturing nop. + * + * @param Comment[] $comments + * @return array + */ + protected function createCommentNopAttributes(array $comments) { + $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; + } + + protected function checkModifier($a, $b, $modifierPos) { + // 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) + )); + } + } +} +args($args) + ); + } + + /** + * Creates a namespace builder. + * + * @param null|string|Node\Name $name Name of the namespace + * + * @return Builder\Namespace_ The created namespace builder + */ + public function namespace($name) : Builder\Namespace_ { + return new Builder\Namespace_($name); + } + + /** + * Creates a class builder. + * + * @param string $name Name of the class + * + * @return Builder\Class_ The created class builder + */ + public function class(string $name) : Builder\Class_ { + return new Builder\Class_($name); + } + + /** + * 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 Builder\Interface_($name); + } + + /** + * 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_ { + return new Builder\Trait_($name); + } + + /** + * Creates an enum builder. + * + * @param string $name Name of the enum + * + * @return Builder\Enum_ The created enum builder + */ + public function enum(string $name) : Builder\Enum_ { + return new Builder\Enum_($name); + } + + /** + * 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 new Builder\TraitUse(...$traits); + } + + /** + * 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 { + if ($method === null) { + $method = $trait; + $trait = null; + } + + return new Builder\TraitUseAdaptation($trait, $method); + } + + /** + * 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 new Builder\Method($name); + } + + /** + * 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 new Builder\Param($name); + } + + /** + * 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 { + return new Builder\Property($name); + } + + /** + * Creates a function builder. + * + * @param string $name Name of the function + * + * @return Builder\Function_ The created function builder + */ + public function function(string $name) : Builder\Function_ { + return new Builder\Function_($name); + } + + /** + * 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 use($name) : Builder\Use_ { + return new Builder\Use_($name, Use_::TYPE_NORMAL); + } + + /** + * 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_ { + return new Builder\Use_($name, Use_::TYPE_FUNCTION); + } + + /** + * 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 function useConst($name) : Builder\Use_ { + return new Builder\Use_($name, Use_::TYPE_CONSTANT); + } + + /** + * Creates a class constant builder. + * + * @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 classConst($name, $value) : Builder\ClassConst { + return new Builder\ClassConst($name, $value); + } + + /** + * Creates an enum case builder. + * + * @param string|Identifier $name Name + * + * @return Builder\EnumCase The created use const builder + */ + public function enumCase($name) : Builder\EnumCase { + return new Builder\EnumCase($name); + } + + /** + * Creates node a for a literal value. + * + * @param Expr|bool|null|int|float|string|array $value $value + * + * @return Expr + */ + public function val($value) : Expr { + return BuilderHelpers::normalizeValue($value); + } + + /** + * Creates variable node. + * + * @param string|Expr $name Name + * + * @return Expr\Variable + */ + public function var($name) : Expr\Variable { + if (!\is_string($name) && !$name instanceof Expr) { + throw new \LogicException('Variable name must be string or Expr'); + } + + return new Expr\Variable($name); + } + + /** + * 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[] + */ + public function args(array $args) : array { + $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; + } + + /** + * Creates a function call node. + * + * @param string|Name|Expr $name Function name + * @param array $args Function arguments + * + * @return Expr\FuncCall + */ + public function funcCall($name, array $args = []) : Expr\FuncCall { + return new Expr\FuncCall( + BuilderHelpers::normalizeNameOrExpr($name), + $this->args($args) + ); + } + + /** + * 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 new Expr\MethodCall( + $var, + BuilderHelpers::normalizeIdentifierOrExpr($name), + $this->args($args) + ); + } + + /** + * 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 new Expr\StaticCall( + BuilderHelpers::normalizeNameOrExpr($class), + BuilderHelpers::normalizeIdentifierOrExpr($name), + $this->args($args) + ); + } + + /** + * 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_ { + return new Expr\New_( + BuilderHelpers::normalizeNameOrExpr($class), + $this->args($args) + ); + } + + /** + * Creates a constant fetch node. + * + * @param string|Name $name Constant name + * + * @return Expr\ConstFetch + */ + public function constFetch($name) : Expr\ConstFetch { + return new Expr\ConstFetch(BuilderHelpers::normalizeName($name)); + } + + /** + * 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 new Expr\PropertyFetch($var, BuilderHelpers::normalizeIdentifierOrExpr($name)); + } + + /** + * 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 new Expr\ClassConstFetch( + BuilderHelpers::normalizeNameOrExpr($class), + BuilderHelpers::normalizeIdentifier($name) + ); + } + + /** + * Creates nested Concat nodes from a list of expressions. + * + * @param Expr|string ...$exprs Expressions or literal strings + * + * @return Concat + */ + public function concat(...$exprs) : Concat { + $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|Expr $expr + * @return Expr + */ + private function normalizeStringExpr($expr) : Expr { + if ($expr instanceof Expr) { + return $expr; + } + + if (\is_string($expr)) { + return new String_($expr); + } + + throw new \LogicException('Expected string or Expr'); + } +} +errors[] = $error; + } + + /** + * Get collected errors. + * + * @return Error[] + */ + public function getErrors() : array { + return $this->errors; + } + + /** + * Check whether there are any errors. + * + * @return bool + */ + public function hasErrors() : bool { + return !empty($this->errors); + } + + /** + * Reset/clear collected errors. + */ + public function clearErrors() { + $this->errors = []; + } +} +addVisitor($visitor); + $traverser->traverse($nodes); + + return $visitor->getFoundNodes(); + } + + /** + * 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) + */ + public function findInstanceOf($nodes, string $class) : array { + return $this->find($nodes, function ($node) use ($class) { + return $node instanceof $class; + }); + } + + /** + * 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) { + if (!is_array($nodes)) { + $nodes = [$nodes]; + } + + $visitor = new FirstFindingVisitor($filter); + + $traverser = new NodeTraverser; + $traverser->addVisitor($visitor); + $traverser->traverse($nodes); + + return $visitor->getFoundNode(); + } + + /** + * 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) + */ + public function findFirstInstanceOf($nodes, string $class) { + return $this->findFirst($nodes, function ($node) use ($class) { + return $node instanceof $class; + }); + } +} +getNode(); + } + + if ($node instanceof 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) : Stmt { + $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'); + } + + /** + * Normalizes strings to Identifier. + * + * @param string|Identifier $name The identifier to normalize + * + * @return Identifier The normalized identifier + */ + public static function normalizeIdentifier($name) : Identifier { + if ($name instanceof Identifier) { + return $name; + } + + if (\is_string($name)) { + return new Identifier($name); + } + + throw new \LogicException('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 Identifier || $name instanceof Expr) { + return $name; + } + + if (\is_string($name)) { + return new Identifier($name); + } + + throw new \LogicException('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 + */ + public static function normalizeName($name) : Name { + 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('Name must be a string or an instance of Node\Name'); + } + + /** + * 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 static function normalizeNameOrExpr($name) { + if ($name instanceof Expr) { + return $name; + } + + if (!is_string($name) && !($name instanceof Name)) { + throw new \LogicException( + 'Name must be a string or an instance of Node\Name or Node\Expr' + ); + } + + return self::normalizeName($name); + } + + /** + * 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) { + 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; + } + + /** + * 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 { + 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'); + } + + /** + * 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 { + if ($docComment instanceof Comment\Doc) { + return $docComment; + } + + if (is_string($docComment)) { + return new Comment\Doc($docComment); + } + + throw new \LogicException('Doc comment must be a string or an instance of PhpParser\Comment\Doc'); + } + + /** + * Normalizes a attribute: Converts attribute to the Attribute Group if needed. + * + * @param Node\Attribute|Node\AttributeGroup $attribute + * + * @return Node\AttributeGroup The Attribute Group + */ + public static function normalizeAttribute($attribute) : Node\AttributeGroup + { + if ($attribute instanceof Node\AttributeGroup) { + return $attribute; + } + + if (!($attribute instanceof Node\Attribute)) { + throw new \LogicException('Attribute must be an instance of PhpParser\Node\Attribute or PhpParser\Node\AttributeGroup'); + } + + return new Node\AttributeGroup([$attribute]); + } + + /** + * 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 { + Stmt\Class_::verifyModifier($modifiers, $modifier); + return $modifiers | $modifier; + } +} +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(Node\Arg $node) { + return ($node->name ? $node->name->toString() . ': ' : '') + . ($node->byRef ? '&' : '') . ($node->unpack ? '...' : '') + . $this->p($node->value); + } + + protected function pVariadicPlaceholder(Node\VariadicPlaceholder $node) { + return '...'; + } + + protected function pConst(Node\Const_ $node) { + return $node->name . ' = ' . $this->p($node->value); + } + + protected function pNullableType(Node\NullableType $node) { + return '?' . $this->p($node->type); + } + + protected function pUnionType(Node\UnionType $node) { + return $this->pImplode($node->types, '|'); + } + + protected function pIntersectionType(Node\IntersectionType $node) { + return $this->pImplode($node->types, '&'); + } + + protected function pIdentifier(Node\Identifier $node) { + return $node->name; + } + + protected function pVarLikeIdentifier(Node\VarLikeIdentifier $node) { + return '$' . $node->name; + } + + protected function pAttribute(Node\Attribute $node) { + 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, '"') . '"'; + } + 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; + } + } + 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)'; + } + + $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 { + $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'; + } + } + + // 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; + } + + protected function pScalar_EncapsedStringPart(Scalar\EncapsedStringPart $node) { + throw new \LogicException('Cannot directly print EncapsedStringPart'); + } + + // Assignments + + protected function pExpr_Assign(Expr\Assign $node) { + return $this->pInfixOp(Expr\Assign::class, $node->var, ' = ', $node->expr); + } + + protected function pExpr_AssignRef(Expr\AssignRef $node) { + return $this->pInfixOp(Expr\AssignRef::class, $node->var, ' =& ', $node->expr); + } + + protected function pExpr_AssignOp_Plus(AssignOp\Plus $node) { + return $this->pInfixOp(AssignOp\Plus::class, $node->var, ' += ', $node->expr); + } + + protected function pExpr_AssignOp_Minus(AssignOp\Minus $node) { + return $this->pInfixOp(AssignOp\Minus::class, $node->var, ' -= ', $node->expr); + } + + protected function pExpr_AssignOp_Mul(AssignOp\Mul $node) { + return $this->pInfixOp(AssignOp\Mul::class, $node->var, ' *= ', $node->expr); + } + + protected function pExpr_AssignOp_Div(AssignOp\Div $node) { + return $this->pInfixOp(AssignOp\Div::class, $node->var, ' /= ', $node->expr); + } + + protected function pExpr_AssignOp_Concat(AssignOp\Concat $node) { + return $this->pInfixOp(AssignOp\Concat::class, $node->var, ' .= ', $node->expr); + } + + protected function pExpr_AssignOp_Mod(AssignOp\Mod $node) { + return $this->pInfixOp(AssignOp\Mod::class, $node->var, ' %= ', $node->expr); + } + + protected function pExpr_AssignOp_BitwiseAnd(AssignOp\BitwiseAnd $node) { + return $this->pInfixOp(AssignOp\BitwiseAnd::class, $node->var, ' &= ', $node->expr); + } + + protected function pExpr_AssignOp_BitwiseOr(AssignOp\BitwiseOr $node) { + return $this->pInfixOp(AssignOp\BitwiseOr::class, $node->var, ' |= ', $node->expr); + } + + protected function pExpr_AssignOp_BitwiseXor(AssignOp\BitwiseXor $node) { + return $this->pInfixOp(AssignOp\BitwiseXor::class, $node->var, ' ^= ', $node->expr); + } + + protected function pExpr_AssignOp_ShiftLeft(AssignOp\ShiftLeft $node) { + return $this->pInfixOp(AssignOp\ShiftLeft::class, $node->var, ' <<= ', $node->expr); + } + + protected function pExpr_AssignOp_ShiftRight(AssignOp\ShiftRight $node) { + return $this->pInfixOp(AssignOp\ShiftRight::class, $node->var, ' >>= ', $node->expr); + } + + protected function pExpr_AssignOp_Pow(AssignOp\Pow $node) { + return $this->pInfixOp(AssignOp\Pow::class, $node->var, ' **= ', $node->expr); + } + + protected function pExpr_AssignOp_Coalesce(AssignOp\Coalesce $node) { + return $this->pInfixOp(AssignOp\Coalesce::class, $node->var, ' ??= ', $node->expr); + } + + // Binary expressions + + protected function pExpr_BinaryOp_Plus(BinaryOp\Plus $node) { + return $this->pInfixOp(BinaryOp\Plus::class, $node->left, ' + ', $node->right); + } + + protected function pExpr_BinaryOp_Minus(BinaryOp\Minus $node) { + return $this->pInfixOp(BinaryOp\Minus::class, $node->left, ' - ', $node->right); + } + + protected function pExpr_BinaryOp_Mul(BinaryOp\Mul $node) { + return $this->pInfixOp(BinaryOp\Mul::class, $node->left, ' * ', $node->right); + } + + protected function pExpr_BinaryOp_Div(BinaryOp\Div $node) { + return $this->pInfixOp(BinaryOp\Div::class, $node->left, ' / ', $node->right); + } + + protected function pExpr_BinaryOp_Concat(BinaryOp\Concat $node) { + return $this->pInfixOp(BinaryOp\Concat::class, $node->left, ' . ', $node->right); + } + + protected function pExpr_BinaryOp_Mod(BinaryOp\Mod $node) { + return $this->pInfixOp(BinaryOp\Mod::class, $node->left, ' % ', $node->right); + } + + protected function pExpr_BinaryOp_BooleanAnd(BinaryOp\BooleanAnd $node) { + return $this->pInfixOp(BinaryOp\BooleanAnd::class, $node->left, ' && ', $node->right); + } + + protected function pExpr_BinaryOp_BooleanOr(BinaryOp\BooleanOr $node) { + return $this->pInfixOp(BinaryOp\BooleanOr::class, $node->left, ' || ', $node->right); + } + + protected function pExpr_BinaryOp_BitwiseAnd(BinaryOp\BitwiseAnd $node) { + return $this->pInfixOp(BinaryOp\BitwiseAnd::class, $node->left, ' & ', $node->right); + } + + protected function pExpr_BinaryOp_BitwiseOr(BinaryOp\BitwiseOr $node) { + return $this->pInfixOp(BinaryOp\BitwiseOr::class, $node->left, ' | ', $node->right); + } + + protected function pExpr_BinaryOp_BitwiseXor(BinaryOp\BitwiseXor $node) { + return $this->pInfixOp(BinaryOp\BitwiseXor::class, $node->left, ' ^ ', $node->right); + } + + protected function pExpr_BinaryOp_ShiftLeft(BinaryOp\ShiftLeft $node) { + return $this->pInfixOp(BinaryOp\ShiftLeft::class, $node->left, ' << ', $node->right); + } + + protected function pExpr_BinaryOp_ShiftRight(BinaryOp\ShiftRight $node) { + return $this->pInfixOp(BinaryOp\ShiftRight::class, $node->left, ' >> ', $node->right); + } + + protected function pExpr_BinaryOp_Pow(BinaryOp\Pow $node) { + return $this->pInfixOp(BinaryOp\Pow::class, $node->left, ' ** ', $node->right); + } + + protected function pExpr_BinaryOp_LogicalAnd(BinaryOp\LogicalAnd $node) { + return $this->pInfixOp(BinaryOp\LogicalAnd::class, $node->left, ' and ', $node->right); + } + + protected function pExpr_BinaryOp_LogicalOr(BinaryOp\LogicalOr $node) { + return $this->pInfixOp(BinaryOp\LogicalOr::class, $node->left, ' or ', $node->right); + } + + protected function pExpr_BinaryOp_LogicalXor(BinaryOp\LogicalXor $node) { + return $this->pInfixOp(BinaryOp\LogicalXor::class, $node->left, ' xor ', $node->right); + } + + protected function pExpr_BinaryOp_Equal(BinaryOp\Equal $node) { + return $this->pInfixOp(BinaryOp\Equal::class, $node->left, ' == ', $node->right); + } + + protected function pExpr_BinaryOp_NotEqual(BinaryOp\NotEqual $node) { + return $this->pInfixOp(BinaryOp\NotEqual::class, $node->left, ' != ', $node->right); + } + + protected function pExpr_BinaryOp_Identical(BinaryOp\Identical $node) { + return $this->pInfixOp(BinaryOp\Identical::class, $node->left, ' === ', $node->right); + } + + protected function pExpr_BinaryOp_NotIdentical(BinaryOp\NotIdentical $node) { + return $this->pInfixOp(BinaryOp\NotIdentical::class, $node->left, ' !== ', $node->right); + } + + protected function pExpr_BinaryOp_Spaceship(BinaryOp\Spaceship $node) { + return $this->pInfixOp(BinaryOp\Spaceship::class, $node->left, ' <=> ', $node->right); + } + + protected function pExpr_BinaryOp_Greater(BinaryOp\Greater $node) { + return $this->pInfixOp(BinaryOp\Greater::class, $node->left, ' > ', $node->right); + } + + protected function pExpr_BinaryOp_GreaterOrEqual(BinaryOp\GreaterOrEqual $node) { + return $this->pInfixOp(BinaryOp\GreaterOrEqual::class, $node->left, ' >= ', $node->right); + } + + protected function pExpr_BinaryOp_Smaller(BinaryOp\Smaller $node) { + return $this->pInfixOp(BinaryOp\Smaller::class, $node->left, ' < ', $node->right); + } + + protected function pExpr_BinaryOp_SmallerOrEqual(BinaryOp\SmallerOrEqual $node) { + return $this->pInfixOp(BinaryOp\SmallerOrEqual::class, $node->left, ' <= ', $node->right); + } + + protected function pExpr_BinaryOp_Coalesce(BinaryOp\Coalesce $node) { + 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) . ')'; + } + return $this->pPrefixOp(Expr\UnaryMinus::class, '-', $node->expr); + } + + protected function pExpr_UnaryPlus(Expr\UnaryPlus $node) { + if ($node->expr instanceof Expr\UnaryPlus || $node->expr instanceof Expr\PreInc) { + // Enforce +(+$expr) instead of ++$expr + return '+(' . $this->p($node->expr) . ')'; + } + return $this->pPrefixOp(Expr\UnaryPlus::class, '+', $node->expr); + } + + protected function pExpr_PreInc(Expr\PreInc $node) { + return $this->pPrefixOp(Expr\PreInc::class, '++', $node->var); + } + + protected function pExpr_PreDec(Expr\PreDec $node) { + return $this->pPrefixOp(Expr\PreDec::class, '--', $node->var); + } + + protected function pExpr_PostInc(Expr\PostInc $node) { + return $this->pPostfixOp(Expr\PostInc::class, $node->var, '++'); + } + + protected function pExpr_PostDec(Expr\PostDec $node) { + return $this->pPostfixOp(Expr\PostDec::class, $node->var, '--'); + } + + protected function pExpr_ErrorSuppress(Expr\ErrorSuppress $node) { + return $this->pPrefixOp(Expr\ErrorSuppress::class, '@', $node->expr); + } + + protected function pExpr_YieldFrom(Expr\YieldFrom $node) { + return $this->pPrefixOp(Expr\YieldFrom::class, 'yield from ', $node->expr); + } + + protected function pExpr_Print(Expr\Print_ $node) { + return $this->pPrefixOp(Expr\Print_::class, 'print ', $node->expr); + } + + // Casts + + protected function pExpr_Cast_Int(Cast\Int_ $node) { + return $this->pPrefixOp(Cast\Int_::class, '(int) ', $node->expr); + } + + protected function pExpr_Cast_Double(Cast\Double $node) { + $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)'; + } + return $this->pPrefixOp(Cast\Double::class, $cast . ' ', $node->expr); + } + + protected function pExpr_Cast_String(Cast\String_ $node) { + return $this->pPrefixOp(Cast\String_::class, '(string) ', $node->expr); + } + + protected function pExpr_Cast_Array(Cast\Array_ $node) { + return $this->pPrefixOp(Cast\Array_::class, '(array) ', $node->expr); + } + + protected function pExpr_Cast_Object(Cast\Object_ $node) { + return $this->pPrefixOp(Cast\Object_::class, '(object) ', $node->expr); + } + + protected function pExpr_Cast_Bool(Cast\Bool_ $node) { + 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; + } + } + + 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) . ')'; + } + } + + 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); + } + + /** + * @param Node[] $nodes + * @return bool + */ + protected function hasNodeWithComments(array $nodes) { + foreach ($nodes as $node) { + if ($node && $node->getComments()) { + return true; + } + } + 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; + } + } + + protected function pAttrGroups(array $nodes, bool $inline = false): string { + $result = ''; + $sep = $inline ? ' ' : $this->nl; + foreach ($nodes as $node) { + $result .= $this->p($node) . $sep; + } + + return $result; + } +} +attributes = $attributes; + $this->name = $name; + $this->args = $args; + } + + public function getSubNodeNames() : array { + return ['name', 'args']; + } + + public function getType() : string { + return 'Attribute'; + } +} +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'; + } +} +attributes = $attributes; + $this->types = $types; + } + + public function getSubNodeNames() : array { + return ['types']; + } + + public function getType() : string { + return 'UnionType'; + } +} +attributes = $attributes; + $this->remaining = $remaining; + } + + public function getSubNodeNames() : array { + return ['remaining']; + } + + public function getType() : string { + return 'Stmt_HaltCompiler'; + } +} +attributes = $attributes; + $this->num = $num; + } + + public function getSubNodeNames() : array { + return ['num']; + } + + public function getType() : string { + return 'Stmt_Break'; + } +} + array(): Statements + * 'elseifs' => array(): Elseif clauses + * 'else' => null : Else clause + * @param array $attributes Additional attributes + */ + public function __construct(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 { + return 'Stmt_If'; + } +} +attributes = $attributes; + $this->cond = $cond; + $this->stmts = $stmts; + } + + public function getSubNodeNames() : array { + return ['cond', 'stmts']; + } + + public function getType() : string { + 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 __construct($name, array $subNodes = [], array $attributes = []) { + $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 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 { + return 'Stmt_Function'; + } +} +attributes = $attributes; + $this->cond = $cond; + $this->stmts = $stmts; + } + + public function getSubNodeNames() : array { + return ['cond', 'stmts']; + } + + public function getType() : string { + return 'Stmt_ElseIf'; + } +} + 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 = []) { + $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 __construct($name, array $subNodes = [], array $attributes = []) { + $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']; + } + + /** + * Whether the class is explicitly abstract. + * + * @return bool + */ + public function isAbstract() : bool { + return (bool) ($this->flags & self::MODIFIER_ABSTRACT); + } + + /** + * Whether the class is final. + * + * @return bool + */ + public function isFinal() : bool { + return (bool) ($this->flags & self::MODIFIER_FINAL); + } + + /** + * Whether the class is anonymous. + * + * @return bool + */ + public function isAnonymous() : bool { + 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 ($a & self::MODIFIER_ABSTRACT && $b & self::MODIFIER_ABSTRACT) { + throw new Error('Multiple abstract modifiers are not allowed'); + } + + if ($a & self::MODIFIER_STATIC && $b & self::MODIFIER_STATIC) { + throw new Error('Multiple static modifiers are not allowed'); + } + + if ($a & self::MODIFIER_FINAL && $b & self::MODIFIER_FINAL) { + throw new Error('Multiple final modifiers are not allowed'); + } + + if ($a & self::MODIFIER_READONLY && $b & self::MODIFIER_READONLY) { + throw new Error('Multiple readonly modifiers are not allowed'); + } + + if ($a & 48 && $b & 48) { + throw new Error('Cannot use the final modifier on an abstract class member'); + } + } + + public function getType() : string { + return 'Stmt_Class'; + } +} +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'; + } +} +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Stmt_Throw'; + } +} +attributes = $attributes; + $this->num = $num; + } + + public function getSubNodeNames() : array { + return ['num']; + } + + public function getType() : string { + return 'Stmt_Continue'; + } +} +attributes = $attributes; + $this->stmts = $stmts; + } + + public function getSubNodeNames() : array { + return ['stmts']; + } + + public function getType() : string { + return 'Stmt_Else'; + } +} + 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 { + return 'Stmt_Trait'; + } +} +attributes = $attributes; + $this->vars = $vars; + } + + public function getSubNodeNames() : array { + return ['vars']; + } + + public function getType() : string { + return 'Stmt_Unset'; + } +} +attributes = $attributes; + $this->exprs = $exprs; + } + + public function getSubNodeNames() : array { + return ['exprs']; + } + + public function getType() : string { + return 'Stmt_Echo'; + } +} +attributes = $attributes; + $this->name = \is_string($name) ? new Identifier($name) : $name; + } + + public function getSubNodeNames() : array { + return ['name']; + } + + public function getType() : string { + return 'Stmt_Label'; + } +} +attributes = $attributes; + $this->cond = $cond; + $this->stmts = $stmts; + } + + public function getSubNodeNames() : array { + return ['stmts', 'cond']; + } + + public function getType() : string { + return 'Stmt_Do'; + } +} +value pair node. + * + * @param string|Node\Identifier $key Key + * @param Node\Expr $value Value + * @param array $attributes Additional attributes + */ + public function __construct($key, Node\Expr $value, array $attributes = []) { + $this->attributes = $attributes; + $this->key = \is_string($key) ? new Node\Identifier($key) : $key; $this->value = $value; } + public function getSubNodeNames() : array { + return ['key', 'value']; + } + + public function getType() : string { + return 'Stmt_DeclareDeclare'; + } +} +line; + public function __construct(array $stmts = [], array $attributes = []) { + $this->attributes = $attributes; + $this->stmts = $stmts; + } + + public function getSubNodeNames() : array { + return ['stmts']; + } + + public function getType() : string { + 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 getSubNodeNames() : array { + return ['trait', 'method', 'insteadof']; + } + + public function getType() : string { + return 'Stmt_TraitUseAdaptation_Precedence'; + } +} +attributes = $attributes; + $this->cond = $cond; + $this->cases = $cases; + } + + public function getSubNodeNames() : array { + return ['cond', 'cases']; + } + + public function getType() : string { + return 'Stmt_Switch'; + } +} +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; + } + } + return $traitUses; } /** + * @return ClassConst[] + */ + public function getConstants() : array { + $constants = []; + foreach ($this->stmts as $stmt) { + if ($stmt instanceof ClassConst) { + $constants[] = $stmt; + } + } + return $constants; + } + + /** + * @return Property[] + */ + public function getProperties() : array { + $properties = []; + foreach ($this->stmts as $stmt) { + if ($stmt instanceof Property) { + $properties[] = $stmt; + } + } + return $properties; + } + + /** + * 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 + */ + public function getProperty(string $name) { + 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 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; + } +} +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'; + } +} +attributes = $attributes; + $this->declares = $declares; + $this->stmts = $stmts; + } + + public function getSubNodeNames() : array { + return ['declares', 'stmts']; + } + + public function getType() : string { + 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'; + } +} + 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(Node\Expr $expr, Node\Expr $valueVar, array $subNodes = [], array $attributes = []) { + $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'; + } +} +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']; + } + + /** + * Get alias. If not explicitly given this is the last component of the used name. + * + * @return Identifier + */ + public function getAlias() : Identifier { + if (null !== $this->alias) { + return $this->alias; + } + + return new Identifier($this->name->getLast()); + } + + public function getType() : string { + 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 __construct($name, array $subNodes = [], array $attributes = []) { + $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, + ]; + + /** + * 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 __construct($name, array $subNodes = [], array $attributes = []) { + $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; + } + + /** + * Whether the method 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 method is protected. + * + * @return bool + */ + public function isProtected() : bool { + return (bool) ($this->flags & Class_::MODIFIER_PROTECTED); + } + + /** + * Whether the method is private. + * + * @return bool + */ + public function isPrivate() : bool { + return (bool) ($this->flags & Class_::MODIFIER_PRIVATE); + } + + /** + * Whether the method is abstract. + * + * @return bool + */ + public function isAbstract() : bool { + return (bool) ($this->flags & Class_::MODIFIER_ABSTRACT); + } + + /** + * Whether the method is final. + * + * @return bool + */ + public function isFinal() : bool { + return (bool) ($this->flags & Class_::MODIFIER_FINAL); + } + + /** + * Whether the method is static. + * + * @return bool + */ + public function isStatic() : bool { + return (bool) ($this->flags & Class_::MODIFIER_STATIC); + } + + /** + * Whether the method is magic. + * + * @return bool + */ + public function isMagic() : bool { + 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'; + } +} +attributes = $attributes; + $this->vars = $vars; + } + + public function getSubNodeNames() : array { + return ['vars']; + } + + public function getType() : string { + return 'Stmt_Global'; + } +} +attributes = $attributes; + $this->name = \is_string($name) ? new Node\VarLikeIdentifier($name) : $name; + $this->default = $default; + } + + public function getSubNodeNames() : array { + return ['name', 'default']; + } + + public function getType() : string { + return 'Stmt_PropertyProperty'; + } +} + array(): Init expressions + * 'cond' => array(): Loop conditions + * 'loop' => array(): Loop expressions + * 'stmts' => array(): Statements + * @param array $attributes Additional attributes + */ + public function __construct(array $subNodes = [], array $attributes = []) { + $this->attributes = $attributes; + $this->init = $subNodes['init'] ?? []; + $this->cond = $subNodes['cond'] ?? []; + $this->loop = $subNodes['loop'] ?? []; + $this->stmts = $subNodes['stmts'] ?? []; + } + + public function getSubNodeNames() : array { + return ['init', 'cond', 'loop', 'stmts']; + } + + public function getType() : string { + 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'; + } +} +attributes = $attributes; + $this->name = $name; + $this->stmts = $stmts; + } + + public function getSubNodeNames() : array { + return ['name', 'stmts']; + } + + public function getType() : string { + return 'Stmt_Namespace'; + } +} +attributes = $attributes; + $this->flags = $flags; + $this->consts = $consts; + $this->attrGroups = $attrGroups; + } + + public function getSubNodeNames() : array { + return ['attrGroups', 'flags', 'consts']; + } + + /** + * Whether constant 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 constant is protected. + * + * @return bool + */ + public function isProtected() : bool { + return (bool) ($this->flags & Class_::MODIFIER_PROTECTED); + } + + /** + * Whether constant is private. + * + * @return bool + */ + public function isPrivate() : bool { + return (bool) ($this->flags & Class_::MODIFIER_PRIVATE); + } + + /** + * Whether constant is final. + * + * @return bool + */ + public function isFinal() : bool { + return (bool) ($this->flags & Class_::MODIFIER_FINAL); + } + + public function getType() : string { + return 'Stmt_ClassConst'; + } +} +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'; + } +} +attributes = $attributes; + $this->key = $key; + $this->value = $value; + $this->byRef = $byRef; + $this->unpack = $unpack; + } + + public function getSubNodeNames() : array { + return ['key', 'value', 'byRef', 'unpack']; + } + + public function getType() : string { + return 'Expr_ArrayItem'; + } +} +attributes = $attributes; + $this->var = $var; + } + + public function getSubNodeNames() : array { + return ['var']; + } + + public function getType() : string { + return 'Expr_PreDec'; + } +} +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } +} +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Expr_Clone'; + } +} +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Expr_UnaryPlus'; + } +} +attributes = $attributes; + $this->var = $var; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['var', 'expr']; + } +} + + */ + abstract public function getRawArgs(): array; + + /** + * Returns whether this call expression is actually a first class callable. + */ + public function isFirstClassCallable(): bool { + foreach ($this->getRawArgs() as $arg) { + if ($arg instanceof VariadicPlaceholder) { + return true; + } + } + return false; + } + + /** + * Assert that this is not a first-class callable and return only ordinary Args. + * + * @return Arg[] + */ + public function getArgs(): array { + assert(!$this->isFirstClassCallable()); + return $this->getRawArgs(); + } +}attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Expr_Throw'; + } +} +attributes = $attributes; + $this->key = $key; + $this->value = $value; + } + + public function getSubNodeNames() : array { + return ['key', 'value']; + } + + public function getType() : string { + return 'Expr_Yield'; + } +} +attributes = $attributes; + $this->cond = $cond; + $this->arms = $arms; + } + + public function getSubNodeNames() : array { + return ['cond', 'arms']; + } + + public function getType() : string { + return 'Expr_Match'; + } +} +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Expr_Print'; + } +} +attributes = $attributes; + $this->name = $name; + } + + public function getSubNodeNames() : array { + return ['name']; + } + + public function getType() : string { + return 'Expr_Variable'; + } +} +attributes = $attributes; + } + + public function getSubNodeNames() : array { + return []; + } + + public function getType() : string { + return 'Expr_Error'; + } +} +attributes = $attributes; + $this->var = $var; + } + + public function getSubNodeNames() : array { + return ['var']; + } + + public function getType() : string { + return 'Expr_PostDec'; + } +} +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Expr_Eval'; + } +} + Arguments */ + public $args; + + /** + * Constructs a function call node. + * + * @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 function __construct($class, array $args = [], array $attributes = []) { + $this->attributes = $attributes; + $this->class = $class; + $this->args = $args; + } + + public function getSubNodeNames() : array { + return ['class', 'args']; + } + + public function getType() : string { + return 'Expr_New'; + } + + public function getRawArgs(): array { + return $this->args; + } +} + 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 Node\Identifier($returnType) : $returnType; + $this->expr = $subNodes['expr']; + $this->attrGroups = $subNodes['attrGroups'] ?? []; + } + + public function getSubNodeNames() : array { + return ['attrGroups', 'static', 'byRef', 'params', 'returnType', 'expr']; + } + + 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\Return_[] + */ + public function getStmts() : array { + return [new Node\Stmt\Return_($this->expr)]; + } + + public function getType() : string { + return 'Expr_ArrowFunction'; + } +} +'; + } + + public function getType() : string { + return 'Expr_BinaryOp_Spaceship'; + } +} +'; + } + + public function getType() : string { + return 'Expr_BinaryOp_Greater'; + } +} +>'; + } + + public function getType() : string { + return 'Expr_BinaryOp_ShiftRight'; + } +} +='; + } + + public function getType() : string { + return 'Expr_BinaryOp_GreaterOrEqual'; + } +} +attributes = $attributes; + $this->var = $var; + } + + public function getSubNodeNames() : array { + 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'; + } +} + Arguments */ + public $args; + + /** + * Constructs a nullsafe method call node. + * + * @param Expr $var Variable holding object + * @param string|Identifier|Expr $name Method name + * @param array $args Arguments + * @param array $attributes Additional attributes + */ + 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_NullsafeMethodCall'; + } + + public function getRawArgs(): array { + return $this->args; + } +} +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'; + } +} +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'; + } +} +attributes = $attributes; + $this->var = $var; + $this->dim = $dim; + } + + public function getSubNodeNames() : array { + return ['var', 'dim']; + } + + public function getType() : string { + return 'Expr_ArrayDimFetch'; + } +} +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; + } + + public function getSubNodeNames() : array { + return ['name']; + } + + public function getType() : string { + return 'Expr_ConstFetch'; + } +} + Arguments */ + public $args; + + /** + * Constructs a static method call node. + * + * @param Node\Name|Expr $class Class name + * @param string|Identifier|Expr $name Method name + * @param array $args Arguments + * @param array $attributes Additional attributes + */ + public function __construct($class, $name, array $args = [], array $attributes = []) { + $this->attributes = $attributes; + $this->class = $class; + $this->name = \is_string($name) ? new Identifier($name) : $name; + $this->args = $args; + } + + 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; + + /** + * Constructs a function call node. + * + * @param Node\Name|Expr $name Function name + * @param array $args Arguments + * @param array $attributes Additional attributes + */ + 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; + } +} +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'; + } +} +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'; + } +} +attributes = $attributes; + $this->items = $items; + } + + public function getSubNodeNames() : array { + return ['items']; + } + + public function getType() : string { + return 'Expr_Array'; + } +} +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 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'; + } +} +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'; + } +} +attributes = $attributes; + $this->left = $left; + $this->right = $right; + } + + public function getSubNodeNames() : array { + return ['left', 'right']; + } + + /** + * 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 function getName(): string { + abstract public function getOperatorSigil() : string; +} +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'; + } +} +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; + + /** + * Constructs a function call node. + * + * @param Expr $var Variable holding object + * @param string|Identifier|Expr $name Method name + * @param array $args Arguments + * @param array $attributes Additional attributes + */ + 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; + } +} +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Expr_BitwiseNot'; + } +} +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'; + } +} +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'; + } +} +attributes = $attributes; + $this->type = \is_string($type) ? new Identifier($type) : $type; + } + + public function getSubNodeNames() : array { + return ['type']; + } + + public function getType() : string { + return 'NullableType'; + } +} +attributes = $attributes; + } + + public function getSubNodeNames() : array { + return []; + } + + /** + * Get name of magic constant. + * + * @return string Name of magic constant + */ + abstract public function getName() : string; +} +attributes = $attributes; + $this->value = $value; + } + + public function getSubNodeNames() : array { + return ['value']; + } + + public function getType() : string { + return 'Scalar_EncapsedStringPart'; + } +} +attributes = $attributes; + $this->value = $value; + } + + public function getSubNodeNames() : array { + return ['value']; + } + + /** + * 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 + */ + 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' => "\x1B", + ]; + + /** + * Constructs a string scalar node. + * + * @param string $value Value of the string + * @param array $attributes Additional attributes + */ + public function __construct(string $value, array $attributes = []) { + $this->attributes = $attributes; + $this->value = $value; + } + + public function getSubNodeNames() : array { + return ['value']; + } + + /** + * @internal + * + * Parses a string token. + * + * @param string $str String token content + * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes + * + * @return string The parsed string + */ + 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 + ); + } + } + + /** + * @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 + * + * @return string String with escape sequences parsed + */ + 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 + ); + } + + /** + * Converts a Unicode code point to its UTF-8 encoded representation. + * + * @param int $num Code point + * + * @return string UTF-8 representation of code point + */ + 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'; + } +} +attributes = $attributes; + $this->value = $value; + } + + public function getSubNodeNames() : array { + return ['value']; + } + + /** + * @internal + * + * Parses a DNUMBER token like PHP would. + * + * @param string $str A string number + * + * @return float The parsed number + */ + 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'; + } +} +attributes = $attributes; + } + + public function getType(): string { + return 'VariadicPlaceholder'; + } + + public function getSubNodeNames(): array { + return []; + } +} true, + 'parent' => true, + 'static' => true, + ]; + + /** + * Constructs a name node. + * + * @param string|string[]|self $name Name as string, part array or Name instance (copy ctor) + * @param array $attributes Additional attributes + */ + public function __construct($name, array $attributes = []) { + $this->attributes = $attributes; + $this->parts = self::prepareName($name); + } + + public function getSubNodeNames() : array { + return ['parts']; + } + + /** + * Gets the first part of the name, i.e. everything before the first namespace separator. + * + * @return string First part of the name + */ + public function getFirst() : string { + return $this->parts[0]; + } + + /** + * Gets the last part of the name, i.e. everything after the last namespace separator. + * + * @return string Last part of the name + */ + public function getLast() : string { + return $this->parts[count($this->parts) - 1]; + } + + /** + * Checks whether the name is unqualified. (E.g. Name) + * + * @return bool Whether the name is unqualified + */ + public function isUnqualified() : bool { + return 1 === count($this->parts); + } + + /** + * Checks whether the name is qualified. (E.g. Name\Name) + * + * @return bool Whether the name is qualified + */ + public function isQualified() : bool { + return 1 < count($this->parts); + } + + /** + * Checks whether the name is fully qualified. (E.g. \Name) + * + * @return bool Whether the name is fully qualified + */ + public function isFullyQualified() : bool { + return false; + } + + /** + * Checks whether the name is explicitly relative to the current namespace. (E.g. namespace\Name) + * + * @return bool Whether the name is relative + */ + public function isRelative() : bool { + return false; + } + + /** + * 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). + * + * @return string String representation + */ + public function toString() : string { + return implode('\\', $this->parts); + } + + /** + * 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 + */ + public function toCodeString() : string { + return $this->toString(); + } + + /** + * 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 + */ + public function toLowerString() : string { + return strtolower(implode('\\', $this->parts)); + } + + /** + * Checks whether the identifier is a special class name (self, parent or static). + * + * @return bool Whether identifier is a special class name + */ + public function isSpecialClassName() : bool { + return count($this->parts) === 1 + && isset(self::$specialClassNames[strtolower($this->parts[0])]); + } + + /** + * Returns a string representation of the name by imploding the namespace parts with the + * namespace separator. + * + * @return string String representation + */ + public function __toString() : string { + return implode('\\', $this->parts); + } + + /** + * 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 + */ + 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); + } + + /** + * 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 + */ + 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 + ); + } + } + + /** + * 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 + */ + 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, + ]; + + /** + * Constructs an identifier node. + * + * @param string $name Identifier as string + * @param array $attributes Additional attributes + */ + public function __construct(string $name, array $attributes = []) { + $this->attributes = $attributes; + $this->name = $name; + } + + public function getSubNodeNames() : array { + return ['name']; + } + + /** + * Get identifier as string. + * + * @return string Identifier as string. + */ + public function toString() : string { return $this->name; } /** - * @return string + * Get lowercased identifier as string. + * + * @return string Lowercased identifier as string */ - public function getValue(): string { - return $this->value; + public function toLowerString() : string { + return strtolower($this->name); } + /** + * Checks whether the identifier is a special class name (self, parent or static). + * + * @return bool Whether identifier is a special class name + */ + public function isSpecialClassName() : bool { + return isset(self::$specialClassNames[strtolower($this->name)]); + } + + /** + * Get identifier as string. + * + * @return string Identifier as string + */ + 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'; + } +} +visitors[] = $visitor; + } + + /** + * Removes an added visitor. + * + * @param NodeVisitor $visitor + */ + public function removeVisitor(NodeVisitor $visitor) { + foreach ($this->visitors as $index => $storedVisitor) { + if ($storedVisitor === $visitor) { + unset($this->visitors[$index]); + break; + } + } + } + + /** + * 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 { + $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; + } + + /** + * Recursively traverse a node. + * + * @param Node $node Node to traverse. + * + * @return Node Result of traversal (may be original node or new one) + */ + 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; + } + + /** + * Recursively traverse array (usually of nodes). + * + * @param array $nodes Array to traverse + * + * @return array Result of traversal (may be original array or changed one) + */ + 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()})" + ); + } + } +} + $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) + */ + public function enterNode(Node $node); + + /** + * Called when leaving a node. + * + * 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 Node $node Node + * + * @return null|int|Node|Node[] Replacement node (or special return value) + */ + public function leaveNode(Node $node); + + /** + * Called once after 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 + */ + 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; + } + + /** + * Gets line the node started in (alias of getStartLine). + * + * @return int Start line (or -1 if not available) + */ + public function getLine() : int { + return $this->attributes['startLine'] ?? -1; + } + + /** + * 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 { + return $this->attributes['startLine'] ?? -1; + } + + /** + * 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 { + return $this->attributes['endLine'] ?? -1; + } + + /** + * 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 { + return $this->attributes['startTokenPos'] ?? -1; + } + + /** + * 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 { + return $this->attributes['endTokenPos'] ?? -1; + } + + /** + * 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 { + return $this->attributes['startFilePos'] ?? -1; + } + + /** + * 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 { + return $this->attributes['endFilePos'] ?? -1; + } + + /** + * Gets all comments directly preceding this node. + * + * The comments are also available through the "comments" attribute. + * + * @return Comment[] + */ + public function getComments() : array { + return $this->attributes['comments'] ?? []; + } + + /** + * Gets the doc comment of the node. + * + * @return null|Comment\Doc Doc comment object or null + */ + 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; + } + + /** + * 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) { + $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; + } + + /** + * @return array + */ + public function jsonSerialize() : array { + return ['nodeType' => $this->getType()] + get_object_vars($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']); + } + + /** + * 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, 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; + } + + /** + * Check whether comment token is unterminated. + * + * @return bool + */ + 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, + ])); + } + } + } + + /** + * 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 + * + * @return int Token id + */ + 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'); + } + + /** + * 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. + * + * @return array Array of tokens in token_get_all() format + */ + public function getTokens() : array { + return $this->tokens; + } + + /** + * Handles __halt_compiler() by returning the text after it. + * + * @return string Remaining text + */ + 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; + } + + /** + * 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 ';'. + * + * @return array The token map + */ + protected function createTokenMap() : array { + $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); + } + } + } + + // HHVM uses a special token for numbers that overflow to double + if (defined('T_ONUMBER')) { + $tokenMap[\T_ONUMBER] = 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] = Tokens::T_STRING; + } + + // 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; + } + + private function createIdentifierTokenMap(): array { + // 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); + } } '", + "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( + 132, 133, 134, 569, 135, 136, 0, 722, 723, 724, + 137, 37, 834, 911, 835, 469,-32766,-32766,-32766,-32767, + -32767,-32767,-32767, 101, 102, 103, 104, 105, 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, 447, 448, 449, 370, 371, 2, 267, + 138, 396, 729, 730, 731, 732, 414,-32766, 420,-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, 336, 337, 758, 759, 744, 745, + 746, 748, 749, 750, 346, 790, 791, 792, 793, 794, + 795, 751, 752, 571, 572, 784, 775, 773, 774, 787, + 770, 771, 283, 420, 573, 574, 769, 575, 576, 577, + 578, 579, 580, 598, -575, 470, 14, 798, 772, 581, + 582, -575, 139,-32766,-32766,-32766, 132, 133, 134, 569, + 135, 136, 1017, 722, 723, 724, 137, 37, 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, 372, 371, 1258,-32766,-32766,-32766, -572, + 106, 107, 108, 414, 270, 891, -572, 240, 1193, 1192, + 1194, 725,-32766,-32766,-32766, 1046, 109,-32766,-32766,-32766, + -32766, 986, 985, 984, 987, 267, 138, 396, 729, 730, + 731, 732, 12,-32766, 420,-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, + 336, 337, 758, 759, 744, 745, 746, 748, 749, 750, + 346, 790, 791, 792, 793, 794, 795, 751, 752, 571, + 572, 784, 775, 773, 774, 787, 770, 771, 881, 321, + 573, 574, 769, 575, 576, 577, 578, 579, 580,-32766, + 82, 83, 84, -575, 772, 581, 582, -575, 148, 747, + 717, 718, 719, 720, 721, 1278, 722, 723, 724, 760, + 761, 36, 1277, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 996, 270, 150, + -32766,-32766,-32766, 455, 456, 81, 34, -264, -572, 1016, + 109, 320, -572, 893, 725, 682, 803, 128, 998, 999, + 592,-32766, 1044,-32766,-32766,-32766, 809, 151, 726, 727, + 728, 729, 730, 731, 732, -88, 1198, 796, 278, -526, + 283,-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, 144, 804, 762, 768, 769, 776, 777, 779, 778, + 780, 781, -314, -526, -526, -193, -192, 772, 783, 782, + 49, 50, 51, 500, 52, 53, 239, 807, -526, -86, + 54, 55, -111, 56, 996, 253,-32766, -111, 800, -111, + -526, 541, -532, -352, 300, -352, 304, -111, -111, -111, + -111, -111, -111, -111, -111, 998, 999, 998, 999, 153, + -32766,-32766,-32766, 1191, 807, 126, 306, 1293, 57, 58, + 103, 104, 105, -111, 59, 1218, 60, 246, 247, 61, + 62, 63, 64, 65, 66, 67, 68, -525, 27, 268, + 69, 436, 501, -328, 808, -86, 1224, 1225, 502, 1189, + 807, 1198, 1230, 293, 1222, 41, 24, 503, 74, 504, + 953, 505, 320, 506, 802, 154, 507, 508, 279, 684, + 280, 43, 44, 437, 367, 366, 891, 45, 509, 35, + 249, -16, -566, 358, 332, 318, -566, 1198, 1193, 1192, + 1194, -527, 510, 511, 512, 333, -524, 1274, 48, 716, + 715, -525, -525, 334, 513, 514, 807, 1212, 1213, 1214, + 1215, 1209, 1210, 292, 360, 284, -525, 285, -314, 1216, + 1211, -193, -192, 1193, 1192, 1194, 293, 891, -525, 364, + -531, 70, 807, 316, 317, 320, 31, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + -153, -153, -153, 638, 25, -527, -527, 687, 379, 881, + -524, -524, 296, 297, 891, -153, 432, -153, 807, -153, + -527, -153, 716, 715, 433, -524, 798, 363, -111, 1105, + 1107, 365, -527, 434, 891, 140, 435, -524, 954, 127, + -524, 320, -111, -111, 688, 813, 381, -529, 11, 834, + 155, 835, 867, -111, -111, -111, -111, 47, 293,-32766, + 881, 654, 655, 74, 689, 1191, 1045, 320, 708, 149, + 399, 157,-32766,-32766,-32766, 32,-32766, -79,-32766, 123, + -32766, 716, 715,-32766, 893, 891, 682, -153,-32766,-32766, + -32766, 716, 715, 891,-32766,-32766, 124, 881, 129, 74, + -32766, 411, 130, 320, -524, -524, 143, 141, -75,-32766, + 158, -529, -529, 320, 27, 691, 159, 881, 160, -524, + 161, 294, 295, 698, 368, 369, 807, -73,-32766, -72, + 1222, -524, 373, 374, 1191, 893, -71, 682, -529, 73, + -70,-32766,-32766,-32766, -69,-32766, -68,-32766, 125,-32766, + 630, 631,-32766, -67, -66, -47, -51,-32766,-32766,-32766, + -18, 147, 271,-32766,-32766, 277, 697, 700, 881,-32766, + 411, 890, 893, 146, 682, 282, 881, 907,-32766, 281, + 513, 514, 286, 1212, 1213, 1214, 1215, 1209, 1210, 326, + 131, 145, 939, 287, 682, 1216, 1211, 109, 270,-32766, + 798, 807,-32766, 662, 639, 1191, 657, 72, 675, 1075, + 317, 320,-32766,-32766,-32766, 1305,-32766, 301,-32766, 628, + -32766, 431, 543,-32766,-32766, 923, 555, 924,-32766,-32766, + -32766, 1229, 549,-32766,-32766,-32766, -4, 891, -490, 1191, + -32766, 411, 644, 893, 299, 682,-32766,-32766,-32766,-32766, + -32766, 893,-32766, 682,-32766, 13, 1231,-32766, 452, 480, + 645, 909,-32766,-32766,-32766,-32766, 658, -480,-32766,-32766, + 0, 1191, 0, 0,-32766, 411, 0, 298,-32766,-32766, + -32766, 305,-32766,-32766,-32766, 0,-32766, 0, 806,-32766, + 0, 0, 0, 475,-32766,-32766,-32766,-32766, 0, 7, + -32766,-32766, 16, 1191, 561, 596,-32766, 411, 1219, 891, + -32766,-32766,-32766, 362,-32766,-32766,-32766, 818,-32766, -267, + 881,-32766, 39, 293, 0, 0,-32766,-32766,-32766, 40, + 705, 706,-32766,-32766, 872, 963, 940, 947,-32766, 411, + 937, 948, 365, 870, 427, 891, 935,-32766, 1049, 291, + 1244, 1052, 1053, -111, -111, 1050, 1051, 1057, -560, 1262, + 1296, 633, 0, 826, -111, -111, -111, -111, 33, 315, + -32766, 361, 683, 686, 690, 692, 1191, 693, 694, 695, + 699, 685, 320,-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, 411, 955, 365, 27, 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, + 365, -530, 1, 28, 29, 38, 42, 46, 71, 0, + 75, -111, -111, 76, 77, 78, 79, 893, 80, 682, + -242, 867, -111, -111, -111, -111, 142, 152, 156, 245, + 322, 347, 514, 348, 1212, 1213, 1214, 1215, 1209, 1210, + 349, 350, 351, 352, 353, 354, 1216, 1211, 355, 356, + 357, 359, 428, 893, -265, 682, -241, -264, 72, 0, + 18, 317, 320, 19, 20, 21, 23, 398, 471, 472, + 479, 482, 483, 484, 485, 489, 490, 491, 498, 669, + 1202, 1145, 1220, 1019, 1018, 1181, -269, -103, 17, 22, + 26, 290, 397, 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, 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, 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, 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,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, 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( + 194, 194, 670, 422, 643, 463, 1264, 1265, 1022, 416, + 308, 309, 329, 563, 314, 421, 330, 423, 622, 801, + 678, 637, 586, 651, 652, 653, 165, 165, 165, 165, + 218, 195, 191, 191, 175, 177, 213, 191, 191, 191, + 191, 191, 192, 192, 192, 192, 192, 192, 186, 187, + 188, 189, 190, 215, 213, 216, 521, 522, 412, 523, + 525, 526, 527, 528, 529, 530, 531, 532, 1091, 166, + 167, 168, 193, 169, 170, 171, 164, 172, 173, 174, + 176, 212, 214, 217, 235, 238, 241, 242, 244, 255, + 256, 257, 258, 259, 260, 261, 263, 264, 265, 266, + 274, 275, 311, 312, 313, 417, 418, 419, 568, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 178, 234, 179, 196, 197, 198, + 236, 186, 187, 188, 189, 190, 215, 1091, 199, 180, + 181, 182, 200, 196, 183, 237, 201, 199, 163, 202, + 203, 184, 204, 205, 206, 185, 207, 208, 209, 210, + 211, 323, 323, 323, 323, 827, 608, 608, 824, 547, + 538, 342, 1221, 1221, 1221, 1221, 1221, 1221, 1221, 1221, + 1221, 1221, 1239, 1239, 288, 288, 288, 288, 1239, 1239, + 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 388, 538, + 547, 556, 557, 395, 566, 588, 602, 603, 832, 825, + 880, 875, 876, 889, 15, 833, 877, 830, 878, 879, + 831, 799, 251, 251, 883, 919, 992, 1000, 1004, 1001, + 1005, 1237, 1237, 938, 1043, 1039, 1040, 1237, 1237, 1237, + 1237, 1237, 1237, 1237, 1237, 1237, 1237, 858, 248, 248, + 248, 248, 250, 252, 533, 533, 533, 533, 487, 590, + 488, 1190, 1190, 997, 1190, 997, 494, 1290, 1290, 560, + 997, 997, 997, 997, 997, 997, 997, 997, 997, 997, + 997, 997, 1261, 1261, 1290, 1261, 340, 1190, 930, 402, + 677, 1279, 1190, 1190, 1190, 1190, 959, 345, 1190, 1190, + 1190, 1271, 1271, 1271, 1271, 606, 640, 345, 345, 1273, + 1273, 1273, 1273, 820, 820, 805, 896, 884, 840, 885, + 897, 345, 345, 5, 345, 6, 1306, 384, 535, 535, + 559, 535, 415, 852, 597, 1257, 839, 540, 524, 524, + 345, 1289, 1289, 642, 524, 524, 524, 524, 524, 524, + 524, 524, 524, 524, 445, 805, 1140, 805, 1289, 932, + 932, 932, 932, 1063, 1064, 445, 926, 933, 386, 390, + 548, 587, 591, 1030, 1292, 331, 554, 1259, 1259, 1030, + 704, 621, 623, 823, 641, 1250, 319, 303, 660, 664, + 973, 668, 676, 969, 429, 553, 962, 936, 936, 934, + 936, 703, 601, 537, 971, 966, 343, 344, 663, 817, + 595, 609, 612, 613, 614, 615, 634, 635, 636, 680, + 439, 1186, 845, 454, 454, 439, 439, 1266, 1267, 820, + 901, 1079, 454, 394, 539, 551, 1183, 605, 540, 539, + 842, 551, 978, 272, 387, 618, 619, 981, 536, 536, + 844, 707, 646, 957, 567, 457, 458, 459, 838, 850, + 254, 254, 1297, 1298, 400, 401, 976, 976, 464, 649, + 1182, 650, 1028, 404, 405, 406, 1187, 661, 424, 1032, + 407, 564, 600, 815, 338, 424, 854, 848, 853, 841, + 1027, 1031, 1009, 1002, 1006, 1003, 1007, 1185, 941, 1188, + 1247, 1248, 943, 0, 1074, 439, 439, 439, 439, 439, + 439, 439, 439, 439, 439, 439, 0, 468, 439, 585, + 1056, 931, 681, 667, 667, 0, 495, 673, 1054, 1171, + 912, 0, 0, 1172, 1175, 913, 1176, 0, 0, 0, + 0, 0, 0, 1072, 857 + ); + + protected $gotoCheck = array( + 42, 42, 72, 65, 65, 166, 166, 166, 119, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 7, + 9, 84, 122, 84, 84, 84, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 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, 26, 75, + 75, 93, 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 160, 160, 24, 24, 24, 24, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 160, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 15, 27, + 15, 15, 15, 15, 75, 15, 15, 15, 15, 15, + 15, 6, 5, 5, 15, 87, 87, 87, 87, 87, + 87, 161, 161, 49, 15, 15, 15, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 45, 5, 5, + 5, 5, 5, 5, 103, 103, 103, 103, 147, 103, + 147, 72, 72, 72, 72, 72, 147, 173, 173, 162, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 122, 122, 173, 122, 169, 72, 89, 89, + 89, 171, 72, 72, 72, 72, 99, 14, 72, 72, + 72, 9, 9, 9, 9, 55, 55, 14, 14, 122, + 122, 122, 122, 22, 22, 12, 72, 64, 35, 64, + 72, 14, 14, 46, 14, 46, 14, 61, 19, 19, + 100, 19, 13, 35, 13, 122, 35, 14, 163, 163, + 14, 172, 172, 63, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 19, 12, 143, 12, 172, 19, + 19, 19, 19, 136, 136, 19, 19, 19, 58, 58, + 58, 58, 58, 122, 172, 29, 48, 122, 122, 122, + 48, 48, 48, 25, 48, 14, 159, 159, 48, 48, + 48, 48, 48, 48, 109, 9, 25, 25, 25, 25, + 25, 25, 9, 25, 25, 25, 93, 93, 14, 18, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 23, 20, 39, 141, 141, 23, 23, 168, 168, 22, + 17, 17, 141, 28, 9, 9, 152, 17, 14, 9, + 37, 9, 17, 24, 9, 83, 83, 106, 24, 24, + 17, 95, 17, 17, 9, 9, 9, 9, 17, 9, + 5, 5, 9, 9, 80, 80, 103, 103, 149, 80, + 17, 80, 121, 80, 80, 80, 20, 80, 113, 124, + 80, 2, 2, 20, 80, 113, 41, 9, 16, 16, + 16, 16, 113, 113, 113, 113, 113, 14, 16, 20, + 20, 20, 92, -1, 139, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, -1, 82, 23, 8, + 8, 16, 8, 8, 8, -1, 8, 8, 8, 78, + 78, -1, -1, 78, 78, 78, 78, -1, -1, -1, + -1, -1, -1, 16, 16 + ); + + protected $gotoBase = array( + 0, 0, -203, 0, 0, 221, 208, 10, 512, 7, + 0, 0, 24, 1, 5, -174, 47, -23, 105, 61, + 38, 0, -10, 158, 181, 379, 164, 205, 102, 84, + 0, 0, 0, 0, 0, -43, 0, 107, 0, 104, + 0, 54, -1, 0, 0, 235, -384, 0, -307, 210, + 0, 0, 0, 0, 0, 266, 0, 0, 324, 0, + 0, 286, 0, 103, 298, -236, 0, 0, 0, 0, + 0, 0, -6, 0, 0, -167, 0, 0, 129, 62, + -14, 0, 53, -22, -669, 0, 0, -52, 0, -11, + 0, 0, 68, -299, 0, 52, 0, 0, 0, 262, + 288, 0, 0, 227, -73, 0, 87, 0, 0, 118, + 0, 0, 0, 209, 0, 0, 0, 0, 0, 6, + 0, 108, 15, 0, 46, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 91, 0, 0, 69, + 0, 390, 0, 86, 0, 0, 0, -224, 0, 37, + 0, 0, 77, 0, 0, 0, 0, 0, 0, 70, + -57, -8, 241, 99, 0, 0, -290, 0, 65, 257, + 0, 261, 39, -35, 0, 0 + ); + + protected $gotoDefault = array( + -32768, 499, 711, 4, 712, 905, 788, 797, 583, 515, + 679, 339, 610, 413, 1255, 882, 1078, 565, 816, 1199, + 1207, 446, 819, 324, 701, 864, 865, 866, 391, 376, + 382, 389, 632, 611, 481, 851, 442, 843, 473, 846, + 441, 855, 162, 410, 497, 859, 3, 861, 542, 892, + 377, 869, 378, 656, 871, 550, 873, 874, 385, 392, + 393, 1083, 558, 607, 886, 243, 552, 887, 375, 888, + 895, 380, 383, 665, 453, 492, 486, 403, 1058, 594, + 629, 450, 467, 617, 616, 604, 466, 425, 408, 928, + 474, 451, 942, 341, 950, 709, 1090, 624, 476, 958, + 625, 965, 968, 516, 517, 465, 980, 269, 983, 477, + 1015, 647, 648, 995, 626, 627, 1013, 460, 584, 1021, + 443, 1029, 1243, 444, 1033, 262, 1036, 276, 409, 426, + 1041, 1042, 8, 1048, 671, 672, 10, 273, 496, 1073, + 666, 440, 1089, 430, 1159, 1161, 544, 478, 1179, 1178, + 659, 493, 1184, 1246, 438, 518, 461, 310, 519, 302, + 327, 307, 534, 289, 328, 520, 462, 1252, 1260, 325, + 30, 1280, 1291, 335, 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() { + $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 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 = $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)]; }; + }, + 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(); } + } + + }, + 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); + } + + }, + 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; }; + if ($this->semValue === null) $this->semValue = array(); /* 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)]; } + }, + 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; }; + if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $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); } }; $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); } }; $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); + }, + 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)]; + }, + ]; + } +} +parsers = $parsers; + } + + public function parse(string $code, ErrorHandler $errorHandler = null) { + if (null === $errorHandler) { + $errorHandler = new ErrorHandler\Throwing; + } + + list($firstStmts, $firstError) = $this->tryParse($this->parsers[0], $errorHandler, $code); + if ($firstError === null) { + return $firstStmts; + } + + 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; + } + } + + throw $firstError; + } + + private function tryParse(Parser $parser, ErrorHandler $errorHandler, $code) { + $stmts = null; + $error = null; + try { + $stmts = $parser->parse($code, $errorHandler); + } catch (Error $error) {} + return [$stmts, $error]; + } +} +'", + "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() { + $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 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 = $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)]; }; + }, + 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; }; + if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $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 { + $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(); } + } + + }, + 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; }; + if ($this->semValue === null) $this->semValue = array(); /* 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)]; } + }, + 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; }; + 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 SebastianBergmann\Template; -/** - * A simple template engine. - * - * @since Class available since Release 1.0.0 - */ -class Text_Template +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 { /** * @var string */ - protected $template = ''; + private $template = ''; /** * @var string */ - protected $openDelimiter = '{'; + private $openDelimiter; /** * @var string */ - protected $closeDelimiter = '}'; + private $closeDelimiter; /** * @var array */ - protected $values = array(); + private $values = []; /** - * Constructor. - * - * @param string $file * @throws InvalidArgumentException */ - public function __construct($file = '', $openDelimiter = '{', $closeDelimiter = '}') + public function __construct(string $file = '', string $openDelimiter = '{', string $closeDelimiter = '}') { $this->setFile($file); + $this->openDelimiter = $openDelimiter; $this->closeDelimiter = $closeDelimiter; } /** - * Sets the template file. - * - * @param string $file * @throws InvalidArgumentException */ - public function setFile($file) + public function setFile(string $file): void { $distFile = $file . '.dist'; if (file_exists($file)) { $this->template = file_get_contents($file); - } - - else if (file_exists($distFile)) { + } elseif (file_exists($distFile)) { $this->template = file_get_contents($distFile); - } - - else { + } else { throw new InvalidArgumentException( - 'Template file could not be loaded.' + sprintf( + 'Failed to load template "%s"', + $file + ) ); } } - /** - * Sets one or more template variables. - * - * @param array $values - * @param bool $merge - */ - public function setVar(array $values, $merge = TRUE) + public function setVar(array $values, bool $merge = true): void { if (!$merge || empty($this->values)) { $this->values = $values; @@ -1877,14 +23509,9 @@ class Text_Template } } - /** - * Renders the template and returns the result. - * - * @return string - */ - public function render() + public function render(): string { - $keys = array(); + $keys = []; foreach ($this->values as $key => $value) { $keys[] = $this->openDelimiter . $key . $this->closeDelimiter; @@ -1894,34 +23521,183 @@ class Text_Template } /** - * Renders the template and writes the result to a file. - * - * @param string $target + * @codeCoverageIgnore */ - public function renderTo($target) + public function renderTo(string $target): void { - $fp = @fopen($target, 'wt'); - - if ($fp) { - fwrite($fp, $this->render()); - fclose($fp); - } else { - $error = error_get_last(); - + if (!file_put_contents($target, $this->render())) { throw new RuntimeException( - sprintf( - 'Could not write to %s: %s', - $target, - substr( - $error['message'], - strpos($error['message'], ':') + 2 + sprintf( + 'Writing rendered result to "%s" failed', + $target ) - ) ); } } } + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Template; +use InvalidArgumentException; + +final class RuntimeException extends InvalidArgumentException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace 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 SebastianBergmann\Template; + +final 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 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) + { + if (!$this->canInvokeWithTimeout()) { + throw new ProcessControlExtensionNotLoadedException( + 'The pcntl (process control) extension for PHP is required' + ); + } + + 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); + } + } + + public function canInvokeWithTimeout(): bool + { + return function_exists('pcntl_signal') && function_exists('pcntl_async_signals') && function_exists('pcntl_alarm'); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Invoker; + +use RuntimeException; + +final class ProcessControlExtensionNotLoadedException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Invoker; + +use RuntimeException; + +final class TimeoutException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Invoker; + +use Throwable; + +interface Exception extends Throwable +{ +} */ - private static $sizes = [ + private $startTimes = []; + + public function start(): void + { + $this->startTimes[] = (float) hrtime(true); + } + + /** + * @throws NoActiveTimerException + */ + public function stop(): Duration + { + if (empty($this->startTimes)) { + throw new NoActiveTimerException( + 'Timer::start() has to be called before Timer::stop()' + ); + } + + 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 SebastianBergmann\Timer; + +use function is_float; +use function memory_get_peak_usage; +use function microtime; +use function sprintf; + +final class ResourceUsageFormatter +{ + /** + * @psalm-var array + */ + private const SIZES = [ 'GB' => 1073741824, 'MB' => 1048576, 'KB' => 1024, ]; - /** - * @var int[] - */ - private static $times = [ - 'hour' => 3600000, - 'minute' => 60000, - 'second' => 1000, - ]; - - /** - * @var float[] - */ - private static $startTimes = []; - - public static function start(): void + public function resourceUsage(Duration $duration): string { - self::$startTimes[] = \microtime(true); - } - - public static function stop(): float - { - return \microtime(true) - \array_pop(self::$startTimes); - } - - public static function bytesToString(float $bytes): string - { - foreach (self::$sizes as $unit => $value) { - if ($bytes >= $value) { - return \sprintf('%.2f %s', $bytes >= 1024 ? $bytes / $value : $bytes, $unit); - } - } - - return $bytes . ' byte' . ((int) $bytes !== 1 ? 's' : ''); - } - - public static function secondsToTimeString(float $time): string - { - $ms = \round($time * 1000); - - foreach (self::$times as $unit => $value) { - if ($ms >= $value) { - $time = \floor($ms / $value * 100.0) / 100.0; - - return $time . ' ' . ($time == 1 ? $unit : $unit . 's'); - } - } - - return $ms . ' ms'; - } - - /** - * @throws RuntimeException - */ - public static function timeSinceStartOfRequest(): string - { - if (isset($_SERVER['REQUEST_TIME_FLOAT'])) { - $startOfRequest = $_SERVER['REQUEST_TIME_FLOAT']; - } elseif (isset($_SERVER['REQUEST_TIME'])) { - $startOfRequest = $_SERVER['REQUEST_TIME']; - } else { - throw new RuntimeException('Cannot determine time at which the request started'); - } - - return self::secondsToTimeString(\microtime(true) - $startOfRequest); - } - - /** - * @throws RuntimeException - */ - public static function resourceUsage(): string - { - return \sprintf( + return sprintf( 'Time: %s, Memory: %s', - self::timeSinceStartOfRequest(), - self::bytesToString(\memory_get_peak_usage(true)) + $duration->asString(), + $this->bytesToString(memory_get_peak_usage(true)) ); } + + /** + * @throws TimeSinceStartOfRequestNotAvailableException + */ + public function resourceUsageSinceStartOfRequest(): string + { + 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'])) + ) + ); + } + + private function bytesToString(int $bytes): string + { + 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 + } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Timer; - -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 SebastianBergmann\FileIterator; - -class Factory -{ - /** - * @param array|string $paths - * @param array|string $suffixes - * @param array|string $prefixes - * @param array $exclude - * - * @return \AppendIterator - */ - public function getFileIterator($paths, $suffixes = '', $prefixes = '', array $exclude = []): \AppendIterator - { - 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; - } - - protected function getPathsAfterResolvingWildcards(array $paths): 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); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\FileIterator; - -class Iterator extends \FilterIterator -{ - const PREFIX = 0; - const SUFFIX = 1; - - /** - * @var string - */ - private $basePath; - - /** - * @var array - */ - private $suffixes = []; - - /** - * @var array - */ - private $prefixes = []; - - /** - * @var array - */ - private $exclude = []; - - /** - * @param string $basePath - * @param \Iterator $iterator - * @param array $suffixes - * @param array $prefixes - * @param array $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() - { - $current = $this->getInnerIterator()->current(); - $filename = $current->getFilename(); - $realPath = $current->getRealPath(); - - 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. - */ - -namespace SebastianBergmann\FileIterator; - -class Facade -{ - /** - * @param array|string $paths - * @param array|string $suffixes - * @param array|string $prefixes - * @param array $exclude - * @param bool $commonPath - * - * @return array - */ - public function getFilesAsArray($paths, $suffixes = '', $prefixes = '', array $exclude = [], bool $commonPath = false): array - { - if (\is_string($paths)) { - $paths = [$paths]; - } - - $factory = new Factory; - - $iterator = $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; - } - - 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; - } - } - - $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. - */ - -final class PHP_Token_Util -{ - public static function getClass($object): string - { - $parts = explode('\\', get_class($object)); - - return array_pop($parts); - } -} - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ +use function floor; +use function sprintf; /** - * A caching factory for token stream objects. + * @psalm-immutable */ -class PHP_Token_Stream_CachingFactory +final class Duration { /** - * @var array + * @var float */ - protected static $cache = []; - - /** - * @param string $filename - * - * @return PHP_Token_Stream - */ - public static function get($filename) - { - if (!isset(self::$cache[$filename])) { - self::$cache[$filename] = new PHP_Token_Stream($filename); - } - - return self::$cache[$filename]; - } - - /** - * @param string $filename - */ - public static function clear($filename = null) - { - if (is_string($filename)) { - unset(self::$cache[$filename]); - } else { - self::$cache = []; - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -/** - * A stream of PHP tokens. - */ -class PHP_Token_Stream implements ArrayAccess, Countable, SeekableIterator -{ - /** - * @var array - */ - protected static $customTokens = [ - '(' => 'PHP_Token_OPEN_BRACKET', - ')' => 'PHP_Token_CLOSE_BRACKET', - '[' => 'PHP_Token_OPEN_SQUARE', - ']' => 'PHP_Token_CLOSE_SQUARE', - '{' => 'PHP_Token_OPEN_CURLY', - '}' => 'PHP_Token_CLOSE_CURLY', - ';' => 'PHP_Token_SEMICOLON', - '.' => 'PHP_Token_DOT', - ',' => 'PHP_Token_COMMA', - '=' => 'PHP_Token_EQUAL', - '<' => 'PHP_Token_LT', - '>' => 'PHP_Token_GT', - '+' => 'PHP_Token_PLUS', - '-' => 'PHP_Token_MINUS', - '*' => 'PHP_Token_MULT', - '/' => 'PHP_Token_DIV', - '?' => 'PHP_Token_QUESTION_MARK', - '!' => 'PHP_Token_EXCLAMATION_MARK', - ':' => 'PHP_Token_COLON', - '"' => 'PHP_Token_DOUBLE_QUOTES', - '@' => 'PHP_Token_AT', - '&' => 'PHP_Token_AMPERSAND', - '%' => 'PHP_Token_PERCENT', - '|' => 'PHP_Token_PIPE', - '$' => 'PHP_Token_DOLLAR', - '^' => 'PHP_Token_CARET', - '~' => 'PHP_Token_TILDE', - '`' => 'PHP_Token_BACKTICK' - ]; - - /** - * @var string - */ - protected $filename; - - /** - * @var array - */ - protected $tokens = []; + private $nanoseconds; /** * @var int */ - protected $position = 0; - - /** - * @var array - */ - protected $linesOfCode = ['loc' => 0, 'cloc' => 0, 'ncloc' => 0]; - - /** - * @var array - */ - protected $classes; - - /** - * @var array - */ - protected $functions; - - /** - * @var array - */ - protected $includes; - - /** - * @var array - */ - protected $interfaces; - - /** - * @var array - */ - protected $traits; - - /** - * @var array - */ - protected $lineToFunctionMap = []; - - /** - * Constructor. - * - * @param string $sourceCode - */ - public function __construct($sourceCode) - { - if (is_file($sourceCode)) { - $this->filename = $sourceCode; - $sourceCode = file_get_contents($sourceCode); - } - - $this->scan($sourceCode); - } - - /** - * Destructor. - */ - public function __destruct() - { - $this->tokens = []; - } - - /** - * @return string - */ - public function __toString() - { - $buffer = ''; - - foreach ($this as $token) { - $buffer .= $token; - } - - return $buffer; - } - - /** - * @return string - */ - public function getFilename() - { - return $this->filename; - } - - /** - * Scans the source for sequences of characters and converts them into a - * stream of tokens. - * - * @param string $sourceCode - */ - protected function scan($sourceCode) - { - $id = 0; - $line = 1; - $tokens = token_get_all($sourceCode); - $numTokens = count($tokens); - - $lastNonWhitespaceTokenWasDoubleColon = false; - - for ($i = 0; $i < $numTokens; ++$i) { - $token = $tokens[$i]; - $skip = 0; - - if (is_array($token)) { - $name = substr(token_name($token[0]), 2); - $text = $token[1]; - - if ($lastNonWhitespaceTokenWasDoubleColon && $name == 'CLASS') { - $name = 'CLASS_NAME_CONSTANT'; - } elseif ($name == 'USE' && isset($tokens[$i + 2][0]) && $tokens[$i + 2][0] == T_FUNCTION) { - $name = 'USE_FUNCTION'; - $text .= $tokens[$i + 1][1] . $tokens[$i + 2][1]; - $skip = 2; - } - - $tokenClass = 'PHP_Token_' . $name; - } else { - $text = $token; - $tokenClass = self::$customTokens[$token]; - } - - $this->tokens[] = new $tokenClass($text, $line, $this, $id++); - $lines = substr_count($text, "\n"); - $line += $lines; - - if ($tokenClass == 'PHP_Token_HALT_COMPILER') { - break; - } elseif ($tokenClass == 'PHP_Token_COMMENT' || - $tokenClass == 'PHP_Token_DOC_COMMENT') { - $this->linesOfCode['cloc'] += $lines + 1; - } - - if ($name == 'DOUBLE_COLON') { - $lastNonWhitespaceTokenWasDoubleColon = true; - } elseif ($name != 'WHITESPACE') { - $lastNonWhitespaceTokenWasDoubleColon = false; - } - - $i += $skip; - } - - $this->linesOfCode['loc'] = substr_count($sourceCode, "\n"); - $this->linesOfCode['ncloc'] = $this->linesOfCode['loc'] - - $this->linesOfCode['cloc']; - } - - /** - * @return int - */ - public function count() - { - return count($this->tokens); - } - - /** - * @return PHP_Token[] - */ - public function tokens() - { - return $this->tokens; - } - - /** - * @return array - */ - public function getClasses() - { - if ($this->classes !== null) { - return $this->classes; - } - - $this->parse(); - - return $this->classes; - } - - /** - * @return array - */ - public function getFunctions() - { - if ($this->functions !== null) { - return $this->functions; - } - - $this->parse(); - - return $this->functions; - } - - /** - * @return array - */ - public function getInterfaces() - { - if ($this->interfaces !== null) { - return $this->interfaces; - } - - $this->parse(); - - return $this->interfaces; - } - - /** - * @return array - */ - public function getTraits() - { - if ($this->traits !== null) { - return $this->traits; - } - - $this->parse(); - - return $this->traits; - } - - /** - * Gets the names of all files that have been included - * using include(), include_once(), require() or require_once(). - * - * Parameter $categorize set to TRUE causing this function to return a - * multi-dimensional array with categories in the keys of the first dimension - * and constants and their values in the second dimension. - * - * Parameter $category allow to filter following specific inclusion type - * - * @param bool $categorize OPTIONAL - * @param string $category OPTIONAL Either 'require_once', 'require', - * 'include_once', 'include'. - * - * @return array - */ - public function getIncludes($categorize = false, $category = null) - { - if ($this->includes === null) { - $this->includes = [ - 'require_once' => [], - 'require' => [], - 'include_once' => [], - 'include' => [] - ]; - - foreach ($this->tokens as $token) { - switch (PHP_Token_Util::getClass($token)) { - case 'PHP_Token_REQUIRE_ONCE': - case 'PHP_Token_REQUIRE': - case 'PHP_Token_INCLUDE_ONCE': - case 'PHP_Token_INCLUDE': - $this->includes[$token->getType()][] = $token->getName(); - break; - } - } - } - - if (isset($this->includes[$category])) { - $includes = $this->includes[$category]; - } elseif ($categorize === false) { - $includes = array_merge( - $this->includes['require_once'], - $this->includes['require'], - $this->includes['include_once'], - $this->includes['include'] - ); - } else { - $includes = $this->includes; - } - - return $includes; - } - - /** - * Returns the name of the function or method a line belongs to. - * - * @return string or null if the line is not in a function or method - */ - public function getFunctionForLine($line) - { - $this->parse(); - - if (isset($this->lineToFunctionMap[$line])) { - return $this->lineToFunctionMap[$line]; - } - } - - protected function parse() - { - $this->interfaces = []; - $this->classes = []; - $this->traits = []; - $this->functions = []; - $class = []; - $classEndLine = []; - $trait = false; - $traitEndLine = false; - $interface = false; - $interfaceEndLine = false; - - foreach ($this->tokens as $token) { - switch (PHP_Token_Util::getClass($token)) { - case 'PHP_Token_HALT_COMPILER': - return; - - case 'PHP_Token_INTERFACE': - $interface = $token->getName(); - $interfaceEndLine = $token->getEndLine(); - - $this->interfaces[$interface] = [ - 'methods' => [], - 'parent' => $token->getParent(), - 'keywords' => $token->getKeywords(), - 'docblock' => $token->getDocblock(), - 'startLine' => $token->getLine(), - 'endLine' => $interfaceEndLine, - 'package' => $token->getPackage(), - 'file' => $this->filename - ]; - break; - - case 'PHP_Token_CLASS': - case 'PHP_Token_TRAIT': - $tmp = [ - 'methods' => [], - 'parent' => $token->getParent(), - 'interfaces'=> $token->getInterfaces(), - 'keywords' => $token->getKeywords(), - 'docblock' => $token->getDocblock(), - 'startLine' => $token->getLine(), - 'endLine' => $token->getEndLine(), - 'package' => $token->getPackage(), - 'file' => $this->filename - ]; - - if ($token->getName() !== null) { - if ($token instanceof PHP_Token_CLASS) { - $class[] = $token->getName(); - $classEndLine[] = $token->getEndLine(); - - $this->classes[$class[count($class) - 1]] = $tmp; - } else { - $trait = $token->getName(); - $traitEndLine = $token->getEndLine(); - $this->traits[$trait] = $tmp; - } - } - break; - - case 'PHP_Token_FUNCTION': - $name = $token->getName(); - $tmp = [ - 'docblock' => $token->getDocblock(), - 'keywords' => $token->getKeywords(), - 'visibility'=> $token->getVisibility(), - 'signature' => $token->getSignature(), - 'startLine' => $token->getLine(), - 'endLine' => $token->getEndLine(), - 'ccn' => $token->getCCN(), - 'file' => $this->filename - ]; - - if (empty($class) && - $trait === false && - $interface === false) { - $this->functions[$name] = $tmp; - - $this->addFunctionToMap( - $name, - $tmp['startLine'], - $tmp['endLine'] - ); - } elseif (!empty($class)) { - $this->classes[$class[count($class) - 1]]['methods'][$name] = $tmp; - - $this->addFunctionToMap( - $class[count($class) - 1] . '::' . $name, - $tmp['startLine'], - $tmp['endLine'] - ); - } elseif ($trait !== false) { - $this->traits[$trait]['methods'][$name] = $tmp; - - $this->addFunctionToMap( - $trait . '::' . $name, - $tmp['startLine'], - $tmp['endLine'] - ); - } else { - $this->interfaces[$interface]['methods'][$name] = $tmp; - } - break; - - case 'PHP_Token_CLOSE_CURLY': - if (!empty($classEndLine) && - $classEndLine[count($classEndLine) - 1] == $token->getLine()) { - array_pop($classEndLine); - array_pop($class); - } elseif ($traitEndLine !== false && - $traitEndLine == $token->getLine()) { - $trait = false; - $traitEndLine = false; - } elseif ($interfaceEndLine !== false && - $interfaceEndLine == $token->getLine()) { - $interface = false; - $interfaceEndLine = false; - } - break; - } - } - } - - /** - * @return array - */ - public function getLinesOfCode() - { - return $this->linesOfCode; - } - - /** - */ - public function rewind() - { - $this->position = 0; - } - - /** - * @return bool - */ - public function valid() - { - return isset($this->tokens[$this->position]); - } - - /** - * @return int - */ - public function key() - { - return $this->position; - } - - /** - * @return PHP_Token - */ - public function current() - { - return $this->tokens[$this->position]; - } - - /** - */ - public function next() - { - $this->position++; - } - - /** - * @param int $offset - * - * @return bool - */ - public function offsetExists($offset) - { - return isset($this->tokens[$offset]); - } - - /** - * @param int $offset - * - * @return mixed - * - * @throws OutOfBoundsException - */ - public function offsetGet($offset) - { - if (!$this->offsetExists($offset)) { - throw new OutOfBoundsException( - sprintf( - 'No token at position "%s"', - $offset - ) - ); - } - - return $this->tokens[$offset]; - } - - /** - * @param int $offset - * @param mixed $value - */ - public function offsetSet($offset, $value) - { - $this->tokens[$offset] = $value; - } - - /** - * @param int $offset - * - * @throws OutOfBoundsException - */ - public function offsetUnset($offset) - { - if (!$this->offsetExists($offset)) { - throw new OutOfBoundsException( - sprintf( - 'No token at position "%s"', - $offset - ) - ); - } - - unset($this->tokens[$offset]); - } - - /** - * Seek to an absolute position. - * - * @param int $position - * - * @throws OutOfBoundsException - */ - public function seek($position) - { - $this->position = $position; - - if (!$this->valid()) { - throw new OutOfBoundsException( - sprintf( - 'No token at position "%s"', - $this->position - ) - ); - } - } - - /** - * @param string $name - * @param int $startLine - * @param int $endLine - */ - private function addFunctionToMap($name, $startLine, $endLine) - { - for ($line = $startLine; $line <= $endLine; $line++) { - $this->lineToFunctionMap[$line] = $name; - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -/** - * A PHP token. - */ -abstract class PHP_Token -{ - /** - * @var string - */ - protected $text; + private $hours; /** * @var int */ - protected $line; - - /** - * @var PHP_Token_Stream - */ - protected $tokenStream; + private $minutes; /** * @var int */ - protected $id; - - /** - * @param string $text - * @param int $line - * @param PHP_Token_Stream $tokenStream - * @param int $id - */ - public function __construct($text, $line, PHP_Token_Stream $tokenStream, $id) - { - $this->text = $text; - $this->line = $line; - $this->tokenStream = $tokenStream; - $this->id = $id; - } - - /** - * @return string - */ - public function __toString() - { - return $this->text; - } - - /** - * @return int - */ - public function getLine() - { - return $this->line; - } - - /** - * @return int - */ - public function getId() - { - return $this->id; - } -} - -abstract class PHP_TokenWithScope extends PHP_Token -{ - /** - * @var int - */ - protected $endTokenId; - - /** - * Get the docblock for this token - * - * This method will fetch the docblock belonging to the current token. The - * docblock must be placed on the line directly above the token to be - * recognized. - * - * @return string|null Returns the docblock as a string if found - */ - public function getDocblock() - { - $tokens = $this->tokenStream->tokens(); - $currentLineNumber = $tokens[$this->id]->getLine(); - $prevLineNumber = $currentLineNumber - 1; - - for ($i = $this->id - 1; $i; $i--) { - if (!isset($tokens[$i])) { - return; - } - - if ($tokens[$i] instanceof PHP_Token_FUNCTION || - $tokens[$i] instanceof PHP_Token_CLASS || - $tokens[$i] instanceof PHP_Token_TRAIT) { - // Some other trait, class or function, no docblock can be - // used for the current token - break; - } - - $line = $tokens[$i]->getLine(); - - if ($line == $currentLineNumber || - ($line == $prevLineNumber && - $tokens[$i] instanceof PHP_Token_WHITESPACE)) { - continue; - } - - if ($line < $currentLineNumber && - !$tokens[$i] instanceof PHP_Token_DOC_COMMENT) { - break; - } - - return (string) $tokens[$i]; - } - } - - /** - * @return int - */ - public function getEndTokenId() - { - $block = 0; - $i = $this->id; - $tokens = $this->tokenStream->tokens(); - - while ($this->endTokenId === null && isset($tokens[$i])) { - if ($tokens[$i] instanceof PHP_Token_OPEN_CURLY || - $tokens[$i] instanceof PHP_Token_DOLLAR_OPEN_CURLY_BRACES || - $tokens[$i] instanceof PHP_Token_CURLY_OPEN) { - $block++; - } elseif ($tokens[$i] instanceof PHP_Token_CLOSE_CURLY) { - $block--; - - if ($block === 0) { - $this->endTokenId = $i; - } - } elseif (($this instanceof PHP_Token_FUNCTION || - $this instanceof PHP_Token_NAMESPACE) && - $tokens[$i] instanceof PHP_Token_SEMICOLON) { - if ($block === 0) { - $this->endTokenId = $i; - } - } - - $i++; - } - - if ($this->endTokenId === null) { - $this->endTokenId = $this->id; - } - - return $this->endTokenId; - } - - /** - * @return int - */ - public function getEndLine() - { - return $this->tokenStream[$this->getEndTokenId()]->getLine(); - } -} - -abstract class PHP_TokenWithScopeAndVisibility extends PHP_TokenWithScope -{ - /** - * @return string - */ - public function getVisibility() - { - $tokens = $this->tokenStream->tokens(); - - for ($i = $this->id - 2; $i > $this->id - 7; $i -= 2) { - if (isset($tokens[$i]) && - ($tokens[$i] instanceof PHP_Token_PRIVATE || - $tokens[$i] instanceof PHP_Token_PROTECTED || - $tokens[$i] instanceof PHP_Token_PUBLIC)) { - return strtolower( - str_replace('PHP_Token_', '', PHP_Token_Util::getClass($tokens[$i])) - ); - } - if (isset($tokens[$i]) && - !($tokens[$i] instanceof PHP_Token_STATIC || - $tokens[$i] instanceof PHP_Token_FINAL || - $tokens[$i] instanceof PHP_Token_ABSTRACT)) { - // no keywords; stop visibility search - break; - } - } - } - - /** - * @return string - */ - public function getKeywords() - { - $keywords = []; - $tokens = $this->tokenStream->tokens(); - - for ($i = $this->id - 2; $i > $this->id - 7; $i -= 2) { - if (isset($tokens[$i]) && - ($tokens[$i] instanceof PHP_Token_PRIVATE || - $tokens[$i] instanceof PHP_Token_PROTECTED || - $tokens[$i] instanceof PHP_Token_PUBLIC)) { - continue; - } - - if (isset($tokens[$i]) && - ($tokens[$i] instanceof PHP_Token_STATIC || - $tokens[$i] instanceof PHP_Token_FINAL || - $tokens[$i] instanceof PHP_Token_ABSTRACT)) { - $keywords[] = strtolower( - str_replace('PHP_Token_', '', PHP_Token_Util::getClass($tokens[$i])) - ); - } - } - - return implode(',', $keywords); - } -} - -abstract class PHP_Token_Includes extends PHP_Token -{ - /** - * @var string - */ - protected $name; - - /** - * @var string - */ - protected $type; - - /** - * @return string - */ - public function getName() - { - if ($this->name === null) { - $this->process(); - } - - return $this->name; - } - - /** - * @return string - */ - public function getType() - { - if ($this->type === null) { - $this->process(); - } - - return $this->type; - } - - private function process() - { - $tokens = $this->tokenStream->tokens(); - - if ($tokens[$this->id + 2] instanceof PHP_Token_CONSTANT_ENCAPSED_STRING) { - $this->name = trim($tokens[$this->id + 2], "'\""); - $this->type = strtolower( - str_replace('PHP_Token_', '', PHP_Token_Util::getClass($tokens[$this->id])) - ); - } - } -} - -class PHP_Token_FUNCTION extends PHP_TokenWithScopeAndVisibility -{ - /** - * @var array - */ - protected $arguments; + private $seconds; /** * @var int */ - protected $ccn; + private $milliseconds; - /** - * @var string - */ - protected $name; - - /** - * @var string - */ - protected $signature; - - /** - * @var bool - */ - private $anonymous = false; - - /** - * @return array - */ - public function getArguments() + public static function fromMicroseconds(float $microseconds): self { - if ($this->arguments !== null) { - return $this->arguments; - } - - $this->arguments = []; - $tokens = $this->tokenStream->tokens(); - $typeDeclaration = null; - - // Search for first token inside brackets - $i = $this->id + 2; - - while (!$tokens[$i - 1] instanceof PHP_Token_OPEN_BRACKET) { - $i++; - } - - while (!$tokens[$i] instanceof PHP_Token_CLOSE_BRACKET) { - if ($tokens[$i] instanceof PHP_Token_STRING) { - $typeDeclaration = (string) $tokens[$i]; - } elseif ($tokens[$i] instanceof PHP_Token_VARIABLE) { - $this->arguments[(string) $tokens[$i]] = $typeDeclaration; - $typeDeclaration = null; - } - - $i++; - } - - return $this->arguments; + return new self($microseconds * 1000); } - /** - * @return string - */ - public function getName() + public static function fromNanoseconds(float $nanoseconds): self { - if ($this->name !== null) { - return $this->name; - } - - $tokens = $this->tokenStream->tokens(); - - $i = $this->id + 1; - - if ($tokens[$i] instanceof PHP_Token_WHITESPACE) { - $i++; - } - - if ($tokens[$i] instanceof PHP_Token_AMPERSAND) { - $i++; - } - - if ($tokens[$i + 1] instanceof PHP_Token_OPEN_BRACKET) { - $this->name = (string) $tokens[$i]; - } elseif ($tokens[$i + 1] instanceof PHP_Token_WHITESPACE && $tokens[$i + 2] instanceof PHP_Token_OPEN_BRACKET) { - $this->name = (string) $tokens[$i]; - } else { - $this->anonymous = true; - - $this->name = sprintf( - 'anonymousFunction:%s#%s', - $this->getLine(), - $this->getId() - ); - } - - if (!$this->isAnonymous()) { - for ($i = $this->id; $i; --$i) { - if ($tokens[$i] instanceof PHP_Token_NAMESPACE) { - $this->name = $tokens[$i]->getName() . '\\' . $this->name; - - break; - } - - if ($tokens[$i] instanceof PHP_Token_INTERFACE) { - break; - } - } - } - - return $this->name; + return new self($nanoseconds); } - /** - * @return int - */ - public function getCCN() + private function __construct(float $nanoseconds) { - if ($this->ccn !== null) { - return $this->ccn; - } - - $this->ccn = 1; - $end = $this->getEndTokenId(); - $tokens = $this->tokenStream->tokens(); - - for ($i = $this->id; $i <= $end; $i++) { - switch (PHP_Token_Util::getClass($tokens[$i])) { - case 'PHP_Token_IF': - case 'PHP_Token_ELSEIF': - case 'PHP_Token_FOR': - case 'PHP_Token_FOREACH': - case 'PHP_Token_WHILE': - case 'PHP_Token_CASE': - case 'PHP_Token_CATCH': - case 'PHP_Token_BOOLEAN_AND': - case 'PHP_Token_LOGICAL_AND': - case 'PHP_Token_BOOLEAN_OR': - case 'PHP_Token_LOGICAL_OR': - case 'PHP_Token_QUESTION_MARK': - $this->ccn++; - break; - } - } - - return $this->ccn; + $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 string - */ - public function getSignature() + public function asNanoseconds(): float { - if ($this->signature !== null) { - return $this->signature; - } - - if ($this->isAnonymous()) { - $this->signature = 'anonymousFunction'; - $i = $this->id + 1; - } else { - $this->signature = ''; - $i = $this->id + 2; - } - - $tokens = $this->tokenStream->tokens(); - - while (isset($tokens[$i]) && - !$tokens[$i] instanceof PHP_Token_OPEN_CURLY && - !$tokens[$i] instanceof PHP_Token_SEMICOLON) { - $this->signature .= $tokens[$i++]; - } - - $this->signature = trim($this->signature); - - return $this->signature; + return $this->nanoseconds; } - /** - * @return bool - */ - public function isAnonymous() + public function asMicroseconds(): float { - return $this->anonymous; - } -} - -class PHP_Token_INTERFACE extends PHP_TokenWithScopeAndVisibility -{ - /** - * @var array - */ - protected $interfaces; - - /** - * @return string - */ - public function getName() - { - return (string) $this->tokenStream[$this->id + 2]; + return $this->nanoseconds / 1000; } - /** - * @return bool - */ - public function hasParent() + public function asMilliseconds(): float { - return $this->tokenStream[$this->id + 4] instanceof PHP_Token_EXTENDS; + return $this->nanoseconds / 1000000; } - /** - * @return array - */ - public function getPackage() + public function asSeconds(): float { - $className = $this->getName(); - $docComment = $this->getDocblock(); - - $result = [ - 'namespace' => '', - 'fullPackage' => '', - 'category' => '', - 'package' => '', - 'subpackage' => '' - ]; - - for ($i = $this->id; $i; --$i) { - if ($this->tokenStream[$i] instanceof PHP_Token_NAMESPACE) { - $result['namespace'] = $this->tokenStream[$i]->getName(); - break; - } - } - - if (preg_match('/@category[\s]+([\.\w]+)/', $docComment, $matches)) { - $result['category'] = $matches[1]; - } - - if (preg_match('/@package[\s]+([\.\w]+)/', $docComment, $matches)) { - $result['package'] = $matches[1]; - $result['fullPackage'] = $matches[1]; - } - - if (preg_match('/@subpackage[\s]+([\.\w]+)/', $docComment, $matches)) { - $result['subpackage'] = $matches[1]; - $result['fullPackage'] .= '.' . $matches[1]; - } - - if (empty($result['fullPackage'])) { - $result['fullPackage'] = $this->arrayToName( - explode('_', str_replace('\\', '_', $className)), - '.' - ); - } - - return $result; + return $this->nanoseconds / 1000000000; } - /** - * @param array $parts - * @param string $join - * - * @return string - */ - protected function arrayToName(array $parts, $join = '\\') + public function asString(): string { $result = ''; - if (count($parts) > 1) { - array_pop($parts); + if ($this->hours > 0) { + $result = sprintf('%02d', $this->hours) . ':'; + } - $result = implode($join, $parts); + $result .= sprintf('%02d', $this->minutes) . ':'; + $result .= sprintf('%02d', $this->seconds); + + if ($this->milliseconds > 0) { + $result .= '.' . sprintf('%03d', $this->milliseconds); } return $result; } - - /** - * @return bool|string - */ - public function getParent() - { - if (!$this->hasParent()) { - return false; - } - - $i = $this->id + 6; - $tokens = $this->tokenStream->tokens(); - $className = (string) $tokens[$i]; - - while (isset($tokens[$i + 1]) && - !$tokens[$i + 1] instanceof PHP_Token_WHITESPACE) { - $className .= (string) $tokens[++$i]; - } - - return $className; - } - - /** - * @return bool - */ - public function hasInterfaces() - { - return (isset($this->tokenStream[$this->id + 4]) && - $this->tokenStream[$this->id + 4] instanceof PHP_Token_IMPLEMENTS) || - (isset($this->tokenStream[$this->id + 8]) && - $this->tokenStream[$this->id + 8] instanceof PHP_Token_IMPLEMENTS); - } - - /** - * @return array|bool - */ - public function getInterfaces() - { - if ($this->interfaces !== null) { - return $this->interfaces; - } - - if (!$this->hasInterfaces()) { - return ($this->interfaces = false); - } - - if ($this->tokenStream[$this->id + 4] instanceof PHP_Token_IMPLEMENTS) { - $i = $this->id + 3; - } else { - $i = $this->id + 7; - } - - $tokens = $this->tokenStream->tokens(); - - while (!$tokens[$i + 1] instanceof PHP_Token_OPEN_CURLY) { - $i++; - - if ($tokens[$i] instanceof PHP_Token_STRING) { - $this->interfaces[] = (string) $tokens[$i]; - } - } - - return $this->interfaces; - } -} - -class PHP_Token_ABSTRACT extends PHP_Token -{ -} - -class PHP_Token_AMPERSAND extends PHP_Token -{ -} - -class PHP_Token_AND_EQUAL extends PHP_Token -{ -} - -class PHP_Token_ARRAY extends PHP_Token -{ -} - -class PHP_Token_ARRAY_CAST extends PHP_Token -{ -} - -class PHP_Token_AS extends PHP_Token -{ -} - -class PHP_Token_AT extends PHP_Token -{ -} - -class PHP_Token_BACKTICK extends PHP_Token -{ -} - -class PHP_Token_BAD_CHARACTER extends PHP_Token -{ -} - -class PHP_Token_BOOLEAN_AND extends PHP_Token -{ -} - -class PHP_Token_BOOLEAN_OR extends PHP_Token -{ -} - -class PHP_Token_BOOL_CAST extends PHP_Token -{ -} - -class PHP_Token_BREAK extends PHP_Token -{ -} - -class PHP_Token_CARET extends PHP_Token -{ -} - -class PHP_Token_CASE extends PHP_Token -{ -} - -class PHP_Token_CATCH extends PHP_Token -{ -} - -class PHP_Token_CHARACTER extends PHP_Token -{ -} - -class PHP_Token_CLASS extends PHP_Token_INTERFACE -{ - /** - * @var bool - */ - private $anonymous = false; - - /** - * @var string - */ - private $name; - - /** - * @return string - */ - public function getName() - { - if ($this->name !== null) { - return $this->name; - } - - $next = $this->tokenStream[$this->id + 1]; - - if ($next instanceof PHP_Token_WHITESPACE) { - $next = $this->tokenStream[$this->id + 2]; - } - - if ($next instanceof PHP_Token_STRING) { - $this->name =(string) $next; - - return $this->name; - } - - if ($next instanceof PHP_Token_OPEN_CURLY || - $next instanceof PHP_Token_EXTENDS || - $next instanceof PHP_Token_IMPLEMENTS) { - - $this->name = sprintf( - 'AnonymousClass:%s#%s', - $this->getLine(), - $this->getId() - ); - - $this->anonymous = true; - - return $this->name; - } - } - - public function isAnonymous() - { - return $this->anonymous; - } -} - -class PHP_Token_CLASS_C extends PHP_Token -{ -} - -class PHP_Token_CLASS_NAME_CONSTANT extends PHP_Token -{ -} - -class PHP_Token_CLONE extends PHP_Token -{ -} - -class PHP_Token_CLOSE_BRACKET extends PHP_Token -{ -} - -class PHP_Token_CLOSE_CURLY extends PHP_Token -{ -} - -class PHP_Token_CLOSE_SQUARE extends PHP_Token -{ -} - -class PHP_Token_CLOSE_TAG extends PHP_Token -{ -} - -class PHP_Token_COLON extends PHP_Token -{ -} - -class PHP_Token_COMMA extends PHP_Token -{ -} - -class PHP_Token_COMMENT extends PHP_Token -{ -} - -class PHP_Token_CONCAT_EQUAL extends PHP_Token -{ -} - -class PHP_Token_CONST extends PHP_Token -{ -} - -class PHP_Token_CONSTANT_ENCAPSED_STRING extends PHP_Token -{ -} - -class PHP_Token_CONTINUE extends PHP_Token -{ -} - -class PHP_Token_CURLY_OPEN extends PHP_Token -{ -} - -class PHP_Token_DEC extends PHP_Token -{ -} - -class PHP_Token_DECLARE extends PHP_Token -{ -} - -class PHP_Token_DEFAULT extends PHP_Token -{ -} - -class PHP_Token_DIV extends PHP_Token -{ -} - -class PHP_Token_DIV_EQUAL extends PHP_Token -{ -} - -class PHP_Token_DNUMBER extends PHP_Token -{ -} - -class PHP_Token_DO extends PHP_Token -{ -} - -class PHP_Token_DOC_COMMENT extends PHP_Token -{ -} - -class PHP_Token_DOLLAR extends PHP_Token -{ -} - -class PHP_Token_DOLLAR_OPEN_CURLY_BRACES extends PHP_Token -{ -} - -class PHP_Token_DOT extends PHP_Token -{ -} - -class PHP_Token_DOUBLE_ARROW extends PHP_Token -{ -} - -class PHP_Token_DOUBLE_CAST extends PHP_Token -{ -} - -class PHP_Token_DOUBLE_COLON extends PHP_Token -{ -} - -class PHP_Token_DOUBLE_QUOTES extends PHP_Token -{ -} - -class PHP_Token_ECHO extends PHP_Token -{ -} - -class PHP_Token_ELSE extends PHP_Token -{ -} - -class PHP_Token_ELSEIF extends PHP_Token -{ -} - -class PHP_Token_EMPTY extends PHP_Token -{ -} - -class PHP_Token_ENCAPSED_AND_WHITESPACE extends PHP_Token -{ -} - -class PHP_Token_ENDDECLARE extends PHP_Token -{ -} - -class PHP_Token_ENDFOR extends PHP_Token -{ -} - -class PHP_Token_ENDFOREACH extends PHP_Token -{ -} - -class PHP_Token_ENDIF extends PHP_Token -{ -} - -class PHP_Token_ENDSWITCH extends PHP_Token -{ -} - -class PHP_Token_ENDWHILE extends PHP_Token -{ -} - -class PHP_Token_END_HEREDOC extends PHP_Token -{ -} - -class PHP_Token_EQUAL extends PHP_Token -{ -} - -class PHP_Token_EVAL extends PHP_Token -{ -} - -class PHP_Token_EXCLAMATION_MARK extends PHP_Token -{ -} - -class PHP_Token_EXIT extends PHP_Token -{ -} - -class PHP_Token_EXTENDS extends PHP_Token -{ -} - -class PHP_Token_FILE extends PHP_Token -{ -} - -class PHP_Token_FINAL extends PHP_Token -{ -} - -class PHP_Token_FOR extends PHP_Token -{ -} - -class PHP_Token_FOREACH extends PHP_Token -{ -} - -class PHP_Token_FUNC_C extends PHP_Token -{ -} - -class PHP_Token_GLOBAL extends PHP_Token -{ -} - -class PHP_Token_GT extends PHP_Token -{ -} - -class PHP_Token_IF extends PHP_Token -{ -} - -class PHP_Token_IMPLEMENTS extends PHP_Token -{ -} - -class PHP_Token_INC extends PHP_Token -{ -} - -class PHP_Token_INCLUDE extends PHP_Token_Includes -{ -} - -class PHP_Token_INCLUDE_ONCE extends PHP_Token_Includes -{ } + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Timer; -class PHP_Token_INLINE_HTML extends PHP_Token -{ -} - -class PHP_Token_INSTANCEOF extends PHP_Token -{ -} - -class PHP_Token_INT_CAST extends PHP_Token -{ -} - -class PHP_Token_ISSET extends PHP_Token -{ -} - -class PHP_Token_IS_EQUAL extends PHP_Token -{ -} - -class PHP_Token_IS_GREATER_OR_EQUAL extends PHP_Token -{ -} - -class PHP_Token_IS_IDENTICAL extends PHP_Token -{ -} - -class PHP_Token_IS_NOT_EQUAL extends PHP_Token -{ -} - -class PHP_Token_IS_NOT_IDENTICAL extends PHP_Token -{ -} - -class PHP_Token_IS_SMALLER_OR_EQUAL extends PHP_Token -{ -} - -class PHP_Token_LINE extends PHP_Token -{ -} - -class PHP_Token_LIST extends PHP_Token -{ -} - -class PHP_Token_LNUMBER extends PHP_Token -{ -} - -class PHP_Token_LOGICAL_AND extends PHP_Token -{ -} - -class PHP_Token_LOGICAL_OR extends PHP_Token -{ -} - -class PHP_Token_LOGICAL_XOR extends PHP_Token -{ -} - -class PHP_Token_LT extends PHP_Token -{ -} - -class PHP_Token_METHOD_C extends PHP_Token -{ -} - -class PHP_Token_MINUS extends PHP_Token -{ -} - -class PHP_Token_MINUS_EQUAL extends PHP_Token -{ -} - -class PHP_Token_MOD_EQUAL extends PHP_Token -{ -} - -class PHP_Token_MULT extends PHP_Token -{ -} - -class PHP_Token_MUL_EQUAL extends PHP_Token -{ -} - -class PHP_Token_NEW extends PHP_Token -{ -} - -class PHP_Token_NUM_STRING extends PHP_Token -{ -} - -class PHP_Token_OBJECT_CAST extends PHP_Token -{ -} - -class PHP_Token_OBJECT_OPERATOR extends PHP_Token -{ -} - -class PHP_Token_OPEN_BRACKET extends PHP_Token -{ -} - -class PHP_Token_OPEN_CURLY extends PHP_Token -{ -} - -class PHP_Token_OPEN_SQUARE extends PHP_Token -{ -} - -class PHP_Token_OPEN_TAG extends PHP_Token -{ -} - -class PHP_Token_OPEN_TAG_WITH_ECHO extends PHP_Token -{ -} - -class PHP_Token_OR_EQUAL extends PHP_Token -{ -} - -class PHP_Token_PAAMAYIM_NEKUDOTAYIM extends PHP_Token -{ -} - -class PHP_Token_PERCENT extends PHP_Token -{ -} - -class PHP_Token_PIPE extends PHP_Token -{ -} - -class PHP_Token_PLUS extends PHP_Token -{ -} - -class PHP_Token_PLUS_EQUAL extends PHP_Token -{ -} - -class PHP_Token_PRINT extends PHP_Token -{ -} - -class PHP_Token_PRIVATE extends PHP_Token -{ -} - -class PHP_Token_PROTECTED extends PHP_Token -{ -} - -class PHP_Token_PUBLIC extends PHP_Token -{ -} - -class PHP_Token_QUESTION_MARK extends PHP_Token -{ -} - -class PHP_Token_REQUIRE extends PHP_Token_Includes -{ -} - -class PHP_Token_REQUIRE_ONCE extends PHP_Token_Includes -{ -} - -class PHP_Token_RETURN extends PHP_Token -{ -} - -class PHP_Token_SEMICOLON extends PHP_Token -{ -} - -class PHP_Token_SL extends PHP_Token -{ -} - -class PHP_Token_SL_EQUAL extends PHP_Token -{ -} - -class PHP_Token_SR extends PHP_Token -{ -} - -class PHP_Token_SR_EQUAL extends PHP_Token -{ -} - -class PHP_Token_START_HEREDOC extends PHP_Token -{ -} - -class PHP_Token_STATIC extends PHP_Token -{ -} - -class PHP_Token_STRING extends PHP_Token -{ -} - -class PHP_Token_STRING_CAST extends PHP_Token -{ -} - -class PHP_Token_STRING_VARNAME extends PHP_Token -{ -} - -class PHP_Token_SWITCH extends PHP_Token -{ -} - -class PHP_Token_THROW extends PHP_Token -{ -} - -class PHP_Token_TILDE extends PHP_Token -{ -} - -class PHP_Token_TRY extends PHP_Token -{ -} - -class PHP_Token_UNSET extends PHP_Token -{ -} - -class PHP_Token_UNSET_CAST extends PHP_Token -{ -} - -class PHP_Token_USE extends PHP_Token -{ -} - -class PHP_Token_USE_FUNCTION extends PHP_Token -{ -} - -class PHP_Token_VAR extends PHP_Token -{ -} - -class PHP_Token_VARIABLE extends PHP_Token -{ -} - -class PHP_Token_WHILE extends PHP_Token -{ -} - -class PHP_Token_WHITESPACE extends PHP_Token -{ -} - -class PHP_Token_XOR_EQUAL extends PHP_Token -{ -} - -// Tokens introduced in PHP 5.1 -class PHP_Token_HALT_COMPILER extends PHP_Token -{ -} - -// Tokens introduced in PHP 5.3 -class PHP_Token_DIR extends PHP_Token -{ -} - -class PHP_Token_GOTO extends PHP_Token -{ -} - -class PHP_Token_NAMESPACE extends PHP_TokenWithScope -{ - /** - * @return string - */ - public function getName() - { - $tokens = $this->tokenStream->tokens(); - $namespace = (string) $tokens[$this->id + 2]; - - for ($i = $this->id + 3;; $i += 2) { - if (isset($tokens[$i]) && - $tokens[$i] instanceof PHP_Token_NS_SEPARATOR) { - $namespace .= '\\' . $tokens[$i + 1]; - } else { - break; - } - } - - return $namespace; - } -} - -class PHP_Token_NS_C extends PHP_Token -{ -} - -class PHP_Token_NS_SEPARATOR extends PHP_Token -{ -} - -// Tokens introduced in PHP 5.4 -class PHP_Token_CALLABLE extends PHP_Token -{ -} - -class PHP_Token_INSTEADOF extends PHP_Token -{ -} - -class PHP_Token_TRAIT extends PHP_Token_INTERFACE -{ -} - -class PHP_Token_TRAIT_C extends PHP_Token -{ -} - -// Tokens introduced in PHP 5.5 -class PHP_Token_FINALLY extends PHP_Token -{ -} - -class PHP_Token_YIELD extends PHP_Token -{ -} - -// Tokens introduced in PHP 5.6 -class PHP_Token_ELLIPSIS extends PHP_Token -{ -} - -class PHP_Token_POW extends PHP_Token -{ -} - -class PHP_Token_POW_EQUAL extends PHP_Token -{ -} - -// Tokens introduced in PHP 7.0 -class PHP_Token_COALESCE extends PHP_Token -{ -} - -class PHP_Token_SPACESHIP extends PHP_Token -{ -} - -class PHP_Token_YIELD_FROM extends PHP_Token -{ -} - -// Tokens introduced in PHP 7.4 -class PHP_Token_COALESCE_EQUAL extends PHP_Token -{ -} +use LogicException; -class PHP_Token_FN extends PHP_Token +final class NoActiveTimerException extends LogicException implements Exception { } + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Timer; + +use RuntimeException; + +final class TimeSinceStartOfRequestNotAvailableException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Timer; + +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 SebastianBergmann\CodeCoverage\Util; + +use function sprintf; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Percentage +{ + /** + * @var float + */ + private $fraction; + + /** + * @var float + */ + 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 + { + 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 SebastianBergmann\CodeCoverage\Util; + +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 Filesystem +{ + /** + * @throws DirectoryCouldNotBeCreatedException + */ + public static function createDirectory(string $directory): void + { + $success = !(!is_dir($directory) && !@mkdir($directory, 0777, true) && !is_dir($directory)); + + if (!$success) { + throw new DirectoryCouldNotBeCreatedException( + sprintf( + 'Directory "%s" could not be created', + $directory + ) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use RuntimeException; +use 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. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use RuntimeException; +use SebastianBergmann\CodeCoverage\Exception; + +final class PhpdbgNotAvailableException extends RuntimeException implements Exception +{ + public function __construct() + { + parent::__construct('The PHPDBG SAPI is not available'); + } +} + * @@ -4402,23 +24120,2442 @@ class PHP_Token_FN extends PHP_Token */ namespace SebastianBergmann\CodeCoverage; +use RuntimeException; + +final class NoCodeCoverageDriverAvailableException extends RuntimeException implements Exception +{ + public function __construct() + { + 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 SebastianBergmann\CodeCoverage; + +use RuntimeException; + +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 SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class ReportAlreadyFinalizedException extends RuntimeException implements Exception +{ + public function __construct() + { + 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 SebastianBergmann\CodeCoverage\Driver; + +use RuntimeException; +use SebastianBergmann\CodeCoverage\Exception; + +final class XdebugNotAvailableException extends RuntimeException implements Exception +{ + 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 SebastianBergmann\CodeCoverage\Driver; + +use function sprintf; +use RuntimeException; +use SebastianBergmann\CodeCoverage\Exception; + +final class WriteOperationFailedException extends RuntimeException implements Exception +{ + public function __construct(string $path) + { + 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 SebastianBergmann\CodeCoverage\Util; + +use RuntimeException; +use SebastianBergmann\CodeCoverage\Exception; + +final class DirectoryCouldNotBeCreatedException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class DeadCodeDetectionNotSupportedException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use RuntimeException; +use SebastianBergmann\CodeCoverage\Exception; + +final class Xdebug2NotEnabledException extends RuntimeException implements Exception +{ + public function __construct() + { + 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 SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class TestIdMissingException extends RuntimeException implements Exception +{ + public function __construct() + { + 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 SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class ReflectionException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use RuntimeException; +use SebastianBergmann\CodeCoverage\Exception; + +final class Xdebug3NotEnabledException extends RuntimeException implements Exception +{ + public function __construct() + { + 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 SebastianBergmann\CodeCoverage; + +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. + */ +namespace SebastianBergmann\CodeCoverage; + +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 SebastianBergmann\CodeCoverage\Driver; + +use RuntimeException; +use SebastianBergmann\CodeCoverage\Exception; + +final class PcovNotAvailableException extends RuntimeException implements Exception +{ + public function __construct() + { + 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 SebastianBergmann\CodeCoverage\Driver; + +use function sprintf; +use RuntimeException; +use SebastianBergmann\CodeCoverage\Exception; + +final class PathExistsButIsNotDirectoryException extends RuntimeException implements Exception +{ + public function __construct(string $path) + { + 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 SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class StaticAnalysisCacheNotConfiguredException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class UnintentionallyCoveredCodeException extends RuntimeException implements Exception +{ + /** + * @var array + */ + private $unintentionallyCoveredUnits; + + public function __construct(array $unintentionallyCoveredUnits) + { + $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 SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class BranchAndPathCoverageNotSupportedException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +final 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 SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class ParserException extends RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use function array_keys; +use function is_file; +use function realpath; +use function strpos; +use 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 SebastianBergmann\CodeCoverage\Driver; + +use const pcov\inclusive; +use function array_intersect; +use function extension_loaded; +use function pcov\clear; +use function pcov\collect; +use function pcov\start; +use function pcov\stop; +use function pcov\waiting; +use function phpversion; +use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\RawCodeCoverageData; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class PcovDriver extends Driver +{ + /** + * @var Filter + */ + private $filter; + + /** + * @throws PcovNotAvailableException + */ + public function __construct(Filter $filter) + { + if (!extension_loaded('pcov')) { + throw new PcovNotAvailableException; + } + + $this->filter = $filter; + } + + public function start(): void + { + start(); + } + + public function stop(): RawCodeCoverageData + { + stop(); + + $filesToCollectCoverageFor = waiting(); + $collected = []; + + if ($filesToCollectCoverageFor) { + if (!$this->filter->isEmpty()) { + $filesToCollectCoverageFor = array_intersect($filesToCollectCoverageFor, $this->filter->files()); + } + + $collected = collect(inclusive, $filesToCollectCoverageFor); + + clear(); + } + + return RawCodeCoverageData::fromXdebugWithoutPathCoverage($collected); + } + + 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 SebastianBergmann\CodeCoverage\Driver; + +use function sprintf; +use SebastianBergmann\CodeCoverage\BranchAndPathCoverageNotSupportedException; +use SebastianBergmann\CodeCoverage\DeadCodeDetectionNotSupportedException; +use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverAvailableException; +use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverWithPathCoverageSupportAvailableException; +use SebastianBergmann\CodeCoverage\RawCodeCoverageData; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +abstract class Driver +{ + /** + * @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; + + /** + * @var int + * + * @see http://xdebug.org/docs/code_coverage + */ + 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 + { + return (new Selector)->forLineCoverage($filter); + } + + /** + * @throws NoCodeCoverageDriverWithPathCoverageSupportAvailableException + * @throws Xdebug2NotEnabledException + * @throws Xdebug3NotEnabledException + * @throws XdebugNotAvailableException + * + * @deprecated Use DriverSelector::forLineAndPathCoverage() instead + */ + public static function forLineAndPathCoverage(Filter $filter): self + { + return (new Selector)->forLineAndPathCoverage($filter); + } + + public function canCollectBranchAndPathCoverage(): bool + { + return false; + } + + public function collectsBranchAndPathCoverage(): bool + { + 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; + } + + /** + * @throws DeadCodeDetectionNotSupportedException + */ + public function enableDeadCodeDetection(): void + { + if (!$this->canDetectDeadCode()) { + throw new DeadCodeDetectionNotSupportedException( + sprintf( + '%s does not support dead code detection', + $this->nameAndVersion() + ) + ); + } + + $this->detectDeadCode = true; + } + + public function disableDeadCodeDetection(): void + { + $this->detectDeadCode = false; + } + + abstract public function nameAndVersion(): string; + + abstract public function start(): void; + + abstract public function stop(): RawCodeCoverageData; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +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 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 SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\RawCodeCoverageData; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Xdebug3Driver extends Driver +{ + /** + * @throws WrongXdebugVersionException + * @throws Xdebug3NotEnabledException + * @throws XdebugNotAvailableException + */ + public function __construct(Filter $filter) + { + 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() + ); + } + } + + public function canCollectBranchAndPathCoverage(): bool + { + return true; + } + + public function canDetectDeadCode(): bool + { + return true; + } + + public function start(): void + { + $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 SebastianBergmann\CodeCoverage\Driver; + +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 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 SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\RawCodeCoverageData; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Xdebug2Driver extends Driver +{ + /** + * @var bool + */ + private $pathCoverageIsMixedCoverage; + + /** + * @throws WrongXdebugVersionException + * @throws Xdebug2NotEnabledException + * @throws XdebugNotAvailableException + */ + public function __construct(Filter $filter) + { + 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 canCollectBranchAndPathCoverage(): bool + { + return true; + } + + public function canDetectDeadCode(): bool + { + return true; + } + + public function start(): void + { + $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()) { + if ($this->pathCoverageIsMixedCoverage) { + return RawCodeCoverageData::fromXdebugWithMixedCoverage($data); + } + + 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 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 SebastianBergmann\CodeCoverage\RawCodeCoverageData; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class PhpdbgDriver extends Driver +{ + /** + * @throws PhpdbgNotAvailableException + */ + public function __construct() + { + if (PHP_SAPI !== 'phpdbg') { + throw new PhpdbgNotAvailableException; + } + } + + public function start(): void + { + phpdbg_start_oplog(); + } + + public function stop(): 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]); + } + } + + 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 nameAndVersion(): string + { + return 'PHPDBG ' . PHP_VERSION; + } + + private function detectExecutedLines(array $sourceLines, array $dbgData): array + { + 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 SebastianBergmann\CodeCoverage\Driver; + +use function phpversion; +use function version_compare; +use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverAvailableException; +use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverWithPathCoverageSupportAvailableException; +use SebastianBergmann\Environment\Runtime; + +final class Selector +{ + /** + * @throws NoCodeCoverageDriverAvailableException + * @throws PcovNotAvailableException + * @throws PhpdbgNotAvailableException + * @throws Xdebug2NotEnabledException + * @throws Xdebug3NotEnabledException + * @throws XdebugNotAvailableException + */ + public function forLineCoverage(Filter $filter): Driver + { + $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 NoCodeCoverageDriverWithPathCoverageSupportAvailableException + * @throws Xdebug2NotEnabledException + * @throws Xdebug3NotEnabledException + * @throws XdebugNotAvailableException + */ + public function forLineAndPathCoverage(Filter $filter): Driver + { + 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; + } + + throw new NoCodeCoverageDriverWithPathCoverageSupportAvailableException; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use PhpParser\Node; +use PhpParser\Node\Expr\Array_; +use PhpParser\Node\Expr\ArrayDimFetch; +use PhpParser\Node\Expr\ArrayItem; +use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\BinaryOp; +use PhpParser\Node\Expr\CallLike; +use PhpParser\Node\Expr\Cast; +use PhpParser\Node\Expr\Closure; +use PhpParser\Node\Expr\Match_; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\NullsafePropertyFetch; +use PhpParser\Node\Expr\PropertyFetch; +use PhpParser\Node\Expr\StaticPropertyFetch; +use PhpParser\Node\Expr\Ternary; +use PhpParser\Node\MatchArm; +use PhpParser\Node\Scalar\Encapsed; +use PhpParser\Node\Stmt\Break_; +use PhpParser\Node\Stmt\Case_; +use PhpParser\Node\Stmt\Catch_; +use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Continue_; +use PhpParser\Node\Stmt\Do_; +use PhpParser\Node\Stmt\Echo_; +use PhpParser\Node\Stmt\Else_; +use PhpParser\Node\Stmt\ElseIf_; +use PhpParser\Node\Stmt\Expression; +use PhpParser\Node\Stmt\Finally_; +use PhpParser\Node\Stmt\For_; +use PhpParser\Node\Stmt\Foreach_; +use PhpParser\Node\Stmt\Goto_; +use PhpParser\Node\Stmt\If_; +use PhpParser\Node\Stmt\Property; +use PhpParser\Node\Stmt\Return_; +use PhpParser\Node\Stmt\Switch_; +use PhpParser\Node\Stmt\Throw_; +use PhpParser\Node\Stmt\TryCatch; +use PhpParser\Node\Stmt\Unset_; +use PhpParser\Node\Stmt\While_; +use PhpParser\NodeVisitorAbstract; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract +{ + /** + * @psalm-var array + */ + private $executableLines = []; + + /** + * @psalm-var array + */ + private $propertyLines = []; + + /** + * @psalm-var array + */ + private $returns = []; + + public function enterNode(Node $node): void + { + $this->savePropertyLines($node); + + if (!$this->isExecutable($node)) { + return; + } + + foreach ($this->getLines($node) as $line) { + if (isset($this->propertyLines[$line])) { + return; + } + + $this->executableLines[$line] = $line; + } + } + + /** + * @psalm-return array + */ + public function executableLines(): array + { + $this->computeReturns(); + + sort($this->executableLines); + + return $this->executableLines; + } + + private function savePropertyLines(Node $node): void + { + if (!$node instanceof Property && !$node instanceof Node\Stmt\ClassConst) { + return; + } + + foreach (range($node->getStartLine(), $node->getEndLine()) as $index) { + $this->propertyLines[$index] = $index; + } + } + + private function computeReturns(): void + { + foreach ($this->returns as $return) { + foreach (range($return->getStartLine(), $return->getEndLine()) as $loc) { + if (isset($this->executableLines[$loc])) { + continue 2; + } + } + + $line = $return->getEndLine(); + + if ($return->expr !== null) { + $line = $return->expr->getStartLine(); + } + + $this->executableLines[$line] = $line; + } + } + + /** + * @return int[] + */ + private function getLines(Node $node): array + { + if ($node instanceof Cast || + $node instanceof PropertyFetch || + $node instanceof NullsafePropertyFetch || + $node instanceof StaticPropertyFetch) { + return [$node->getEndLine()]; + } + + if ($node instanceof ArrayDimFetch) { + if (null === $node->dim) { + return []; + } + + return [$node->dim->getStartLine()]; + } + + if ($node instanceof Array_) { + $startLine = $node->getStartLine(); + + if (isset($this->executableLines[$startLine])) { + return []; + } + + if ([] === $node->items) { + return [$node->getEndLine()]; + } + + if ($node->items[0] instanceof ArrayItem) { + return [$node->items[0]->getStartLine()]; + } + } + + if ($node instanceof ClassMethod) { + if ($node->name->name !== '__construct') { + return []; + } + + $existsAPromotedProperty = false; + + foreach ($node->getParams() as $param) { + if (0 !== ($param->flags & Class_::VISIBILITY_MODIFIER_MASK)) { + $existsAPromotedProperty = true; + + break; + } + } + + if ($existsAPromotedProperty) { + // Only the line with `function` keyword should be listed here + // but `nikic/php-parser` doesn't provide a way to fetch it + return range($node->getStartLine(), $node->name->getEndLine()); + } + + return []; + } + + if ($node instanceof MethodCall) { + return [$node->name->getStartLine()]; + } + + if ($node instanceof Ternary) { + $lines = [$node->cond->getStartLine()]; + + if (null !== $node->if) { + $lines[] = $node->if->getStartLine(); + } + + $lines[] = $node->else->getStartLine(); + + return $lines; + } + + if ($node instanceof Match_) { + return [$node->cond->getStartLine()]; + } + + if ($node instanceof MatchArm) { + return [$node->body->getStartLine()]; + } + + if ($node instanceof Expression && ( + $node->expr instanceof Cast || + $node->expr instanceof Match_ || + $node->expr instanceof MethodCall + )) { + return []; + } + + if ($node instanceof Return_) { + $this->returns[] = $node; + + return []; + } + + return [$node->getStartLine()]; + } + + private function isExecutable(Node $node): bool + { + return $node instanceof Assign || + $node instanceof ArrayDimFetch || + $node instanceof Array_ || + $node instanceof BinaryOp || + $node instanceof Break_ || + $node instanceof CallLike || + $node instanceof Case_ || + $node instanceof Cast || + $node instanceof Catch_ || + $node instanceof ClassMethod || + $node instanceof Closure || + $node instanceof Continue_ || + $node instanceof Do_ || + $node instanceof Echo_ || + $node instanceof ElseIf_ || + $node instanceof Else_ || + $node instanceof Encapsed || + $node instanceof Expression || + $node instanceof Finally_ || + $node instanceof For_ || + $node instanceof Foreach_ || + $node instanceof Goto_ || + $node instanceof If_ || + $node instanceof Match_ || + $node instanceof MatchArm || + $node instanceof MethodCall || + $node instanceof NullsafePropertyFetch || + $node instanceof PropertyFetch || + $node instanceof Return_ || + $node instanceof StaticPropertyFetch || + $node instanceof Switch_ || + $node instanceof Ternary || + $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 SebastianBergmann\CodeCoverage\StaticAnalysis; + +use function array_unique; +use function assert; +use function file_get_contents; +use function is_array; +use function max; +use function sprintf; +use function substr_count; +use function token_get_all; +use function trim; +use PhpParser\Error; +use PhpParser\Lexer; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitor\NameResolver; +use PhpParser\NodeVisitor\ParentConnectingVisitor; +use PhpParser\ParserFactory; +use SebastianBergmann\CodeCoverage\ParserException; +use SebastianBergmann\LinesOfCode\LineCountingVisitor; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class ParsingFileAnalyser implements FileAnalyser +{ + /** + * @var array + */ + private $classes = []; + + /** + * @var array + */ + private $traits = []; + + /** + * @var array + */ + private $functions = []; + + /** + * @var array + */ + private $linesOfCode = []; + + /** + * @var array + */ + private $ignoredLines = []; + + /** + * @var array + */ + private $executableLines = []; + + /** + * @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 + { + $this->analyse($filename); + + return $this->classes[$filename]; + } + + public function traitsIn(string $filename): array + { + $this->analyse($filename); + + return $this->traits[$filename]; + } + + public function functionsIn(string $filename): array + { + $this->analyse($filename); + + return $this->functions[$filename]; + } + + /** + * @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int} + */ + public function linesOfCodeFor(string $filename): array + { + $this->analyse($filename); + + return $this->linesOfCode[$filename]; + } + + public function executableLinesIn(string $filename): array + { + $this->analyse($filename); + + return $this->executableLines[$filename]; + } + + public function ignoredLinesFor(string $filename): array + { + $this->analyse($filename); + + return $this->ignoredLines[$filename]; + } + + /** + * @throws ParserException + */ + private function analyse(string $filename): void + { + if (isset($this->classes[$filename])) { + return; + } + + $source = file_get_contents($filename); + $linesOfCode = max(substr_count($source, "\n") + 1, substr_count($source, "\r") + 1); + + 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); + $executableLinesFindingVisitor = new ExecutableLinesFindingVisitor; + + $traverser->addVisitor(new NameResolver); + $traverser->addVisitor(new ParentConnectingVisitor); + $traverser->addVisitor($codeUnitFindingVisitor); + $traverser->addVisitor($lineCountingVisitor); + $traverser->addVisitor($ignoredLinesFindingVisitor); + $traverser->addVisitor($executableLinesFindingVisitor); + + /* @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->executableLines[$filename] = $executableLinesFindingVisitor->executableLines(); + $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]); + + $result = $lineCountingVisitor->result(); + + $this->linesOfCode[$filename] = [ + 'linesOfCode' => $result->linesOfCode(), + 'commentLinesOfCode' => $result->commentLinesOfCode(), + 'nonCommentLinesOfCode' => $result->nonCommentLinesOfCode(), + ]; + } + + private function findLinesIgnoredByLineBasedAnnotations(string $filename, string $source, bool $useAnnotationsForIgnoringCode): void + { + $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 SebastianBergmann\CodeCoverage\StaticAnalysis; + +use function implode; +use function rtrim; +use function trim; +use PhpParser\Node; +use PhpParser\Node\ComplexType; +use PhpParser\Node\Identifier; +use PhpParser\Node\IntersectionType; +use PhpParser\Node\Name; +use PhpParser\Node\NullableType; +use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Enum_; +use PhpParser\Node\Stmt\Function_; +use PhpParser\Node\Stmt\Interface_; +use PhpParser\Node\Stmt\Trait_; +use PhpParser\Node\UnionType; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; +use SebastianBergmann\Complexity\CyclomaticComplexityCalculatingVisitor; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class CodeUnitFindingVisitor extends NodeVisitorAbstract +{ + /** + * @psalm-var array}> + */ + private $classes = []; + + /** + * @psalm-var array}> + */ + private $traits = []; + + /** + * @psalm-var array + */ + private $functions = []; + + public function enterNode(Node $node): void + { + 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; + } + + if ($node instanceof ClassMethod) { + $parentNode = $node->getAttribute('parent'); + + if ($parentNode instanceof Class_ && $parentNode->isAnonymous()) { + return; + } + + $this->processMethod($node); + + return; + } + + $this->processFunction($node); + } + + /** + * @psalm-return array}> + */ + public function classes(): array + { + return $this->classes; + } + + /** + * @psalm-return array}> + */ + public function traits(): array + { + return $this->traits; + } + + /** + * @psalm-return array + */ + public function functions(): array + { + return $this->functions; + } + + /** + * @psalm-param ClassMethod|Function_ $node + */ + private function cyclomaticComplexity(Node $node): int + { + 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(); + } + + /** + * @psalm-param ClassMethod|Function_ $node + */ + private function signature(Node $node): string + { + 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; + } + + $signature .= implode(', ', $parameters) . ')'; + + $returnType = $node->getReturnType(); + + if ($returnType !== null) { + $signature .= ': ' . $this->type($returnType); + } + + return $signature; + } + + /** + * @psalm-param Identifier|Name|ComplexType $type + */ + private function type(Node $type): string + { + assert($type instanceof Identifier || $type instanceof Name || $type instanceof ComplexType); + + if ($type instanceof NullableType) { + return '?' . $type->type; + } + + if ($type instanceof UnionType || $type instanceof IntersectionType) { + return $this->unionOrIntersectionAsString($type); + } + + return $type->toString(); + } + + private function visibility(ClassMethod $node): string + { + if ($node->isPrivate()) { + return 'private'; + } + + if ($node->isProtected()) { + return 'protected'; + } + + return 'public'; + } + + private function processClass(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(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(ClassMethod $node): void + { + $parentNode = $node->getAttribute('parent'); + + if ($parentNode instanceof Interface_) { + return; + } + + assert($parentNode instanceof Class_ || $parentNode instanceof Trait_ || $parentNode instanceof Enum_); + 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), + ]; + } + + private function processFunction(Function_ $node): void + { + 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), + ]; + } + + private function namespace(string $namespacedName, string $name): string + { + return trim(rtrim($namespacedName, $name), '\\'); + } + + /** + * @psalm-param UnionType|IntersectionType $type + */ + private function unionOrIntersectionAsString(ComplexType $type): string + { + if ($type instanceof UnionType) { + $separator = '|'; + } else { + $separator = '&'; + } + + $types = []; + + foreach ($type->types as $_type) { + if ($_type instanceof Name) { + $types[] = $_type->toCodeString(); + } else { + $types[] = $_type->toString(); + } + } + + return implode($separator, $types); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +interface FileAnalyser +{ + public function classesIn(string $filename): array; + + public function traitsIn(string $filename): array; + + public function functionsIn(string $filename): array; + + /** + * @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int} + */ + public function linesOfCodeFor(string $filename): array; + + public function executableLinesIn(string $filename): array; + + 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 SebastianBergmann\CodeCoverage\StaticAnalysis; + +use function assert; +use function crc32; +use function file_get_contents; +use function file_put_contents; +use function is_file; +use function serialize; +use GlobIterator; +use SebastianBergmann\CodeCoverage\Util\Filesystem; +use SplFileInfo; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class CachingFileAnalyser implements FileAnalyser +{ + /** + * @var ?string + */ + private static $cacheVersion; + + /** + * @var FileAnalyser + */ + private $analyser; + + /** + * @var array + */ + private $cache = []; + + /** + * @var string + */ + private $directory; + + public function __construct(string $directory, FileAnalyser $analyser) + { + Filesystem::createDirectory($directory); + + $this->analyser = $analyser; + $this->directory = $directory; + + if (self::$cacheVersion === null) { + $this->calculateCacheVersion(); + } + } + + public function classesIn(string $filename): array + { + if (!isset($this->cache[$filename])) { + $this->process($filename); + } + + return $this->cache[$filename]['classesIn']; + } + + public function traitsIn(string $filename): array + { + if (!isset($this->cache[$filename])) { + $this->process($filename); + } + + return $this->cache[$filename]['traitsIn']; + } + + public function functionsIn(string $filename): array + { + if (!isset($this->cache[$filename])) { + $this->process($filename); + } + + return $this->cache[$filename]['functionsIn']; + } + + /** + * @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int} + */ + public function linesOfCodeFor(string $filename): array + { + if (!isset($this->cache[$filename])) { + $this->process($filename); + } + + return $this->cache[$filename]['linesOfCodeFor']; + } + + public function executableLinesIn(string $filename): array + { + if (!isset($this->cache[$filename])) { + $this->process($filename); + } + + return $this->cache[$filename]['executableLinesIn']; + } + + public function ignoredLinesFor(string $filename): array + { + if (!isset($this->cache[$filename])) { + $this->process($filename); + } + + return $this->cache[$filename]['ignoredLinesFor']; + } + + public function process(string $filename): void + { + $cache = $this->read($filename); + + if ($cache !== false) { + $this->cache[$filename] = $cache; + + return; + } + + $this->cache[$filename] = [ + 'classesIn' => $this->analyser->classesIn($filename), + 'traitsIn' => $this->analyser->traitsIn($filename), + 'functionsIn' => $this->analyser->functionsIn($filename), + 'linesOfCodeFor' => $this->analyser->linesOfCodeFor($filename), + 'ignoredLinesFor' => $this->analyser->ignoredLinesFor($filename), + 'executableLinesIn' => $this->analyser->executableLinesIn($filename), + ]; + + $this->write($filename, $this->cache[$filename]); + } + + /** + * @return mixed + */ + private function read(string $filename) + { + $cacheFile = $this->cacheFile($filename); + + if (!is_file($cacheFile)) { + return false; + } + + return unserialize( + file_get_contents($cacheFile), + ['allowed_classes' => false] + ); + } + + /** + * @param mixed $data + */ + private function write(string $filename, $data): void + { + file_put_contents( + $this->cacheFile($filename), + serialize($data) + ); + } + + private function cacheFile(string $filename): string + { + return $this->directory . DIRECTORY_SEPARATOR . hash('sha256', $filename . crc32(file_get_contents($filename)) . self::$cacheVersion); + } + + private function calculateCacheVersion(): void + { + $buffer = ''; + + foreach (new GlobIterator(__DIR__ . '/*.php') as $file) { + assert($file instanceof SplFileInfo); + + $buffer .= file_get_contents($file->getPathname()); + } + + self::$cacheVersion = (string) crc32($buffer); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use function array_merge; +use function range; +use function strpos; +use PhpParser\Node; +use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Function_; +use PhpParser\Node\Stmt\Interface_; +use PhpParser\Node\Stmt\Trait_; +use PhpParser\NodeVisitorAbstract; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class IgnoredLinesFindingVisitor extends NodeVisitorAbstract +{ + /** + * @psalm-var list + */ + private $ignoredLines = []; + + /** + * @var bool + */ + private $useAnnotationsForIgnoringCode; + + /** + * @var bool + */ + private $ignoreDeprecated; + + public function __construct(bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecated) + { + $this->useAnnotationsForIgnoringCode = $useAnnotationsForIgnoringCode; + $this->ignoreDeprecated = $ignoreDeprecated; + } + + public function enterNode(Node $node): void + { + if (!$node instanceof Class_ && + !$node instanceof Trait_ && + !$node instanceof Interface_ && + !$node instanceof ClassMethod && + !$node instanceof Function_) { + return; + } + + if ($node instanceof Class_ && $node->isAnonymous()) { + return; + } + + // 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; + } + + if ($node instanceof Interface_) { + return; + } + + $docComment = $node->getDocComment(); + + if ($docComment === null) { + return; + } + + 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()) + ); + } + } + + /** + * @psalm-return list + */ + public function ignoredLines(): array + { + return $this->ignoredLines; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\StaticAnalysis; + +use SebastianBergmann\CodeCoverage\Filter; + +final class CacheWarmer +{ + public function warmCache(string $cacheDirectory, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode, Filter $filter): void + { + $analyser = new CachingFileAnalyser( + $cacheDirectory, + new ParsingFileAnalyser( + $useAnnotationsForIgnoringCode, + $ignoreDeprecatedCode + ) + ); + + foreach ($filter->files() as $file) { + $analyser->process($file); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use function dirname; +use SebastianBergmann\Version as VersionId; + +final class Version +{ + /** + * @var string + */ + private static $version; + + public static function id(): string + { + if (self::$version === null) { + self::$version = (new VersionId('9.2.15', 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 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 SebastianBergmann\CodeCoverage\Driver\Driver; -use SebastianBergmann\CodeCoverage\Driver\PCOV; -use SebastianBergmann\CodeCoverage\Driver\PHPDBG; -use SebastianBergmann\CodeCoverage\Driver\Xdebug; use SebastianBergmann\CodeCoverage\Node\Builder; use SebastianBergmann\CodeCoverage\Node\Directory; +use SebastianBergmann\CodeCoverage\StaticAnalysis\CachingFileAnalyser; +use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser; +use SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingFileAnalyser; use SebastianBergmann\CodeUnitReverseLookup\Wizard; -use SebastianBergmann\Environment\Runtime; /** * Provides collection functionality for PHP code coverage information. */ final class CodeCoverage { + private const UNCOVERED_FILES = 'UNCOVERED_FILES'; + /** * @var Driver */ @@ -4434,11 +26571,6 @@ final class CodeCoverage */ private $wizard; - /** - * @var bool - */ - private $cacheTokens = false; - /** * @var bool */ @@ -4447,27 +26579,12 @@ final class CodeCoverage /** * @var bool */ - private $forceCoversAnnotation = false; + private $includeUncoveredFiles = true; /** * @var bool */ - private $checkForUnexecutedCoveredCode = false; - - /** - * @var bool - */ - private $checkForMissingCoversAnnotation = false; - - /** - * @var bool - */ - private $addUncoveredFilesFromWhitelist = true; - - /** - * @var bool - */ - private $processUncoveredFilesFromWhitelist = false; + private $processUncoveredFiles = false; /** * @var bool @@ -4482,19 +26599,14 @@ final class CodeCoverage /** * Code coverage data. * - * @var array + * @var ProcessedCodeCoverageData */ - private $data = []; - - /** - * @var array - */ - private $ignoredLines = []; + private $data; /** * @var bool */ - private $disableIgnoredLines = false; + private $useAnnotationsForIgnoringCode = true; /** * Test data. @@ -4504,45 +26616,25 @@ final class CodeCoverage private $tests = []; /** - * @var string[] + * @psalm-var list */ - private $unintentionallyCoveredSubclassesWhitelist = []; + private $parentClassesExcludedFromUnintentionallyCoveredCodeCheck = []; /** - * Determine if the data has been initialized or not - * - * @var bool + * @var ?FileAnalyser */ - private $isInitialized = false; + private $analyser; /** - * Determine whether we need to check for dead and unused code on each test - * - * @var bool + * @var ?string */ - private $shouldCheckForDeadAndUnused = true; + private $cacheDirectory; - /** - * @var Directory - */ - private $report; - - /** - * @throws RuntimeException - */ - public function __construct(Driver $driver = null, Filter $filter = null) + public function __construct(Driver $driver, Filter $filter) { - if ($filter === null) { - $filter = new Filter; - } - - if ($driver === null) { - $driver = $this->selectDriver($filter); - } - $this->driver = $driver; $this->filter = $filter; - + $this->data = new ProcessedCodeCoverageData; $this->wizard = new Wizard; } @@ -4551,11 +26643,7 @@ final class CodeCoverage */ public function getReport(): Directory { - if ($this->report === null) { - $this->report = (new Builder)->build($this); - } - - return $this->report; + return (new Builder($this->analyser()))->build($this); } /** @@ -4563,11 +26651,9 @@ final class CodeCoverage */ public function clear(): void { - $this->isInitialized = false; - $this->currentId = null; - $this->data = []; - $this->tests = []; - $this->report = null; + $this->currentId = null; + $this->data = new ProcessedCodeCoverageData; + $this->tests = []; } /** @@ -4581,10 +26667,14 @@ final class CodeCoverage /** * Returns the collected code coverage data. */ - public function getData(bool $raw = false): array + public function getData(bool $raw = false): ProcessedCodeCoverageData { - if (!$raw && $this->addUncoveredFilesFromWhitelist) { - $this->addUncoveredFilesFromWhitelist(); + if (!$raw) { + if ($this->processUncoveredFiles) { + $this->processUncoveredFilesFromFilter(); + } elseif ($this->includeUncoveredFiles) { + $this->addUncoveredFilesFromFilter(); + } } return $this->data; @@ -4593,10 +26683,9 @@ final class CodeCoverage /** * Sets the coverage data. */ - public function setData(array $data): void + public function setData(ProcessedCodeCoverageData $data): void { - $this->data = $data; - $this->report = null; + $this->data = $data; } /** @@ -4619,8 +26708,6 @@ final class CodeCoverage * Start collection of code coverage information. * * @param PhptTestCase|string|TestCase $id - * - * @throws RuntimeException */ public function start($id, bool $clear = false): void { @@ -4628,37 +26715,26 @@ final class CodeCoverage $this->clear(); } - if ($this->isInitialized === false) { - $this->initializeData(); - } - $this->currentId = $id; - $this->driver->start($this->shouldCheckForDeadAndUnused); + $this->driver->start(); } /** * Stop collection of code coverage information. * * @param array|false $linesToBeCovered - * - * @throws MissingCoversAnnotationException - * @throws CoveredCodeNotExecutedException - * @throws RuntimeException - * @throws InvalidArgumentException - * @throws \ReflectionException */ - public function stop(bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = [], bool $ignoreForceCoversAnnotation = false): array + public function stop(bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = []): RawCodeCoverageData { - if (!\is_array($linesToBeCovered) && $linesToBeCovered !== false) { - throw InvalidArgumentException::create( - 2, - 'array or false' + 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, $ignoreForceCoversAnnotation); + $this->append($data, null, $append, $linesToBeCovered, $linesToBeUsed); $this->currentId = null; @@ -4671,215 +26747,197 @@ final class CodeCoverage * @param PhptTestCase|string|TestCase $id * @param array|false $linesToBeCovered * - * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException - * @throws \SebastianBergmann\CodeCoverage\MissingCoversAnnotationException - * @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException - * @throws \ReflectionException - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws RuntimeException + * @throws ReflectionException + * @throws TestIdMissingException + * @throws UnintentionallyCoveredCodeException */ - public function append(array $data, $id = null, bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = [], bool $ignoreForceCoversAnnotation = false): void + 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 RuntimeException; + throw new TestIdMissingException; } - $this->applyWhitelistFilter($data); - $this->applyIgnoredLinesFilter($data); - $this->initializeFilesThatAreSeenTheFirstTime($data); + $this->applyFilter($rawData); + + $this->applyExecutableLinesFilter($rawData); + + if ($this->useAnnotationsForIgnoringCode) { + $this->applyIgnoredLinesFilter($rawData); + } + + $this->data->initializeUnseenData($rawData); if (!$append) { return; } - if ($id !== 'UNCOVERED_FILES_FROM_WHITELIST') { + if ($id !== self::UNCOVERED_FILES) { $this->applyCoversAnnotationFilter( - $data, + $rawData, $linesToBeCovered, - $linesToBeUsed, - $ignoreForceCoversAnnotation + $linesToBeUsed ); - } - if (empty($data)) { - return; - } - - $size = 'unknown'; - $status = -1; - - if ($id instanceof TestCase) { - $_size = $id->getSize(); - - if ($_size === Test::SMALL) { - $size = 'small'; - } elseif ($_size === Test::MEDIUM) { - $size = 'medium'; - } elseif ($_size === Test::LARGE) { - $size = 'large'; + if (empty($rawData->lineCoverage())) { + return; } - $status = $id->getStatus(); - $id = \get_class($id) . '::' . $id->getName(); - } elseif ($id instanceof PhptTestCase) { - $size = 'large'; - $id = $id->getName(); - } + $size = 'unknown'; + $status = -1; + $fromTestcase = false; - $this->tests[$id] = ['size' => $size, 'status' => $status]; + if ($id instanceof TestCase) { + $fromTestcase = true; + $_size = $id->getSize(); - foreach ($data as $file => $lines) { - if (!$this->filter->isFile($file)) { - continue; - } - - foreach ($lines as $k => $v) { - if ($v === Driver::LINE_EXECUTED) { - if (empty($this->data[$file][$k]) || !\in_array($id, $this->data[$file][$k])) { - $this->data[$file][$k][] = $id; - } + if ($_size === Test::SMALL) { + $size = 'small'; + } elseif ($_size === Test::MEDIUM) { + $size = 'medium'; + } elseif ($_size === Test::LARGE) { + $size = 'large'; } - } - } - $this->report = null; + $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); + } } /** * Merges the data from another instance. - * - * @param CodeCoverage $that */ public function merge(self $that): void { - $this->filter->setWhitelistedFiles( - \array_merge($this->filter->getWhitelistedFiles(), $that->filter()->getWhitelistedFiles()) + $this->filter->includeFiles( + $that->filter()->files() ); - foreach ($that->data as $file => $lines) { - if (!isset($this->data[$file])) { - if (!$this->filter->isFiltered($file)) { - $this->data[$file] = $lines; - } + $this->data->merge($that->data); - continue; - } - - // we should compare the lines if any of two contains data - $compareLineNumbers = \array_unique( - \array_merge( - \array_keys($this->data[$file]), - \array_keys($that->data[$file]) - ) - ); - - foreach ($compareLineNumbers as $line) { - $thatPriority = $this->getLinePriority($that->data[$file], $line); - $thisPriority = $this->getLinePriority($this->data[$file], $line); - - if ($thatPriority > $thisPriority) { - $this->data[$file][$line] = $that->data[$file][$line]; - } elseif ($thatPriority === $thisPriority && \is_array($this->data[$file][$line])) { - $this->data[$file][$line] = \array_unique( - \array_merge($this->data[$file][$line], $that->data[$file][$line]) - ); - } - } - } - - $this->tests = \array_merge($this->tests, $that->getTests()); - $this->report = null; + $this->tests = array_merge($this->tests, $that->getTests()); } - public function setCacheTokens(bool $flag): void + public function enableCheckForUnintentionallyCoveredCode(): void { - $this->cacheTokens = $flag; + $this->checkForUnintentionallyCoveredCode = true; } - public function getCacheTokens(): bool + public function disableCheckForUnintentionallyCoveredCode(): void { - return $this->cacheTokens; + $this->checkForUnintentionallyCoveredCode = false; } - public function setCheckForUnintentionallyCoveredCode(bool $flag): void + public function includeUncoveredFiles(): void { - $this->checkForUnintentionallyCoveredCode = $flag; + $this->includeUncoveredFiles = true; } - public function setForceCoversAnnotation(bool $flag): void + public function excludeUncoveredFiles(): void { - $this->forceCoversAnnotation = $flag; + $this->includeUncoveredFiles = false; } - public function setCheckForMissingCoversAnnotation(bool $flag): void + public function processUncoveredFiles(): void { - $this->checkForMissingCoversAnnotation = $flag; + $this->processUncoveredFiles = true; } - public function setCheckForUnexecutedCoveredCode(bool $flag): void + public function doNotProcessUncoveredFiles(): void { - $this->checkForUnexecutedCoveredCode = $flag; + $this->processUncoveredFiles = false; } - public function setAddUncoveredFilesFromWhitelist(bool $flag): void + public function enableAnnotationsForIgnoringCode(): void { - $this->addUncoveredFilesFromWhitelist = $flag; + $this->useAnnotationsForIgnoringCode = true; } - public function setProcessUncoveredFilesFromWhitelist(bool $flag): void + public function disableAnnotationsForIgnoringCode(): void { - $this->processUncoveredFilesFromWhitelist = $flag; + $this->useAnnotationsForIgnoringCode = false; } - public function setDisableIgnoredLines(bool $flag): void + public function ignoreDeprecatedCode(): void { - $this->disableIgnoredLines = $flag; + $this->ignoreDeprecatedCode = true; } - public function setIgnoreDeprecatedCode(bool $flag): void + public function doNotIgnoreDeprecatedCode(): void { - $this->ignoreDeprecatedCode = $flag; - } - - public function setUnintentionallyCoveredSubclassesWhitelist(array $whitelist): void - { - $this->unintentionallyCoveredSubclassesWhitelist = $whitelist; + $this->ignoreDeprecatedCode = false; } /** - * 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. - * - * @param array $data - * @param int $line - * - * @return int + * @psalm-assert-if-true !null $this->cacheDirectory */ - private function getLinePriority($data, $line) + public function cachesStaticAnalysis(): bool { - if (!\array_key_exists($line, $data)) { - return 1; + return $this->cacheDirectory !== null; + } + + public function cacheStaticAnalysis(string $directory): void + { + $this->cacheDirectory = $directory; + } + + public function doNotCacheStaticAnalysis(): void + { + $this->cacheDirectory = null; + } + + /** + * @throws StaticAnalysisCacheNotConfiguredException + */ + public function cacheDirectory(): string + { + if (!$this->cachesStaticAnalysis()) { + throw new StaticAnalysisCacheNotConfiguredException( + 'The static analysis cache is not configured' + ); } - if (\is_array($data[$line]) && \count($data[$line]) === 0) { - return 2; - } + return $this->cacheDirectory; + } - if ($data[$line] === null) { - return 3; - } + /** + * @psalm-param class-string $className + */ + public function excludeSubclassesOfThisClassFromUnintentionallyCoveredCodeCheck(string $className): void + { + $this->parentClassesExcludedFromUnintentionallyCoveredCodeCheck[] = $className; + } - return 4; + 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(); } /** @@ -4887,20 +26945,13 @@ final class CodeCoverage * * @param array|false $linesToBeCovered * - * @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException - * @throws \ReflectionException - * @throws MissingCoversAnnotationException + * @throws ReflectionException * @throws UnintentionallyCoveredCodeException */ - private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, array $linesToBeUsed, bool $ignoreForceCoversAnnotation): void + private function applyCoversAnnotationFilter(RawCodeCoverageData $rawData, $linesToBeCovered, array $linesToBeUsed): void { - if ($linesToBeCovered === false || - ($this->forceCoversAnnotation && empty($linesToBeCovered) && !$ignoreForceCoversAnnotation)) { - if ($this->checkForMissingCoversAnnotation) { - throw new MissingCoversAnnotationException; - } - - $data = []; + if ($linesToBeCovered === false) { + $rawData->clear(); return; } @@ -4912,281 +26963,114 @@ final class CodeCoverage if ($this->checkForUnintentionallyCoveredCode && (!$this->currentId instanceof TestCase || (!$this->currentId->isMedium() && !$this->currentId->isLarge()))) { - $this->performUnintentionallyCoveredCodeCheck($data, $linesToBeCovered, $linesToBeUsed); + $this->performUnintentionallyCoveredCodeCheck($rawData, $linesToBeCovered, $linesToBeUsed); } - if ($this->checkForUnexecutedCoveredCode) { - $this->performUnexecutedCoveredCodeCheck($data, $linesToBeCovered, $linesToBeUsed); + $rawLineData = $rawData->lineCoverage(); + $filesWithNoCoverage = array_diff_key($rawLineData, $linesToBeCovered); + + foreach (array_keys($filesWithNoCoverage) as $fileWithNoCoverage) { + $rawData->removeCoverageDataForFile($fileWithNoCoverage); } - $data = \array_intersect_key($data, $linesToBeCovered); - - foreach (\array_keys($data) as $filename) { - $_linesToBeCovered = \array_flip($linesToBeCovered[$filename]); - $data[$filename] = \array_intersect_key($data[$filename], $_linesToBeCovered); - } - } - - private function applyWhitelistFilter(array &$data): void - { - foreach (\array_keys($data) as $filename) { - if ($this->filter->isFiltered($filename)) { - unset($data[$filename]); + if (is_array($linesToBeCovered)) { + foreach ($linesToBeCovered as $fileToBeCovered => $includedLines) { + $rawData->keepLineCoverageDataOnlyForLines($fileToBeCovered, $includedLines); + $rawData->keepFunctionCoverageDataOnlyForLines($fileToBeCovered, $includedLines); } } } - /** - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - */ - private function applyIgnoredLinesFilter(array &$data): void + private function applyFilter(RawCodeCoverageData $data): void { - foreach (\array_keys($data) as $filename) { + if ($this->filter->isEmpty()) { + return; + } + + foreach (array_keys($data->lineCoverage()) as $filename) { + if ($this->filter->isExcluded($filename)) { + $data->removeCoverageDataForFile($filename); + } + } + } + + private function applyExecutableLinesFilter(RawCodeCoverageData $data): void + { + foreach (array_keys($data->lineCoverage()) as $filename) { if (!$this->filter->isFile($filename)) { continue; } - foreach ($this->getLinesToBeIgnored($filename) as $line) { - unset($data[$filename][$line]); - } + $data->keepLineCoverageDataOnlyForLines( + $filename, + $this->analyser()->executableLinesIn($filename) + ); } } - private function initializeFilesThatAreSeenTheFirstTime(array $data): void + private function applyIgnoredLinesFilter(RawCodeCoverageData $data): void { - foreach ($data as $file => $lines) { - if (!isset($this->data[$file]) && $this->filter->isFile($file)) { - $this->data[$file] = []; - - foreach ($lines as $k => $v) { - $this->data[$file][$k] = $v === -2 ? null : []; - } + foreach (array_keys($data->lineCoverage()) as $filename) { + if (!$this->filter->isFile($filename)) { + continue; } + + $data->removeCoverageDataForLines( + $filename, + $this->analyser()->ignoredLinesFor($filename) + ); } } /** - * @throws CoveredCodeNotExecutedException - * @throws InvalidArgumentException - * @throws MissingCoversAnnotationException - * @throws RuntimeException * @throws UnintentionallyCoveredCodeException - * @throws \ReflectionException */ - private function addUncoveredFilesFromWhitelist(): void + private function addUncoveredFilesFromFilter(): void { - $data = []; - $uncoveredFiles = \array_diff( - $this->filter->getWhitelist(), - \array_keys($this->data) + $uncoveredFiles = array_diff( + $this->filter->files(), + $this->data->coveredFiles() ); foreach ($uncoveredFiles as $uncoveredFile) { - if (!\file_exists($uncoveredFile)) { - continue; - } - - $data[$uncoveredFile] = []; - - $lines = \count(\file($uncoveredFile)); - - for ($i = 1; $i <= $lines; $i++) { - $data[$uncoveredFile][$i] = Driver::LINE_NOT_EXECUTED; + if (is_file($uncoveredFile)) { + $this->append( + RawCodeCoverageData::fromUncoveredFile( + $uncoveredFile, + $this->analyser() + ), + self::UNCOVERED_FILES + ); } } - - $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); - } - - private function getLinesToBeIgnored(string $fileName): array - { - if (isset($this->ignoredLines[$fileName])) { - return $this->ignoredLines[$fileName]; - } - - try { - return $this->getLinesToBeIgnoredInner($fileName); - } catch (\OutOfBoundsException $e) { - // This can happen with PHP_Token_Stream if the file is syntactically invalid, - // and probably affects a file that wasn't executed. - return []; - } - } - - private function getLinesToBeIgnoredInner(string $fileName): array - { - $this->ignoredLines[$fileName] = []; - - $lines = \file($fileName); - - foreach ($lines as $index => $line) { - if (!\trim($line)) { - $this->ignoredLines[$fileName][] = $index + 1; - } - } - - if ($this->cacheTokens) { - $tokens = \PHP_Token_Stream_CachingFactory::get($fileName); - } else { - $tokens = new \PHP_Token_Stream($fileName); - } - - foreach ($tokens->getInterfaces() as $interface) { - $interfaceStartLine = $interface['startLine']; - $interfaceEndLine = $interface['endLine']; - - foreach (\range($interfaceStartLine, $interfaceEndLine) as $line) { - $this->ignoredLines[$fileName][] = $line; - } - } - - foreach (\array_merge($tokens->getClasses(), $tokens->getTraits()) as $classOrTrait) { - $classOrTraitStartLine = $classOrTrait['startLine']; - $classOrTraitEndLine = $classOrTrait['endLine']; - - if (empty($classOrTrait['methods'])) { - foreach (\range($classOrTraitStartLine, $classOrTraitEndLine) as $line) { - $this->ignoredLines[$fileName][] = $line; - } - - continue; - } - - $firstMethod = \array_shift($classOrTrait['methods']); - $firstMethodStartLine = $firstMethod['startLine']; - $lastMethodEndLine = $firstMethod['endLine']; - - do { - $lastMethod = \array_pop($classOrTrait['methods']); - } while ($lastMethod !== null && 0 === \strpos($lastMethod['signature'], 'anonymousFunction')); - - if ($lastMethod !== null) { - $lastMethodEndLine = $lastMethod['endLine']; - } - - foreach (\range($classOrTraitStartLine, $firstMethodStartLine) as $line) { - $this->ignoredLines[$fileName][] = $line; - } - - foreach (\range($lastMethodEndLine + 1, $classOrTraitEndLine) as $line) { - $this->ignoredLines[$fileName][] = $line; - } - } - - if ($this->disableIgnoredLines) { - $this->ignoredLines[$fileName] = \array_unique($this->ignoredLines[$fileName]); - \sort($this->ignoredLines[$fileName]); - - return $this->ignoredLines[$fileName]; - } - - $ignore = false; - $stop = false; - - foreach ($tokens->tokens() as $token) { - switch (\get_class($token)) { - case \PHP_Token_COMMENT::class: - case \PHP_Token_DOC_COMMENT::class: - $_token = \trim((string) $token); - $_line = \trim($lines[$token->getLine() - 1]); - - if ($_token === '// @codeCoverageIgnore' || - $_token === '//@codeCoverageIgnore') { - $ignore = true; - $stop = true; - } elseif ($_token === '// @codeCoverageIgnoreStart' || - $_token === '//@codeCoverageIgnoreStart') { - $ignore = true; - } elseif ($_token === '// @codeCoverageIgnoreEnd' || - $_token === '//@codeCoverageIgnoreEnd') { - $stop = true; - } - - if (!$ignore) { - $start = $token->getLine(); - $end = $start + \substr_count((string) $token, "\n"); - - // Do not ignore the first line when there is a token - // before the comment - if (0 !== \strpos($_token, $_line)) { - $start++; - } - - for ($i = $start; $i < $end; $i++) { - $this->ignoredLines[$fileName][] = $i; - } - - // A DOC_COMMENT token or a COMMENT token starting with "/*" - // does not contain the final \n character in its text - if (isset($lines[$i - 1]) && 0 === \strpos($_token, '/*') && '*/' === \substr(\trim($lines[$i - 1]), -2)) { - $this->ignoredLines[$fileName][] = $i; - } - } - - break; - - case \PHP_Token_INTERFACE::class: - case \PHP_Token_TRAIT::class: - case \PHP_Token_CLASS::class: - case \PHP_Token_FUNCTION::class: - /* @var \PHP_Token_Interface $token */ - - $docblock = (string) $token->getDocblock(); - - $this->ignoredLines[$fileName][] = $token->getLine(); - - if (\strpos($docblock, '@codeCoverageIgnore') || ($this->ignoreDeprecatedCode && \strpos($docblock, '@deprecated'))) { - $endLine = $token->getEndLine(); - - for ($i = $token->getLine(); $i <= $endLine; $i++) { - $this->ignoredLines[$fileName][] = $i; - } - } - - break; - - /* @noinspection PhpMissingBreakStatementInspection */ - case \PHP_Token_NAMESPACE::class: - $this->ignoredLines[$fileName][] = $token->getEndLine(); - - // Intentional fallthrough - case \PHP_Token_DECLARE::class: - case \PHP_Token_OPEN_TAG::class: - case \PHP_Token_CLOSE_TAG::class: - case \PHP_Token_USE::class: - case \PHP_Token_USE_FUNCTION::class: - $this->ignoredLines[$fileName][] = $token->getLine(); - - break; - } - - if ($ignore) { - $this->ignoredLines[$fileName][] = $token->getLine(); - - if ($stop) { - $ignore = false; - $stop = false; - } - } - } - - $this->ignoredLines[$fileName][] = \count($lines) + 1; - - $this->ignoredLines[$fileName] = \array_unique( - $this->ignoredLines[$fileName] - ); - - $this->ignoredLines[$fileName] = \array_unique($this->ignoredLines[$fileName]); - \sort($this->ignoredLines[$fileName]); - - return $this->ignoredLines[$fileName]; } /** - * @throws \ReflectionException * @throws UnintentionallyCoveredCodeException */ - private function performUnintentionallyCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed): void + private function processUncoveredFilesFromFilter(): void + { + $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); + } + + /** + * @throws ReflectionException + * @throws UnintentionallyCoveredCodeException + */ + private function performUnintentionallyCoveredCodeCheck(RawCodeCoverageData $data, array $linesToBeCovered, array $linesToBeUsed): void { $allowedLines = $this->getAllowedLines( $linesToBeCovered, @@ -5195,7 +27079,7 @@ final class CodeCoverage $unintentionallyCoveredUnits = []; - foreach ($data as $file => $_data) { + foreach ($data->lineCoverage() as $file => $_data) { foreach ($_data as $line => $flag) { if ($flag === 1 && !isset($allowedLines[$file][$line])) { $unintentionallyCoveredUnits[] = $this->wizard->lookup($file, $line); @@ -5212,66 +27096,35 @@ final class CodeCoverage } } - /** - * @throws CoveredCodeNotExecutedException - */ - private function performUnexecutedCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed): void - { - $executedCodeUnits = $this->coverageToCodeUnits($data); - $message = ''; - - foreach ($this->linesToCodeUnits($linesToBeCovered) as $codeUnit) { - if (!\in_array($codeUnit, $executedCodeUnits)) { - $message .= \sprintf( - '- %s is expected to be executed (@covers) but was not executed' . "\n", - $codeUnit - ); - } - } - - foreach ($this->linesToCodeUnits($linesToBeUsed) as $codeUnit) { - if (!\in_array($codeUnit, $executedCodeUnits)) { - $message .= \sprintf( - '- %s is expected to be executed (@uses) but was not executed' . "\n", - $codeUnit - ); - } - } - - if (!empty($message)) { - throw new CoveredCodeNotExecutedException($message); - } - } - private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed): array { $allowedLines = []; - foreach (\array_keys($linesToBeCovered) as $file) { + foreach (array_keys($linesToBeCovered) as $file) { if (!isset($allowedLines[$file])) { $allowedLines[$file] = []; } - $allowedLines[$file] = \array_merge( + $allowedLines[$file] = array_merge( $allowedLines[$file], $linesToBeCovered[$file] ); } - foreach (\array_keys($linesToBeUsed) as $file) { + foreach (array_keys($linesToBeUsed) as $file) { if (!isset($allowedLines[$file])) { $allowedLines[$file] = []; } - $allowedLines[$file] = \array_merge( + $allowedLines[$file] = array_merge( $allowedLines[$file], $linesToBeUsed[$file] ); } - foreach (\array_keys($allowedLines) as $file) { - $allowedLines[$file] = \array_flip( - \array_unique($allowedLines[$file]) + foreach (array_keys($allowedLines) as $file) { + $allowedLines[$file] = array_flip( + array_unique($allowedLines[$file]) ); } @@ -5279,131 +27132,66 @@ final class CodeCoverage } /** - * @throws RuntimeException + * @throws ReflectionException */ - private function selectDriver(Filter $filter): Driver - { - $runtime = new Runtime; - - if (!$runtime->canCollectCodeCoverage()) { - throw new RuntimeException('No code coverage driver available'); - } - - if ($runtime->isPHPDBG()) { - return new PHPDBG; - } - - if ($runtime->hasXdebug()) { - return new Xdebug($filter); - } - - if ($runtime->hasPCOV()) { - return new PCOV; - } - - throw new RuntimeException('No code coverage driver available'); - } - private function processUnintentionallyCoveredUnits(array $unintentionallyCoveredUnits): array { - $unintentionallyCoveredUnits = \array_unique($unintentionallyCoveredUnits); - \sort($unintentionallyCoveredUnits); + $unintentionallyCoveredUnits = array_unique($unintentionallyCoveredUnits); + sort($unintentionallyCoveredUnits); - foreach (\array_keys($unintentionallyCoveredUnits) as $k => $v) { - $unit = \explode('::', $unintentionallyCoveredUnits[$k]); + foreach (array_keys($unintentionallyCoveredUnits) as $k => $v) { + $unit = explode('::', $unintentionallyCoveredUnits[$k]); - if (\count($unit) !== 2) { + if (count($unit) !== 2) { continue; } - $class = new \ReflectionClass($unit[0]); + try { + $class = new ReflectionClass($unit[0]); - foreach ($this->unintentionallyCoveredSubclassesWhitelist as $whitelisted) { - if ($class->isSubclassOf($whitelisted)) { - unset($unintentionallyCoveredUnits[$k]); + foreach ($this->parentClassesExcludedFromUnintentionallyCoveredCodeCheck as $parentClass) { + if ($class->isSubclassOf($parentClass)) { + unset($unintentionallyCoveredUnits[$k]); - break; - } - } - } - - return \array_values($unintentionallyCoveredUnits); - } - - /** - * @throws CoveredCodeNotExecutedException - * @throws InvalidArgumentException - * @throws MissingCoversAnnotationException - * @throws RuntimeException - * @throws UnintentionallyCoveredCodeException - * @throws \ReflectionException - */ - private function initializeData(): void - { - $this->isInitialized = true; - - if ($this->processUncoveredFilesFromWhitelist) { - $this->shouldCheckForDeadAndUnused = false; - - $this->driver->start(); - - foreach ($this->filter->getWhitelist() as $file) { - if ($this->filter->isFile($file)) { - include_once $file; - } - } - - $data = []; - - foreach ($this->driver->stop() as $file => $fileCoverage) { - if ($this->filter->isFiltered($file)) { - continue; - } - - foreach (\array_keys($fileCoverage) as $key) { - if ($fileCoverage[$key] === Driver::LINE_EXECUTED) { - $fileCoverage[$key] = Driver::LINE_NOT_EXECUTED; + break; } } - - $data[$file] = $fileCoverage; + } catch (\ReflectionException $e) { + throw new ReflectionException( + $e->getMessage(), + (int) $e->getCode(), + $e + ); } - - $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); } + + return array_values($unintentionallyCoveredUnits); } - private function coverageToCodeUnits(array $data): array + private function analyser(): FileAnalyser { - $codeUnits = []; - - foreach ($data as $filename => $lines) { - foreach ($lines as $line => $flag) { - if ($flag === 1) { - $codeUnits[] = $this->wizard->lookup($filename, $line); - } - } + if ($this->analyser !== null) { + return $this->analyser; } - return \array_unique($codeUnits); - } + $this->analyser = new ParsingFileAnalyser( + $this->useAnnotationsForIgnoringCode, + $this->ignoreDeprecatedCode + ); - private function linesToCodeUnits(array $data): array - { - $codeUnits = []; - - foreach ($data as $filename => $lines) { - foreach ($lines as $line) { - $codeUnits[] = $this->wizard->lookup($filename, $line); - } + if ($this->cachesStaticAnalysis()) { + $this->analyser = new CachingFileAnalyser( + $this->cacheDirectory, + $this->analyser + ); } - return \array_unique($codeUnits); + return $this->analyser; } } * @@ -5412,2921 +27200,747 @@ final class CodeCoverage */ namespace SebastianBergmann\CodeCoverage; -use SebastianBergmann\Version as VersionId; - -final class Version -{ - /** - * @var string - */ - private static $version; - - public static function id(): string - { - if (self::$version === null) { - $version = new VersionId('7.0.8', \dirname(__DIR__)); - self::$version = $version->getVersion(); - } - - return self::$version; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace 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 SebastianBergmann\CodeCoverage\Driver\Driver; /** - * Utility methods. + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -final class Util +final class ProcessedCodeCoverageData { /** - * @return float|int|string - */ - public static function percent(float $a, float $b, bool $asString = false, bool $fixedWidth = false) - { - if ($asString && $b == 0) { - return ''; - } - - $percent = 100; - - if ($b > 0) { - $percent = ($a / $b) * 100; - } - - if ($asString) { - $format = $fixedWidth ? '%6.2F%%' : '%01.2F%%'; - - return \sprintf($format, $percent); - } - - return $percent; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Node; - -use SebastianBergmann\CodeCoverage\Util; - -/** - * Base class for nodes in the code coverage information tree. - */ -abstract class AbstractNode implements \Countable -{ - /** - * @var string - */ - private $name; - - /** - * @var string - */ - private $path; - - /** + * 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 $pathArray; + private $lineCoverage = []; /** - * @var AbstractNode + * 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 $parent; + private $functionCoverage = []; - /** - * @var string - */ - private $id; - - public function __construct(string $name, self $parent = null) + public function initializeUnseenData(RawCodeCoverageData $rawData): void { - if (\substr($name, -1) == \DIRECTORY_SEPARATOR) { - $name = \substr($name, 0, -1); + 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 : []; + } + } } - $this->name = $name; - $this->parent = $parent; - } - - public function getName(): string - { - return $this->name; - } - - public function getId(): string - { - if ($this->id === null) { - $parent = $this->getParent(); - - if ($parent === null) { - $this->id = 'index'; - } else { - $parentId = $parent->getId(); - - if ($parentId === 'index') { - $this->id = \str_replace(':', '_', $this->name); + foreach ($rawData->functionCoverage() as $file => $functions) { + foreach ($functions as $functionName => $functionData) { + if (isset($this->functionCoverage[$file][$functionName])) { + $this->initPreviouslySeenFunction($file, $functionName, $functionData); } else { - $this->id = $parentId . '/' . $this->name; + $this->initPreviouslyUnseenFunction($file, $functionName, $functionData); + } + } + } + } + + 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; } } } - return $this->id; - } - - public function getPath(): string - { - if ($this->path === null) { - if ($this->parent === null || $this->parent->getPath() === null || $this->parent->getPath() === false) { - $this->path = $this->name; - } else { - $this->path = $this->parent->getPath() . \DIRECTORY_SEPARATOR . $this->name; - } - } - - return $this->path; - } - - public function getPathAsArray(): array - { - if ($this->pathArray === null) { - if ($this->parent === null) { - $this->pathArray = []; - } else { - $this->pathArray = $this->parent->getPathAsArray(); - } - - $this->pathArray[] = $this; - } - - return $this->pathArray; - } - - public function getParent(): ?self - { - return $this->parent; - } - - /** - * Returns the percentage of classes that has been tested. - * - * @return int|string - */ - public function getTestedClassesPercent(bool $asString = true) - { - return Util::percent( - $this->getNumTestedClasses(), - $this->getNumClasses(), - $asString - ); - } - - /** - * Returns the percentage of traits that has been tested. - * - * @return int|string - */ - public function getTestedTraitsPercent(bool $asString = true) - { - return Util::percent( - $this->getNumTestedTraits(), - $this->getNumTraits(), - $asString - ); - } - - /** - * Returns the percentage of classes and traits that has been tested. - * - * @return int|string - */ - public function getTestedClassesAndTraitsPercent(bool $asString = true) - { - return Util::percent( - $this->getNumTestedClassesAndTraits(), - $this->getNumClassesAndTraits(), - $asString - ); - } - - /** - * Returns the percentage of functions that has been tested. - * - * @return int|string - */ - public function getTestedFunctionsPercent(bool $asString = true) - { - return Util::percent( - $this->getNumTestedFunctions(), - $this->getNumFunctions(), - $asString - ); - } - - /** - * Returns the percentage of methods that has been tested. - * - * @return int|string - */ - public function getTestedMethodsPercent(bool $asString = true) - { - return Util::percent( - $this->getNumTestedMethods(), - $this->getNumMethods(), - $asString - ); - } - - /** - * Returns the percentage of functions and methods that has been tested. - * - * @return int|string - */ - public function getTestedFunctionsAndMethodsPercent(bool $asString = true) - { - return Util::percent( - $this->getNumTestedFunctionsAndMethods(), - $this->getNumFunctionsAndMethods(), - $asString - ); - } - - /** - * Returns the percentage of executed lines. - * - * @return int|string - */ - public function getLineExecutedPercent(bool $asString = true) - { - return Util::percent( - $this->getNumExecutedLines(), - $this->getNumExecutableLines(), - $asString - ); - } - - /** - * Returns the number of classes and traits. - */ - public function getNumClassesAndTraits(): int - { - return $this->getNumClasses() + $this->getNumTraits(); - } - - /** - * Returns the number of tested classes and traits. - */ - public function getNumTestedClassesAndTraits(): int - { - return $this->getNumTestedClasses() + $this->getNumTestedTraits(); - } - - /** - * Returns the classes and traits of this node. - */ - public function getClassesAndTraits(): array - { - return \array_merge($this->getClasses(), $this->getTraits()); - } - - /** - * Returns the number of functions and methods. - */ - public function getNumFunctionsAndMethods(): int - { - return $this->getNumFunctions() + $this->getNumMethods(); - } - - /** - * Returns the number of tested functions and methods. - */ - public function getNumTestedFunctionsAndMethods(): int - { - return $this->getNumTestedFunctions() + $this->getNumTestedMethods(); - } - - /** - * Returns the functions and methods of this node. - */ - public function getFunctionsAndMethods(): array - { - return \array_merge($this->getFunctions(), $this->getMethods()); - } - - /** - * Returns the classes of this node. - */ - abstract public function getClasses(): array; - - /** - * Returns the traits of this node. - */ - abstract public function getTraits(): array; - - /** - * Returns the functions of this node. - */ - abstract public function getFunctions(): array; - - /** - * Returns the LOC/CLOC/NCLOC of this node. - */ - abstract public function getLinesOfCode(): array; - - /** - * Returns the number of executable lines. - */ - abstract public function getNumExecutableLines(): int; - - /** - * Returns the number of executed lines. - */ - abstract public function getNumExecutedLines(): int; - - /** - * Returns the number of classes. - */ - abstract public function getNumClasses(): int; - - /** - * Returns the number of tested classes. - */ - abstract public function getNumTestedClasses(): int; - - /** - * Returns the number of traits. - */ - abstract public function getNumTraits(): int; - - /** - * Returns the number of tested traits. - */ - abstract public function getNumTestedTraits(): int; - - /** - * Returns the number of methods. - */ - abstract public function getNumMethods(): int; - - /** - * Returns the number of tested methods. - */ - abstract public function getNumTestedMethods(): int; - - /** - * Returns the number of functions. - */ - abstract public function getNumFunctions(): int; - - /** - * Returns the number of tested functions. - */ - abstract public function getNumTestedFunctions(): int; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Node; - -/** - * Recursive iterator for node object graphs. - */ -final class Iterator implements \RecursiveIterator -{ - /** - * @var int - */ - private $position; - - /** - * @var AbstractNode[] - */ - private $nodes; - - public function __construct(Directory $node) - { - $this->nodes = $node->getChildNodes(); - } - - /** - * Rewinds the Iterator to the first element. - */ - public function rewind(): void - { - $this->position = 0; - } - - /** - * Checks if there is a current element after calls to rewind() or next(). - */ - public function valid(): bool - { - return $this->position < \count($this->nodes); - } - - /** - * Returns the key of the current element. - */ - public function key(): int - { - return $this->position; - } - - /** - * Returns the current element. - */ - public function current(): AbstractNode - { - return $this->valid() ? $this->nodes[$this->position] : null; - } - - /** - * Moves forward to next element. - */ - public function next(): void - { - $this->position++; - } - - /** - * Returns the sub iterator for the current element. - * - * @return Iterator - */ - public function getChildren(): self - { - return new self($this->nodes[$this->position]); - } - - /** - * Checks whether the current element has children. - */ - public function hasChildren(): bool - { - 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 SebastianBergmann\CodeCoverage\Node; - -use SebastianBergmann\CodeCoverage\CodeCoverage; - -final class Builder -{ - public function build(CodeCoverage $coverage): Directory - { - $files = $coverage->getData(); - $commonPath = $this->reducePaths($files); - $root = new Directory( - $commonPath, - null - ); - - $this->addItems( - $root, - $this->buildDirectoryStructure($files), - $coverage->getTests(), - $coverage->getCacheTokens() - ); - - return $root; - } - - private function addItems(Directory $root, array $items, array $tests, bool $cacheTokens): void - { - foreach ($items as $key => $value) { - $key = (string) $key; - - if (\substr($key, -2) === '/f') { - $key = \substr($key, 0, -2); - - if (\file_exists($root->getPath() . \DIRECTORY_SEPARATOR . $key)) { - $root->addFile($key, $value, $tests, $cacheTokens); - } - } else { - $child = $root->addDirectory($key); - $this->addItems($child, $value, $tests, $cacheTokens); - } - } - } - - /** - * 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 - * ( - * ... - * ) - * ) - * ) - * - */ - private function buildDirectoryStructure(array $files): array - { - $result = []; - - foreach ($files as $path => $file) { - $path = \explode(\DIRECTORY_SEPARATOR, $path); - $pointer = &$result; - $max = \count($path); - - for ($i = 0; $i < $max; $i++) { - $type = ''; - - if ($i === ($max - 1)) { - $type = '/f'; + 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; + } } - $pointer = &$pointer[$path[$i] . $type]; - } - - $pointer = $file; - } - - return $result; - } - - /** - * 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 - * ( - * ... - * ) - * ) - * - */ - private function reducePaths(array &$files): string - { - if (empty($files)) { - return '.'; - } - - $commonPath = ''; - $paths = \array_keys($files); - - if (\count($files) === 1) { - $commonPath = \dirname($paths[0]) . \DIRECTORY_SEPARATOR; - $files[\basename($paths[0])] = $files[$paths[0]]; - - unset($files[$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 = \array_keys($files); - $max = \count($original); - - for ($i = 0; $i < $max; $i++) { - $files[\implode(\DIRECTORY_SEPARATOR, $paths[$i])] = $files[$original[$i]]; - unset($files[$original[$i]]); - } - - \ksort($files); - - 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 SebastianBergmann\CodeCoverage\Node; - -use SebastianBergmann\CodeCoverage\InvalidArgumentException; - -/** - * Represents a directory in the code coverage information tree. - */ -final class Directory extends AbstractNode implements \IteratorAggregate -{ - /** - * @var AbstractNode[] - */ - private $children = []; - - /** - * @var Directory[] - */ - private $directories = []; - - /** - * @var File[] - */ - private $files = []; - - /** - * @var array - */ - private $classes; - - /** - * @var array - */ - private $traits; - - /** - * @var array - */ - private $functions; - - /** - * @var array - */ - private $linesOfCode; - - /** - * @var int - */ - private $numFiles = -1; - - /** - * @var int - */ - private $numExecutableLines = -1; - - /** - * @var int - */ - private $numExecutedLines = -1; - - /** - * @var int - */ - private $numClasses = -1; - - /** - * @var int - */ - private $numTestedClasses = -1; - - /** - * @var int - */ - private $numTraits = -1; - - /** - * @var int - */ - private $numTestedTraits = -1; - - /** - * @var int - */ - private $numMethods = -1; - - /** - * @var int - */ - private $numTestedMethods = -1; - - /** - * @var int - */ - private $numFunctions = -1; - - /** - * @var int - */ - private $numTestedFunctions = -1; - - /** - * Returns the number of files in/under this node. - */ - public function count(): int - { - if ($this->numFiles === -1) { - $this->numFiles = 0; - - foreach ($this->children as $child) { - $this->numFiles += \count($child); - } - } - - return $this->numFiles; - } - - /** - * Returns an iterator for this node. - */ - public function getIterator(): \RecursiveIteratorIterator - { - return new \RecursiveIteratorIterator( - new Iterator($this), - \RecursiveIteratorIterator::SELF_FIRST - ); - } - - /** - * Adds a new directory. - */ - public function addDirectory(string $name): self - { - $directory = new self($name, $this); - - $this->children[] = $directory; - $this->directories[] = &$this->children[\count($this->children) - 1]; - - return $directory; - } - - /** - * Adds a new file. - * - * @throws InvalidArgumentException - */ - public function addFile(string $name, array $coverageData, array $testData, bool $cacheTokens): File - { - $file = new File($name, $this, $coverageData, $testData, $cacheTokens); - - $this->children[] = $file; - $this->files[] = &$this->children[\count($this->children) - 1]; - - $this->numExecutableLines = -1; - $this->numExecutedLines = -1; - - return $file; - } - - /** - * Returns the directories in this directory. - */ - public function getDirectories(): array - { - return $this->directories; - } - - /** - * Returns the files in this directory. - */ - public function getFiles(): array - { - return $this->files; - } - - /** - * Returns the child nodes of this node. - */ - public function getChildNodes(): array - { - return $this->children; - } - - /** - * Returns the classes of this node. - */ - public function getClasses(): array - { - if ($this->classes === null) { - $this->classes = []; - - foreach ($this->children as $child) { - $this->classes = \array_merge( - $this->classes, - $child->getClasses() - ); - } - } - - return $this->classes; - } - - /** - * Returns the traits of this node. - */ - public function getTraits(): array - { - if ($this->traits === null) { - $this->traits = []; - - foreach ($this->children as $child) { - $this->traits = \array_merge( - $this->traits, - $child->getTraits() - ); - } - } - - return $this->traits; - } - - /** - * Returns the functions of this node. - */ - public function getFunctions(): array - { - if ($this->functions === null) { - $this->functions = []; - - foreach ($this->children as $child) { - $this->functions = \array_merge( - $this->functions, - $child->getFunctions() - ); - } - } - - return $this->functions; - } - - /** - * Returns the LOC/CLOC/NCLOC of this node. - */ - public function getLinesOfCode(): array - { - if ($this->linesOfCode === null) { - $this->linesOfCode = ['loc' => 0, 'cloc' => 0, 'ncloc' => 0]; - - foreach ($this->children as $child) { - $linesOfCode = $child->getLinesOfCode(); - - $this->linesOfCode['loc'] += $linesOfCode['loc']; - $this->linesOfCode['cloc'] += $linesOfCode['cloc']; - $this->linesOfCode['ncloc'] += $linesOfCode['ncloc']; - } - } - - return $this->linesOfCode; - } - - /** - * Returns the number of executable lines. - */ - public function getNumExecutableLines(): int - { - if ($this->numExecutableLines === -1) { - $this->numExecutableLines = 0; - - foreach ($this->children as $child) { - $this->numExecutableLines += $child->getNumExecutableLines(); - } - } - - return $this->numExecutableLines; - } - - /** - * Returns the number of executed lines. - */ - public function getNumExecutedLines(): int - { - if ($this->numExecutedLines === -1) { - $this->numExecutedLines = 0; - - foreach ($this->children as $child) { - $this->numExecutedLines += $child->getNumExecutedLines(); - } - } - - return $this->numExecutedLines; - } - - /** - * Returns the number of classes. - */ - public function getNumClasses(): int - { - if ($this->numClasses === -1) { - $this->numClasses = 0; - - foreach ($this->children as $child) { - $this->numClasses += $child->getNumClasses(); - } - } - - return $this->numClasses; - } - - /** - * Returns the number of tested classes. - */ - public function getNumTestedClasses(): int - { - if ($this->numTestedClasses === -1) { - $this->numTestedClasses = 0; - - foreach ($this->children as $child) { - $this->numTestedClasses += $child->getNumTestedClasses(); - } - } - - return $this->numTestedClasses; - } - - /** - * Returns the number of traits. - */ - public function getNumTraits(): int - { - if ($this->numTraits === -1) { - $this->numTraits = 0; - - foreach ($this->children as $child) { - $this->numTraits += $child->getNumTraits(); - } - } - - return $this->numTraits; - } - - /** - * Returns the number of tested traits. - */ - public function getNumTestedTraits(): int - { - if ($this->numTestedTraits === -1) { - $this->numTestedTraits = 0; - - foreach ($this->children as $child) { - $this->numTestedTraits += $child->getNumTestedTraits(); - } - } - - return $this->numTestedTraits; - } - - /** - * Returns the number of methods. - */ - public function getNumMethods(): int - { - if ($this->numMethods === -1) { - $this->numMethods = 0; - - foreach ($this->children as $child) { - $this->numMethods += $child->getNumMethods(); - } - } - - return $this->numMethods; - } - - /** - * Returns the number of tested methods. - */ - public function getNumTestedMethods(): int - { - if ($this->numTestedMethods === -1) { - $this->numTestedMethods = 0; - - foreach ($this->children as $child) { - $this->numTestedMethods += $child->getNumTestedMethods(); - } - } - - return $this->numTestedMethods; - } - - /** - * Returns the number of functions. - */ - public function getNumFunctions(): int - { - if ($this->numFunctions === -1) { - $this->numFunctions = 0; - - foreach ($this->children as $child) { - $this->numFunctions += $child->getNumFunctions(); - } - } - - return $this->numFunctions; - } - - /** - * Returns the number of tested functions. - */ - public function getNumTestedFunctions(): int - { - if ($this->numTestedFunctions === -1) { - $this->numTestedFunctions = 0; - - foreach ($this->children as $child) { - $this->numTestedFunctions += $child->getNumTestedFunctions(); - } - } - - return $this->numTestedFunctions; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Node; - -/** - * Represents a file in the code coverage information tree. - */ -final class File extends AbstractNode -{ - /** - * @var array - */ - private $coverageData; - - /** - * @var array - */ - private $testData; - - /** - * @var int - */ - private $numExecutableLines = 0; - - /** - * @var int - */ - private $numExecutedLines = 0; - - /** - * @var array - */ - private $classes = []; - - /** - * @var array - */ - private $traits = []; - - /** - * @var array - */ - private $functions = []; - - /** - * @var array - */ - 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 bool - */ - private $cacheTokens; - - /** - * @var array - */ - private $codeUnitsByLine = []; - - public function __construct(string $name, AbstractNode $parent, array $coverageData, array $testData, bool $cacheTokens) - { - parent::__construct($name, $parent); - - $this->coverageData = $coverageData; - $this->testData = $testData; - $this->cacheTokens = $cacheTokens; - - $this->calculateStatistics(); - } - - /** - * Returns the number of files in/under this node. - */ - public function count(): int - { - return 1; - } - - /** - * Returns the code coverage data of this node. - */ - public function getCoverageData(): array - { - return $this->coverageData; - } - - /** - * Returns the test data of this node. - */ - public function getTestData(): array - { - return $this->testData; - } - - /** - * Returns the classes of this node. - */ - public function getClasses(): array - { - return $this->classes; - } - - /** - * Returns the traits of this node. - */ - public function getTraits(): array - { - return $this->traits; - } - - /** - * Returns the functions of this node. - */ - public function getFunctions(): array - { - return $this->functions; - } - - /** - * Returns the LOC/CLOC/NCLOC of this node. - */ - public function getLinesOfCode(): array - { - return $this->linesOfCode; - } - - /** - * Returns the number of executable lines. - */ - public function getNumExecutableLines(): int - { - return $this->numExecutableLines; - } - - /** - * Returns the number of executed lines. - */ - public function getNumExecutedLines(): int - { - return $this->numExecutedLines; - } - - /** - * Returns the number of classes. - */ - public function getNumClasses(): int - { - 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; + foreach ($functionData['paths'] as $pathId => $pathData) { + if ($pathData['hit'] === Driver::BRANCH_HIT) { + $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'][] = $testCaseId; } } } } - - return $this->numClasses; } - /** - * Returns the number of tested classes. - */ - public function getNumTestedClasses(): int + public function setLineCoverage(array $lineCoverage): void { - return $this->numTestedClasses; + $this->lineCoverage = $lineCoverage; } - /** - * Returns the number of traits. - */ - public function getNumTraits(): int + public function lineCoverage(): array { - if ($this->numTraits === null) { - $this->numTraits = 0; + ksort($this->lineCoverage); - foreach ($this->traits as $trait) { - foreach ($trait['methods'] as $method) { - if ($method['executableLines'] > 0) { - $this->numTraits++; - - continue 2; - } - } - } - } - - return $this->numTraits; + return $this->lineCoverage; } - /** - * Returns the number of tested traits. - */ - public function getNumTestedTraits(): int + public function setFunctionCoverage(array $functionCoverage): void { - return $this->numTestedTraits; + $this->functionCoverage = $functionCoverage; } - /** - * Returns the number of methods. - */ - public function getNumMethods(): int + public function functionCoverage(): array { - if ($this->numMethods === null) { - $this->numMethods = 0; + ksort($this->functionCoverage); - 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; + return $this->functionCoverage; } - /** - * Returns the number of tested methods. - */ - public function getNumTestedMethods(): int + public function coveredFiles(): array { - if ($this->numTestedMethods === null) { - $this->numTestedMethods = 0; + ksort($this->lineCoverage); - 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->numTestedMethods; + return array_keys($this->lineCoverage); } - /** - * Returns the number of functions. - */ - public function getNumFunctions(): int + public function renameFile(string $oldFile, string $newFile): void { - return \count($this->functions); + $this->lineCoverage[$newFile] = $this->lineCoverage[$oldFile]; + + if (isset($this->functionCoverage[$oldFile])) { + $this->functionCoverage[$newFile] = $this->functionCoverage[$oldFile]; + } + + unset($this->lineCoverage[$oldFile], $this->functionCoverage[$oldFile]); } - /** - * Returns the number of tested functions. - */ - public function getNumTestedFunctions(): int + public function merge(self $newData): void { - if ($this->numTestedFunctions === null) { - $this->numTestedFunctions = 0; + foreach ($newData->lineCoverage as $file => $lines) { + if (!isset($this->lineCoverage[$file])) { + $this->lineCoverage[$file] = $lines; - foreach ($this->functions as $function) { - if ($function['executableLines'] > 0 && - $function['coverage'] === 100) { - $this->numTestedFunctions++; - } - } - } - - return $this->numTestedFunctions; - } - - private function calculateStatistics(): void - { - if ($this->cacheTokens) { - $tokens = \PHP_Token_Stream_CachingFactory::get($this->getPath()); - } else { - $tokens = new \PHP_Token_Stream($this->getPath()); - } - - $this->linesOfCode = $tokens->getLinesOfCode(); - - foreach (\range(1, $this->linesOfCode['loc']) as $lineNumber) { - $this->codeUnitsByLine[$lineNumber] = []; - } - - try { - $this->processClasses($tokens); - $this->processTraits($tokens); - $this->processFunctions($tokens); - } catch (\OutOfBoundsException $e) { - // This can happen with PHP_Token_Stream if the file is syntactically invalid, - // and probably affects a file that wasn't executed. - } - unset($tokens); - - foreach (\range(1, $this->linesOfCode['loc']) as $lineNumber) { - if (isset($this->coverageData[$lineNumber])) { - foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) { - $codeUnit['executableLines']++; - } - - unset($codeUnit); - - $this->numExecutableLines++; - - if (\count($this->coverageData[$lineNumber]) > 0) { - foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) { - $codeUnit['executedLines']++; - } - - unset($codeUnit); - - $this->numExecutedLines++; - } - } - } - - foreach ($this->traits as &$trait) { - foreach ($trait['methods'] as &$method) { - if ($method['executableLines'] > 0) { - $method['coverage'] = ($method['executedLines'] / - $method['executableLines']) * 100; - } else { - $method['coverage'] = 100; - } - - $method['crap'] = $this->crap( - $method['ccn'], - $method['coverage'] - ); - - $trait['ccn'] += $method['ccn']; - } - - unset($method); - - if ($trait['executableLines'] > 0) { - $trait['coverage'] = ($trait['executedLines'] / - $trait['executableLines']) * 100; - - if ($trait['coverage'] === 100) { - $this->numTestedClasses++; - } - } else { - $trait['coverage'] = 100; - } - - $trait['crap'] = $this->crap( - $trait['ccn'], - $trait['coverage'] - ); - } - - unset($trait); - - foreach ($this->classes as &$class) { - foreach ($class['methods'] as &$method) { - if ($method['executableLines'] > 0) { - $method['coverage'] = ($method['executedLines'] / - $method['executableLines']) * 100; - } else { - $method['coverage'] = 100; - } - - $method['crap'] = $this->crap( - $method['ccn'], - $method['coverage'] - ); - - $class['ccn'] += $method['ccn']; - } - - unset($method); - - if ($class['executableLines'] > 0) { - $class['coverage'] = ($class['executedLines'] / - $class['executableLines']) * 100; - - if ($class['coverage'] === 100) { - $this->numTestedClasses++; - } - } else { - $class['coverage'] = 100; - } - - $class['crap'] = $this->crap( - $class['ccn'], - $class['coverage'] - ); - } - - unset($class); - - foreach ($this->functions as &$function) { - if ($function['executableLines'] > 0) { - $function['coverage'] = ($function['executedLines'] / - $function['executableLines']) * 100; - } else { - $function['coverage'] = 100; - } - - if ($function['coverage'] === 100) { - $this->numTestedFunctions++; - } - - $function['crap'] = $this->crap( - $function['ccn'], - $function['coverage'] - ); - } - } - - private function processClasses(\PHP_Token_Stream $tokens): void - { - $classes = $tokens->getClasses(); - $link = $this->getId() . '.html#'; - - foreach ($classes as $className => $class) { - if (\strpos($className, 'anonymous') === 0) { continue; } - if (!empty($class['package']['namespace'])) { - $className = $class['package']['namespace'] . '\\' . $className; - } + // 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]) + ) + ); - $this->classes[$className] = [ - 'className' => $className, - 'methods' => [], - 'startLine' => $class['startLine'], - 'executableLines' => 0, - 'executedLines' => 0, - 'ccn' => 0, - 'coverage' => 0, - 'crap' => 0, - 'package' => $class['package'], - 'link' => $link . $class['startLine'], - ]; + foreach ($compareLineNumbers as $line) { + $thatPriority = $this->priorityForLine($newData->lineCoverage[$file], $line); + $thisPriority = $this->priorityForLine($this->lineCoverage[$file], $line); - foreach ($class['methods'] as $methodName => $method) { - if (\strpos($methodName, 'anonymous') === 0) { - continue; - } - - $this->classes[$className]['methods'][$methodName] = $this->newMethod($methodName, $method, $link); - - foreach (\range($method['startLine'], $method['endLine']) as $lineNumber) { - $this->codeUnitsByLine[$lineNumber] = [ - &$this->classes[$className], - &$this->classes[$className]['methods'][$methodName], - ]; + 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]) + ); } } } - } - private function processTraits(\PHP_Token_Stream $tokens): void - { - $traits = $tokens->getTraits(); - $link = $this->getId() . '.html#'; + foreach ($newData->functionCoverage as $file => $functions) { + if (!isset($this->functionCoverage[$file])) { + $this->functionCoverage[$file] = $functions; - foreach ($traits as $traitName => $trait) { - $this->traits[$traitName] = [ - 'traitName' => $traitName, - 'methods' => [], - 'startLine' => $trait['startLine'], - 'executableLines' => 0, - 'executedLines' => 0, - 'ccn' => 0, - 'coverage' => 0, - 'crap' => 0, - 'package' => $trait['package'], - 'link' => $link . $trait['startLine'], - ]; - - foreach ($trait['methods'] as $methodName => $method) { - if (\strpos($methodName, 'anonymous') === 0) { - continue; - } - - $this->traits[$traitName]['methods'][$methodName] = $this->newMethod($methodName, $method, $link); - - foreach (\range($method['startLine'], $method['endLine']) as $lineNumber) { - $this->codeUnitsByLine[$lineNumber] = [ - &$this->traits[$traitName], - &$this->traits[$traitName]['methods'][$methodName], - ]; - } - } - } - } - - private function processFunctions(\PHP_Token_Stream $tokens): void - { - $functions = $tokens->getFunctions(); - $link = $this->getId() . '.html#'; - - foreach ($functions as $functionName => $function) { - if (\strpos($functionName, 'anonymous') === 0) { continue; } - $this->functions[$functionName] = [ - 'functionName' => $functionName, - 'signature' => $function['signature'], - 'startLine' => $function['startLine'], - 'executableLines' => 0, - 'executedLines' => 0, - 'ccn' => $function['ccn'], - 'coverage' => 0, - 'crap' => 0, - 'link' => $link . $function['startLine'], - ]; + foreach ($functions as $functionName => $functionData) { + if (isset($this->functionCoverage[$file][$functionName])) { + $this->initPreviouslySeenFunction($file, $functionName, $functionData); + } else { + $this->initPreviouslyUnseenFunction($file, $functionName, $functionData); + } - foreach (\range($function['startLine'], $function['endLine']) as $lineNumber) { - $this->codeUnitsByLine[$lineNumber] = [&$this->functions[$functionName]]; + 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'])); + } } } } - private function crap(int $ccn, float $coverage): string - { - if ($coverage === 0) { - return (string) ($ccn ** 2 + $ccn); - } - - if ($coverage >= 95) { - return (string) $ccn; - } - - return \sprintf( - '%01.2F', - $ccn ** 2 * (1 - $coverage / 100) ** 3 + $ccn - ); - } - - private function newMethod(string $methodName, array $method, string $link): array - { - return [ - 'methodName' => $methodName, - 'visibility' => $method['visibility'], - 'signature' => $method['signature'], - 'startLine' => $method['startLine'], - 'endLine' => $method['endLine'], - 'executableLines' => 0, - 'executedLines' => 0, - 'ccn' => $method['ccn'], - 'coverage' => 0, - 'crap' => 0, - 'link' => $link . $method['startLine'], - ]; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -final class Project extends Node -{ - public function __construct(string $directory) - { - $this->init(); - $this->setProjectSourceDirectory($directory); - } - - public function getProjectSourceDirectory(): string - { - return $this->getContextNode()->getAttribute('source'); - } - - public function getBuildInformation(): BuildInformation - { - $buildNode = $this->getDom()->getElementsByTagNameNS( - 'https://schema.phpunit.de/coverage/1.0', - 'build' - )->item(0); - - if (!$buildNode) { - $buildNode = $this->getDom()->documentElement->appendChild( - $this->getDom()->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'build' - ) - ); - } - - return new BuildInformation($buildNode); - } - - public function getTests(): Tests - { - $testsNode = $this->getContextNode()->getElementsByTagNameNS( - 'https://schema.phpunit.de/coverage/1.0', - 'tests' - )->item(0); - - if (!$testsNode) { - $testsNode = $this->getContextNode()->appendChild( - $this->getDom()->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'tests' - ) - ); - } - - return new Tests($testsNode); - } - - public function asDom(): \DOMDocument - { - return $this->getDom(); - } - - private function init(): void - { - $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->getContextNode()->setAttribute('source', $name); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -use TheSeer\Tokenizer\NamespaceUri; -use TheSeer\Tokenizer\Tokenizer; -use TheSeer\Tokenizer\XMLSerializer; - -final class Source -{ - /** @var \DOMElement */ - private $context; - - public function __construct(\DOMElement $context) - { - $this->context = $context; - } - - public function setSourceCode(string $source): void - { - $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 SebastianBergmann\CodeCoverage\Report\Xml; - -final class Report extends File -{ - public function __construct(string $name) - { - $dom = new \DOMDocument(); - $dom->loadXML(''); - - $contextNode = $dom->getElementsByTagNameNS( - 'https://schema.phpunit.de/coverage/1.0', - 'file' - )->item(0); - - parent::__construct($contextNode); - - $this->setName($name); - } - - public function asDom(): \DOMDocument - { - return $this->getDomDocument(); - } - - public function getFunctionObject($name): Method - { - $node = $this->getContextNode()->appendChild( - $this->getDomDocument()->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'function' - ) - ); - - return new Method($node, $name); - } - - public function getClassObject($name): Unit - { - return $this->getUnitObject('class', $name); - } - - public function getTraitObject($name): Unit - { - return $this->getUnitObject('trait', $name); - } - - public function getSource(): Source - { - $source = $this->getContextNode()->getElementsByTagNameNS( - 'https://schema.phpunit.de/coverage/1.0', - 'source' - )->item(0); - - if (!$source) { - $source = $this->getContextNode()->appendChild( - $this->getDomDocument()->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'source' - ) - ); - } - - return new Source($source); - } - - private function setName($name): void - { - $this->getContextNode()->setAttribute('name', \basename($name)); - $this->getContextNode()->setAttribute('path', \dirname($name)); - } - - private function getUnitObject($tagName, $name): Unit - { - $node = $this->getContextNode()->appendChild( - $this->getDomDocument()->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 SebastianBergmann\CodeCoverage\Report\Xml; - -final class Unit -{ /** - * @var \DOMElement + * 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 $contextNode; - - public function __construct(\DOMElement $context, string $name) + private function priorityForLine(array $data, int $line): int { - $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 setPackage(string $full, string $package, string $sub, string $category): void - { - $node = $this->contextNode->getElementsByTagNameNS( - 'https://schema.phpunit.de/coverage/1.0', - 'package' - )->item(0); - - if (!$node) { - $node = $this->contextNode->appendChild( - $this->contextNode->ownerDocument->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'package' - ) - ); + if (!array_key_exists($line, $data)) { + return 1; } - $node->setAttribute('full', $full); - $node->setAttribute('name', $package); - $node->setAttribute('sub', $sub); - $node->setAttribute('category', $category); - } - - 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' - ) - ); + if (is_array($data[$line]) && count($data[$line]) === 0) { + return 2; } - $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 SebastianBergmann\CodeCoverage\Report\Xml; - -abstract class Node -{ - /** - * @var \DOMDocument - */ - private $dom; - - /** - * @var \DOMElement - */ - private $contextNode; - - public function __construct(\DOMElement $context) - { - $this->setContextNode($context); - } - - public function getDom(): \DOMDocument - { - return $this->dom; - } - - public function getTotals(): Totals - { - $totalsContainer = $this->getContextNode()->firstChild; - - if (!$totalsContainer) { - $totalsContainer = $this->getContextNode()->appendChild( - $this->dom->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'totals' - ) - ); + if ($data[$line] === null) { + return 3; } - return new Totals($totalsContainer); - } - - public function addDirectory(string $name): Directory - { - $dirNode = $this->getDom()->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'directory' - ); - - $dirNode->setAttribute('name', $name); - $this->getContextNode()->appendChild($dirNode); - - return new Directory($dirNode); - } - - public function addFile(string $name, string $href): File - { - $fileNode = $this->getDom()->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'file' - ); - - $fileNode->setAttribute('name', $name); - $fileNode->setAttribute('href', $href); - $this->getContextNode()->appendChild($fileNode); - - return new File($fileNode); - } - - protected function setContextNode(\DOMElement $context): void - { - $this->dom = $context->ownerDocument; - $this->contextNode = $context; - } - - protected function getContextNode(): \DOMElement - { - return $this->contextNode; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -final class Directory extends Node -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -use SebastianBergmann\CodeCoverage\RuntimeException; - -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); + return 4; } /** - * @throws RuntimeException + * For a function we have never seen before, copy all data over and simply init the 'hit' array. */ - public function addTest(string $test): void + private function initPreviouslyUnseenFunction(string $file, string $functionName, array $functionData): void { - if ($this->finalized) { - throw new RuntimeException('Coverage Report already finalized'); + $this->functionCoverage[$file][$functionName] = $functionData; + + foreach (array_keys($functionData['branches']) as $branchId) { + $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = []; } - $this->writer->startElement('covered'); - $this->writer->writeAttribute('by', $test); - $this->writer->endElement(); + foreach (array_keys($functionData['paths']) as $pathId) { + $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = []; + } } - 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 SebastianBergmann\CodeCoverage\Report\Xml; - -final class Method -{ /** - * @var \DOMElement + * 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 $contextNode; - - public function __construct(\DOMElement $context, string $name) + private function initPreviouslySeenFunction(string $file, string $functionName, array $functionData): void { - $this->contextNode = $context; + 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'] = []; + } + } - $this->setName($name); - } - - public function setSignature(string $signature): void - { - $this->contextNode->setAttribute('signature', $signature); - } - - public function setLines(string $start, ?string $end = null): void - { - $this->contextNode->setAttribute('start', $start); - - if ($end !== null) { - $this->contextNode->setAttribute('end', $end); + 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'] = []; + } } } - - public function setTotals(string $executable, string $executed, string $coverage): void - { - $this->contextNode->setAttribute('executable', $executable); - $this->contextNode->setAttribute('executed', $executed); - $this->contextNode->setAttribute('coverage', $coverage); - } - - public function setCrap(string $crap): void - { - $this->contextNode->setAttribute('crap', $crap); - } - - 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 SebastianBergmann\CodeCoverage\Report\Xml; - -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', // PHPUnit_Runner_BaseTestRunner::STATUS_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 SebastianBergmann\CodeCoverage\Report\Xml; +namespace 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 SebastianBergmann\CodeCoverage\CodeCoverage; -use SebastianBergmann\CodeCoverage\Node\AbstractNode; -use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; -use SebastianBergmann\CodeCoverage\Node\File as FileNode; -use SebastianBergmann\CodeCoverage\RuntimeException; -use SebastianBergmann\CodeCoverage\Version; -use SebastianBergmann\Environment\Runtime; +use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException; +use SebastianBergmann\CodeCoverage\Node\File; +use SebastianBergmann\CodeCoverage\Util\Filesystem; -final class Facade +final class Crap4j { /** - * @var string + * @var int */ - private $target; + private $threshold; - /** - * @var Project - */ - private $project; - - /** - * @var string - */ - private $phpUnitVersion; - - public function __construct(string $version) + public function __construct(int $threshold = 30) { - $this->phpUnitVersion = $version; + $this->threshold = $threshold; } /** - * @throws RuntimeException + * @throws WriteOperationFailedException */ - public function process(CodeCoverage $coverage, string $target): void + public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string { - if (\substr($target, -1, 1) !== \DIRECTORY_SEPARATOR) { - $target .= \DIRECTORY_SEPARATOR; - } + $document = new DOMDocument('1.0', 'UTF-8'); + $document->formatOutput = true; - $this->target = $target; - $this->initTargetDirectory($target); + $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); - $this->project = new Project( - $coverage->getReport()->getName() - ); + $fullMethodCount = 0; + $fullCrapMethodCount = 0; + $fullCrapLoad = 0; + $fullCrap = 0; - $this->setBuildInformation(); - $this->processTests($coverage->getTests()); - $this->processDirectory($report, $this->project); + foreach ($report as $item) { + $namespace = 'global'; - $this->saveDocument($this->project->asDom(), 'index'); - } - - private function setBuildInformation(): void - { - $buildNode = $this->project->getBuildInformation(); - $buildNode->setRuntimeInformation(new Runtime()); - $buildNode->setBuildTime(\DateTime::createFromFormat('U', (string) $_SERVER['REQUEST_TIME'])); - $buildNode->setGeneratorVersions($this->phpUnitVersion, Version::id()); - } - - /** - * @throws RuntimeException - */ - private function initTargetDirectory(string $directory): void - { - if (\file_exists($directory)) { - if (!\is_dir($directory)) { - throw new RuntimeException( - "'$directory' exists but is not a directory." - ); - } - - if (!\is_writable($directory)) { - throw new RuntimeException( - "'$directory' exists but is not writable." - ); - } - } elseif (!$this->createDirectory($directory)) { - throw new RuntimeException( - "'$directory' could not be created." - ); - } - } - - private function processDirectory(DirectoryNode $directory, Node $context): void - { - $directoryName = $directory->getName(); - - if ($this->project->getProjectSourceDirectory() === $directoryName) { - $directoryName = '/'; - } - - $directoryObject = $context->addDirectory($directoryName); - - $this->setTotals($directory, $directoryObject->getTotals()); - - foreach ($directory->getDirectories() as $node) { - $this->processDirectory($node, $directoryObject); - } - - foreach ($directory->getFiles() as $node) { - $this->processFile($node, $directoryObject); - } - } - - /** - * @throws RuntimeException - */ - private function processFile(FileNode $file, Directory $context): void - { - $fileObject = $context->addFile( - $file->getName(), - $file->getId() . '.xml' - ); - - $this->setTotals($file, $fileObject->getTotals()); - - $path = \substr( - $file->getPath(), - \strlen($this->project->getProjectSourceDirectory()) - ); - - $fileReport = new Report($path); - - $this->setTotals($file, $fileReport->getTotals()); - - foreach ($file->getClassesAndTraits() as $unit) { - $this->processUnit($unit, $fileReport); - } - - foreach ($file->getFunctions() as $function) { - $this->processFunction($function, $fileReport); - } - - foreach ($file->getCoverageData() as $line => $tests) { - if (!\is_array($tests) || \count($tests) === 0) { + if (!$item instanceof File) { continue; } - $coverage = $fileReport->getLineCoverage((string) $line); + $file = $document->createElement('file'); + $file->setAttribute('name', $item->pathAsString()); - foreach ($tests as $test) { - $coverage->addTest($test); + $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); + } } - - $coverage->finalize(); } - $fileReport->getSource()->setSourceCode( - \file_get_contents($file->getPath()) - ); + $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)); - $this->saveDocument($fileReport->asDom(), $file->getId()); + $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) { + Filesystem::createDirectory(dirname($target)); + + if (@file_put_contents($target, $buffer) === false) { + throw new WriteOperationFailedException($target); + } + } + + return $buffer; } - private function processUnit(array $unit, Report $report): void + private function crapLoad(float $crapValue, int $cyclomaticComplexity, float $coveragePercent): float { - if (isset($unit['className'])) { - $unitObject = $report->getClassObject($unit['className']); + $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 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 SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\Node\File; +use SebastianBergmann\CodeCoverage\Util\Percentage; + +final class Text +{ + /** + * @var string + */ + private const COLOR_GREEN = "\x1b[30;42m"; + + /** + * @var string + */ + private const COLOR_YELLOW = "\x1b[30;43m"; + + /** + * @var string + */ + private const COLOR_RED = "\x1b[37;41m"; + + /** + * @var string + */ + private const COLOR_HEADER = "\x1b[1;37;40m"; + + /** + * @var string + */ + private const COLOR_RESET = "\x1b[0m"; + + /** + * @var string + */ + private const COLOR_EOL = "\x1b[2K"; + + /** + * @var int + */ + private $lowUpperBound; + + /** + * @var int + */ + private $highLowerBound; + + /** + * @var bool + */ + private $showUncoveredFiles; + + /** + * @var bool + */ + private $showOnlySummary; + + public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, bool $showUncoveredFiles = false, bool $showOnlySummary = false) + { + $this->lowUpperBound = $lowUpperBound; + $this->highLowerBound = $highLowerBound; + $this->showUncoveredFiles = $showUncoveredFiles; + $this->showOnlySummary = $showOnlySummary; + } + + public function process(CodeCoverage $coverage, bool $showColors = false): 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)', + 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 { - $unitObject = $report->getTraitObject($unit['traitName']); + $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:'); } - $unitObject->setLines( - $unit['startLine'], - $unit['executableLines'], - $unit['executedLines'] - ); + $output .= $this->format($colors['classes'], $padding, $classes); + $output .= $this->format($colors['methods'], $padding, $methods); - $unitObject->setCrap((float) $unit['crap']); - - $unitObject->setPackage( - $unit['package']['fullPackage'], - $unit['package']['package'], - $unit['package']['subpackage'], - $unit['package']['category'] - ); - - $unitObject->setNamespace($unit['package']['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'] - ); + if ($hasBranchCoverage) { + $output .= $this->format($colors['paths'], $padding, $paths); + $output .= $this->format($colors['branches'], $padding, $branches); } - } + $output .= $this->format($colors['lines'], $padding, $lines); - private function processFunction(array $function, Report $report): void - { - $functionObject = $report->getFunctionObject($function['functionName']); + if ($this->showOnlySummary) { + return $output . PHP_EOL; + } - $functionObject->setSignature($function['signature']); - $functionObject->setLines((string) $function['startLine']); - $functionObject->setCrap($function['crap']); - $functionObject->setTotals((string) $function['executableLines'], (string) $function['executedLines'], (string) $function['coverage']); - } + $classCoverage = []; - private function processTests(array $tests): void - { - $testsObject = $this->project->getTests(); - - foreach ($tests as $test => $result) { - if ($test === 'UNCOVERED_FILES_FROM_WHITELIST') { + foreach ($report as $item) { + if (!$item instanceof File) { continue; } - $testsObject->addTest($test, $result); + $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; + } + } + + return $output . PHP_EOL; } - private function setTotals(AbstractNode $node, Totals $totals): void + private function coverageColor(int $numberOfCoveredElements, int $totalNumberOfElements): string { - $loc = $node->getLinesOfCode(); - - $totals->setNumLines( - $loc['loc'], - $loc['cloc'], - $loc['ncloc'], - $node->getNumExecutableLines(), - $node->getNumExecutedLines() + $coverage = Percentage::fromFractionAndTotal( + $numberOfCoveredElements, + $totalNumberOfElements ); - $totals->setNumClasses( - $node->getNumClasses(), - $node->getNumTestedClasses() - ); + if ($coverage->asFloat() >= $this->highLowerBound) { + return self::COLOR_GREEN; + } - $totals->setNumTraits( - $node->getNumTraits(), - $node->getNumTestedTraits() - ); + if ($coverage->asFloat() > $this->lowUpperBound) { + return self::COLOR_YELLOW; + } - $totals->setNumMethods( - $node->getNumMethods(), - $node->getNumTestedMethods() - ); - - $totals->setNumFunctions( - $node->getNumFunctions(), - $node->getNumTestedFunctions() - ); + return self::COLOR_RED; } - private function getTargetDirectory(): string + private function printCoverageCounts(int $numberOfCoveredElements, int $totalNumberOfElements, int $precision): string { - return $this->target; + $format = '%' . $precision . 's'; + + return Percentage::fromFractionAndTotal( + $numberOfCoveredElements, + $totalNumberOfElements + )->asFixedWidthString() . + ' (' . sprintf($format, $numberOfCoveredElements) . '/' . + sprintf($format, $totalNumberOfElements) . ')'; } /** - * @throws RuntimeException + * @param false|string $string */ - private function saveDocument(\DOMDocument $document, string $name): void + private function format(string $color, int $padding, $string): string { - $filename = \sprintf('%s/%s.xml', $this->getTargetDirectory(), $name); + $reset = $color ? self::COLOR_RESET : ''; - $document->formatOutput = true; - $document->preserveWhiteSpace = false; - $this->initTargetDirectory(\dirname($filename)); - - $document->save($filename); - } - - private function createDirectory(string $directory): bool - { - return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); + return $color . str_pad((string) $string, $padding) . $reset . PHP_EOL; } } - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -use SebastianBergmann\CodeCoverage\Util; - -final class Totals -{ - /** - * @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 getContainer(): \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', Util::percent($executed, $executable)) - ); - } - - 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', Util::percent($tested, $count)) - ); - } - - 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', Util::percent($tested, $count)) - ); - } - - 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', Util::percent($tested, $count)) - ); - } - - 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', Util::percent($tested, $count)) - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -use SebastianBergmann\Environment\Runtime; - -final class BuildInformation -{ - /** - * @var \DOMElement - */ - private $contextNode; - - public function __construct(\DOMElement $contextNode) - { - $this->contextNode = $contextNode; - } - - public function setRuntimeInformation(Runtime $runtime): void - { - $runtimeNode = $this->getNodeByName('runtime'); - - $runtimeNode->setAttribute('name', $runtime->getName()); - $runtimeNode->setAttribute('version', $runtime->getVersion()); - $runtimeNode->setAttribute('url', $runtime->getVendorUrl()); - - $driverNode = $this->getNodeByName('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(\DateTime $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 getNodeByName(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 SebastianBergmann\CodeCoverage\Report\Xml; - -class File -{ - /** - * @var \DOMDocument - */ - private $dom; - - /** - * @var \DOMElement - */ - private $contextNode; - - public function __construct(\DOMElement $context) - { - $this->dom = $context->ownerDocument; - $this->contextNode = $context; - } - - public function getTotals(): Totals - { - $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); - } - - public function getLineCoverage(string $line): 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 Coverage($lineNode, $line); - } - - protected function getContextNode(): \DOMElement - { - return $this->contextNode; - } - - protected function getDomDocument(): \DOMDocument - { - return $this->dom; - } -} - * @@ -8335,14 +27949,167 @@ class File */ namespace SebastianBergmann\CodeCoverage\Report\Html; +use const DIRECTORY_SEPARATOR; +use function copy; +use function date; +use function dirname; +use function substr; +use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\InvalidArgumentException; +use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; +use SebastianBergmann\CodeCoverage\Util\Filesystem; + +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 = '') + { + if ($lowUpperBound > $highLowerBound) { + throw new InvalidArgumentException( + '$lowUpperBound must not be larger than $highLowerBound' + ); + } + + $this->generator = $generator; + $this->highLowerBound = $highLowerBound; + $this->lowUpperBound = $lowUpperBound; + $this->templatePath = __DIR__ . '/Renderer/Template/'; + } + + public function process(CodeCoverage $coverage, string $target): void + { + $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) { + Filesystem::createDirectory($target . $id); + + $directory->render($node, $target . $id . '/index.html'); + $dashboard->render($node, $target . $id . '/dashboard.html'); + } else { + $dir = dirname($target . $id); + + Filesystem::createDirectory($dir); + + $file->render($node, $target . $id); + } + } + + $this->copyFiles($target); + } + + private function copyFiles(string $target): void + { + $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 directory(string $directory): string + { + if (substr($directory, -1, 1) != DIRECTORY_SEPARATOR) { + $directory .= DIRECTORY_SEPARATOR; + } + + Filesystem::createDirectory($directory); + + return $directory; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Html; + +use function array_pop; +use function count; +use function sprintf; +use function str_repeat; +use function substr_count; use SebastianBergmann\CodeCoverage\Node\AbstractNode; use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; use SebastianBergmann\CodeCoverage\Node\File as FileNode; use SebastianBergmann\CodeCoverage\Version; use SebastianBergmann\Environment\Runtime; +use SebastianBergmann\Template\Template; /** - * Base class for node renderers. + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ abstract class Renderer { @@ -8371,32 +28138,38 @@ abstract class Renderer */ protected $highLowerBound; + /** + * @var bool + */ + protected $hasBranchCoverage; + /** * @var string */ protected $version; - public function __construct(string $templatePath, string $generator, string $date, int $lowUpperBound, int $highLowerBound) + 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 = Version::id(); + $this->templatePath = $templatePath; + $this->generator = $generator; + $this->date = $date; + $this->lowUpperBound = $lowUpperBound; + $this->highLowerBound = $highLowerBound; + $this->version = Version::id(); + $this->hasBranchCoverage = $hasBranchCoverage; } - protected function renderItemTemplate(\Text_Template $template, array $data): string + protected function renderItemTemplate(Template $template, array $data): string { - $numSeparator = ' / '; + $numSeparator = ' / '; if (isset($data['numClasses']) && $data['numClasses'] > 0) { - $classesLevel = $this->getColorLevel($data['testedClassesPercent']); + $classesLevel = $this->colorLevel($data['testedClassesPercent']); $classesNumber = $data['numTestedClasses'] . $numSeparator . $data['numClasses']; - $classesBar = $this->getCoverageBar( + $classesBar = $this->coverageBar( $data['testedClassesPercent'] ); } else { @@ -8407,12 +28180,12 @@ abstract class Renderer } if ($data['numMethods'] > 0) { - $methodsLevel = $this->getColorLevel($data['testedMethodsPercent']); + $methodsLevel = $this->colorLevel($data['testedMethodsPercent']); $methodsNumber = $data['numTestedMethods'] . $numSeparator . $data['numMethods']; - $methodsBar = $this->getCoverageBar( + $methodsBar = $this->coverageBar( $data['testedMethodsPercent'] ); } else { @@ -8423,12 +28196,12 @@ abstract class Renderer } if ($data['numExecutableLines'] > 0) { - $linesLevel = $this->getColorLevel($data['linesExecutedPercent']); + $linesLevel = $this->colorLevel($data['linesExecutedPercent']); $linesNumber = $data['numExecutedLines'] . $numSeparator . $data['numExecutableLines']; - $linesBar = $this->getCoverageBar( + $linesBar = $this->coverageBar( $data['linesExecutedPercent'] ); } else { @@ -8438,40 +28211,80 @@ abstract class Renderer $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, - '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, + '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(\Text_Template $template, AbstractNode $node): void + protected function setCommonTemplateVariables(Template $template, AbstractNode $node): void { $template->setVar( [ - 'id' => $node->getId(), - 'full_path' => $node->getPath(), - 'path_to_root' => $this->getPathToRoot($node), - 'breadcrumbs' => $this->getBreadcrumbs($node), + '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->getRuntimeString(), + 'runtime' => $this->runtimeString(), 'generator' => $this->generator, 'low_upper_bound' => $this->lowUpperBound, 'high_lower_bound' => $this->highLowerBound, @@ -8479,40 +28292,40 @@ abstract class Renderer ); } - protected function getBreadcrumbs(AbstractNode $node): string + protected function breadcrumbs(AbstractNode $node): string { $breadcrumbs = ''; - $path = $node->getPathAsArray(); + $path = $node->pathAsArray(); $pathToRoot = []; - $max = \count($path); + $max = count($path); if ($node instanceof FileNode) { $max--; } for ($i = 0; $i < $max; $i++) { - $pathToRoot[] = \str_repeat('../', $i); + $pathToRoot[] = str_repeat('../', $i); } foreach ($path as $step) { if ($step !== $node) { - $breadcrumbs .= $this->getInactiveBreadcrumb( + $breadcrumbs .= $this->inactiveBreadcrumb( $step, - \array_pop($pathToRoot) + array_pop($pathToRoot) ); } else { - $breadcrumbs .= $this->getActiveBreadcrumb($step); + $breadcrumbs .= $this->activeBreadcrumb($step); } } return $breadcrumbs; } - protected function getActiveBreadcrumb(AbstractNode $node): string + protected function activeBreadcrumb(AbstractNode $node): string { - $buffer = \sprintf( + $buffer = sprintf( ' ' . "\n", - $node->getName() + $node->name() ); if ($node instanceof DirectoryNode) { @@ -8522,44 +28335,45 @@ abstract class Renderer return $buffer; } - protected function getInactiveBreadcrumb(AbstractNode $node, string $pathToRoot): string + protected function inactiveBreadcrumb(AbstractNode $node, string $pathToRoot): string { - return \sprintf( + return sprintf( ' ' . "\n", $pathToRoot, - $node->getName() + $node->name() ); } - protected function getPathToRoot(AbstractNode $node): string + protected function pathToRoot(AbstractNode $node): string { - $id = $node->getId(); - $depth = \substr_count($id, '/'); + $id = $node->id(); + $depth = substr_count($id, '/'); if ($id !== 'index' && $node instanceof DirectoryNode) { $depth++; } - return \str_repeat('../', $depth); + return str_repeat('../', $depth); } - protected function getCoverageBar(float $percent): string + protected function coverageBar(float $percent): string { - $level = $this->getColorLevel($percent); + $level = $this->colorLevel($percent); - $template = new \Text_Template( - $this->templatePath . 'coverage_bar.html', + $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'coverage_bar_branch.html' : 'coverage_bar.html'); + $template = new Template( + $templateName, '{{', '}}' ); - $template->setVar(['level' => $level, 'percent' => \sprintf('%.2F', $percent)]); + $template->setVar(['level' => $level, 'percent' => sprintf('%.2F', $percent)]); return $template->render(); } - protected function getColorLevel(float $percent): string + protected function colorLevel(float $percent): string { if ($percent <= $this->lowUpperBound) { return 'danger'; @@ -8573,411 +28387,16 @@ abstract class Renderer return 'success'; } - private function getRuntimeString(): string + private function runtimeString(): string { $runtime = new Runtime; - $buffer = \sprintf( + return sprintf( '%s %s', $runtime->getVendorUrl(), $runtime->getName(), $runtime->getVersion() ); - - if ($runtime->hasXdebug() && !$runtime->hasPHPDBGCodeCoverage()) { - $buffer .= \sprintf( - ' with Xdebug %s', - \phpversion('xdebug') - ); - } - - if ($runtime->hasPCOV() && !$runtime->hasPHPDBGCodeCoverage()) { - $buffer .= \sprintf( - ' with PCOV %s', - \phpversion('pcov') - ); - } - - return $buffer; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Html; - -use SebastianBergmann\CodeCoverage\Node\AbstractNode as Node; -use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; - -/** - * Renders a directory node. - */ -final class Directory extends Renderer -{ - /** - * @throws \InvalidArgumentException - * @throws \RuntimeException - */ - public function render(DirectoryNode $node, string $file): void - { - $template = new \Text_Template($this->templatePath . 'directory.html', '{{', '}}'); - - $this->setCommonTemplateVariables($template, $node); - - $items = $this->renderItem($node, true); - - foreach ($node->getDirectories() as $item) { - $items .= $this->renderItem($item); - } - - foreach ($node->getFiles() as $item) { - $items .= $this->renderItem($item); - } - - $template->setVar( - [ - 'id' => $node->getId(), - 'items' => $items, - ] - ); - - $template->renderTo($file); - } - - protected function renderItem(Node $node, bool $total = false): string - { - $data = [ - 'numClasses' => $node->getNumClassesAndTraits(), - 'numTestedClasses' => $node->getNumTestedClassesAndTraits(), - 'numMethods' => $node->getNumFunctionsAndMethods(), - 'numTestedMethods' => $node->getNumTestedFunctionsAndMethods(), - 'linesExecutedPercent' => $node->getLineExecutedPercent(false), - 'linesExecutedPercentAsString' => $node->getLineExecutedPercent(), - 'numExecutedLines' => $node->getNumExecutedLines(), - 'numExecutableLines' => $node->getNumExecutableLines(), - 'testedMethodsPercent' => $node->getTestedFunctionsAndMethodsPercent(false), - 'testedMethodsPercentAsString' => $node->getTestedFunctionsAndMethodsPercent(), - 'testedClassesPercent' => $node->getTestedClassesAndTraitsPercent(false), - 'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent(), - ]; - - if ($total) { - $data['name'] = 'Total'; - } else { - if ($node instanceof DirectoryNode) { - $data['name'] = \sprintf( - '%s', - $node->getName(), - $node->getName() - ); - - $up = \str_repeat('../', \count($node->getPathAsArray()) - 2); - - $data['icon'] = \sprintf('', $up); - } else { - $data['name'] = \sprintf( - '%s', - $node->getName(), - $node->getName() - ); - - $up = \str_repeat('../', \count($node->getPathAsArray()) - 2); - - $data['icon'] = \sprintf('', $up); - } - } - - return $this->renderItemTemplate( - new \Text_Template($this->templatePath . 'directory_item.html', '{{', '}}'), - $data - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Html; - -use SebastianBergmann\CodeCoverage\Node\AbstractNode; -use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; - -/** - * Renders the dashboard for a directory node. - */ -final class Dashboard extends Renderer -{ - /** - * @throws \InvalidArgumentException - * @throws \RuntimeException - */ - public function render(DirectoryNode $node, string $file): void - { - $classes = $node->getClassesAndTraits(); - $template = new \Text_Template( - $this->templatePath . 'dashboard.html', - '{{', - '}}' - ); - - $this->setCommonTemplateVariables($template, $node); - - $baseLink = $node->getId() . '/'; - $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); - } - - /** - * Returns the data for the Class/Method Complexity charts. - */ - protected 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 - ), - ]; - } - - $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. - */ - protected 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]++; - } - } - - 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. - */ - protected 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']; - } - } - - 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; - } - - /** - * Returns the project risks according to the CRAP index. - */ - protected 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']; - } - } - - if ($class['coverage'] < $this->highLowerBound && - $class['ccn'] > \count($class['methods'])) { - $classRisks[$className] = $class['crap']; - } - } - - \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; - } - - protected function getActiveBreadcrumb(AbstractNode $node): string - { - return \sprintf( - ' ' . "\n" . - ' ' . "\n", - $node->getName() - ); } } @@ -9015,9 +28434,9 @@ final class Dashboard extends Renderer   -
Classes and Traits
-
Functions and Methods
Lines
+
Functions and Methods
+
Classes and Traits
@@ -9025,19 +28444,12 @@ final class Dashboard extends Renderer - - {{lines}} - -
+{{structure}}