diff --git a/.phan/config.php b/.phan/config.php new file mode 100644 index 0000000..40fe568 --- /dev/null +++ b/.phan/config.php @@ -0,0 +1,247 @@ + false, + + // Allow null to be cast as any type and for any + // type to be cast to null. + "null_casts_as_any_type" => false, + + // If enabled, scalars (int, float, bool, string, null) + // are treated as if they can cast to each other. + 'scalar_implicit_cast' => false, + + // If true, seemingly undeclared variables in the global + // scope will be ignored. This is useful for projects + // with complicated cross-file globals that you have no + // hope of fixing. + 'ignore_undeclared_variables_in_global_scope' => false, + + // Backwards Compatibility Checking + 'backward_compatibility_checks' => false, + + // If enabled, check all methods that override a + // parent method to make sure its signature is + // compatible with the parent's. This check + // can add quite a bit of time to the analysis. + 'analyze_signature_compatibility' => true, + + // Set to true in order to attempt to detect dead + // (unreferenced) code. Keep in mind that the + // results will only be a guess given that classes, + // properties, constants and methods can be referenced + // as variables (like `$class->$property` or + // `$class->$method()`) in ways that we're unable + // to make sense of. + 'dead_code_detection' => false, + + // Run a quick version of checks that takes less + // time + "quick_mode" => false, + + // Enable or disable support for generic templated + // class types. + 'generic_types_enabled' => true, + + // By default, Phan will not analyze all node types + // in order to save time. If this config is set to true, + // Phan will dig deeper into the AST tree and do an + // analysis on all nodes, possibly finding more issues. + // + // See \Phan\Analysis::shouldVisit for the set of skipped + // nodes. + 'should_visit_all_nodes' => true, + + 'runkit_superglobals' => ['_DATE', '_RK'], + + 'globals_type_map' => [ + '_DATE' => '\\DateTime', + '_RK' => '\\RunkitGlobal', + ], + + // The minimum severity level to report on. This can be + // set to Issue::SEVERITY_LOW, Issue::SEVERITY_NORMAL or + // Issue::SEVERITY_CRITICAL. + 'minimum_severity' => Issue::SEVERITY_LOW, + + // Add any issue types (such as 'PhanUndeclaredMethod') + // here to inhibit them from being reported + 'suppress_issue_types' => [ + // 'PhanUndeclaredMethod', + ], + + // If empty, no filter against issues types will be applied. + // If non-empty, only issues within the list will be emitted + // by Phan. + 'whitelist_issue_types' => [ + // 'PhanAccessMethodPrivate', + // 'PhanAccessMethodProtected', + // 'PhanAccessNonStaticToStatic', + // 'PhanAccessPropertyPrivate', + // 'PhanAccessPropertyProtected', + // 'PhanAccessSignatureMismatch', + // 'PhanAccessSignatureMismatchInternal', + // 'PhanAccessStaticToNonStatic', + // 'PhanCompatibleExpressionPHP7', + // 'PhanCompatiblePHP7', + // 'PhanContextNotObject', + // 'PhanDeprecatedClass', + // 'PhanDeprecatedFunction', + // 'PhanDeprecatedProperty', + // 'PhanEmptyFile', + // 'PhanNonClassMethodCall', + // 'PhanNoopArray', + // 'PhanNoopClosure', + // 'PhanNoopConstant', + // 'PhanNoopProperty', + // 'PhanNoopVariable', + // 'PhanParamRedefined', + // 'PhanParamReqAfterOpt', + // 'PhanParamSignatureMismatch', + // 'PhanParamSignatureMismatchInternal', + // 'PhanParamSpecial1', + // 'PhanParamSpecial2', + // 'PhanParamSpecial3', + // 'PhanParamSpecial4', + // 'PhanParamTooFew', + // 'PhanParamTooFewInternal', + // 'PhanParamTooMany', + // 'PhanParamTooManyInternal', + // 'PhanParamTypeMismatch', + // 'PhanParentlessClass', + // 'PhanRedefineClass', + // 'PhanRedefineClassInternal', + // 'PhanRedefineFunction', + // 'PhanRedefineFunctionInternal', + // 'PhanStaticCallToNonStatic', + // 'PhanSyntaxError', + // 'PhanTraitParentReference', + // 'PhanTypeArrayOperator', + // 'PhanTypeArraySuspicious', + // 'PhanTypeComparisonFromArray', + // 'PhanTypeComparisonToArray', + // 'PhanTypeConversionFromArray', + // 'PhanTypeInstantiateAbstract', + // 'PhanTypeInstantiateInterface', + // 'PhanTypeInvalidLeftOperand', + // 'PhanTypeInvalidRightOperand', + // 'PhanTypeMismatchArgument', + // 'PhanTypeMismatchArgumentInternal', + // 'PhanTypeMismatchDefault', + // 'PhanTypeMismatchForeach', + // 'PhanTypeMismatchProperty', + // 'PhanTypeMismatchReturn', + // 'PhanTypeMissingReturn', + // 'PhanTypeNonVarPassByRef', + // 'PhanTypeParentConstructorCalled', + // 'PhanTypeVoidAssignment', + // 'PhanUnanalyzable', + // 'PhanUndeclaredClass', + // 'PhanUndeclaredClassCatch', + // 'PhanUndeclaredClassConstant', + // 'PhanUndeclaredClassInstanceof', + // 'PhanUndeclaredClassMethod', + // 'PhanUndeclaredClassReference', + // 'PhanUndeclaredConstant', + // 'PhanUndeclaredExtendedClass', + // 'PhanUndeclaredFunction', + // 'PhanUndeclaredInterface', + // 'PhanUndeclaredMethod', + // 'PhanUndeclaredProperty', + // 'PhanUndeclaredStaticMethod', + // 'PhanUndeclaredStaticProperty', + // 'PhanUndeclaredTrait', + // 'PhanUndeclaredTypeParameter', + // 'PhanUndeclaredTypeProperty', + // 'PhanUndeclaredVariable', + // 'PhanUnreferencedClass', + // 'PhanUnreferencedConstant', + // 'PhanUnreferencedMethod', + // 'PhanUnreferencedProperty', + // 'PhanVariableUseClause', + ], + + // A list of files to include in analysis + 'file_list' => [ + // 'vendor/phpunit/phpunit/src/Framework/TestCase.php', + ], + + // A file list that defines files that will be excluded + // from parsing and analysis and will not be read at all. + // + // This is useful for excluding hopelessly unanalyzable + // files that can't be removed for whatever reason. + 'exclude_file_list' => [], + + // The number of processes to fork off during the analysis + // phase. + 'processes' => 1, + + // A list of directories that should be parsed for class and + // method information. After excluding the directories + // defined in exclude_analysis_directory_list, the remaining + // files will be statically analyzed for errors. + // + // Thus, both first-party and third-party code being used by + // your application should be included in this list. + 'directory_list' => [ + 'src', + 'vendor/nikic/php-parser/lib', + ], + + // A directory list that defines files that will be excluded + // from static analysis, but whose class and method + // information should be included. + // + // Generally, you'll want to include the directories for + // third-party code (such as "vendor/") in this list. + // + // n.b.: If you'd like to parse but not analyze 3rd + // party code, directories containing that code + // should be added to the `directory_list` as + // to `exclude_analysis_directory_list`. + "exclude_analysis_directory_list" => [ + 'vendor/nikic/php-parser/lib', + ], + + // A list of plugin files to execute + 'plugins' => [ + // NOTE: src/Phan/Language/Internal/FunctionSignatureMap.php mixes value without key as return type with values having keys deliberately. + 'vendor/etsy/phan/.phan/plugins/AlwaysReturnPlugin.php', // (TODO: make BlockExitStatus more reliable) + 'vendor/etsy/phan/.phan/plugins/DollarDollarPlugin.php', + 'vendor/etsy/phan/.phan/plugins/DuplicateArrayKeyPlugin.php', + 'vendor/etsy/phan/.phan/plugins/UnreachableCodePlugin.php', // (TODO: make BlockExitStatus more reliable) + // NOTE: This plugin only produces correct results when + // Phan is run on a single core (-j1). + // '.phan/plugins/UnusedSuppressionPlugin.php', + ], + +]; diff --git a/.travis.yml b/.travis.yml index b358602..78a3dc2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,4 +15,5 @@ install: - composer --prefer-dist install script: + - vendor/bin/phan - ./test diff --git a/README.md b/README.md index 18c3700..d850ccf 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,14 @@ PHP-Parser to php-ast [![Build Status](https://travis-ci.org/TysonAndre/php-parser-to-php-ast.svg?branch=master)](https://travis-ci.org/TysonAndre/php-parser-to-php-ast) +This converts ASTs(Abstract Syntax Trees) from [PHP-Parser](https://github.com/nikic/PHP-Parser) to [php-ast](https://github.com/nikic/php-ast/). +It can be used as a PHP-only implementation of php-ast. + +Supported [php-ast AST versions](https://github.com/nikic/php-ast#version-changelog): 40, 50 + +Current Status +-------------- + No tests are failing - This is 90% done @@ -31,7 +39,7 @@ Using it as an error-tolerant substitute for php-ast (e.g. for use in IDEs) Running unit tests ------------------ -To run unit tests, you must install [nikic/php-ast](https://github.com/nikic/php-ast) +To run unit tests, you must install [nikic/php-ast](https://github.com/nikic/php-ast). A version supporting AST versions 40 and/or 50 should be installed (`~0.1.5` is preferred) - Then run `vendor/bin/phpunit` diff --git a/composer.json b/composer.json index 250ed54..d26fa94 100644 --- a/composer.json +++ b/composer.json @@ -1,15 +1,16 @@ { - "name": "tysonandre/php-parser", - "description": "A php-parser to php-ast converter written in php", + "name": "tysonandre/php-parser-to-php-ast", + "description": "A php-parser to php-ast converter written in php. Can be used to test out projects using php-ast without compiling php-ast.", "require": { "php": ">=7.1", - "nikic/PHP-Parser": "3.0.5" + "nikic/PHP-Parser": "~3.1.0" }, "require-dev": { - "phpunit/phpunit": "^5.7" + "phpunit/phpunit": "^5.7", + "etsy/phan": "~0.9.4" }, "suggest": { - "ext-ast": "~0.1.4" + "ext-ast": "~0.1.5" }, "autoload": { "psr-4": {"ASTConverter\\": "src/ASTConverter"} diff --git a/composer.lock b/composer.lock index 3cf070f..c643581 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "cf0e7bee43429609880fc658e9b0c017", + "content-hash": "7beae8f069e15b813586fa101cbd1f35", "packages": [ { "name": "nikic/php-parser", - "version": "v3.0.5", + "version": "v3.1.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "2b9e2f71b722f7c53918ab0c25f7646c2013f17d" + "reference": "4d4896e553f2094e657fe493506dc37c509d4e2b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/2b9e2f71b722f7c53918ab0c25f7646c2013f17d", - "reference": "2b9e2f71b722f7c53918ab0c25f7646c2013f17d", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4d4896e553f2094e657fe493506dc37c509d4e2b", + "reference": "4d4896e553f2094e657fe493506dc37c509d4e2b", "shasum": "" }, "require": { @@ -55,7 +55,7 @@ "parser", "php" ], - "time": "2017-03-05T18:23:57+00:00" + "time": "2017-07-28T14:45:09+00:00" } ], "packages-dev": [ @@ -113,6 +113,59 @@ ], "time": "2017-07-22T11:58:36+00:00" }, + { + "name": "etsy/phan", + "version": "0.9.4", + "source": { + "type": "git", + "url": "https://github.com/phan/phan.git", + "reference": "d2f3fbd719cb9236f1d85fb6018f9c8b7e41ffaa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phan/phan/zipball/d2f3fbd719cb9236f1d85fb6018f9c8b7e41ffaa", + "reference": "d2f3fbd719cb9236f1d85fb6018f9c8b7e41ffaa", + "shasum": "" + }, + "require": { + "ext-ast": "^0.1.4", + "php": "~7.1.0", + "symfony/console": "~2.3|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.2.1" + }, + "bin": [ + "phan", + "phan_client", + "tocheckstyle" + ], + "type": "project", + "autoload": { + "psr-4": { + "Phan\\": "src/Phan" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Rasmus Lerdorf" + }, + { + "name": "Andrew S. Morrison" + } + ], + "description": "A static analyzer for PHP", + "keywords": [ + "analyzer", + "php", + "static" + ], + "time": "2017-08-15T19:31:44+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.6.1", @@ -754,6 +807,53 @@ ], "time": "2017-06-30T09:13:00+00:00" }, + { + "name": "psr/log", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2016-10-10T12:19:37+00:00" + }, { "name": "sebastian/code-unit-reverse-lookup", "version": "1.0.1", @@ -1267,6 +1367,189 @@ "homepage": "https://github.com/sebastianbergmann/version", "time": "2016-10-03T07:35:21+00:00" }, + { + "name": "symfony/console", + "version": "v3.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "d6596cb5022b6a0bd940eae54a1de78646a5fda6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/d6596cb5022b6a0bd940eae54a1de78646a5fda6", + "reference": "d6596cb5022b6a0bd940eae54a1de78646a5fda6", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/debug": "~2.8|~3.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.3", + "symfony/dependency-injection": "~3.3", + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/filesystem": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/filesystem": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2017-08-27T14:52:21+00:00" + }, + { + "name": "symfony/debug", + "version": "v3.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "084d804fe35808eb2ef596ec83d85d9768aa6c9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/084d804fe35808eb2ef596ec83d85d9768aa6c9d", + "reference": "084d804fe35808eb2ef596ec83d85d9768aa6c9d", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/http-kernel": "~2.8|~3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Debug Component", + "homepage": "https://symfony.com", + "time": "2017-08-27T14:52:21+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7c8fae0ac1d216eb54349e6a8baa57d515fe8803", + "reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2017-06-14T15:44:48+00:00" + }, { "name": "symfony/yaml", "version": "v3.3.8", diff --git a/src/ASTConverter/ASTConverter.php b/src/ASTConverter/ASTConverter.php index e4f14db..5eabc28 100644 --- a/src/ASTConverter/ASTConverter.php +++ b/src/ASTConverter/ASTConverter.php @@ -12,9 +12,21 @@ */ class ASTConverter { // The latest stable version of php-ast. - // For something > 40, update the library's release. + // For something > 50, update the library's release. // For something < 40, there are no releases. - const AST_VERSION = 40; + const AST_VERSION = 50; + // The versions that this supports + const SUPPORTED_AST_VERSIONS = [40, 50]; + + /** + * @var int - A version in SUPPORTED_AST_VERSIONS + */ + private static $ast_version = self::AST_VERSION; + + /** + * @var int - A version in SUPPORTED_AST_VERSIONS + */ + private static $decl_id = 0; private static $should_add_placeholders = false; @@ -23,8 +35,8 @@ public static function set_should_add_placeholders(bool $value) : void { } public static function ast_parse_code_fallback(string $source, int $version, bool $suppressErrors = false, array &$errors = null) { - if ($version !== self::AST_VERSION) { - throw new \InvalidArgumentException(sprintf("Unexpected version: want %d, got %d", self::AST_VERSION, $version)); + if (!\in_array($version, self::SUPPORTED_AST_VERSIONS)) { + throw new \InvalidArgumentException(sprintf("Unexpected version: want %d, got %d", implode(', ', self::SUPPORTED_AST_VERSIONS), $version)); } // Aside: this can be implemented as a stub. $parserNode = self::phpparser_parse($source, $suppressErrors, $errors); @@ -46,17 +58,25 @@ public static function phpparser_parse(string $source, bool $suppressErrors = fa /** * @param PhpParser\Node|PhpParser\Node[] $parserNode * @param int $version + * @return \ast\Node */ public static function phpparser_to_phpast($parserNode, int $version) { - if ($version !== self::AST_VERSION) { - throw new \InvalidArgumentException(sprintf("Unexpected version: want %d, got %d", self::AST_VERSION, $version)); + if (!\in_array($version, self::SUPPORTED_AST_VERSIONS)) { + throw new \InvalidArgumentException(sprintf("Unexpected version: want %s, got %d", implode(', ', self::SUPPORTED_AST_VERSIONS), $version)); } + self::_start_parsing($version); if (is_array($parserNode)) { return self::_phpparser_stmtlist_to_ast_node($parserNode, 1); } + // Unreachable? return self::_phpparser_node_to_ast_node($parserNode); } + private static function _start_parsing(int $ast_version) { + self::$ast_version = $ast_version; + self::$decl_id = 0; + } + private static function _phpparser_stmtlist_to_ast_node(array $parserNodes, ?int $lineno) : \ast\Node { $stmts = new \ast\Node(); $stmts->kind = \ast\AST_STMT_LIST; @@ -110,7 +130,7 @@ private static function _phpparser_expr_list_to_expr_list(array $exprs, int $lin break; } } - return astnode( + return new \ast\Node( \ast\AST_EXPR_LIST, 0, $children, @@ -149,7 +169,7 @@ private static final function _phpparser_node_to_ast_node($n) { * - In php <= 7.1, the interpreter would loop through all possible cases, and compare against the value one by one. * - There are a lot of local variables to look at. * - * @return Closure[] + * @return \Closure[] */ private static function _init_handle_map() : array { $closures = [ @@ -160,7 +180,7 @@ private static function _init_handle_map() : array { return self::_phpparser_array_to_ast_array($n, $startLine); }, 'PhpParser\Node\Expr\ArrayDimFetch' => function(PhpParser\Node\Expr\ArrayDimFetch $n, int $startLine) : \ast\Node { - return astnode(\ast\AST_DIM, 0, [ + return new \ast\Node(\ast\AST_DIM, 0, [ 'expr' => self::_phpparser_node_to_ast_node($n->var), 'dim' => $n->dim !== null ? self::_phpparser_node_to_ast_node($n->dim) : null, ], $startLine); @@ -345,16 +365,16 @@ private static function _init_handle_map() : array { return self::_phpparser_classconstfetch_to_ast_classconstfetch($n, $startLine); }, 'PhpParser\Node\Expr\Clone_' => function(PhpParser\Node\Expr\Clone_ $n, int $startLine) : \ast\Node { - return astnode(\ast\AST_CLONE, 0, ['expr' => self::_phpparser_node_to_ast_node($n->expr)], $startLine); + return new \ast\Node(\ast\AST_CLONE, 0, ['expr' => self::_phpparser_node_to_ast_node($n->expr)], $startLine); }, 'PhpParser\Node\Expr\ConstFetch' => function(PhpParser\Node\Expr\ConstFetch $n, int $startLine) : \ast\Node { - return astnode(\ast\AST_CONST, 0, ['name' => self::_phpparser_node_to_ast_node($n->name)], $startLine); + return new \ast\Node(\ast\AST_CONST, 0, ['name' => self::_phpparser_node_to_ast_node($n->name)], $startLine); }, 'PhpParser\Node\Expr\ErrorSuppress' => function(PhpParser\Node\Expr\ErrorSuppress $n, int $startLine) : \ast\Node { return self::_ast_node_unary_op(\ast\flags\UNARY_SILENCE, self::_phpparser_node_to_ast_node($n->expr), $startLine); }, 'PhpParser\Node\Expr\Empty_' => function(PhpParser\Node\Expr\Empty_ $n, int $startLine) : \ast\Node { - return astnode(\ast\AST_EMPTY, 0, ['expr' => self::_phpparser_node_to_ast_node($n->expr)], $startLine); + return new \ast\Node(\ast\AST_EMPTY, 0, ['expr' => self::_phpparser_node_to_ast_node($n->expr)], $startLine); }, 'PhpParser\Node\Expr\Eval_' => function(PhpParser\Node\Expr\Eval_ $n, int $startLine) : \ast\Node { return self::_ast_node_eval( @@ -368,7 +388,7 @@ private static function _init_handle_map() : array { return null; }, 'PhpParser\Node\Expr\Exit_' => function(PhpParser\Node\Expr\Exit_ $n, int $startLine) { - return astnode(\ast\AST_EXIT, 0, ['expr' => self::_phpparser_node_to_ast_node($n->expr)], $startLine); + return new \ast\Node(\ast\AST_EXIT, 0, ['expr' => self::_phpparser_node_to_ast_node($n->expr)], $startLine); }, 'PhpParser\Node\Expr\FuncCall' => function(PhpParser\Node\Expr\FuncCall $n, int $startLine) : \ast\Node { return self::_ast_node_call( @@ -385,7 +405,7 @@ private static function _init_handle_map() : array { ); }, 'PhpParser\Node\Expr\Instanceof_' => function(PhpParser\Node\Expr\Instanceof_ $n, int $startLine) : \ast\Node { - return astnode(\ast\AST_INSTANCEOF, 0, [ + return new \ast\Node(\ast\AST_INSTANCEOF, 0, [ 'expr' => self::_phpparser_node_to_ast_node($n->expr), 'class' => self::_phpparser_node_to_ast_node($n->class), ], $startLine); @@ -393,14 +413,14 @@ private static function _init_handle_map() : array { 'PhpParser\Node\Expr\Isset_' => function(PhpParser\Node\Expr\Isset_ $n, int $startLine) : \ast\Node { $astIssets = []; foreach ($n->vars as $var) { - $astIssets[] = astnode(\ast\AST_ISSET, 0, [ + $astIssets[] = new \ast\Node(\ast\AST_ISSET, 0, [ 'var' => self::_phpparser_node_to_ast_node($var), ], $startLine); } $e = $astIssets[0]; for ($i = 1; $i < \count($astIssets); $i++) { $right = $astIssets[$i]; - $e = astnode( + $e = new \ast\Node( \ast\AST_BINARY_OP, \ast\flags\BINARY_BOOL_AND, [ @@ -424,25 +444,25 @@ private static function _init_handle_map() : array { ); }, 'PhpParser\Node\Expr\New_' => function(PhpParser\Node\Expr\New_ $n, int $startLine) : \ast\Node { - return astnode(\ast\AST_NEW, 0, [ + return new \ast\Node(\ast\AST_NEW, 0, [ 'class' => self::_phpparser_node_to_ast_node($n->class), 'args' => self::_phpparser_arg_list_to_ast_arg_list($n->args, $startLine), ], $startLine); }, 'PhpParser\Node\Expr\PreInc' => function(PhpParser\Node\Expr\PreInc $n, int $startLine) : \ast\Node { - return astnode(\ast\AST_PRE_INC, 0, ['var' => self::_phpparser_node_to_ast_node($n->var)], $startLine); + return new \ast\Node(\ast\AST_PRE_INC, 0, ['var' => self::_phpparser_node_to_ast_node($n->var)], $startLine); }, 'PhpParser\Node\Expr\PreDec' => function(PhpParser\Node\Expr\PreDec $n, int $startLine) : \ast\Node { - return astnode(\ast\AST_PRE_DEC, 0, ['var' => self::_phpparser_node_to_ast_node($n->var)], $startLine); + return new \ast\Node(\ast\AST_PRE_DEC, 0, ['var' => self::_phpparser_node_to_ast_node($n->var)], $startLine); }, 'PhpParser\Node\Expr\PostInc' => function(PhpParser\Node\Expr\PostInc $n, int $startLine) : \ast\Node { - return astnode(\ast\AST_POST_INC, 0, ['var' => self::_phpparser_node_to_ast_node($n->var)], $startLine); + return new \ast\Node(\ast\AST_POST_INC, 0, ['var' => self::_phpparser_node_to_ast_node($n->var)], $startLine); }, 'PhpParser\Node\Expr\PostDec' => function(PhpParser\Node\Expr\PostDec $n, int $startLine) : \ast\Node { - return astnode(\ast\AST_POST_DEC, 0, ['var' => self::_phpparser_node_to_ast_node($n->var)], $startLine); + return new \ast\Node(\ast\AST_POST_DEC, 0, ['var' => self::_phpparser_node_to_ast_node($n->var)], $startLine); }, 'PhpParser\Node\Expr\Print_' => function(PhpParser\Node\Expr\Print_ $n, int $startLine) : \ast\Node { - return astnode( + return new \ast\Node( \ast\AST_PRINT, 0, ['expr' => self::_phpparser_node_to_ast_node($n->expr)], @@ -458,9 +478,9 @@ private static function _init_handle_map() : array { $value = $parts[0]->value; } else { $value_inner = array_map(function(PhpParser\Node $node) { return self::_phpparser_node_to_ast_node($node); }, $parts); - $value = astnode(\ast\AST_ENCAPS_LIST, 0, $value_inner, $startLine); + $value = new \ast\Node(\ast\AST_ENCAPS_LIST, 0, $value_inner, $startLine); } - return astnode(\ast\AST_SHELL_EXEC, 0, ['expr' => $value], $startLine); + return new \ast\Node(\ast\AST_SHELL_EXEC, 0, ['expr' => $value], $startLine); }, 'PhpParser\Node\Expr\StaticCall' => function(PhpParser\Node\Expr\StaticCall $n, int $startLine) : \ast\Node { return self::_ast_node_static_call( @@ -471,7 +491,7 @@ private static function _init_handle_map() : array { ); }, 'PhpParser\Node\Expr\StaticPropertyFetch' => function(PhpParser\Node\Expr\StaticPropertyFetch $n, int $startLine) : \ast\Node { - return astnode( + return new \ast\Node( \ast\AST_STATIC_PROP, 0, [ @@ -482,7 +502,7 @@ private static function _init_handle_map() : array { ); }, 'PhpParser\Node\Expr\Ternary' => function(PhpParser\Node\Expr\Ternary $n, int $startLine) : \ast\Node { - return astnode( + return new \ast\Node( \ast\AST_CONDITIONAL, 0, [ @@ -503,7 +523,7 @@ private static function _init_handle_map() : array { return self::_ast_node_variable($n->name, $startLine); }, 'PhpParser\Node\Expr\Yield_' => function(PhpParser\Node\Expr\Yield_ $n, int $startLine) : \ast\Node { - return astnode( + return new \ast\Node( \ast\AST_YIELD, 0, [ @@ -514,7 +534,7 @@ private static function _init_handle_map() : array { ); }, 'PhpParser\Node\Expr\YieldFrom' => function(PhpParser\Node\Expr\YieldFrom $n, int $startLine) : \ast\Node { - return astnode( + return new \ast\Node( \ast\AST_YIELD_FROM, 0, ['expr' => self::_phpparser_node_to_ast_node($n->expr)], @@ -527,9 +547,6 @@ private static function _init_handle_map() : array { $startLine ); }, - /** - * @suppress PhanDeprecatedProperty TODO: figure out alternative - */ 'PhpParser\Node\Name\FullyQualified' => function(PhpParser\Node\Name\FullyQualified $n, int $startLine) : \ast\Node { return self::_ast_node_name_fullyqualified( implode('\\', $n->parts), @@ -555,7 +572,7 @@ private static function _init_handle_map() : array { ); }, 'PhpParser\Node\Scalar\Encapsed' => function(PhpParser\Node\Scalar\Encapsed $n, int $startLine) : \ast\Node { - return astnode( + return new \ast\Node( \ast\AST_ENCAPS_LIST, 0, array_map(function(PhpParser\Node $n) { return self::_phpparser_node_to_ast_node($n); }, $n->parts), @@ -599,7 +616,7 @@ private static function _init_handle_map() : array { return self::_ast_magic_const(\ast\flags\MAGIC_TRAIT, $startLine); }, 'PhpParser\Node\Stmt\Break_' => function(PhpParser\Node\Stmt\Break_ $n, int $startLine) : \ast\Node { - return astnode(\ast\AST_BREAK, 0, ['depth' => isset($n->num) ? self::_phpparser_node_to_ast_node($n->num) : null], $startLine); + return new \ast\Node(\ast\AST_BREAK, 0, ['depth' => isset($n->num) ? self::_phpparser_node_to_ast_node($n->num) : null], $startLine); }, 'PhpParser\Node\Stmt\Catch_' => function(PhpParser\Node\Stmt\Catch_ $n, int $startLine) : \ast\Node { return self::_ast_stmt_catch( @@ -626,7 +643,7 @@ private static function _init_handle_map() : array { return self::_phpparser_class_const_to_ast_node($n, $startLine); }, 'PhpParser\Node\Stmt\ClassMethod' => function(PhpParser\Node\Stmt\ClassMethod $n, int $startLine) : \ast\Node { - return astdecl( + return self::_new_ast_decl( \ast\AST_METHOD, self::_phpparser_visibility_to_ast_visibility($n->flags) | ($n->byRef ? \ast\flags\RETURNS_REF : 0), [ @@ -638,18 +655,19 @@ private static function _init_handle_map() : array { $startLine, self::_extract_phpdoc_comment($n->getAttribute('comments')), $n->name, - $n->getAttribute('endLine') + $n->getAttribute('endLine'), + self::_next_decl_id() ); }, 'PhpParser\Node\Stmt\Const_' => function(PhpParser\Node\Stmt\Const_ $n, int $startLine) : \ast\Node { return self::_phpparser_const_to_ast_node($n, $startLine); }, 'PhpParser\Node\Stmt\Continue_' => function(PhpParser\Node\Stmt\Continue_ $n, int $startLine) : \ast\Node { - return astnode(\ast\AST_CONTINUE, 0, ['depth' => isset($n->num) ? self::_phpparser_node_to_ast_node($n->num) : null], $startLine); + return new \ast\Node(\ast\AST_CONTINUE, 0, ['depth' => isset($n->num) ? self::_phpparser_node_to_ast_node($n->num) : null], $startLine); }, 'PhpParser\Node\Stmt\Declare_' => function(PhpParser\Node\Stmt\Declare_ $n, int $startLine) : \ast\Node { return self::_ast_stmt_declare( - self::_phpparser_declare_list_to_ast_declares($n->declares, $startLine), + self::_phpparser_declare_list_to_ast_declares($n->declares, $startLine, self::_extract_phpdoc_comment($n->getAttribute('comments'))), is_array($n->stmts) ? self::_phpparser_stmtlist_to_ast_node($n->stmts, $startLine) : null, $startLine ); @@ -677,14 +695,14 @@ private static function _init_handle_map() : array { 'PhpParser\Node\Stmt\Foreach_' => function(PhpParser\Node\Stmt\Foreach_ $n, int $startLine) : \ast\Node { $value = self::_phpparser_node_to_ast_node($n->valueVar); if ($n->byRef) { - $value = astnode( + $value = new \ast\Node( \ast\AST_REF, 0, ['var' => $value], $value->lineno ?? $startLine ); } - return astnode( + return new \ast\Node( \ast\AST_FOREACH, 0, [ @@ -722,7 +740,7 @@ private static function _init_handle_map() : array { 'PhpParser\Node\Stmt\Global_' => function(PhpParser\Node\Stmt\Global_ $n, int $startLine) { $globalNodes = []; foreach ($n->vars as $var) { - $globalNodes[] = astnode(\ast\AST_GLOBAL, 0, ['var' => self::_phpparser_node_to_ast_node($var)], sl($var) ?: $startLine); + $globalNodes[] = new \ast\Node(\ast\AST_GLOBAL, 0, ['var' => self::_phpparser_node_to_ast_node($var)], sl($var) ?: $startLine); } return \count($globalNodes) === 1 ? $globalNodes[0] : $globalNodes; }, @@ -746,7 +764,7 @@ private static function _init_handle_map() : array { ); }, 'PhpParser\Node\Stmt\For_' => function(PhpParser\Node\Stmt\For_ $n, int $startLine) : \ast\Node { - return astnode( + return new \ast\Node( \ast\AST_FOR, 0, [ @@ -771,7 +789,7 @@ private static function _init_handle_map() : array { 'dumpComments' => true, 'dumpPositions' => true, ]); - return astnode( + return new \ast\Node( \ast\AST_NAMESPACE, 0, [ @@ -795,8 +813,8 @@ private static function _init_handle_map() : array { 'PhpParser\Node\Stmt\Static_' => function(PhpParser\Node\Stmt\Static_ $n, int $startLine) { $staticNodes = []; foreach ($n->vars as $var) { - $staticNodes[] = astnode(\ast\AST_STATIC, 0, [ - 'var' => astnode(\ast\AST_VAR, 0, ['name' => $var->name], sl($var) ?: $startLine), + $staticNodes[] = new \ast\Node(\ast\AST_STATIC, 0, [ + 'var' => new \ast\Node(\ast\AST_VAR, 0, ['name' => $var->name], sl($var) ?: $startLine), 'default' => $var->default !== null ? self::_phpparser_node_to_ast_node($var->default) : null, ], sl($var) ?: $startLine); } @@ -806,7 +824,7 @@ private static function _init_handle_map() : array { return self::_phpparser_switch_list_to_ast_switch($n); }, 'PhpParser\Node\Stmt\Throw_' => function(PhpParser\Node\Stmt\Throw_ $n, int $startLine) : \ast\Node { - return astnode( + return new \ast\Node( \ast\AST_THROW, 0, ['expr' => self::_phpparser_node_to_ast_node($n->expr)], @@ -831,11 +849,11 @@ private static function _init_handle_map() : array { $adaptations_inner = array_map(function(PhpParser\Node\Stmt\TraitUseAdaptation $n) : \ast\Node { return self::_phpparser_node_to_ast_node($n); }, $n->adaptations); - $adaptations = astnode(\ast\AST_TRAIT_ADAPTATIONS, 0, $adaptations_inner, $adaptations_inner[0]->lineno ?: $startLine); + $adaptations = new \ast\Node(\ast\AST_TRAIT_ADAPTATIONS, 0, $adaptations_inner, $adaptations_inner[0]->lineno ?: $startLine); } else { $adaptations = null; } - return astnode( + return new \ast\Node( \ast\AST_USE_TRAIT, 0, [ @@ -849,8 +867,8 @@ private static function _init_handle_map() : array { $old_class = $n->trait !== null ? self::_phpparser_node_to_ast_node($n->trait) : null; $flags = ($n->trait instanceof PhpParser\Node\Name\FullyQualified) ? \ast\flags\NAME_FQ : \ast\flags\NAME_NOT_FQ; // TODO: flags for visibility - return astnode(\ast\AST_TRAIT_ALIAS, self::_phpparser_visibility_to_ast_visibility($n->newModifier ?? 0, false), [ - 'method' => astnode(\ast\AST_METHOD_REFERENCE, 0, [ + return new \ast\Node(\ast\AST_TRAIT_ALIAS, self::_phpparser_visibility_to_ast_visibility($n->newModifier ?? 0, false), [ + 'method' => new \ast\Node(\ast\AST_METHOD_REFERENCE, 0, [ 'class' => $old_class, 'method' => $n->method, ], $startLine), @@ -861,9 +879,9 @@ private static function _init_handle_map() : array { $old_class = $n->trait !== null ? self::_phpparser_name_to_string($n->trait) : null; $flags = ($n->trait instanceof PhpParser\Node\Name\FullyQualified) ? \ast\flags\NAME_FQ : \ast\flags\NAME_NOT_FQ; // TODO: flags for visibility - return astnode(\ast\AST_TRAIT_PRECEDENCE, 0, [ - 'method' => astnode(\ast\AST_METHOD_REFERENCE, 0, [ - 'class' => astnode(\ast\AST_NAME, $flags, ['name' => $old_class], $startLine), + return new \ast\Node(\ast\AST_TRAIT_PRECEDENCE, 0, [ + 'method' => new \ast\Node(\ast\AST_METHOD_REFERENCE, 0, [ + 'class' => new \ast\Node(\ast\AST_NAME, $flags, ['name' => $old_class], $startLine), 'method' => $n->method, ], $startLine), 'insteadof' => self::_phpparser_name_list_to_ast_name_list($n->insteadof, $startLine), @@ -884,7 +902,7 @@ private static function _init_handle_map() : array { 'PhpParser\Node\Stmt\Unset_' => function(PhpParser\Node\Stmt\Unset_ $n, int $startLine) { $stmts = []; foreach ($n->vars as $var) { - $stmts[] = astnode(\ast\AST_UNSET, 0, ['var' => self::_phpparser_node_to_ast_node($var)], sl($var) ?: $startLine); + $stmts[] = new \ast\Node(\ast\AST_UNSET, 0, ['var' => self::_phpparser_node_to_ast_node($var)], sl($var) ?: $startLine); } return \count($stmts) === 1 ? $stmts[0] : $stmts; }, @@ -939,7 +957,7 @@ private static function _ast_stmt_catch($types, string $var, $stmts, int $lineno $node->flags = 0; $node->children = [ 'class' => $types, - 'var' => astnode(\ast\AST_VAR, 0, ['name' => $var], end($types->children)->lineno), // FIXME AST_VAR + 'var' => new \ast\Node(\ast\AST_VAR, 0, ['name' => $var], end($types->children)->lineno), // FIXME AST_VAR 'stmts' => $stmts, ]; return $node; @@ -963,11 +981,11 @@ private static function _phpparser_name_list_to_ast_name_list(array $types, int foreach ($types as $type) { $astTypes[] = self::_phpparser_node_to_ast_node($type); } - return astnode(\ast\AST_NAME_LIST, 0, $astTypes, $line); + return new \ast\Node(\ast\AST_NAME_LIST, 0, $astTypes, $line); } private static function _ast_node_while($cond, $stmts, int $startLine) : \ast\Node { - return astnode( + return new \ast\Node( \ast\AST_WHILE, 0, [ @@ -979,7 +997,7 @@ private static function _ast_node_while($cond, $stmts, int $startLine) : \ast\No } private static function _ast_node_do_while($cond, $stmts, int $startLine) : \ast\Node { - return astnode( + return new \ast\Node( \ast\AST_DO_WHILE, 0, [ @@ -998,27 +1016,22 @@ private static function _ast_node_assign($var, $expr, int $line, bool $ref) : ?\ return null; } } - $node = new \ast\Node(); - $node->kind = $ref ? \ast\AST_ASSIGN_REF : \ast\AST_ASSIGN; - $node->flags = 0; - $node->children = [ + return new \ast\Node($ref ? \ast\AST_ASSIGN_REF : \ast\AST_ASSIGN, 0, [ 'var' => $var, 'expr' => $expr, - ]; - $node->lineno = $line; - return $node; + ], $line); } private static function _ast_node_unary_op(int $flags, $expr, int $line) : \ast\Node { - return astnode(\ast\AST_UNARY_OP, $flags, ['expr' => $expr], $line); + return new \ast\Node(\ast\AST_UNARY_OP, $flags, ['expr' => $expr], $line); } private static function _ast_node_cast(int $flags, PhpParser\Node\Expr\Cast $n, int $line) : \ast\Node { - return astnode(\ast\AST_CAST, $flags, ['expr' => self::_phpparser_node_to_ast_node($n->expr)], sl($n) ?: $line); + return new \ast\Node(\ast\AST_CAST, $flags, ['expr' => self::_phpparser_node_to_ast_node($n->expr)], sl($n) ?: $line); } private static function _ast_node_eval($expr, int $line) : \ast\Node { - return astnode(\ast\AST_INCLUDE_OR_EVAL, \ast\flags\EXEC_EVAL, ['expr' => $expr], $line); + return new \ast\Node(\ast\AST_INCLUDE_OR_EVAL, \ast\flags\EXEC_EVAL, ['expr' => $expr], $line); } private static function _phpparser_include_flags_to_ast_include_flags(int $type) : int { @@ -1037,7 +1050,7 @@ private static function _phpparser_include_flags_to_ast_include_flags(int $type) } private static function _ast_node_include($expr, int $line, int $type) : \ast\Node { $flags = self::_phpparser_include_flags_to_ast_include_flags($type); - return astnode(\ast\AST_INCLUDE_OR_EVAL, $flags, ['expr' => $expr], $line); + return new \ast\Node(\ast\AST_INCLUDE_OR_EVAL, $flags, ['expr' => $expr], $line); } /** @@ -1063,7 +1076,18 @@ private static function _phpparser_type_to_ast_node($type, int $line) { case 'array': $flags = \ast\flags\TYPE_ARRAY; break; case 'object': - $flags = \ast\flags\TYPE_OBJECT; break; + if (self::$ast_version >= 45) { + $flags = \ast\flags\TYPE_OBJECT; break; + } else { + return new \ast\Node( + \ast\AST_NAME, + substr($type, 0, 1) === '\\' ? \ast\flags\NAME_FQ : \ast\flags\NAME_NOT_FQ, // FIXME wrong. + [ + 'name' => $type, + ], + $line + ); + } case 'callable': $flags = \ast\flags\TYPE_CALLABLE; break; case 'void': @@ -1071,14 +1095,14 @@ private static function _phpparser_type_to_ast_node($type, int $line) { case 'iterable': $flags = \ast\flags\TYPE_ITERABLE; break; default: - $node = new \ast\Node(); - $node->kind = \ast\AST_NAME; - $node->flags = substr($type, 0, 1) === '\\' ? \ast\flags\NAME_FQ : \ast\flags\NAME_NOT_FQ; // FIXME wrong. - $node->lineno = $line; - $node->children = [ - 'name' => $type, - ]; - return $node; + return new \ast\Node( + \ast\AST_NAME, + substr($type, 0, 1) === '\\' ? \ast\flags\NAME_FQ : \ast\flags\NAME_NOT_FQ, // FIXME wrong. + [ + 'name' => $type, + ], + $line + ); } $node = new \ast\Node(); $node->kind = \ast\AST_TYPE; @@ -1119,11 +1143,11 @@ private static function _ast_node_nullable_type(\ast\Node $type, int $line) { } private static function _ast_node_name(string $name, int $line) : \ast\Node { - return astnode(\ast\AST_NAME, \ast\flags\NAME_NOT_FQ, ['name' => $name], $line); + return new \ast\Node(\ast\AST_NAME, \ast\flags\NAME_NOT_FQ, ['name' => $name], $line); } private static function _ast_node_name_fullyqualified(string $name, int $line) : \ast\Node { - return astnode(\ast\AST_NAME, \ast\flags\NAME_FQ, ['name' => $name], $line); + return new \ast\Node(\ast\AST_NAME, \ast\flags\NAME_FQ, ['name' => $name], $line); } private static function _ast_node_variable($expr, int $line) : ?\ast\Node { @@ -1147,7 +1171,7 @@ private static function _ast_node_variable($expr, int $line) : ?\ast\Node { } private static function _ast_magic_const(int $flags, int $line) { - return astnode(\ast\AST_MAGIC_CONST, $flags, [], $line); + return new \ast\Node(\ast\AST_MAGIC_CONST, $flags, [], $line); } private static function _phpparser_params_to_ast_params(array $parserParams, int $line) : \ast\Node { @@ -1194,9 +1218,9 @@ private static function _phpparser_closure_uses_to_ast_closure_uses( } $astUses = []; foreach ($uses as $use) { - $astUses[] = astnode(\ast\AST_CLOSURE_VAR, $use->byRef ? 1 : 0, ['name' => $use->var], $use->getAttribute('startLine')); + $astUses[] = new \ast\Node(\ast\AST_CLOSURE_VAR, $use->byRef ? 1 : 0, ['name' => $use->var], $use->getAttribute('startLine')); } - return astnode(\ast\AST_CLOSURE_USES, 0, $astUses, $astUses[0]->lineno ?? $line); + return new \ast\Node(\ast\AST_CLOSURE_USES, 0, $astUses, $astUses[0]->lineno ?? $line); } @@ -1210,21 +1234,22 @@ private static function _ast_decl_closure( int $startLine, int $endLine, ?string $docComment - ) : \ast\Node\Decl { - $node = new \ast\Node\Decl; - $node->kind = \ast\AST_CLOSURE; - $node->flags = ($byRef ? \ast\flags\RETURNS_REF : 0) | ($static ? \ast\flags\MODIFIER_STATIC : 0); - $node->lineno = $startLine; - $node->endLineno = $endLine; - if ($docComment) { $node->docComment = $docComment; } - $node->children = [ - 'params' => $params, - 'uses' => $uses, - 'stmts' => $stmts, - 'returnType' => $returnType, - ]; - $node->name = '{closure}'; - return $node; + ) : \ast\Node { + return self::_new_ast_decl( + \ast\AST_CLOSURE, + ($byRef ? \ast\flags\RETURNS_REF : 0) | ($static ? \ast\flags\MODIFIER_STATIC : 0), + [ + 'params' => $params, + 'uses' => $uses, + 'stmts' => $stmts, + 'returnType' => $returnType, + ], + $startLine, + $docComment, + '{closure}', + $endLine, + self::_next_decl_id() + ); } /** @@ -1240,21 +1265,22 @@ private static function _ast_decl_function( int $line, int $endLine, ?string $docComment - ) : \ast\Node\Decl { - $node = new \ast\Node\Decl; - $node->kind = \ast\AST_FUNC_DECL; - $node->flags = 0; - $node->lineno = $line; - $node->endLineno = $endLine; - $node->children = [ - 'params' => $params, - 'uses' => $uses, - 'stmts' => $stmts, - 'returnType' => $returnType, - ]; - $node->name = $name; - $node->docComment = $docComment; - return $node; + ) : \ast\Node { + return self::_new_ast_decl( + \ast\AST_FUNC_DECL, + 0, + [ + 'params' => $params, + 'uses' => $uses, + 'stmts' => $stmts, + 'returnType' => $returnType, + ], + $line, + $docComment, + $name, + $endLine, + self::_next_decl_id() + ); } private static function _phpparser_class_flags_to_ast_class_flags(int $flags) { @@ -1287,7 +1313,7 @@ private static function _ast_stmt_class( int $line, int $endLine, ?string $docComment - ) : \ast\Node\Decl { + ) : \ast\Node { if ($name === null) { $flags |= \ast\flags\CLASS_ANONYMOUS; } @@ -1302,12 +1328,12 @@ private static function _ast_stmt_class( foreach ($implements as $implement) { $ast_implements_inner[] = self::_phpparser_node_to_ast_node($implement); } - $ast_implements = astnode(\ast\AST_NAME_LIST, 0, $ast_implements_inner, $ast_implements_inner[0]->lineno); + $ast_implements = new \ast\Node(\ast\AST_NAME_LIST, 0, $ast_implements_inner, $ast_implements_inner[0]->lineno); } else { $ast_implements = null; } - return astdecl( + return self::_new_ast_decl( \ast\AST_CLASS, $flags, [ @@ -1318,7 +1344,8 @@ private static function _ast_stmt_class( $line, $docComment, $name, - $endLine + $endLine, + self::_next_decl_id() ); } @@ -1412,7 +1439,7 @@ private static function _ast_stmt_return($expr, int $line) : \ast\Node { } private static function _ast_if_elem($cond, $stmts, int $line) : \ast\Node { - return astnode(\ast\AST_IF_ELEM, 0, ['cond' => $cond, 'stmts' => $stmts], $line); + return new \ast\Node(\ast\AST_IF_ELEM, 0, ['cond' => $cond, 'stmts' => $stmts], $line); } private static function _phpparser_switch_list_to_ast_switch(PhpParser\Node\Stmt\Switch_ $node) { @@ -1420,7 +1447,7 @@ private static function _phpparser_switch_list_to_ast_switch(PhpParser\Node\Stmt $nodeLine = sl($node) ?? 0; foreach ($node->cases as $case) { $caseLine = sl($case); - $stmts[] = astnode( + $stmts[] = new \ast\Node( \ast\AST_SWITCH_CASE, 0, [ @@ -1430,9 +1457,9 @@ private static function _phpparser_switch_list_to_ast_switch(PhpParser\Node\Stmt $caseLine ?? $nodeLine ); } - return astnode(\ast\AST_SWITCH, 0, [ + return new \ast\Node(\ast\AST_SWITCH, 0, [ 'cond' => self::_phpparser_node_to_ast_node($node->cond), - 'stmts' => astnode(\ast\AST_SWITCH_LIST, 0, $stmts, $stmts[0]->lineno ?? $nodeLine), + 'stmts' => new \ast\Node(\ast\AST_SWITCH_LIST, 0, $stmts, $stmts[0]->lineno ?? $nodeLine), ], $nodeLine); } @@ -1464,7 +1491,7 @@ private static function _phpparser_if_stmt_to_ast_if_stmt(PhpParser\Node $node) $parserElseLine ); } - return astnode(\ast\AST_IF, 0, $ifElems, $startLine); + return new \ast\Node(\ast\AST_IF, 0, $ifElems, $startLine); } @@ -1472,7 +1499,7 @@ private static function _phpparser_if_stmt_to_ast_if_stmt(PhpParser\Node $node) * @suppress PhanUndeclaredProperty */ private static function _ast_node_assignop(int $flags, PhpParser\Node $node, int $startLine) { - return astnode( + return new \ast\Node( \ast\AST_ASSIGN_OP, $flags, [ @@ -1487,7 +1514,7 @@ private static function _ast_node_assignop(int $flags, PhpParser\Node $node, int * @suppress PhanUndeclaredProperty */ private static function _ast_node_binaryop(int $flags, PhpParser\Node $n, int $startLine) { - return astnode( + return new \ast\Node( \ast\AST_BINARY_OP, $flags, self::_phpparser_nodes_to_left_right_children($n->left, $n->right), @@ -1510,7 +1537,7 @@ private static function _phpparser_propelem_to_ast_propelem(PhpParser\Node\Stmt\ $startLine = $n->getAttribute('startLine'); - return astnode(\ast\AST_PROP_ELEM, 0, $children, $startLine, self::_extract_phpdoc_comment($n->getAttribute('comments') ?? $docComment)); + return self::_new_ast_node(\ast\AST_PROP_ELEM, 0, $children, $startLine, self::_extract_phpdoc_comment($n->getAttribute('comments') ?? $docComment)); } private static function _phpparser_constelem_to_ast_constelem(PhpParser\Node\Const_ $n, ?string $docComment) : \ast\Node{ @@ -1521,7 +1548,7 @@ private static function _phpparser_constelem_to_ast_constelem(PhpParser\Node\Con $startLine = $n->getAttribute('startLine'); - return astnode(\ast\AST_CONST_ELEM, 0, $children, $startLine, self::_extract_phpdoc_comment($n->getAttribute('comments') ?? $docComment)); + return self::_new_ast_node(\ast\AST_CONST_ELEM, 0, $children, $startLine, self::_extract_phpdoc_comment($n->getAttribute('comments') ?? $docComment)); } private static function _phpparser_visibility_to_ast_visibility(int $visibility, bool $automatically_add_public = true) : int { @@ -1559,7 +1586,7 @@ private static function _phpparser_property_to_ast_node(PhpParser\Node $n, int $ } $flags = self::_phpparser_visibility_to_ast_visibility($n->flags); - return astnode(\ast\AST_PROP_DECL, $flags, $propElems, $propElems[0]->lineno ?: $startLine); + return new \ast\Node(\ast\AST_PROP_DECL, $flags, $propElems, $propElems[0]->lineno ?: $startLine); } private static function _phpparser_class_const_to_ast_node(PhpParser\Node\Stmt\ClassConst $n, int $startLine) : \ast\Node { @@ -1570,7 +1597,7 @@ private static function _phpparser_class_const_to_ast_node(PhpParser\Node\Stmt\C } $flags = self::_phpparser_visibility_to_ast_visibility($n->flags); - return astnode(\ast\AST_CLASS_CONST_DECL, $flags, $constElems, $constElems[0]->lineno ?: $startLine); + return new \ast\Node(\ast\AST_CLASS_CONST_DECL, $flags, $constElems, $constElems[0]->lineno ?: $startLine); } private static function _phpparser_const_to_ast_node(PhpParser\Node\Stmt\Const_ $n, int $startLine) : \ast\Node { @@ -1580,19 +1607,31 @@ private static function _phpparser_const_to_ast_node(PhpParser\Node\Stmt\Const_ $constElems[] = self::_phpparser_constelem_to_ast_constelem($prop, $i === 0 ? $docComment : null); } - return astnode(\ast\AST_CONST_DECL, 0, $constElems, $constElems[0]->lineno ?: $startLine); + return new \ast\Node(\ast\AST_CONST_DECL, 0, $constElems, $constElems[0]->lineno ?: $startLine); } - private static function _phpparser_declare_list_to_ast_declares(array $declares, int $startLine) : \ast\Node { + /** + * @suppress PhanUndeclaredProperty + */ + private static function _phpparser_declare_list_to_ast_declares(array $declares, int $startLine, string $firstDocComment = null) : \ast\Node { $astDeclareElements = []; foreach ($declares as $declare) { $children = [ 'name' => $declare->key, 'value' => self::_phpparser_node_to_ast_node($declare->value), ]; - $astDeclareElements[] = astnode(\ast\AST_CONST_ELEM, 0, $children, $declare->getAttribute('startLine')); + $docComment = self::_extract_phpdoc_comment($declare->getAttribute('comments')) ?? $firstDocComment; + $firstDocComment = null; + if (self::$ast_version >= 50) { + $children['docComment'] = $docComment; + } + $node = new \ast\Node(\ast\AST_CONST_ELEM, 0, $children, $declare->getAttribute('startLine')); + if (self::$ast_version < 50 && is_string($docComment)) { + $node->docComment = $docComment; + } + $astDeclareElements[] = $node; } - return astnode(\ast\AST_CONST_DECL, 0, $astDeclareElements, $startLine); + return new \ast\Node(\ast\AST_CONST_DECL, 0, $astDeclareElements, $startLine); } @@ -1601,7 +1640,7 @@ private static function _ast_stmt_declare(\ast\Node $declares, ?\ast\Node $stmts 'declares' => $declares, 'stmts' => $stmts, ]; - return astnode(\ast\AST_DECLARE, 0, $children, $startLine); + return new \ast\Node(\ast\AST_DECLARE, 0, $children, $startLine); } private static function _ast_node_call($expr, $args, int $startLine) : \ast\Node{ @@ -1609,13 +1648,13 @@ private static function _ast_node_call($expr, $args, int $startLine) : \ast\Node if (substr($expr, 0, 1) === '\\') { $expr = substr($expr, 1); } - $expr = astnode(\ast\AST_NAME, \ast\flags\NAME_FQ, ['name' => $expr], $startLine); + $expr = new \ast\Node(\ast\AST_NAME, \ast\flags\NAME_FQ, ['name' => $expr], $startLine); } - return astnode(\ast\AST_CALL, 0, ['expr' => $expr, 'args' => $args], $startLine); + return new \ast\Node(\ast\AST_CALL, 0, ['expr' => $expr, 'args' => $args], $startLine); } private static function _ast_node_method_call($expr, $method, \ast\Node $args, int $startLine) : \ast\Node { - return astnode(\ast\AST_METHOD_CALL, 0, ['expr' => $expr, 'method' => $method, 'args' => $args], $startLine); + return new \ast\Node(\ast\AST_METHOD_CALL, 0, ['expr' => $expr, 'method' => $method, 'args' => $args], $startLine); } private static function _ast_node_static_call($class, $method, \ast\Node $args, int $startLine) : \ast\Node { @@ -1624,9 +1663,9 @@ private static function _ast_node_static_call($class, $method, \ast\Node $args, if (substr($class, 0, 1) === '\\') { $expr = substr($class, 1); } - $class = astnode(\ast\AST_NAME, \ast\flags\NAME_FQ, ['name' => $class], $startLine); + $class = new \ast\Node(\ast\AST_NAME, \ast\flags\NAME_FQ, ['name' => $class], $startLine); } - return astnode(\ast\AST_STATIC_CALL, 0, ['class' => $class, 'method' => $method, 'args' => $args], $startLine); + return new \ast\Node(\ast\AST_STATIC_CALL, 0, ['class' => $class, 'method' => $method, 'args' => $args], $startLine); } private static function _extract_phpdoc_comment($comments) : ?string { @@ -1657,13 +1696,13 @@ private static function _phpparser_list_to_ast_list(PhpParser\Node\Expr\List_ $n if ($item === null) { $astItems[] = null; } else { - $astItems[] = astnode(\ast\AST_ARRAY_ELEM, 0, [ + $astItems[] = new \ast\Node(\ast\AST_ARRAY_ELEM, 0, [ 'value' => self::_phpparser_node_to_ast_node($item->value), 'key' => $item->key !== null ? self::_phpparser_node_to_ast_node($item->key) : null, ], $item->getAttribute('startLine')); } } - return astnode(\ast\AST_ARRAY, \ast\flags\ARRAY_SYNTAX_LIST, $astItems, $startLine); + return new \ast\Node(\ast\AST_ARRAY, \ast\flags\ARRAY_SYNTAX_LIST, $astItems, $startLine); } private static function _phpparser_array_to_ast_array(PhpParser\Node\Expr\Array_ $n, int $startLine) : \ast\Node { @@ -1672,13 +1711,13 @@ private static function _phpparser_array_to_ast_array(PhpParser\Node\Expr\Array_ if ($item === null) { $astItems[] = null; } else { - $astItems[] = astnode(\ast\AST_ARRAY_ELEM, 0, [ + $astItems[] = new \ast\Node(\ast\AST_ARRAY_ELEM, 0, [ 'value' => self::_phpparser_node_to_ast_node($item->value), 'key' => $item->key !== null ? self::_phpparser_node_to_ast_node($item->key) : null, ], $item->getAttribute('startLine')); } } - return astnode(\ast\AST_ARRAY, \ast\flags\ARRAY_SYNTAX_SHORT, $astItems, $startLine); + return new \ast\Node(\ast\AST_ARRAY, \ast\flags\ARRAY_SYNTAX_SHORT, $astItems, $startLine); } private static function _phpparser_propertyfetch_to_ast_prop(PhpParser\Node\Expr\PropertyFetch $n, int $startLine) : ?\ast\Node { @@ -1693,7 +1732,7 @@ private static function _phpparser_propertyfetch_to_ast_prop(PhpParser\Node\Expr return null; } } - return astnode(\ast\AST_PROP, 0, [ + return new \ast\Node(\ast\AST_PROP, 0, [ 'expr' => self::_phpparser_node_to_ast_node($n->var), 'prop' => is_object($name) ? : $name, ], $startLine); @@ -1711,58 +1750,85 @@ private static function _phpparser_classconstfetch_to_ast_classconstfetch(PhpPar return null; } } - return astnode(\ast\AST_CLASS_CONST, 0, [ + return new \ast\Node(\ast\AST_CLASS_CONST, 0, [ 'class' => self::_phpparser_node_to_ast_node($n->class), 'const' => $name, ], $startLine); } - /** - * @suppress PhanDeprecatedProperty TODO: figure out alternative - */ private static function _phpparser_name_to_string(PhpParser\Node\Name $name) : string { return implode('\\', $name->parts); } -} -/** - * @suppress PhanTypeMismatchProperty https://github.com/etsy/phan/issues/609 - * @suppress PhanUndeclaredProperty - docComment really exists. - * NOTE: this may be removed in the future. - * - * Phan was used while developing this. The asserts can be cleaned up in the future. - */ -function astnode(int $kind, int $flags, ?array $children, int $lineno, ?string $docComment = null) : \ast\Node { - $node = new \ast\Node(); - $node->kind = $kind; - $node->flags = $flags; - $node->lineno = $lineno; - $node->children = $children; - if (\is_string($docComment)) { - $node->docComment = $docComment; - } - return $node; -} + const _NODES_WITH_NULL_DOC_COMMENT = [ + \ast\AST_CONST_ELEM => true, + \ast\AST_PROP_ELEM => true, + ]; + + /** + * @suppress PhanTypeMismatchProperty https://github.com/etsy/phan/issues/609 + * @suppress PhanUndeclaredProperty - docComment really exists. + * NOTE: this may be removed in the future. + * + * Phan was used while developing this. The asserts can be cleaned up in the future. + * + * NOTE: in AST version <= 40, may creates docComment as a property, but in version >= 45, adds it to $children + * + * @return \ast\Node + */ + private static function _new_ast_node(int $kind, int $flags, array $children, int $lineno, string $docComment = null) : \ast\Node { + if (self::$ast_version >= 45) { + if (is_string($docComment) || array_key_exists($kind, self::_NODES_WITH_NULL_DOC_COMMENT)) { + $children['docComment'] = $docComment; + } + return new \ast\Node($kind, $flags, $children, $lineno); + } + $node = new \ast\Node($kind, $flags, $children, $lineno); + if (is_string($docComment)) { + $node->docComment = $docComment; + } + return $node; + } + + /** + * @suppress PhanTypeMismatchProperty https://github.com/etsy/phan/issues/609 + * @suppress PhanUndeclaredProperty - docComment really exists. + * NOTE: this may be removed in the future. + * + * Phan was used while developing this. The asserts can be cleaned up in the future. + * + * NOTE: in AST version >= 45, this returns Node, but in version <=40, this returns Decl + * + * @return \ast\Node|\ast\Node\Decl + */ + private static function _new_ast_decl(int $kind, int $flags, array $children, int $lineno, string $docComment = null, string $name = null, int $endLineno = 0, int $declId = -1) : \ast\Node { + if (self::$ast_version >= 45) { + $children45 = []; + $children45['name'] = $name; + $children45['docComment'] = $docComment; + $children45 += $children; + if ($declId >= 0 && self::$ast_version >= 50) { + $children45['__declId'] = $declId; + } + $node = new \ast\Node($kind, $flags, $children45, $lineno); + if (is_int($endLineno)) { + $node->endLineno = $endLineno; + } + return $node; + } + $decl = new \ast\Node\Decl($kind, $flags, $children, $lineno); + if (\is_string($docComment)) { + $decl->docComment = $docComment; + } + $decl->name = $name; + $decl->endLineno = $endLineno; + return $decl; + } + + private static function _next_decl_id() : int { + return self::$decl_id++; + } -/** - * @suppress PhanTypeMismatchProperty https://github.com/etsy/phan/issues/609 - * @suppress PhanUndeclaredProperty - docComment really exists. - * NOTE: this may be removed in the future. - * - * Phan was used while developing this. The asserts can be cleaned up in the future. - */ -function astdecl(int $kind, int $flags, ?array $children, int $lineno, string $docComment = null, string $name = null, int $endLineno = 0) : \ast\Node\Decl { - $node = new \ast\Node\Decl(); - $node->kind = $kind; - $node->flags = $flags; - $node->lineno = $lineno; - $node->children = $children; - if (\is_string($docComment)) { - $node->docComment = $docComment; - } - $node->name = $name; - $node->endLineno = $endLineno; - return $node; } function sl($node) : ?int { diff --git a/src/ASTConverter/Bootstrap.php b/src/ASTConverter/Bootstrap.php index a968e25..f494627 100644 --- a/src/ASTConverter/Bootstrap.php +++ b/src/ASTConverter/Bootstrap.php @@ -13,7 +13,6 @@ __DIR__.'/../../../../../vendor/autoload.php', // autoloader is in parent project ] as $file) { if (file_exists($file)) { - echo "Found the autoloader at $file\n"; require_once($file); break; } diff --git a/src/util.php b/src/util.php index 5b75f32..d3a177e 100644 --- a/src/util.php +++ b/src/util.php @@ -1,4 +1,5 @@ kind); @@ -179,13 +183,20 @@ function ast_dump($ast, int $options = 0) : string { if (ast\kind_uses_flags($ast->kind)) { $result .= "\n flags: " . format_flags($ast->kind, $ast->flags); } - if (isset($ast->name)) { - $result .= "\n name: $ast->name"; + $name = $ast->name ?? $ast->children['name'] ?? null; + if (is_string($name)) { + $result .= "\n name: $name"; } - if (isset($ast->docComment)) { - $result .= "\n docComment: $ast->docComment"; + $docComment = $ast->docComment ?? $ast->children['docComment'] ?? null; + if ($docComment !== null) { + $result .= "\n docComment: $docComment"; } foreach ($ast->children as $i => $child) { + if ($i === 'name' && $child === $name) { + continue; + } else if ($i === 'docComment' && $child === $docComment) { + continue; + } $result .= "\n $i: " . str_replace("\n", "\n ", ast_dump($child, $options)); } return $result; diff --git a/test_files/src/stmt_list.php b/test_files/src/stmt_list.php index aa43e63..1de1c6e 100644 --- a/test_files/src/stmt_list.php +++ b/test_files/src/stmt_list.php @@ -4,4 +4,5 @@ while ($a) { $b; } declare(ticks=1); +/** Doc comment on decl */ declare(ticks=1) {} diff --git a/test_files/src/type_hints.php b/test_files/src/type_hints.php index 67c8e82..ef6a057 100644 --- a/test_files/src/type_hints.php +++ b/test_files/src/type_hints.php @@ -1,2 +1,2 @@ _scanSourceDirForPHP($sourceDir); + $supports40 = self::hasNativeASTSupport(40); + $supports50 = self::hasNativeASTSupport(50); + if (!($supports40 || $supports50)) { + throw new RuntimeException("Neither AST version 40 nor 50 are natively supported"); + } foreach ($files as $file) { - $tests[] = [$sourceDir . '/' . $file]; + $path = $sourceDir . '/' . $file; + if ($supports40) { + $tests[] = [$path, 40]; + } + if ($supports50) { + $tests[] = [$path, 50]; + } } return $tests; } @@ -52,15 +78,15 @@ private static function normalizeOriginalAST($node) { } /** @dataProvider astValidFileExampleProvider */ - public function testFallbackFromParser(string $fileName) { + public function testFallbackFromParser(string $fileName, int $astVersion) { $contents = file_get_contents($fileName); if ($contents === false) { $this->fail("Failed to read $fileName"); } - $ast = \ast\parse_code($contents, ASTConverter::AST_VERSION); + $ast = \ast\parse_code($contents, $astVersion); self::normalizeOriginalAST($ast); $this->assertInstanceOf('\ast\Node', $ast, 'Examples must be syntactically valid PHP parseable by php-ast'); - $fallback_ast = \ASTConverter\ASTConverter::ast_parse_code_fallback($contents, ASTConverter::AST_VERSION); + $fallback_ast = \ASTConverter\ASTConverter::ast_parse_code_fallback($contents, $astVersion); $this->assertInstanceOf('\ast\Node', $fallback_ast, 'The fallback must also return a tree of php-ast nodes'); $fallbackASTRepr = var_export($fallback_ast, true); $originalASTRepr = var_export($ast, true); diff --git a/tests/ASTConverter/ErrorTolerantConversionTest.php b/tests/ASTConverter/ErrorTolerantConversionTest.php index 52e8644..e663f7c 100644 --- a/tests/ASTConverter/ErrorTolerantConversionTest.php +++ b/tests/ASTConverter/ErrorTolerantConversionTest.php @@ -177,11 +177,25 @@ function foo() { } private function _testFallbackFromParser(string $incompleteContents, string $validContents) { - $ast = \ast\parse_code($validContents, ASTConverter::AST_VERSION); + $supports40 = ConversionTest::hasNativeASTSupport(40); + $supports50 = ConversionTest::hasNativeASTSupport(50); + if (!($supports40 || $supports50)) { + $this->fail('No supported AST versions to test'); + } + if ($supports40) { + $this->_testFallbackFromParserForASTVersion($incompleteContents, $validContents, 40); + } + if ($supports50) { + $this->_testFallbackFromParserForASTVersion($incompleteContents, $validContents, 50); + } + } + + private function _testFallbackFromParserForASTVersion(string $incompleteContents, string $validContents, int $astVersion) { + $ast = \ast\parse_code($validContents, $astVersion); $this->assertInstanceOf('\ast\Node', $ast, 'Examples(for validContents) must be syntactically valid PHP parseable by php-ast'); $errors = []; $phpParserNode = ASTConverter::phpparser_parse($incompleteContents, true, $errors); - $fallback_ast = ASTConverter::phpparser_to_phpast($phpParserNode, ASTConverter::AST_VERSION); + $fallback_ast = ASTConverter::phpparser_to_phpast($phpParserNode, $astVersion); $this->assertInstanceOf('\ast\Node', $fallback_ast, 'The fallback must also return a tree of php-ast nodes'); $fallbackASTRepr = var_export($fallback_ast, true); $originalASTRepr = var_export($ast, true); @@ -197,7 +211,7 @@ private function _testFallbackFromParser(string $incompleteContents, string $val } catch (\PhpParser\Error $e) { } $original_ast_dump = \ast_dump($ast); - $parser_export = var_export($phpParserNode, true); + // $parser_export = var_export($phpParserNode, true); $this->assertSame($originalASTRepr, $fallbackASTRepr, <<> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini -} - -# Ensure the build directory exists -[[ -d "build" ]] || mkdir build - -# Ensure that the PHP version hasn't changed under us. If it has, we'll have to -# rebuild the extension. -if [[ -e "build/phpversion.txt" ]]; then - if ! diff -q build/phpversion.txt <(php -r "echo PHP_VERSION_ID;"); then - # Something has changed, so nuke the build/ast directory if it exists. - echo "New version of PHP detected. Removing build/ast so we can do a fresh build." - rm -rf build/ast - fi -fi - -# Ensure that we have a copy of the ast extension source code. -if [[ ! -e "build/ast/config.m4" ]]; then - # If build/ast exists, but build/ast/config.m4 doesn't, nuke it and start over. - [[ ! -d "build/ast" ]] || rm -rf build/ast - git clone --depth 1 https://github.com/nikic/php-ast.git build/ast -fi - -# Install the ast extension -pushd ./build/ast - # If we don't have ast.so, we have to build it. - if [[ ! -e "modules/ast.so" ]]; then - echo "No cached extension found. Building..." - build - else - # If there are new commits, we need to rebuild the extension. - git fetch origin master - newCommits=$(git rev-list HEAD...origin/master --count) - if [[ "$newCommits" != "0" ]]; then - echo "New commits found upstream. Updating and rebuilding..." - git pull origin master - cleanBuild - else - echo "Using cached extension." - fi - fi - - # No matter what, we still have to move the .so into place and enable it. - install -popd - -# Note the PHP version for later builds. -php -r "echo PHP_VERSION_ID;" > build/phpversion.txt +# This has been tested with 0.1.5 but not any other versions +pecl install -f ast-0.1.5 # Disable xdebug, since we aren't currently gathering code coverage data and # having xdebug slows down Composer a bit.