diff --git a/composer.json b/composer.json index 7d53da3d..77532315 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "ext-tokenizer": "*", "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", - "nikic/php-parser": "^4.0" + "nikic/php-parser": "^5.0 || ^4.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.2" diff --git a/src/CodeCleaner/ListPass.php b/src/CodeCleaner/ListPass.php index b590b7fc..2b5c9578 100644 --- a/src/CodeCleaner/ListPass.php +++ b/src/CodeCleaner/ListPass.php @@ -77,9 +77,9 @@ public function enterNode(Node $node) /** * Validate whether a given item in an array is valid for short assignment. * - * @param Expr $item + * @param Node $item */ - private static function isValidArrayItem(Expr $item): bool + private static function isValidArrayItem(Node $item): bool { $value = ($item instanceof ArrayItem) ? $item->value : $item; diff --git a/src/CodeCleaner/NamespaceAwarePass.php b/src/CodeCleaner/NamespaceAwarePass.php index ba82f9d8..12de7575 100644 --- a/src/CodeCleaner/NamespaceAwarePass.php +++ b/src/CodeCleaner/NamespaceAwarePass.php @@ -49,7 +49,7 @@ public function beforeTraverse(array $nodes) public function enterNode(Node $node) { if ($node instanceof Namespace_) { - $this->namespace = isset($node->name) ? $node->name->parts : []; + $this->namespace = isset($node->name) ? $this->getParts($node->name) : []; } } @@ -61,13 +61,25 @@ public function enterNode(Node $node) protected function getFullyQualifiedName($name): string { if ($name instanceof FullyQualifiedName) { - return \implode('\\', $name->parts); - } elseif ($name instanceof Name) { - $name = $name->parts; + return \implode('\\', $this->getParts($name)); + } + + if ($name instanceof Name) { + $name = $this->getParts($name); } elseif (!\is_array($name)) { $name = [$name]; } return \implode('\\', \array_merge($this->namespace, $name)); } -} + + /** + * Backwards compatibility shim for PHP-Parser 4.x + * + * At some point we might want to make $namespace a plain string, to match how Name works? + */ + protected function getParts(Name $name): array + { + return method_exists($name, 'getParts') ? $name->getParts() : $name->parts; + } +} \ No newline at end of file diff --git a/src/CodeCleaner/NamespacePass.php b/src/CodeCleaner/NamespacePass.php index 470f0fa4..75ec844f 100644 --- a/src/CodeCleaner/NamespacePass.php +++ b/src/CodeCleaner/NamespacePass.php @@ -86,6 +86,16 @@ public function beforeTraverse(array $nodes) private function setNamespace($namespace) { $this->namespace = $namespace; - $this->cleaner->setNamespace($namespace === null ? null : $namespace->parts); + $this->cleaner->setNamespace($namespace === null ? null : $this->getParts($namespace)); + } + + /** + * Backwards compatibility shim for PHP-Parser 4.x + * + * At some point we might want to make the namespace a plain string, to match how Name works? + */ + protected function getParts(Name $name): array + { + return method_exists($name, 'getParts') ? $name->getParts() : $name->parts; } } diff --git a/src/CodeCleaner/UseStatementPass.php b/src/CodeCleaner/UseStatementPass.php index 86278513..5d4cbdcf 100644 --- a/src/CodeCleaner/UseStatementPass.php +++ b/src/CodeCleaner/UseStatementPass.php @@ -17,6 +17,7 @@ use PhpParser\Node\Stmt\GroupUse; use PhpParser\Node\Stmt\Namespace_; use PhpParser\Node\Stmt\Use_; +use PhpParser\Node\Stmt\UseItem; use PhpParser\Node\Stmt\UseUse; use PhpParser\NodeTraverser; @@ -72,9 +73,8 @@ public function leaveNode(Node $node) { // Store a reference to every "use" statement, because we'll need them in a bit. if ($node instanceof Use_) { - foreach ($node->uses as $use) { - $alias = $use->alias ?: \end($use->name->parts); - $this->aliases[\strtolower($alias)] = $use->name; + foreach ($node->uses as $useItem) { + $this->aliases[\strtolower($useItem->getAlias())] = $useItem->name; } return NodeTraverser::REMOVE_NODE; @@ -82,11 +82,10 @@ public function leaveNode(Node $node) // Expand every "use" statement in the group into a full, standalone "use" and store 'em with the others. if ($node instanceof GroupUse) { - foreach ($node->uses as $use) { - $alias = $use->alias ?: \end($use->name->parts); - $this->aliases[\strtolower($alias)] = Name::concat($node->prefix, $use->name, [ + foreach ($node->uses as $useItem) { + $this->aliases[\strtolower($useItem->getAlias())] = Name::concat($node->prefix, $useItem->name, [ 'startLine' => $node->prefix->getAttribute('startLine'), - 'endLine' => $use->name->getAttribute('endLine'), + 'endLine' => $useItem->name->getAttribute('endLine'), ]); } @@ -102,8 +101,9 @@ public function leaveNode(Node $node) return; } - // Do nothing with UseUse; this an entry in the list of uses in the use statement. - if ($node instanceof UseUse) { + // Do nothing with UseItem; this an entry in the list of uses in the use statement. + // @todo Remove UseUse once we drop support for PHP-Parser 4.x + if ($node instanceof UseUse || $node instanceof UseItem) { return; } diff --git a/src/CodeCleaner/ValidConstructorPass.php b/src/CodeCleaner/ValidConstructorPass.php index 625ab69c..4d1ff02a 100644 --- a/src/CodeCleaner/ValidConstructorPass.php +++ b/src/CodeCleaner/ValidConstructorPass.php @@ -12,6 +12,7 @@ namespace Psy\CodeCleaner; use PhpParser\Node; +use PhpParser\Node\Name; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Namespace_; @@ -55,7 +56,7 @@ public function beforeTraverse(array $nodes) public function enterNode(Node $node) { if ($node instanceof Namespace_) { - $this->namespace = isset($node->name) ? $node->name->parts : []; + $this->namespace = isset($node->name) ? $this->getParts($node->name) : []; } elseif ($node instanceof Class_) { $constructor = null; foreach ($node->stmts as $stmt) { @@ -107,4 +108,14 @@ private function validateConstructor(Node $constructor, Node $classNode) throw new FatalErrorException($msg, 0, \E_ERROR, null, $classNode->getLine()); } } + + /** + * Backwards compatibility shim for PHP-Parser 4.x + * + * At some point we might want to make $namespace a plain string, to match how Name works? + */ + protected function getParts(Name $name): array + { + return method_exists($name, 'getParts') ? $name->getParts() : $name->parts; + } } diff --git a/src/Command/ThrowUpCommand.php b/src/Command/ThrowUpCommand.php index bb5bf5dd..337a5bdc 100644 --- a/src/Command/ThrowUpCommand.php +++ b/src/Command/ThrowUpCommand.php @@ -14,9 +14,10 @@ use PhpParser\Node\Arg; use PhpParser\Node\Expr\New_; use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Expr\Throw_; use PhpParser\Node\Name\FullyQualified as FullyQualifiedName; use PhpParser\Node\Scalar\String_; -use PhpParser\Node\Stmt\Throw_; +use PhpParser\Node\Stmt\Expression; use PhpParser\PrettyPrinter\Standard as Printer; use Psy\Exception\ThrowUpException; use Psy\Input\CodeArgument; @@ -78,7 +79,7 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output): int { $args = $this->prepareArgs($input->getArgument('exception')); - $throwStmt = new Throw_(new New_(new FullyQualifiedName(ThrowUpException::class), $args)); + $throwStmt = new Expression(new Throw_(new New_(new FullyQualifiedName(ThrowUpException::class), $args))); $throwCode = $this->printer->prettyPrint([$throwStmt]); $shell = $this->getApplication(); diff --git a/src/Exception/ParseErrorException.php b/src/Exception/ParseErrorException.php index 8abfd12f..632e1fd6 100644 --- a/src/Exception/ParseErrorException.php +++ b/src/Exception/ParseErrorException.php @@ -19,13 +19,19 @@ class ParseErrorException extends \PhpParser\Error implements Exception /** * Constructor! * - * @param string $message (default: "") - * @param int $line (default: -1) + * @param string $message (default: '') + * @param array|int $attributes Attributes of node/token where error occurred + * (or start line of error -- deprecated) */ - public function __construct(string $message = '', int $line = -1) + public function __construct(string $message = '', $attributes = []) { $message = \sprintf('PHP Parse error: %s', $message); - parent::__construct($message, $line); + + if (!is_array($attributes)) { + $attributes = ['startLine' => $attributes]; + } + + parent::__construct($message, $attributes); } /** @@ -35,6 +41,6 @@ public function __construct(string $message = '', int $line = -1) */ public static function fromParseError(\PhpParser\Error $e): self { - return new self($e->getRawMessage(), $e->getStartLine()); + return new self($e->getRawMessage(), $e->getAttributes()); } }