diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md index 135a41a6..004bceed 100644 --- a/UPGRADE-5.0.md +++ b/UPGRADE-5.0.md @@ -22,6 +22,36 @@ However, some aspects of PHP 5 parsing are no longer supported: * Declarations of the form `global $$var[0]` are not supported in PHP 7 and will cause a parse error. In error recovery mode, it is possible to continue parsing after such declarations. * The PHP 7 parser will accept many constructs that are not valid in PHP 5. However, this was also true of the dedicated PHP 5 parser. +### Changes to the parser factory + +The `ParserFactory::create()` method is deprecated in favor of three new methods that provide more fine-grained control over the PHP version being targeted: + + * `createForNewestSupportedVersion()`: Use this if you don't know the PHP version of the code you're parsing. It's better to assume a too new version than a too old one. + * `createForHostVersion()`: Use this if you're parsing code for the PHP version you're running on. + * `createForVersion()`: Use this if you know the PHP version of the code you want to parse. + +In all cases, the PHP version is a fairly weak hint that is only used on a best-effort basis. The parser will usually accept code for newer versions if it does not have any backwards-compatibility implications. + +For example, if you specify version `"8.0"`, then `class ReadOnly {}` is treated as a valid class declaration, while using `public readonly int $prop` will lead to a parse error. However, `final public const X = Y;` will be accepted in both cases. + +``` +use PhpParser\ParserFactory; +$factory = new ParserFactory; + +# Before +$parser = $factory->create(ParserFactory::PREFER_PHP7); + +# After (this is roughly equivalent to PREFER_PHP7 behavior) +$parser = $factory->createForNewestSupportedVersion(); +# Or +$parser = $factory->createForHostVersion(); + +# Before +$parser = $factory->create(ParserFactory::ONLY_PHP5); +# After (supported on a best-effort basis) +$parser = $factory->createForVersion("5.6"); +``` + ### Changes to the default pretty printer A number of changes to the standard pretty printer have been made, to make it match contemporary coding style conventions (and in particular PSR-12). Options to restore the previous behavior are not provided, but it is possible to override the formatting methods (such as `pStmt_ClassMethod`) with your preferred formatting. diff --git a/lib/PhpParser/ParserFactory.php b/lib/PhpParser/ParserFactory.php index dae38d1b..788a944d 100644 --- a/lib/PhpParser/ParserFactory.php +++ b/lib/PhpParser/ParserFactory.php @@ -2,6 +2,8 @@ namespace PhpParser; +use PhpParser\Parser\Php7; + class ParserFactory { const PREFER_PHP7 = 1; @@ -15,8 +17,10 @@ class ParserFactory * @param array $parserOptions Parser options. See ParserAbstract::__construct() argument * * @return Parser The parser instance + * + * @deprecated Use createForVersion(), createForNewestSupportedVersion() or createForHostVersion() instead. */ - public function create(int $kind, Lexer $lexer = null, array $parserOptions = []) : Parser { + public function create(int $kind, Lexer $lexer = null, array $parserOptions = []): Parser { if (null === $lexer) { $lexer = new Lexer\Emulative(); } @@ -30,4 +34,50 @@ class ParserFactory ); } } + + /** + * Create a parser targeting the given version on a best-effort basis. The parser will generally + * accept code for the newest supported version, but will try to accommodate code that becomes + * invalid in newer versions or changes in interpretation. + */ + public function createForVersion(string $version, array $lexerOptions = [], array $parserOptions = []): Parser { + if ($version === $this->getHostVersion()) { + $lexer = new Lexer($lexerOptions); + } else { + $lexer = new Lexer\Emulative($lexerOptions + ['phpVersion' => $version]); + } + return new Php7($lexer, $parserOptions + ['phpVersion' => $version]); + } + + /** + * Create a parser targeting the newest version supported by this library. Code for older + * versions will be accepted if there have been no relevant backwards-compatibility breaks in + * PHP. + */ + public function createForNewestSupportedVersion(array $lexerOptions = [], array $parserOptions = []): Parser { + return $this->createForVersion($this->getNewestSupportedVersion(), $lexerOptions, $parserOptions); + } + + /** + * Create a parser targeting the host PHP version, that is the PHP version we're currently + * running on. This parser will not use any token emulation. + */ + public function createForHostVersion(array $lexerOptions = [], array $parserOptions = []): Parser { + return $this->createForVersion($this->getHostVersion(), $lexerOptions, $parserOptions); + } + + /** + * Get the newest PHP version supported by this library. Support for this version may be partial, + * if it is still under development. + */ + public function getNewestSupportedVersion(): string { + return '8.2'; + } + + /** + * Get the host PHP version, that is the PHP version we're currently running on. + */ + public function getHostVersion(): string { + return \PHP_MAJOR_VERSION . '.' . \PHP_MINOR_VERSION; + } } diff --git a/test/PhpParser/CodeParsingTest.php b/test/PhpParser/CodeParsingTest.php index abe5b7f6..63effd98 100644 --- a/test/PhpParser/CodeParsingTest.php +++ b/test/PhpParser/CodeParsingTest.php @@ -38,15 +38,15 @@ class CodeParsingTest extends CodeTestAbstract } public function createParser(?string $version): Parser { - $lexer = new Lexer\Emulative(['usedAttributes' => [ - 'startLine', 'endLine', - 'startFilePos', 'endFilePos', - 'startTokenPos', 'endTokenPos', - 'comments' - ]]); - return new Parser\Php7($lexer, [ - 'phpVersion' => $version, - ]); + $factory = new ParserFactory(); + return $factory->createForVersion( + $version ?? $factory->getNewestSupportedVersion(), + ['usedAttributes' => [ + 'startLine', 'endLine', + 'startFilePos', 'endFilePos', + 'startTokenPos', 'endTokenPos', + 'comments' + ]]); } // Must be public for updateTests.php diff --git a/test/code/parser/stmt/class/readonlyAsClassName.test b/test/code/parser/stmt/class/readonlyAsClassName.test new file mode 100644 index 00000000..7d3010dd --- /dev/null +++ b/test/code/parser/stmt/class/readonlyAsClassName.test @@ -0,0 +1,28 @@ +Readonly as class name +----- +