upgraded dependencies
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -25,9 +25,11 @@ class AbstractClassPass extends CodeCleanerPass
|
||||
private $abstractMethods;
|
||||
|
||||
/**
|
||||
* @throws RuntimeException if the node is an abstract function with a body
|
||||
* @throws FatalErrorException if the node is an abstract function with a body
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
@@ -41,16 +43,18 @@ class AbstractClassPass extends CodeCleanerPass
|
||||
|
||||
if ($node->stmts !== null) {
|
||||
$msg = \sprintf('Abstract function %s cannot contain body', $name);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
|
||||
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException if the node is a non-abstract class with abstract methods
|
||||
* @throws FatalErrorException if the node is a non-abstract class with abstract methods
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|Node[]|null Replacement node (or special return value)
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
@@ -64,7 +68,7 @@ class AbstractClassPass extends CodeCleanerPass
|
||||
($count === 1) ? '' : 's',
|
||||
\implode(', ', $this->abstractMethods)
|
||||
);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
|
||||
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -26,14 +26,16 @@ class AssignThisVariablePass extends CodeCleanerPass
|
||||
/**
|
||||
* Validate that the user input does not assign the `$this` variable.
|
||||
*
|
||||
* @throws RuntimeException if the user assign the `$this` variable
|
||||
* @throws FatalErrorException if the user assign the `$this` variable
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Assign && $node->var instanceof Variable && $node->var->name === 'this') {
|
||||
throw new FatalErrorException('Cannot re-assign $this', 0, E_ERROR, null, $node->getLine());
|
||||
throw new FatalErrorException('Cannot re-assign $this', 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -15,6 +15,7 @@ use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\VariadicPlaceholder;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
@@ -31,9 +32,11 @@ class CallTimePassByReferencePass extends CodeCleanerPass
|
||||
/**
|
||||
* Validate of use call-time pass-by-reference.
|
||||
*
|
||||
* @throws RuntimeException if the user used call-time pass-by-reference
|
||||
* @throws FatalErrorException if the user used call-time pass-by-reference
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
@@ -42,8 +45,12 @@ class CallTimePassByReferencePass extends CodeCleanerPass
|
||||
}
|
||||
|
||||
foreach ($node->args as $arg) {
|
||||
if ($arg instanceof VariadicPlaceholder) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($arg->byRef) {
|
||||
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, E_ERROR, null, $node->getLine());
|
||||
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -17,6 +17,7 @@ use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Trait_;
|
||||
use PhpParser\Node\VariadicPlaceholder;
|
||||
use Psy\Exception\ErrorException;
|
||||
|
||||
/**
|
||||
@@ -29,6 +30,8 @@ class CalledClassPass extends CodeCleanerPass
|
||||
|
||||
/**
|
||||
* @param array $nodes
|
||||
*
|
||||
* @return Node[]|null Array of nodes
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
@@ -39,6 +42,8 @@ class CalledClassPass extends CodeCleanerPass
|
||||
* @throws ErrorException if get_class or get_called_class is called without an object from outside a class
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
@@ -61,13 +66,15 @@ class CalledClassPass extends CodeCleanerPass
|
||||
$name = \strtolower($node->name);
|
||||
if (\in_array($name, ['get_class', 'get_called_class'])) {
|
||||
$msg = \sprintf('%s() called without object from outside a class', $name);
|
||||
throw new ErrorException($msg, 0, E_USER_WARNING, null, $node->getLine());
|
||||
throw new ErrorException($msg, 0, \E_USER_WARNING, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|Node[]|null Replacement node (or special return value)
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
@@ -76,8 +83,12 @@ class CalledClassPass extends CodeCleanerPass
|
||||
}
|
||||
}
|
||||
|
||||
private function isNull(Node $node)
|
||||
private function isNull(Node $node): bool
|
||||
{
|
||||
if ($node instanceof VariadicPlaceholder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $node->value instanceof ConstFetch && \strtolower($node->value->name) === 'null';
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
|
55
vendor/psy/psysh/src/CodeCleaner/EmptyArrayDimFetchPass.php
vendored
Normal file
55
vendor/psy/psysh/src/CodeCleaner/EmptyArrayDimFetchPass.php
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Psy\CodeCleaner;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\ArrayDimFetch;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* Validate empty brackets are only used for assignment.
|
||||
*/
|
||||
class EmptyArrayDimFetchPass extends CodeCleanerPass
|
||||
{
|
||||
const EXCEPTION_MESSAGE = 'Cannot use [] for reading';
|
||||
|
||||
private $theseOnesAreFine = [];
|
||||
|
||||
/**
|
||||
* @return Node[]|null Array of nodes
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
$this->theseOnesAreFine = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FatalErrorException if the user used empty empty array dim fetch outside of assignment
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Assign && $node->var instanceof ArrayDimFetch) {
|
||||
$this->theseOnesAreFine[] = $node->var;
|
||||
}
|
||||
|
||||
if ($node instanceof ArrayDimFetch && $node->dim === null) {
|
||||
if (!\in_array($node, $this->theseOnesAreFine)) {
|
||||
throw new FatalErrorException(self::EXCEPTION_MESSAGE, $node->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -15,6 +15,7 @@ use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Exit_;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
|
||||
use Psy\Exception\BreakException;
|
||||
|
||||
class ExitPass extends CodeCleanerPass
|
||||
{
|
||||
@@ -22,11 +23,13 @@ class ExitPass extends CodeCleanerPass
|
||||
* Converts exit calls to BreakExceptions.
|
||||
*
|
||||
* @param \PhpParser\Node $node
|
||||
*
|
||||
* @return int|Node|Node[]|null Replacement node (or special return value)
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Exit_) {
|
||||
return new StaticCall(new FullyQualifiedName('Psy\Exception\BreakException'), 'exitShell');
|
||||
return new StaticCall(new FullyQualifiedName(BreakException::class), 'exitShell');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -24,6 +24,8 @@ class FinalClassPass extends CodeCleanerPass
|
||||
|
||||
/**
|
||||
* @param array $nodes
|
||||
*
|
||||
* @return Node[]|null Array of nodes
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
@@ -31,9 +33,11 @@ class FinalClassPass extends CodeCleanerPass
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException if the node is a class that extends a final class
|
||||
* @throws FatalErrorException if the node is a class that extends a final class
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
@@ -42,7 +46,7 @@ class FinalClassPass extends CodeCleanerPass
|
||||
$extends = (string) $node->extends;
|
||||
if ($this->isFinalClass($extends)) {
|
||||
$msg = \sprintf('Class %s may not inherit from final class (%s)', $node->name, $extends);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
|
||||
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +61,7 @@ class FinalClassPass extends CodeCleanerPass
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isFinalClass($name)
|
||||
private function isFinalClass(string $name): bool
|
||||
{
|
||||
if (!\class_exists($name)) {
|
||||
return isset($this->finalClasses[\strtolower($name)]);
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -23,12 +23,17 @@ class FunctionContextPass extends CodeCleanerPass
|
||||
|
||||
/**
|
||||
* @param array $nodes
|
||||
*
|
||||
* @return Node[]|null Array of nodes
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
$this->functionDepth = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if ($node instanceof FunctionLike) {
|
||||
@@ -45,12 +50,14 @@ class FunctionContextPass extends CodeCleanerPass
|
||||
// It causes fatal error.
|
||||
if ($node instanceof Yield_) {
|
||||
$msg = 'The "yield" expression can only be used inside a function';
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
|
||||
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \PhpParser\Node $node
|
||||
*
|
||||
* @return int|Node|Node[]|null Replacement node (or special return value)
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -14,12 +14,12 @@ namespace Psy\CodeCleaner;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Array_;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\Empty_;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Expr\Isset_;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Stmt\Unset_;
|
||||
use PhpParser\Node\VariadicPlaceholder;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
@@ -29,33 +29,31 @@ use Psy\Exception\FatalErrorException;
|
||||
*/
|
||||
class FunctionReturnInWriteContextPass extends CodeCleanerPass
|
||||
{
|
||||
const PHP55_MESSAGE = 'Cannot use isset() on the result of a function call (you can use "null !== func()" instead)';
|
||||
const ISSET_MESSAGE = 'Cannot use isset() on the result of an expression (you can use "null !== expression" instead)';
|
||||
const EXCEPTION_MESSAGE = "Can't use function return value in write context";
|
||||
|
||||
private $atLeastPhp55;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->atLeastPhp55 = \version_compare(PHP_VERSION, '5.5', '>=');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the functions are used correctly.
|
||||
*
|
||||
* @throws FatalErrorException if a function is passed as an argument reference
|
||||
* @throws FatalErrorException if a function is used as an argument in the isset
|
||||
* @throws FatalErrorException if a function is used as an argument in the empty, only for PHP < 5.5
|
||||
* @throws FatalErrorException if a value is assigned to a function
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Array_ || $this->isCallNode($node)) {
|
||||
$items = $node instanceof Array_ ? $node->items : $node->args;
|
||||
foreach ($items as $item) {
|
||||
if ($item instanceof VariadicPlaceholder) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($item && $item->byRef && $this->isCallNode($item->value)) {
|
||||
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, E_ERROR, null, $node->getLine());
|
||||
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
} elseif ($node instanceof Isset_ || $node instanceof Unset_) {
|
||||
@@ -64,17 +62,15 @@ class FunctionReturnInWriteContextPass extends CodeCleanerPass
|
||||
continue;
|
||||
}
|
||||
|
||||
$msg = ($node instanceof Isset_ && $this->atLeastPhp55) ? self::PHP55_MESSAGE : self::EXCEPTION_MESSAGE;
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
|
||||
$msg = $node instanceof Isset_ ? self::ISSET_MESSAGE : self::EXCEPTION_MESSAGE;
|
||||
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
} elseif ($node instanceof Empty_ && !$this->atLeastPhp55 && $this->isCallNode($node->expr)) {
|
||||
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, E_ERROR, null, $node->getLine()); // @codeCoverageIgnore
|
||||
} elseif ($node instanceof Assign && $this->isCallNode($node->var)) {
|
||||
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, E_ERROR, null, $node->getLine());
|
||||
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
private function isCallNode(Node $node)
|
||||
private function isCallNode(Node $node): bool
|
||||
{
|
||||
return $node instanceof FuncCall || $node instanceof MethodCall || $node instanceof StaticCall;
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -32,7 +32,7 @@ class ImplicitReturnPass extends CodeCleanerPass
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
public function beforeTraverse(array $nodes): array
|
||||
{
|
||||
return $this->addImplicitReturn($nodes);
|
||||
}
|
||||
@@ -42,7 +42,7 @@ class ImplicitReturnPass extends CodeCleanerPass
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function addImplicitReturn(array $nodes)
|
||||
private function addImplicitReturn(array $nodes): array
|
||||
{
|
||||
// If nodes is empty, it can't have a return value.
|
||||
if (empty($nodes)) {
|
||||
@@ -118,7 +118,7 @@ class ImplicitReturnPass extends CodeCleanerPass
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function isNonExpressionStmt(Node $node)
|
||||
private static function isNonExpressionStmt(Node $node): bool
|
||||
{
|
||||
return $node instanceof Stmt &&
|
||||
!$node instanceof Expression &&
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -12,6 +12,9 @@
|
||||
namespace Psy\CodeCleaner;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Array_;
|
||||
use PhpParser\Node\Expr\BinaryOp;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Expr\ConstFetch;
|
||||
use PhpParser\Node\Expr\Instanceof_;
|
||||
use PhpParser\Node\Scalar;
|
||||
@@ -27,21 +30,40 @@ class InstanceOfPass extends CodeCleanerPass
|
||||
{
|
||||
const EXCEPTION_MSG = 'instanceof expects an object instance, constant given';
|
||||
|
||||
private $atLeastPhp73;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->atLeastPhp73 = \version_compare(\PHP_VERSION, '7.3', '>=');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the instanceof statement does not receive a scalar value or a non-class constant.
|
||||
*
|
||||
* @throws FatalErrorException if a scalar or a non-class constant is given
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
// Basically everything is allowed in PHP 7.3 :)
|
||||
if ($this->atLeastPhp73) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$node instanceof Instanceof_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (($node->expr instanceof Scalar && !$node->expr instanceof Encapsed) || $node->expr instanceof ConstFetch) {
|
||||
throw new FatalErrorException(self::EXCEPTION_MSG, 0, E_ERROR, null, $node->getLine());
|
||||
if (($node->expr instanceof Scalar && !$node->expr instanceof Encapsed) ||
|
||||
$node->expr instanceof BinaryOp ||
|
||||
$node->expr instanceof Array_ ||
|
||||
$node->expr instanceof ConstFetch ||
|
||||
$node->expr instanceof ClassConstFetch
|
||||
) {
|
||||
throw new FatalErrorException(self::EXCEPTION_MSG, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
49
vendor/psy/psysh/src/CodeCleaner/IssetPass.php
vendored
Normal file
49
vendor/psy/psysh/src/CodeCleaner/IssetPass.php
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Psy\CodeCleaner;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\ArrayDimFetch;
|
||||
use PhpParser\Node\Expr\Isset_;
|
||||
use PhpParser\Node\Expr\NullsafePropertyFetch;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* Code cleaner pass to ensure we only allow variables, array fetch and property
|
||||
* fetch expressions in isset() calls.
|
||||
*/
|
||||
class IssetPass extends CodeCleanerPass
|
||||
{
|
||||
const EXCEPTION_MSG = 'Cannot use isset() on the result of an expression (you can use "null !== expression" instead)';
|
||||
|
||||
/**
|
||||
* @throws FatalErrorException
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if (!$node instanceof Isset_) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($node->vars as $var) {
|
||||
if (!$var instanceof Variable && !$var instanceof ArrayDimFetch && !$var instanceof PropertyFetch && !$var instanceof NullsafePropertyFetch) {
|
||||
throw new FatalErrorException(self::EXCEPTION_MSG, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
101
vendor/psy/psysh/src/CodeCleaner/LabelContextPass.php
vendored
Normal file
101
vendor/psy/psysh/src/CodeCleaner/LabelContextPass.php
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Psy\CodeCleaner;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Stmt\Goto_;
|
||||
use PhpParser\Node\Stmt\Label;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* CodeCleanerPass for label context.
|
||||
*
|
||||
* This class partially emulates the PHP label specification.
|
||||
* PsySH can not declare labels by sequentially executing lines with eval,
|
||||
* but since it is not a syntax error, no error is raised.
|
||||
* This class warns before invalid goto causes a fatal error.
|
||||
* Since this is a simple checker, it does not block real fatal error
|
||||
* with complex syntax. (ex. it does not parse inside function.)
|
||||
*
|
||||
* @see http://php.net/goto
|
||||
*/
|
||||
class LabelContextPass extends CodeCleanerPass
|
||||
{
|
||||
/** @var int */
|
||||
private $functionDepth;
|
||||
|
||||
/** @var array */
|
||||
private $labelDeclarations;
|
||||
/** @var array */
|
||||
private $labelGotos;
|
||||
|
||||
/**
|
||||
* @param array $nodes
|
||||
*
|
||||
* @return Node[]|null Array of nodes
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
$this->functionDepth = 0;
|
||||
$this->labelDeclarations = [];
|
||||
$this->labelGotos = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if ($node instanceof FunctionLike) {
|
||||
$this->functionDepth++;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// node is inside function context
|
||||
if ($this->functionDepth !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof Goto_) {
|
||||
$this->labelGotos[\strtolower($node->name)] = $node->getLine();
|
||||
} elseif ($node instanceof Label) {
|
||||
$this->labelDeclarations[\strtolower($node->name)] = $node->getLine();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \PhpParser\Node $node
|
||||
*
|
||||
* @return int|Node|Node[]|null Replacement node (or special return value)
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
if ($node instanceof FunctionLike) {
|
||||
$this->functionDepth--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Node[]|null Array of nodes
|
||||
*/
|
||||
public function afterTraverse(array $nodes)
|
||||
{
|
||||
foreach ($this->labelGotos as $name => $line) {
|
||||
if (!isset($this->labelDeclarations[$name])) {
|
||||
$msg = "'goto' to undefined label '{$name}'";
|
||||
throw new FatalErrorException($msg, 0, \E_ERROR, null, $line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -26,6 +26,8 @@ class LeavePsyshAlonePass extends CodeCleanerPass
|
||||
* @throws RuntimeException if the user is messing with $__psysh__
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
|
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Psy\CodeCleaner;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Empty_;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use Psy\Exception\ParseErrorException;
|
||||
|
||||
/**
|
||||
* Validate that the user did not call the language construct `empty()` on a
|
||||
* statement in PHP < 5.5.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class LegacyEmptyPass extends CodeCleanerPass
|
||||
{
|
||||
private $atLeastPhp55;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->atLeastPhp55 = \version_compare(PHP_VERSION, '5.5', '>=');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate use of empty in PHP < 5.5.
|
||||
*
|
||||
* @throws ParseErrorException if the user used empty with anything but a variable
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if ($this->atLeastPhp55) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$node instanceof Empty_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$node->expr instanceof Variable) {
|
||||
$msg = \sprintf('syntax error, unexpected %s', $this->getUnexpectedThing($node->expr));
|
||||
|
||||
throw new ParseErrorException($msg, $node->expr->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
private function getUnexpectedThing(Node $node)
|
||||
{
|
||||
switch ($node->getType()) {
|
||||
case 'Scalar_String':
|
||||
case 'Scalar_LNumber':
|
||||
case 'Scalar_DNumber':
|
||||
return \json_encode($node->value);
|
||||
|
||||
case 'Expr_ConstFetch':
|
||||
return (string) $node->name;
|
||||
|
||||
default:
|
||||
return $node->getType();
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -33,7 +33,7 @@ class ListPass extends CodeCleanerPass
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->atLeastPhp71 = \version_compare(PHP_VERSION, '7.1', '>=');
|
||||
$this->atLeastPhp71 = \version_compare(\PHP_VERSION, '7.1', '>=');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,6 +42,8 @@ class ListPass extends CodeCleanerPass
|
||||
* @throws ParseErrorException if the user used empty with anything but a variable
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
@@ -97,7 +99,7 @@ class ListPass extends CodeCleanerPass
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function isValidArrayItem(Expr $item)
|
||||
private static function isValidArrayItem(Expr $item): bool
|
||||
{
|
||||
$value = ($item instanceof ArrayItem) ? $item->value : $item;
|
||||
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -32,6 +32,8 @@ class LoopContextPass extends CodeCleanerPass
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return Node[]|null Array of nodes
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
@@ -45,6 +47,8 @@ class LoopContextPass extends CodeCleanerPass
|
||||
* @throws FatalErrorException if the node is a break or continue and has an argument less than 1
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
@@ -63,23 +67,23 @@ class LoopContextPass extends CodeCleanerPass
|
||||
|
||||
if ($this->loopDepth === 0) {
|
||||
$msg = \sprintf("'%s' not in the 'loop' or 'switch' context", $operator);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
|
||||
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
|
||||
if ($node->num instanceof LNumber || $node->num instanceof DNumber) {
|
||||
$num = $node->num->value;
|
||||
if ($node->num instanceof DNumber || $num < 1) {
|
||||
$msg = \sprintf("'%s' operator accepts only positive numbers", $operator);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
|
||||
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
|
||||
if ($num > $this->loopDepth) {
|
||||
$msg = \sprintf("Cannot '%s' %d levels", $operator, $num);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
|
||||
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
} elseif ($node->num) {
|
||||
$msg = \sprintf("'%s' operator with non-constant operand is no longer supported", $operator);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
|
||||
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -87,6 +91,8 @@ class LoopContextPass extends CodeCleanerPass
|
||||
|
||||
/**
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|Node[]|null Replacement node (or special return value)
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -29,7 +29,7 @@ class MagicConstantsPass extends CodeCleanerPass
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return null|FuncCall|String_
|
||||
* @return FuncCall|String_|null
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -29,10 +29,12 @@ abstract class NamespaceAwarePass extends CodeCleanerPass
|
||||
* use afterTraverse or call parent::beforeTraverse() when overloading.
|
||||
*
|
||||
* Reset the namespace and the current scope before beginning analysis
|
||||
*
|
||||
* @return Node[]|null Array of nodes
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
$this->namespace = [];
|
||||
$this->namespace = [];
|
||||
$this->currentScope = [];
|
||||
}
|
||||
|
||||
@@ -41,6 +43,8 @@ abstract class NamespaceAwarePass extends CodeCleanerPass
|
||||
* leaveNode or call parent::enterNode() when overloading
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
@@ -56,7 +60,7 @@ abstract class NamespaceAwarePass extends CodeCleanerPass
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getFullyQualifiedName($name)
|
||||
protected function getFullyQualifiedName($name): string
|
||||
{
|
||||
if ($name instanceof FullyQualifiedName) {
|
||||
return \implode('\\', $name->parts);
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace Psy\CodeCleaner;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use Psy\CodeCleaner;
|
||||
@@ -46,6 +47,8 @@ class NamespacePass extends CodeCleanerPass
|
||||
* is encountered.
|
||||
*
|
||||
* @param array $nodes
|
||||
*
|
||||
* @return Node[]|null Array of nodes
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
@@ -78,7 +81,7 @@ class NamespacePass extends CodeCleanerPass
|
||||
* Remember the namespace and (re)set the namespace on the CodeCleaner as
|
||||
* well.
|
||||
*
|
||||
* @param null|Name $namespace
|
||||
* @param Name|null $namespace
|
||||
*/
|
||||
private function setNamespace($namespace)
|
||||
{
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -26,10 +26,10 @@ class NoReturnValue
|
||||
/**
|
||||
* Get PhpParser AST expression for creating a new NoReturnValue.
|
||||
*
|
||||
* @return PhpParser\Node\Expr\New_
|
||||
* @return New_
|
||||
*/
|
||||
public static function create()
|
||||
public static function create(): New_
|
||||
{
|
||||
return new New_(new FullyQualifiedName('Psy\CodeCleaner\NoReturnValue'));
|
||||
return new New_(new FullyQualifiedName(self::class));
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -13,6 +13,8 @@ namespace Psy\CodeCleaner;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\Array_;
|
||||
use PhpParser\Node\Expr\ArrayDimFetch;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
@@ -32,6 +34,8 @@ class PassableByReferencePass extends CodeCleanerPass
|
||||
* @throws FatalErrorException if non-variables are passed by reference
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
@@ -59,15 +63,20 @@ class PassableByReferencePass extends CodeCleanerPass
|
||||
if (\array_key_exists($key, $node->args)) {
|
||||
$arg = $node->args[$key];
|
||||
if ($param->isPassedByReference() && !$this->isPassableByReference($arg)) {
|
||||
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, E_ERROR, null, $node->getLine());
|
||||
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function isPassableByReference(Node $arg)
|
||||
private function isPassableByReference(Node $arg): bool
|
||||
{
|
||||
// Unpacked arrays can be passed by reference
|
||||
if ($arg->value instanceof Array_) {
|
||||
return $arg->unpack;
|
||||
}
|
||||
|
||||
// FuncCall, MethodCall and StaticCall are all PHP _warnings_ not fatal errors, so we'll let
|
||||
// PHP handle those ones :)
|
||||
return $arg->value instanceof ClassConstFetch ||
|
||||
@@ -75,7 +84,8 @@ class PassableByReferencePass extends CodeCleanerPass
|
||||
$arg->value instanceof Variable ||
|
||||
$arg->value instanceof FuncCall ||
|
||||
$arg->value instanceof MethodCall ||
|
||||
$arg->value instanceof StaticCall;
|
||||
$arg->value instanceof StaticCall ||
|
||||
$arg->value instanceof ArrayDimFetch;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,7 +112,7 @@ class PassableByReferencePass extends CodeCleanerPass
|
||||
} elseif (++$nonPassable > 2) {
|
||||
// There can be *at most* two non-passable-by-reference args in a row. This is about
|
||||
// as close as we can get to validating the arguments for this function :-/
|
||||
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, E_ERROR, null, $node->getLine());
|
||||
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
52
vendor/psy/psysh/src/CodeCleaner/RequirePass.php
vendored
52
vendor/psy/psysh/src/CodeCleaner/RequirePass.php
vendored
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -19,7 +19,6 @@ use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
|
||||
use PhpParser\Node\Scalar\LNumber;
|
||||
use Psy\Exception\ErrorException;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
use Psy\Shell;
|
||||
|
||||
/**
|
||||
* Add runtime validation for `require` and `require_once` calls.
|
||||
@@ -30,6 +29,8 @@ class RequirePass extends CodeCleanerPass
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $origNode)
|
||||
{
|
||||
@@ -49,7 +50,7 @@ class RequirePass extends CodeCleanerPass
|
||||
* $foo = require \Psy\CodeCleaner\RequirePass::resolve($bar)
|
||||
*/
|
||||
$node->expr = new StaticCall(
|
||||
new FullyQualifiedName('Psy\CodeCleaner\RequirePass'),
|
||||
new FullyQualifiedName(self::class),
|
||||
'resolve',
|
||||
[new Arg($origNode->expr), new Arg(new LNumber($origNode->getLine()))],
|
||||
$origNode->getAttributes()
|
||||
@@ -63,15 +64,18 @@ class RequirePass extends CodeCleanerPass
|
||||
*
|
||||
* If $file can be resolved, return $file. Otherwise throw a fatal error exception.
|
||||
*
|
||||
* If $file collides with a path in the currently running PsySH phar, it will be resolved
|
||||
* relative to the include path, to prevent PHP from grabbing the phar version of the file.
|
||||
*
|
||||
* @throws FatalErrorException when unable to resolve include path for $file
|
||||
* @throws ErrorException if $file is empty and E_WARNING is included in error_reporting level
|
||||
*
|
||||
* @param string $file
|
||||
* @param int $lineNumber Line number of the original require expression
|
||||
*
|
||||
* @return string Exactly the same as $file
|
||||
* @return string Exactly the same as $file, unless $file collides with a path in the currently running phar
|
||||
*/
|
||||
public static function resolve($file, $lineNumber = null)
|
||||
public static function resolve($file, $lineNumber = null): string
|
||||
{
|
||||
$file = (string) $file;
|
||||
|
||||
@@ -79,23 +83,51 @@ class RequirePass extends CodeCleanerPass
|
||||
// @todo Shell::handleError would be better here, because we could
|
||||
// fake the file and line number, but we can't call it statically.
|
||||
// So we're duplicating some of the logics here.
|
||||
if (E_WARNING & \error_reporting()) {
|
||||
ErrorException::throwException(E_WARNING, 'Filename cannot be empty', null, $lineNumber);
|
||||
if (\E_WARNING & \error_reporting()) {
|
||||
ErrorException::throwException(\E_WARNING, 'Filename cannot be empty', null, $lineNumber);
|
||||
}
|
||||
// @todo trigger an error as fallback? this is pretty ugly…
|
||||
// trigger_error('Filename cannot be empty', E_USER_WARNING);
|
||||
}
|
||||
|
||||
if ($file === '' || !\stream_resolve_include_path($file)) {
|
||||
$resolvedPath = \stream_resolve_include_path($file);
|
||||
if ($file === '' || !$resolvedPath) {
|
||||
$msg = \sprintf("Failed opening required '%s'", $file);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $lineNumber);
|
||||
throw new FatalErrorException($msg, 0, \E_ERROR, null, $lineNumber);
|
||||
}
|
||||
|
||||
// Special case: if the path is not already relative or absolute, and it would resolve to
|
||||
// something inside the currently running phar (e.g. `vendor/autoload.php`), we'll resolve
|
||||
// it relative to the include path so PHP won't grab the phar version.
|
||||
//
|
||||
// Note that this only works if the phar has `psysh` in the path. We might want to lift this
|
||||
// restriction and special case paths that would collide with any running phar?
|
||||
if ($resolvedPath !== $file && $file[0] !== '.') {
|
||||
$runningPhar = \Phar::running();
|
||||
if (\strpos($runningPhar, 'psysh') !== false && \is_file($runningPhar.\DIRECTORY_SEPARATOR.$file)) {
|
||||
foreach (self::getIncludePath() as $prefix) {
|
||||
$resolvedPath = $prefix.\DIRECTORY_SEPARATOR.$file;
|
||||
if (\is_file($resolvedPath)) {
|
||||
return $resolvedPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
private function isRequireNode(Node $node)
|
||||
private function isRequireNode(Node $node): bool
|
||||
{
|
||||
return $node instanceof Include_ && \in_array($node->type, self::$requireTypes);
|
||||
}
|
||||
|
||||
private static function getIncludePath(): array
|
||||
{
|
||||
if (\PATH_SEPARATOR === ':') {
|
||||
return \preg_split('#:(?!//)#', \get_include_path());
|
||||
}
|
||||
|
||||
return \explode(\PATH_SEPARATOR, \get_include_path());
|
||||
}
|
||||
}
|
||||
|
127
vendor/psy/psysh/src/CodeCleaner/ReturnTypePass.php
vendored
Normal file
127
vendor/psy/psysh/src/CodeCleaner/ReturnTypePass.php
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Psy\CodeCleaner;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\Expr\ConstFetch;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\NullableType;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PhpParser\Node\UnionType;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* Add runtime validation for return types.
|
||||
*/
|
||||
class ReturnTypePass extends CodeCleanerPass
|
||||
{
|
||||
const MESSAGE = 'A function with return type must return a value';
|
||||
const NULLABLE_MESSAGE = 'A function with return type must return a value (did you mean "return null;" instead of "return;"?)';
|
||||
const VOID_MESSAGE = 'A void function must not return a value';
|
||||
const VOID_NULL_MESSAGE = 'A void function must not return a value (did you mean "return;" instead of "return null;"?)';
|
||||
const NULLABLE_VOID_MESSAGE = 'Void type cannot be nullable';
|
||||
|
||||
private $atLeastPhp71;
|
||||
private $returnTypeStack = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->atLeastPhp71 = \version_compare(\PHP_VERSION, '7.1', '>=');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if (!$this->atLeastPhp71) {
|
||||
return; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
if ($this->isFunctionNode($node)) {
|
||||
$this->returnTypeStack[] = $node->returnType;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($this->returnTypeStack) && $node instanceof Return_) {
|
||||
$expectedType = \end($this->returnTypeStack);
|
||||
if ($expectedType === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$msg = null;
|
||||
|
||||
if ($this->typeName($expectedType) === 'void') {
|
||||
// Void functions
|
||||
if ($expectedType instanceof NullableType) {
|
||||
$msg = self::NULLABLE_VOID_MESSAGE;
|
||||
} elseif ($node->expr instanceof ConstFetch && \strtolower($node->expr->name) === 'null') {
|
||||
$msg = self::VOID_NULL_MESSAGE;
|
||||
} elseif ($node->expr !== null) {
|
||||
$msg = self::VOID_MESSAGE;
|
||||
}
|
||||
} else {
|
||||
// Everything else
|
||||
if ($node->expr === null) {
|
||||
$msg = $expectedType instanceof NullableType ? self::NULLABLE_MESSAGE : self::MESSAGE;
|
||||
}
|
||||
}
|
||||
|
||||
if ($msg !== null) {
|
||||
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return int|Node|Node[]|null Replacement node (or special return value)
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
if (!$this->atLeastPhp71) {
|
||||
return; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
if (!empty($this->returnTypeStack) && $this->isFunctionNode($node)) {
|
||||
\array_pop($this->returnTypeStack);
|
||||
}
|
||||
}
|
||||
|
||||
private function isFunctionNode(Node $node): bool
|
||||
{
|
||||
return $node instanceof Function_ || $node instanceof Closure;
|
||||
}
|
||||
|
||||
private function typeName(Node $node): string
|
||||
{
|
||||
if ($node instanceof UnionType) {
|
||||
return \implode('|', \array_map([$this, 'typeName'], $node->types));
|
||||
}
|
||||
|
||||
if ($node instanceof NullableType) {
|
||||
return \strtolower($node->type->name);
|
||||
}
|
||||
|
||||
if ($node instanceof Identifier) {
|
||||
return \strtolower($node->name);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('Unable to find type name');
|
||||
}
|
||||
}
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace Psy\CodeCleaner;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Scalar\LNumber;
|
||||
use PhpParser\Node\Stmt\Declare_;
|
||||
@@ -32,12 +33,6 @@ class StrictTypesPass extends CodeCleanerPass
|
||||
const EXCEPTION_MESSAGE = 'strict_types declaration must have 0 or 1 as its value';
|
||||
|
||||
private $strictTypes = false;
|
||||
private $atLeastPhp7;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->atLeastPhp7 = \version_compare(PHP_VERSION, '7.0', '>=');
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is a standalone strict types declaration, remember it for later.
|
||||
@@ -48,16 +43,14 @@ class StrictTypesPass extends CodeCleanerPass
|
||||
* @throws FatalErrorException if an invalid `strict_types` declaration is found
|
||||
*
|
||||
* @param array $nodes
|
||||
*
|
||||
* @return Node[]|null Array of nodes
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
if (!$this->atLeastPhp7) {
|
||||
return; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$prependStrictTypes = $this->strictTypes;
|
||||
|
||||
foreach ($nodes as $key => $node) {
|
||||
foreach ($nodes as $node) {
|
||||
if ($node instanceof Declare_) {
|
||||
foreach ($node->declares as $declare) {
|
||||
// For PHP Parser 4.x
|
||||
@@ -65,7 +58,7 @@ class StrictTypesPass extends CodeCleanerPass
|
||||
if ($declareKey === 'strict_types') {
|
||||
$value = $declare->value;
|
||||
if (!$value instanceof LNumber || ($value->value !== 0 && $value->value !== 1)) {
|
||||
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, E_ERROR, null, $node->getLine());
|
||||
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
|
||||
$this->strictTypes = $value->value === 1;
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -17,6 +17,7 @@ use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
|
||||
use PhpParser\Node\Stmt\GroupUse;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use PhpParser\Node\Stmt\Use_;
|
||||
use PhpParser\Node\Stmt\UseUse;
|
||||
use PhpParser\NodeTraverser;
|
||||
|
||||
/**
|
||||
@@ -31,8 +32,8 @@ use PhpParser\NodeTraverser;
|
||||
*/
|
||||
class UseStatementPass extends CodeCleanerPass
|
||||
{
|
||||
private $aliases = [];
|
||||
private $lastAliases = [];
|
||||
private $aliases = [];
|
||||
private $lastAliases = [];
|
||||
private $lastNamespace = null;
|
||||
|
||||
/**
|
||||
@@ -43,13 +44,15 @@ class UseStatementPass extends CodeCleanerPass
|
||||
* work like you'd expect.
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Namespace_) {
|
||||
// If this is the same namespace as last namespace, let's do ourselves
|
||||
// a favor and reload all the aliases...
|
||||
if (\strtolower($node->name) === \strtolower($this->lastNamespace)) {
|
||||
if (\strtolower($node->name ?: '') === \strtolower($this->lastNamespace ?: '')) {
|
||||
$this->aliases = $this->lastAliases;
|
||||
}
|
||||
}
|
||||
@@ -62,21 +65,23 @@ class UseStatementPass extends CodeCleanerPass
|
||||
* remembered aliases to the code.
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|Node[]|null Replacement node (or special return value)
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
// Store a reference to every "use" statement, because we'll need them in a bit.
|
||||
if ($node instanceof Use_) {
|
||||
// Store a reference to every "use" statement, because we'll need
|
||||
// them in a bit.
|
||||
foreach ($node->uses as $use) {
|
||||
$alias = $use->alias ?: \end($use->name->parts);
|
||||
$this->aliases[\strtolower($alias)] = $use->name;
|
||||
}
|
||||
|
||||
return NodeTraverser::REMOVE_NODE;
|
||||
} elseif ($node instanceof GroupUse) {
|
||||
// Expand every "use" statement in the group into a full, standalone
|
||||
// "use" and store 'em with the others.
|
||||
}
|
||||
|
||||
// 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, [
|
||||
@@ -86,23 +91,32 @@ class UseStatementPass extends CodeCleanerPass
|
||||
}
|
||||
|
||||
return NodeTraverser::REMOVE_NODE;
|
||||
} elseif ($node instanceof Namespace_) {
|
||||
// Start fresh, since we're done with this namespace.
|
||||
}
|
||||
|
||||
// Start fresh, since we're done with this namespace.
|
||||
if ($node instanceof Namespace_) {
|
||||
$this->lastNamespace = $node->name;
|
||||
$this->lastAliases = $this->aliases;
|
||||
$this->aliases = [];
|
||||
} else {
|
||||
foreach ($node as $name => $subNode) {
|
||||
if ($subNode instanceof Name) {
|
||||
// Implicitly thunk all aliases.
|
||||
if ($replacement = $this->findAlias($subNode)) {
|
||||
$node->$name = $replacement;
|
||||
}
|
||||
$this->lastAliases = $this->aliases;
|
||||
$this->aliases = [];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Do nothing with UseUse; this an entry in the list of uses in the use statement.
|
||||
if ($node instanceof UseUse) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For everything else, we'll implicitly thunk all aliases into fully-qualified names.
|
||||
foreach ($node as $name => $subNode) {
|
||||
if ($subNode instanceof Name) {
|
||||
if ($replacement = $this->findAlias($subNode)) {
|
||||
$node->$name = $replacement;
|
||||
}
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,8 +132,8 @@ class UseStatementPass extends CodeCleanerPass
|
||||
foreach ($this->aliases as $alias => $prefix) {
|
||||
if ($that === $alias) {
|
||||
return new FullyQualifiedName($prefix->toString());
|
||||
} elseif (\substr($that, 0, \strlen($alias) + 1) === $alias . '\\') {
|
||||
return new FullyQualifiedName($prefix->toString() . \substr($name, \strlen($alias)));
|
||||
} elseif (\substr($that, 0, \strlen($alias) + 1) === $alias.'\\') {
|
||||
return new FullyQualifiedName($prefix->toString().\substr($name, \strlen($alias)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -13,9 +13,7 @@ namespace Psy\CodeCleaner;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Expr\New_;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Expr\Ternary;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Do_;
|
||||
@@ -34,17 +32,11 @@ use Psy\Exception\FatalErrorException;
|
||||
*/
|
||||
class ValidClassNamePass extends NamespaceAwarePass
|
||||
{
|
||||
const CLASS_TYPE = 'class';
|
||||
const CLASS_TYPE = 'class';
|
||||
const INTERFACE_TYPE = 'interface';
|
||||
const TRAIT_TYPE = 'trait';
|
||||
const TRAIT_TYPE = 'trait';
|
||||
|
||||
private $conditionalScopes = 0;
|
||||
private $atLeastPhp55;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->atLeastPhp55 = \version_compare(PHP_VERSION, '5.5', '>=');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate class, interface and trait definitions.
|
||||
@@ -54,6 +46,8 @@ class ValidClassNamePass extends NamespaceAwarePass
|
||||
* trait methods.
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
@@ -61,51 +55,42 @@ class ValidClassNamePass extends NamespaceAwarePass
|
||||
|
||||
if (self::isConditional($node)) {
|
||||
$this->conditionalScopes++;
|
||||
} else {
|
||||
// @todo add an "else" here which adds a runtime check for instances where we can't tell
|
||||
// whether a class is being redefined by static analysis alone.
|
||||
if ($this->conditionalScopes === 0) {
|
||||
if ($node instanceof Class_) {
|
||||
$this->validateClassStatement($node);
|
||||
} elseif ($node instanceof Interface_) {
|
||||
$this->validateInterfaceStatement($node);
|
||||
} elseif ($node instanceof Trait_) {
|
||||
$this->validateTraitStatement($node);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->conditionalScopes === 0) {
|
||||
if ($node instanceof Class_) {
|
||||
$this->validateClassStatement($node);
|
||||
} elseif ($node instanceof Interface_) {
|
||||
$this->validateInterfaceStatement($node);
|
||||
} elseif ($node instanceof Trait_) {
|
||||
$this->validateTraitStatement($node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate `new` expressions, class constant fetches, and static calls.
|
||||
*
|
||||
* @throws FatalErrorException if a class, interface or trait is referenced which does not exist
|
||||
* @throws FatalErrorException if a class extends something that is not a class
|
||||
* @throws FatalErrorException if a class implements something that is not an interface
|
||||
* @throws FatalErrorException if an interface extends something that is not an interface
|
||||
* @throws FatalErrorException if a class, interface or trait redefines an existing class, interface or trait name
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|Node[]|null Replacement node (or special return value)
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
if (self::isConditional($node)) {
|
||||
$this->conditionalScopes--;
|
||||
} elseif ($node instanceof New_) {
|
||||
$this->validateNewExpression($node);
|
||||
} elseif ($node instanceof ClassConstFetch) {
|
||||
$this->validateClassConstFetchExpression($node);
|
||||
} elseif ($node instanceof StaticCall) {
|
||||
$this->validateStaticCallExpression($node);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static function isConditional(Node $node)
|
||||
private static function isConditional(Node $node): bool
|
||||
{
|
||||
return $node instanceof If_ ||
|
||||
$node instanceof While_ ||
|
||||
$node instanceof Do_ ||
|
||||
$node instanceof Switch_;
|
||||
$node instanceof Switch_ ||
|
||||
$node instanceof Ternary;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,50 +128,6 @@ class ValidClassNamePass extends NamespaceAwarePass
|
||||
$this->ensureCanDefine($stmt, self::TRAIT_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a `new` expression.
|
||||
*
|
||||
* @param New_ $stmt
|
||||
*/
|
||||
protected function validateNewExpression(New_ $stmt)
|
||||
{
|
||||
// if class name is an expression or an anonymous class, give it a pass for now
|
||||
if (!$stmt->class instanceof Expr && !$stmt->class instanceof Class_) {
|
||||
$this->ensureClassExists($this->getFullyQualifiedName($stmt->class), $stmt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a class constant fetch expression's class.
|
||||
*
|
||||
* @param ClassConstFetch $stmt
|
||||
*/
|
||||
protected function validateClassConstFetchExpression(ClassConstFetch $stmt)
|
||||
{
|
||||
// there is no need to check exists for ::class const for php 5.5 or newer
|
||||
if (\strtolower($stmt->name) === 'class' && $this->atLeastPhp55) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if class name is an expression, give it a pass for now
|
||||
if (!$stmt->class instanceof Expr) {
|
||||
$this->ensureClassOrInterfaceExists($this->getFullyQualifiedName($stmt->class), $stmt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a class constant fetch expression's class.
|
||||
*
|
||||
* @param StaticCall $stmt
|
||||
*/
|
||||
protected function validateStaticCallExpression(StaticCall $stmt)
|
||||
{
|
||||
// if class name is an expression, give it a pass for now
|
||||
if (!$stmt->class instanceof Expr) {
|
||||
$this->ensureMethodExists($this->getFullyQualifiedName($stmt->class), $stmt->name, $stmt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that no class, interface or trait name collides with a new definition.
|
||||
*
|
||||
@@ -195,8 +136,13 @@ class ValidClassNamePass extends NamespaceAwarePass
|
||||
* @param Stmt $stmt
|
||||
* @param string $scopeType
|
||||
*/
|
||||
protected function ensureCanDefine(Stmt $stmt, $scopeType = self::CLASS_TYPE)
|
||||
protected function ensureCanDefine(Stmt $stmt, string $scopeType = self::CLASS_TYPE)
|
||||
{
|
||||
// Anonymous classes don't have a name, and uniqueness shouldn't be enforced.
|
||||
if ($stmt->name === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$name = $this->getFullyQualifiedName($stmt->name);
|
||||
|
||||
// check for name collisions
|
||||
@@ -226,7 +172,7 @@ class ValidClassNamePass extends NamespaceAwarePass
|
||||
* @param string $name
|
||||
* @param Stmt $stmt
|
||||
*/
|
||||
protected function ensureClassExists($name, $stmt)
|
||||
protected function ensureClassExists(string $name, Stmt $stmt)
|
||||
{
|
||||
if (!$this->classExists($name)) {
|
||||
throw $this->createError(\sprintf('Class \'%s\' not found', $name), $stmt);
|
||||
@@ -241,7 +187,7 @@ class ValidClassNamePass extends NamespaceAwarePass
|
||||
* @param string $name
|
||||
* @param Stmt $stmt
|
||||
*/
|
||||
protected function ensureClassOrInterfaceExists($name, $stmt)
|
||||
protected function ensureClassOrInterfaceExists(string $name, Stmt $stmt)
|
||||
{
|
||||
if (!$this->classExists($name) && !$this->interfaceExists($name)) {
|
||||
throw $this->createError(\sprintf('Class \'%s\' not found', $name), $stmt);
|
||||
@@ -256,7 +202,7 @@ class ValidClassNamePass extends NamespaceAwarePass
|
||||
* @param string $name
|
||||
* @param Stmt $stmt
|
||||
*/
|
||||
protected function ensureClassOrTraitExists($name, $stmt)
|
||||
protected function ensureClassOrTraitExists(string $name, Stmt $stmt)
|
||||
{
|
||||
if (!$this->classExists($name) && !$this->traitExists($name)) {
|
||||
throw $this->createError(\sprintf('Class \'%s\' not found', $name), $stmt);
|
||||
@@ -272,7 +218,7 @@ class ValidClassNamePass extends NamespaceAwarePass
|
||||
* @param string $name
|
||||
* @param Stmt $stmt
|
||||
*/
|
||||
protected function ensureMethodExists($class, $name, $stmt)
|
||||
protected function ensureMethodExists(string $class, string $name, Stmt $stmt)
|
||||
{
|
||||
$this->ensureClassOrTraitExists($class, $stmt);
|
||||
|
||||
@@ -304,7 +250,7 @@ class ValidClassNamePass extends NamespaceAwarePass
|
||||
* @param Interface_[] $interfaces
|
||||
* @param Stmt $stmt
|
||||
*/
|
||||
protected function ensureInterfacesExist($interfaces, $stmt)
|
||||
protected function ensureInterfacesExist(array $interfaces, Stmt $stmt)
|
||||
{
|
||||
foreach ($interfaces as $interface) {
|
||||
/** @var string $name */
|
||||
@@ -321,11 +267,13 @@ class ValidClassNamePass extends NamespaceAwarePass
|
||||
* @deprecated No longer used. Scope type should be passed into ensureCanDefine directly.
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @throws FatalErrorException
|
||||
*
|
||||
* @param Stmt $stmt
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getScopeType(Stmt $stmt)
|
||||
protected function getScopeType(Stmt $stmt): string
|
||||
{
|
||||
if ($stmt instanceof Class_) {
|
||||
return self::CLASS_TYPE;
|
||||
@@ -334,6 +282,8 @@ class ValidClassNamePass extends NamespaceAwarePass
|
||||
} elseif ($stmt instanceof Trait_) {
|
||||
return self::TRAIT_TYPE;
|
||||
}
|
||||
|
||||
throw $this->createError('Unsupported statement type', $stmt);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -345,7 +295,7 @@ class ValidClassNamePass extends NamespaceAwarePass
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function classExists($name)
|
||||
protected function classExists(string $name): bool
|
||||
{
|
||||
// Give `self`, `static` and `parent` a pass. This will actually let
|
||||
// some errors through, since we're not checking whether the keyword is
|
||||
@@ -364,7 +314,7 @@ class ValidClassNamePass extends NamespaceAwarePass
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function interfaceExists($name)
|
||||
protected function interfaceExists(string $name): bool
|
||||
{
|
||||
return \interface_exists($name) || $this->findInScope($name) === self::INTERFACE_TYPE;
|
||||
}
|
||||
@@ -376,7 +326,7 @@ class ValidClassNamePass extends NamespaceAwarePass
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function traitExists($name)
|
||||
protected function traitExists(string $name): bool
|
||||
{
|
||||
return \trait_exists($name) || $this->findInScope($name) === self::TRAIT_TYPE;
|
||||
}
|
||||
@@ -388,7 +338,7 @@ class ValidClassNamePass extends NamespaceAwarePass
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function findInScope($name)
|
||||
protected function findInScope(string $name)
|
||||
{
|
||||
$name = \strtolower($name);
|
||||
if (isset($this->currentScope[$name])) {
|
||||
@@ -404,8 +354,8 @@ class ValidClassNamePass extends NamespaceAwarePass
|
||||
*
|
||||
* @return FatalErrorException
|
||||
*/
|
||||
protected function createError($msg, $stmt)
|
||||
protected function createError(string $msg, Stmt $stmt): FatalErrorException
|
||||
{
|
||||
return new FatalErrorException($msg, 0, E_ERROR, null, $stmt->getLine());
|
||||
return new FatalErrorException($msg, 0, \E_ERROR, null, $stmt->getLine());
|
||||
}
|
||||
}
|
||||
|
@@ -1,90 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Psy\CodeCleaner;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Expr\ConstFetch;
|
||||
use PhpParser\Node\Identifier;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* Validate that namespaced constant references will succeed.
|
||||
*
|
||||
* This pass throws a FatalErrorException rather than letting PHP run
|
||||
* headfirst into a real fatal error and die.
|
||||
*
|
||||
* @todo Detect constants defined in the current code snippet?
|
||||
* ... Might not be worth it, since it would need to both be defining and
|
||||
* referencing a namespaced constant, which doesn't seem like that big of
|
||||
* a target for failure
|
||||
*/
|
||||
class ValidConstantPass extends NamespaceAwarePass
|
||||
{
|
||||
/**
|
||||
* Validate that namespaced constant references will succeed.
|
||||
*
|
||||
* Note that this does not (yet) detect constants defined in the current code
|
||||
* snippet. It won't happen very often, so we'll punt for now.
|
||||
*
|
||||
* @throws FatalErrorException if a constant reference is not defined
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
if ($node instanceof ConstFetch && \count($node->name->parts) > 1) {
|
||||
$name = $this->getFullyQualifiedName($node->name);
|
||||
if (!\defined($name)) {
|
||||
$msg = \sprintf('Undefined constant %s', $name);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
|
||||
}
|
||||
} elseif ($node instanceof ClassConstFetch) {
|
||||
$this->validateClassConstFetchExpression($node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a class constant fetch expression.
|
||||
*
|
||||
* @throws FatalErrorException if a class constant is not defined
|
||||
*
|
||||
* @param ClassConstFetch $stmt
|
||||
*/
|
||||
protected function validateClassConstFetchExpression(ClassConstFetch $stmt)
|
||||
{
|
||||
// For PHP Parser 4.x
|
||||
$constName = $stmt->name instanceof Identifier ? $stmt->name->toString() : $stmt->name;
|
||||
|
||||
// give the `class` pseudo-constant a pass
|
||||
if ($constName === 'class') {
|
||||
return;
|
||||
}
|
||||
|
||||
// if class name is an expression, give it a pass for now
|
||||
if (!$stmt->class instanceof Expr) {
|
||||
$className = $this->getFullyQualifiedName($stmt->class);
|
||||
|
||||
// if the class doesn't exist, don't throw an exception… it might be
|
||||
// defined in the same line it's used or something stupid like that.
|
||||
if (\class_exists($className) || \interface_exists($className)) {
|
||||
$refl = new \ReflectionClass($className);
|
||||
if (!$refl->hasConstant($constName)) {
|
||||
$constType = \class_exists($className) ? 'Class' : 'Interface';
|
||||
$msg = \sprintf('%s constant \'%s::%s\' not found', $constType, $className, $constName);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $stmt->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -35,6 +35,9 @@ class ValidConstructorPass extends CodeCleanerPass
|
||||
{
|
||||
private $namespace;
|
||||
|
||||
/**
|
||||
* @return Node[]|null Array of nodes
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
$this->namespace = [];
|
||||
@@ -47,6 +50,8 @@ class ValidConstructorPass extends CodeCleanerPass
|
||||
* @throws FatalErrorException the constructor function has a return type
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
@@ -94,7 +99,7 @@ class ValidConstructorPass extends CodeCleanerPass
|
||||
\implode('\\', \array_merge($this->namespace, (array) $className)),
|
||||
$constructor->name
|
||||
);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $classNode->getLine());
|
||||
throw new FatalErrorException($msg, 0, \E_ERROR, null, $classNode->getLine());
|
||||
}
|
||||
|
||||
if (\method_exists($constructor, 'getReturnType') && $constructor->getReturnType()) {
|
||||
@@ -106,7 +111,7 @@ class ValidConstructorPass extends CodeCleanerPass
|
||||
\implode('\\', \array_merge($this->namespace, (array) $className)),
|
||||
$constructor->name
|
||||
);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $classNode->getLine());
|
||||
throw new FatalErrorException($msg, 0, \E_ERROR, null, $classNode->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2018 Justin Hileman
|
||||
* (c) 2012-2022 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -12,9 +12,6 @@
|
||||
namespace Psy\CodeCleaner;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Stmt\Do_;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\If_;
|
||||
@@ -35,7 +32,11 @@ class ValidFunctionNamePass extends NamespaceAwarePass
|
||||
/**
|
||||
* Store newly defined function names on the way in, to allow recursion.
|
||||
*
|
||||
* @throws FatalErrorException if a function is redefined in a non-conditional scope
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|null Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
@@ -52,7 +53,7 @@ class ValidFunctionNamePass extends NamespaceAwarePass
|
||||
if (\function_exists($name) ||
|
||||
isset($this->currentScope[\strtolower($name)])) {
|
||||
$msg = \sprintf('Cannot redeclare %s()', $name);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
|
||||
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,29 +62,14 @@ class ValidFunctionNamePass extends NamespaceAwarePass
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that function calls will succeed.
|
||||
*
|
||||
* @throws FatalErrorException if a function is redefined
|
||||
* @throws FatalErrorException if the function name is a string (not an expression) and is not defined
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return int|Node|Node[]|null Replacement node (or special return value)
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
if (self::isConditional($node)) {
|
||||
$this->conditionalScopes--;
|
||||
} elseif ($node instanceof FuncCall) {
|
||||
// if function name is an expression or a variable, give it a pass for now.
|
||||
$name = $node->name;
|
||||
if (!$name instanceof Expr && !$name instanceof Variable) {
|
||||
$shortName = \implode('\\', $name->parts);
|
||||
$fullName = $this->getFullyQualifiedName($name);
|
||||
$inScope = isset($this->currentScope[\strtolower($fullName)]);
|
||||
if (!$inScope && !\function_exists($shortName) && !\function_exists($fullName)) {
|
||||
$message = \sprintf('Call to undefined function %s()', $name);
|
||||
throw new FatalErrorException($message, 0, E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user