Composer update
* updated Laravel to v5.6.38 * Added laravel tinker in dev dependencies
This commit is contained in:

committed by
Manish Verma

parent
be4b1231b6
commit
6742e13d81
71
vendor/psy/psysh/src/CodeCleaner/AbstractClassPass.php
vendored
Normal file
71
vendor/psy/psysh/src/CodeCleaner/AbstractClassPass.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?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\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* The abstract class pass handles abstract classes and methods, complaining if there are too few or too many of either.
|
||||
*/
|
||||
class AbstractClassPass extends CodeCleanerPass
|
||||
{
|
||||
private $class;
|
||||
private $abstractMethods;
|
||||
|
||||
/**
|
||||
* @throws RuntimeException if the node is an abstract function with a body
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Class_) {
|
||||
$this->class = $node;
|
||||
$this->abstractMethods = [];
|
||||
} elseif ($node instanceof ClassMethod) {
|
||||
if ($node->isAbstract()) {
|
||||
$name = \sprintf('%s::%s', $this->class->name, $node->name);
|
||||
$this->abstractMethods[] = $name;
|
||||
|
||||
if ($node->stmts !== null) {
|
||||
$msg = \sprintf('Abstract function %s cannot contain body', $name);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException if the node is a non-abstract class with abstract methods
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Class_) {
|
||||
$count = \count($this->abstractMethods);
|
||||
if ($count > 0 && !$node->isAbstract()) {
|
||||
$msg = \sprintf(
|
||||
'Class %s contains %d abstract method%s must therefore be declared abstract or implement the remaining methods (%s)',
|
||||
$node->name,
|
||||
$count,
|
||||
($count === 1) ? '' : 's',
|
||||
\implode(', ', $this->abstractMethods)
|
||||
);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
39
vendor/psy/psysh/src/CodeCleaner/AssignThisVariablePass.php
vendored
Normal file
39
vendor/psy/psysh/src/CodeCleaner/AssignThisVariablePass.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?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\Assign;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* Validate that the user input does not assign the `$this` variable.
|
||||
*
|
||||
* @author Martin Hasoň <martin.hason@gmail.com>
|
||||
*/
|
||||
class AssignThisVariablePass extends CodeCleanerPass
|
||||
{
|
||||
/**
|
||||
* Validate that the user input does not assign the `$this` variable.
|
||||
*
|
||||
* @throws RuntimeException if the user assign the `$this` variable
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
50
vendor/psy/psysh/src/CodeCleaner/CallTimePassByReferencePass.php
vendored
Normal file
50
vendor/psy/psysh/src/CodeCleaner/CallTimePassByReferencePass.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?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\FuncCall;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* Validate that the user did not use the call-time pass-by-reference that causes a fatal error.
|
||||
*
|
||||
* As of PHP 5.4.0, call-time pass-by-reference was removed, so using it will raise a fatal error.
|
||||
*
|
||||
* @author Martin Hasoň <martin.hason@gmail.com>
|
||||
*/
|
||||
class CallTimePassByReferencePass extends CodeCleanerPass
|
||||
{
|
||||
const EXCEPTION_MESSAGE = 'Call-time pass-by-reference has been removed';
|
||||
|
||||
/**
|
||||
* Validate of use call-time pass-by-reference.
|
||||
*
|
||||
* @throws RuntimeException if the user used call-time pass-by-reference
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if (!$node instanceof FuncCall && !$node instanceof MethodCall && !$node instanceof StaticCall) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($node->args as $arg) {
|
||||
if ($arg->byRef) {
|
||||
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
83
vendor/psy/psysh/src/CodeCleaner/CalledClassPass.php
vendored
Normal file
83
vendor/psy/psysh/src/CodeCleaner/CalledClassPass.php
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
<?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\ConstFetch;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Trait_;
|
||||
use Psy\Exception\ErrorException;
|
||||
|
||||
/**
|
||||
* The called class pass throws warnings for get_class() and get_called_class()
|
||||
* outside a class context.
|
||||
*/
|
||||
class CalledClassPass extends CodeCleanerPass
|
||||
{
|
||||
private $inClass;
|
||||
|
||||
/**
|
||||
* @param array $nodes
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
$this->inClass = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ErrorException if get_class or get_called_class is called without an object from outside a class
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Class_ || $node instanceof Trait_) {
|
||||
$this->inClass = true;
|
||||
} elseif ($node instanceof FuncCall && !$this->inClass) {
|
||||
// We'll give any args at all (besides null) a pass.
|
||||
// Technically we should be checking whether the args are objects, but this will do for now.
|
||||
//
|
||||
// @todo switch this to actually validate args when we get context-aware code cleaner passes.
|
||||
if (!empty($node->args) && !$this->isNull($node->args[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We'll ignore name expressions as well (things like `$foo()`)
|
||||
if (!($node->name instanceof Name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node $node
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Class_) {
|
||||
$this->inClass = false;
|
||||
}
|
||||
}
|
||||
|
||||
private function isNull(Node $node)
|
||||
{
|
||||
return $node->value instanceof ConstFetch && \strtolower($node->value->name) === 'null';
|
||||
}
|
||||
}
|
22
vendor/psy/psysh/src/CodeCleaner/CodeCleanerPass.php
vendored
Normal file
22
vendor/psy/psysh/src/CodeCleaner/CodeCleanerPass.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?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\NodeVisitorAbstract;
|
||||
|
||||
/**
|
||||
* A CodeCleaner pass is a PhpParser Node Visitor.
|
||||
*/
|
||||
abstract class CodeCleanerPass extends NodeVisitorAbstract
|
||||
{
|
||||
// Wheee!
|
||||
}
|
32
vendor/psy/psysh/src/CodeCleaner/ExitPass.php
vendored
Normal file
32
vendor/psy/psysh/src/CodeCleaner/ExitPass.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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\Exit_;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
|
||||
|
||||
class ExitPass extends CodeCleanerPass
|
||||
{
|
||||
/**
|
||||
* Converts exit calls to BreakExceptions.
|
||||
*
|
||||
* @param \PhpParser\Node $node
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Exit_) {
|
||||
return new StaticCall(new FullyQualifiedName('Psy\Exception\BreakException'), 'exitShell');
|
||||
}
|
||||
}
|
||||
}
|
70
vendor/psy/psysh/src/CodeCleaner/FinalClassPass.php
vendored
Normal file
70
vendor/psy/psysh/src/CodeCleaner/FinalClassPass.php
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
<?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\Stmt\Class_;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* The final class pass handles final classes.
|
||||
*/
|
||||
class FinalClassPass extends CodeCleanerPass
|
||||
{
|
||||
private $finalClasses;
|
||||
|
||||
/**
|
||||
* @param array $nodes
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
$this->finalClasses = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException if the node is a class that extends a final class
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Class_) {
|
||||
if ($node->extends) {
|
||||
$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());
|
||||
}
|
||||
}
|
||||
|
||||
if ($node->isFinal()) {
|
||||
$this->finalClasses[\strtolower($node->name)] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name Class name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isFinalClass($name)
|
||||
{
|
||||
if (!\class_exists($name)) {
|
||||
return isset($this->finalClasses[\strtolower($name)]);
|
||||
}
|
||||
|
||||
$refl = new \ReflectionClass($name);
|
||||
|
||||
return $refl->isFinal();
|
||||
}
|
||||
}
|
61
vendor/psy/psysh/src/CodeCleaner/FunctionContextPass.php
vendored
Normal file
61
vendor/psy/psysh/src/CodeCleaner/FunctionContextPass.php
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
<?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\Yield_;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
class FunctionContextPass extends CodeCleanerPass
|
||||
{
|
||||
/** @var int */
|
||||
private $functionDepth;
|
||||
|
||||
/**
|
||||
* @param array $nodes
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
$this->functionDepth = 0;
|
||||
}
|
||||
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if ($node instanceof FunctionLike) {
|
||||
$this->functionDepth++;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// node is inside function context
|
||||
if ($this->functionDepth !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \PhpParser\Node $node
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
if ($node instanceof FunctionLike) {
|
||||
$this->functionDepth--;
|
||||
}
|
||||
}
|
||||
}
|
81
vendor/psy/psysh/src/CodeCleaner/FunctionReturnInWriteContextPass.php
vendored
Normal file
81
vendor/psy/psysh/src/CodeCleaner/FunctionReturnInWriteContextPass.php
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
<?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\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 Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* Validate that the functions are used correctly.
|
||||
*
|
||||
* @author Martin Hasoň <martin.hason@gmail.com>
|
||||
*/
|
||||
class FunctionReturnInWriteContextPass extends CodeCleanerPass
|
||||
{
|
||||
const PHP55_MESSAGE = 'Cannot use isset() on the result of a function call (you can use "null !== func()" 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
|
||||
*/
|
||||
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 && $item->byRef && $this->isCallNode($item->value)) {
|
||||
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, E_ERROR, null, $node->getLine());
|
||||
}
|
||||
}
|
||||
} elseif ($node instanceof Isset_ || $node instanceof Unset_) {
|
||||
foreach ($node->vars as $var) {
|
||||
if (!$this->isCallNode($var)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$msg = ($node instanceof Isset_ && $this->atLeastPhp55) ? self::PHP55_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());
|
||||
}
|
||||
}
|
||||
|
||||
private function isCallNode(Node $node)
|
||||
{
|
||||
return $node instanceof FuncCall || $node instanceof MethodCall || $node instanceof StaticCall;
|
||||
}
|
||||
}
|
128
vendor/psy/psysh/src/CodeCleaner/ImplicitReturnPass.php
vendored
Normal file
128
vendor/psy/psysh/src/CodeCleaner/ImplicitReturnPass.php
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
<?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\Exit_;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\Stmt\Break_;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\Node\Stmt\If_;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PhpParser\Node\Stmt\Switch_;
|
||||
|
||||
/**
|
||||
* Add an implicit "return" to the last statement, provided it can be returned.
|
||||
*/
|
||||
class ImplicitReturnPass extends CodeCleanerPass
|
||||
{
|
||||
/**
|
||||
* @param array $nodes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
return $this->addImplicitReturn($nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $nodes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function addImplicitReturn(array $nodes)
|
||||
{
|
||||
// If nodes is empty, it can't have a return value.
|
||||
if (empty($nodes)) {
|
||||
return [new Return_(NoReturnValue::create())];
|
||||
}
|
||||
|
||||
$last = \end($nodes);
|
||||
|
||||
// Special case a few types of statements to add an implicit return
|
||||
// value (even though they technically don't have any return value)
|
||||
// because showing a return value in these instances is useful and not
|
||||
// very surprising.
|
||||
if ($last instanceof If_) {
|
||||
$last->stmts = $this->addImplicitReturn($last->stmts);
|
||||
|
||||
foreach ($last->elseifs as $elseif) {
|
||||
$elseif->stmts = $this->addImplicitReturn($elseif->stmts);
|
||||
}
|
||||
|
||||
if ($last->else) {
|
||||
$last->else->stmts = $this->addImplicitReturn($last->else->stmts);
|
||||
}
|
||||
} elseif ($last instanceof Switch_) {
|
||||
foreach ($last->cases as $case) {
|
||||
// only add an implicit return to cases which end in break
|
||||
$caseLast = \end($case->stmts);
|
||||
if ($caseLast instanceof Break_) {
|
||||
$case->stmts = $this->addImplicitReturn(\array_slice($case->stmts, 0, -1));
|
||||
$case->stmts[] = $caseLast;
|
||||
}
|
||||
}
|
||||
} elseif ($last instanceof Expr && !($last instanceof Exit_)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
$nodes[\count($nodes) - 1] = new Return_($last, [
|
||||
'startLine' => $last->getLine(),
|
||||
'endLine' => $last->getLine(),
|
||||
]);
|
||||
// @codeCoverageIgnoreEnd
|
||||
} elseif ($last instanceof Expression && !($last->expr instanceof Exit_)) {
|
||||
// For PHP Parser 4.x
|
||||
$nodes[\count($nodes) - 1] = new Return_($last->expr, [
|
||||
'startLine' => $last->getLine(),
|
||||
'endLine' => $last->getLine(),
|
||||
]);
|
||||
} elseif ($last instanceof Namespace_) {
|
||||
$last->stmts = $this->addImplicitReturn($last->stmts);
|
||||
}
|
||||
|
||||
// Return a "no return value" for all non-expression statements, so that
|
||||
// PsySH can suppress the `null` that `eval()` returns otherwise.
|
||||
//
|
||||
// Note that statements special cased above (if/elseif/else, switch)
|
||||
// _might_ implicitly return a value before this catch-all return is
|
||||
// reached.
|
||||
//
|
||||
// We're not adding a fallback return after namespace statements,
|
||||
// because code outside namespace statements doesn't really work, and
|
||||
// there's already an implicit return in the namespace statement anyway.
|
||||
if (self::isNonExpressionStmt($last)) {
|
||||
$nodes[] = new Return_(NoReturnValue::create());
|
||||
}
|
||||
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given node is a non-expression statement.
|
||||
*
|
||||
* As of PHP Parser 4.x, Expressions are now instances of Stmt as well, so
|
||||
* we'll exclude them here.
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function isNonExpressionStmt(Node $node)
|
||||
{
|
||||
return $node instanceof Stmt &&
|
||||
!$node instanceof Expression &&
|
||||
!$node instanceof Return_ &&
|
||||
!$node instanceof Namespace_;
|
||||
}
|
||||
}
|
47
vendor/psy/psysh/src/CodeCleaner/InstanceOfPass.php
vendored
Normal file
47
vendor/psy/psysh/src/CodeCleaner/InstanceOfPass.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?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\ConstFetch;
|
||||
use PhpParser\Node\Expr\Instanceof_;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Node\Scalar\Encapsed;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* Validate that the instanceof statement does not receive a scalar value or a non-class constant.
|
||||
*
|
||||
* @author Martin Hasoň <martin.hason@gmail.com>
|
||||
*/
|
||||
class InstanceOfPass extends CodeCleanerPass
|
||||
{
|
||||
const EXCEPTION_MSG = 'instanceof expects an object instance, constant given';
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
36
vendor/psy/psysh/src/CodeCleaner/LeavePsyshAlonePass.php
vendored
Normal file
36
vendor/psy/psysh/src/CodeCleaner/LeavePsyshAlonePass.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?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\Variable;
|
||||
use Psy\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* Validate that the user input does not reference the `$__psysh__` variable.
|
||||
*/
|
||||
class LeavePsyshAlonePass extends CodeCleanerPass
|
||||
{
|
||||
/**
|
||||
* Validate that the user input does not reference the `$__psysh__` variable.
|
||||
*
|
||||
* @throws RuntimeException if the user is messing with $__psysh__
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Variable && $node->name === '__psysh__') {
|
||||
throw new RuntimeException('Don\'t mess with $__psysh__; bad things will happen');
|
||||
}
|
||||
}
|
||||
}
|
73
vendor/psy/psysh/src/CodeCleaner/LegacyEmptyPass.php
vendored
Normal file
73
vendor/psy/psysh/src/CodeCleaner/LegacyEmptyPass.php
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
}
|
112
vendor/psy/psysh/src/CodeCleaner/ListPass.php
vendored
Normal file
112
vendor/psy/psysh/src/CodeCleaner/ListPass.php
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
<?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\Array_;
|
||||
use PhpParser\Node\Expr\ArrayDimFetch;
|
||||
use PhpParser\Node\Expr\ArrayItem;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\List_;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use Psy\Exception\ParseErrorException;
|
||||
|
||||
/**
|
||||
* Validate that the list assignment.
|
||||
*/
|
||||
class ListPass extends CodeCleanerPass
|
||||
{
|
||||
private $atLeastPhp71;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->atLeastPhp71 = \version_compare(PHP_VERSION, '7.1', '>=');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate use of list assignment.
|
||||
*
|
||||
* @throws ParseErrorException if the user used empty with anything but a variable
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if (!$node instanceof Assign) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$node->var instanceof Array_ && !$node->var instanceof List_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->atLeastPhp71 && $node->var instanceof Array_) {
|
||||
$msg = "syntax error, unexpected '='";
|
||||
throw new ParseErrorException($msg, $node->expr->getLine());
|
||||
}
|
||||
|
||||
// Polyfill for PHP-Parser 2.x
|
||||
$items = isset($node->var->items) ? $node->var->items : $node->var->vars;
|
||||
|
||||
if ($items === [] || $items === [null]) {
|
||||
throw new ParseErrorException('Cannot use empty list', $node->var->getLine());
|
||||
}
|
||||
|
||||
$itemFound = false;
|
||||
foreach ($items as $item) {
|
||||
if ($item === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$itemFound = true;
|
||||
|
||||
// List_->$vars in PHP-Parser 2.x is Variable instead of ArrayItem.
|
||||
if (!$this->atLeastPhp71 && $item instanceof ArrayItem && $item->key !== null) {
|
||||
$msg = 'Syntax error, unexpected T_CONSTANT_ENCAPSED_STRING, expecting \',\' or \')\'';
|
||||
throw new ParseErrorException($msg, $item->key->getLine());
|
||||
}
|
||||
|
||||
if (!self::isValidArrayItem($item)) {
|
||||
$msg = 'Assignments can only happen to writable values';
|
||||
throw new ParseErrorException($msg, $item->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
if (!$itemFound) {
|
||||
throw new ParseErrorException('Cannot use empty list');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate whether a given item in an array is valid for short assignment.
|
||||
*
|
||||
* @param Expr $item
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function isValidArrayItem(Expr $item)
|
||||
{
|
||||
$value = ($item instanceof ArrayItem) ? $item->value : $item;
|
||||
|
||||
if ($value instanceof Variable) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($value instanceof ArrayDimFetch || $value instanceof PropertyFetch) {
|
||||
return isset($value->var) && $value->var instanceof Variable;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
103
vendor/psy/psysh/src/CodeCleaner/LoopContextPass.php
vendored
Normal file
103
vendor/psy/psysh/src/CodeCleaner/LoopContextPass.php
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
<?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\Scalar\DNumber;
|
||||
use PhpParser\Node\Scalar\LNumber;
|
||||
use PhpParser\Node\Stmt\Break_;
|
||||
use PhpParser\Node\Stmt\Continue_;
|
||||
use PhpParser\Node\Stmt\Do_;
|
||||
use PhpParser\Node\Stmt\For_;
|
||||
use PhpParser\Node\Stmt\Foreach_;
|
||||
use PhpParser\Node\Stmt\Switch_;
|
||||
use PhpParser\Node\Stmt\While_;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* The loop context pass handles invalid `break` and `continue` statements.
|
||||
*/
|
||||
class LoopContextPass extends CodeCleanerPass
|
||||
{
|
||||
private $loopDepth;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
$this->loopDepth = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FatalErrorException if the node is a break or continue in a non-loop or switch context
|
||||
* @throws FatalErrorException if the node is trying to break out of more nested structures than exist
|
||||
* @throws FatalErrorException if the node is a break or continue and has a non-numeric argument
|
||||
* @throws FatalErrorException if the node is a break or continue and has an argument less than 1
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
switch (true) {
|
||||
case $node instanceof Do_:
|
||||
case $node instanceof For_:
|
||||
case $node instanceof Foreach_:
|
||||
case $node instanceof Switch_:
|
||||
case $node instanceof While_:
|
||||
$this->loopDepth++;
|
||||
break;
|
||||
|
||||
case $node instanceof Break_:
|
||||
case $node instanceof Continue_:
|
||||
$operator = $node instanceof Break_ ? 'break' : 'continue';
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
if ($num > $this->loopDepth) {
|
||||
$msg = \sprintf("Cannot '%s' %d levels", $operator, $num);
|
||||
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());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node $node
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
switch (true) {
|
||||
case $node instanceof Do_:
|
||||
case $node instanceof For_:
|
||||
case $node instanceof Foreach_:
|
||||
case $node instanceof Switch_:
|
||||
case $node instanceof While_:
|
||||
$this->loopDepth--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
42
vendor/psy/psysh/src/CodeCleaner/MagicConstantsPass.php
vendored
Normal file
42
vendor/psy/psysh/src/CodeCleaner/MagicConstantsPass.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?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\FuncCall;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Scalar\MagicConst\Dir;
|
||||
use PhpParser\Node\Scalar\MagicConst\File;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
|
||||
/**
|
||||
* Swap out __DIR__ and __FILE__ magic constants with our best guess?
|
||||
*/
|
||||
class MagicConstantsPass extends CodeCleanerPass
|
||||
{
|
||||
/**
|
||||
* Swap out __DIR__ and __FILE__ constants, because the default ones when
|
||||
* calling eval() don't make sense.
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return null|FuncCall|String_
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Dir) {
|
||||
return new FuncCall(new Name('getcwd'), [], $node->getAttributes());
|
||||
} elseif ($node instanceof File) {
|
||||
return new String_('', $node->getAttributes());
|
||||
}
|
||||
}
|
||||
}
|
71
vendor/psy/psysh/src/CodeCleaner/NamespaceAwarePass.php
vendored
Normal file
71
vendor/psy/psysh/src/CodeCleaner/NamespaceAwarePass.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?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\Name;
|
||||
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
|
||||
/**
|
||||
* Abstract namespace-aware code cleaner pass.
|
||||
*/
|
||||
abstract class NamespaceAwarePass extends CodeCleanerPass
|
||||
{
|
||||
protected $namespace;
|
||||
protected $currentScope;
|
||||
|
||||
/**
|
||||
* @todo should this be final? Extending classes should be sure to either
|
||||
* use afterTraverse or call parent::beforeTraverse() when overloading.
|
||||
*
|
||||
* Reset the namespace and the current scope before beginning analysis
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
$this->namespace = [];
|
||||
$this->currentScope = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo should this be final? Extending classes should be sure to either use
|
||||
* leaveNode or call parent::enterNode() when overloading
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Namespace_) {
|
||||
$this->namespace = isset($node->name) ? $node->name->parts : [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a fully-qualified name (class, function, interface, etc).
|
||||
*
|
||||
* @param mixed $name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getFullyQualifiedName($name)
|
||||
{
|
||||
if ($name instanceof FullyQualifiedName) {
|
||||
return \implode('\\', $name->parts);
|
||||
} elseif ($name instanceof Name) {
|
||||
$name = $name->parts;
|
||||
} elseif (!\is_array($name)) {
|
||||
$name = [$name];
|
||||
}
|
||||
|
||||
return \implode('\\', \array_merge($this->namespace, $name));
|
||||
}
|
||||
}
|
88
vendor/psy/psysh/src/CodeCleaner/NamespacePass.php
vendored
Normal file
88
vendor/psy/psysh/src/CodeCleaner/NamespacePass.php
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
<?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\Name;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use Psy\CodeCleaner;
|
||||
|
||||
/**
|
||||
* Provide implicit namespaces for subsequent execution.
|
||||
*
|
||||
* The namespace pass remembers the last standalone namespace line encountered:
|
||||
*
|
||||
* namespace Foo\Bar;
|
||||
*
|
||||
* ... which it then applies implicitly to all future evaluated code, until the
|
||||
* namespace is replaced by another namespace. To reset to the top level
|
||||
* namespace, enter `namespace {}`. This is a bit ugly, but it does the trick :)
|
||||
*/
|
||||
class NamespacePass extends CodeCleanerPass
|
||||
{
|
||||
private $namespace = null;
|
||||
private $cleaner;
|
||||
|
||||
/**
|
||||
* @param CodeCleaner $cleaner
|
||||
*/
|
||||
public function __construct(CodeCleaner $cleaner)
|
||||
{
|
||||
$this->cleaner = $cleaner;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is a standalone namespace line, remember it for later.
|
||||
*
|
||||
* Otherwise, apply remembered namespaces to the code until a new namespace
|
||||
* is encountered.
|
||||
*
|
||||
* @param array $nodes
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
if (empty($nodes)) {
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
$last = \end($nodes);
|
||||
|
||||
if ($last instanceof Namespace_) {
|
||||
$kind = $last->getAttribute('kind');
|
||||
|
||||
// Treat all namespace statements pre-PHP-Parser v3.1.2 as "open",
|
||||
// even though we really have no way of knowing.
|
||||
if ($kind === null || $kind === Namespace_::KIND_SEMICOLON) {
|
||||
// Save the current namespace for open namespaces
|
||||
$this->setNamespace($last->name);
|
||||
} else {
|
||||
// Clear the current namespace after a braced namespace
|
||||
$this->setNamespace(null);
|
||||
}
|
||||
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
return $this->namespace ? [new Namespace_($this->namespace, $nodes)] : $nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remember the namespace and (re)set the namespace on the CodeCleaner as
|
||||
* well.
|
||||
*
|
||||
* @param null|Name $namespace
|
||||
*/
|
||||
private function setNamespace($namespace)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
$this->cleaner->setNamespace($namespace === null ? null : $namespace->parts);
|
||||
}
|
||||
}
|
35
vendor/psy/psysh/src/CodeCleaner/NoReturnValue.php
vendored
Normal file
35
vendor/psy/psysh/src/CodeCleaner/NoReturnValue.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?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\Expr\New_;
|
||||
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
|
||||
|
||||
/**
|
||||
* A class used internally by CodeCleaner to represent input, such as
|
||||
* non-expression statements, with no return value.
|
||||
*
|
||||
* Note that user code returning an instance of this class will act like it
|
||||
* has no return value, so you prolly shouldn't do that.
|
||||
*/
|
||||
class NoReturnValue
|
||||
{
|
||||
/**
|
||||
* Get PhpParser AST expression for creating a new NoReturnValue.
|
||||
*
|
||||
* @return PhpParser\Node\Expr\New_
|
||||
*/
|
||||
public static function create()
|
||||
{
|
||||
return new New_(new FullyQualifiedName('Psy\CodeCleaner\NoReturnValue'));
|
||||
}
|
||||
}
|
109
vendor/psy/psysh/src/CodeCleaner/PassableByReferencePass.php
vendored
Normal file
109
vendor/psy/psysh/src/CodeCleaner/PassableByReferencePass.php
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
<?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\FuncCall;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* Validate that only variables (and variable-like things) are passed by reference.
|
||||
*/
|
||||
class PassableByReferencePass extends CodeCleanerPass
|
||||
{
|
||||
const EXCEPTION_MESSAGE = 'Only variables can be passed by reference';
|
||||
|
||||
/**
|
||||
* @throws FatalErrorException if non-variables are passed by reference
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
// @todo support MethodCall and StaticCall as well.
|
||||
if ($node instanceof FuncCall) {
|
||||
// if function name is an expression or a variable, give it a pass for now.
|
||||
if ($node->name instanceof Expr || $node->name instanceof Variable) {
|
||||
return;
|
||||
}
|
||||
|
||||
$name = (string) $node->name;
|
||||
|
||||
if ($name === 'array_multisort') {
|
||||
return $this->validateArrayMultisort($node);
|
||||
}
|
||||
|
||||
try {
|
||||
$refl = new \ReflectionFunction($name);
|
||||
} catch (\ReflectionException $e) {
|
||||
// Well, we gave it a shot!
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($refl->getParameters() as $key => $param) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function isPassableByReference(Node $arg)
|
||||
{
|
||||
// FuncCall, MethodCall and StaticCall are all PHP _warnings_ not fatal errors, so we'll let
|
||||
// PHP handle those ones :)
|
||||
return $arg->value instanceof ClassConstFetch ||
|
||||
$arg->value instanceof PropertyFetch ||
|
||||
$arg->value instanceof Variable ||
|
||||
$arg->value instanceof FuncCall ||
|
||||
$arg->value instanceof MethodCall ||
|
||||
$arg->value instanceof StaticCall;
|
||||
}
|
||||
|
||||
/**
|
||||
* Because array_multisort has a problematic signature...
|
||||
*
|
||||
* The argument order is all sorts of wonky, and whether something is passed
|
||||
* by reference or not depends on the values of the two arguments before it.
|
||||
* We'll do a good faith attempt at validating this, but err on the side of
|
||||
* permissive.
|
||||
*
|
||||
* This is why you don't design languages where core code and extensions can
|
||||
* implement APIs that wouldn't be possible in userland code.
|
||||
*
|
||||
* @throws FatalErrorException for clearly invalid arguments
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
private function validateArrayMultisort(Node $node)
|
||||
{
|
||||
$nonPassable = 2; // start with 2 because the first one has to be passable by reference
|
||||
foreach ($node->args as $arg) {
|
||||
if ($this->isPassableByReference($arg)) {
|
||||
$nonPassable = 0;
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
101
vendor/psy/psysh/src/CodeCleaner/RequirePass.php
vendored
Normal file
101
vendor/psy/psysh/src/CodeCleaner/RequirePass.php
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
<?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\Arg;
|
||||
use PhpParser\Node\Expr\Include_;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
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.
|
||||
*/
|
||||
class RequirePass extends CodeCleanerPass
|
||||
{
|
||||
private static $requireTypes = [Include_::TYPE_REQUIRE, Include_::TYPE_REQUIRE_ONCE];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function enterNode(Node $origNode)
|
||||
{
|
||||
if (!$this->isRequireNode($origNode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$node = clone $origNode;
|
||||
|
||||
/*
|
||||
* rewrite
|
||||
*
|
||||
* $foo = require $bar
|
||||
*
|
||||
* to
|
||||
*
|
||||
* $foo = require \Psy\CodeCleaner\RequirePass::resolve($bar)
|
||||
*/
|
||||
$node->expr = new StaticCall(
|
||||
new FullyQualifiedName('Psy\CodeCleaner\RequirePass'),
|
||||
'resolve',
|
||||
[new Arg($origNode->expr), new Arg(new LNumber($origNode->getLine()))],
|
||||
$origNode->getAttributes()
|
||||
);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runtime validation that $file can be resolved as an include path.
|
||||
*
|
||||
* If $file can be resolved, return $file. Otherwise throw a fatal error exception.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public static function resolve($file, $lineNumber = null)
|
||||
{
|
||||
$file = (string) $file;
|
||||
|
||||
if ($file === '') {
|
||||
// @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);
|
||||
}
|
||||
// @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)) {
|
||||
$msg = \sprintf("Failed opening required '%s'", $file);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $lineNumber);
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
private function isRequireNode(Node $node)
|
||||
{
|
||||
return $node instanceof Include_ && \in_array($node->type, self::$requireTypes);
|
||||
}
|
||||
}
|
87
vendor/psy/psysh/src/CodeCleaner/StrictTypesPass.php
vendored
Normal file
87
vendor/psy/psysh/src/CodeCleaner/StrictTypesPass.php
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
<?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\Identifier;
|
||||
use PhpParser\Node\Scalar\LNumber;
|
||||
use PhpParser\Node\Stmt\Declare_;
|
||||
use PhpParser\Node\Stmt\DeclareDeclare;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* Provide implicit strict types declarations for for subsequent execution.
|
||||
*
|
||||
* The strict types pass remembers the last strict types declaration:
|
||||
*
|
||||
* declare(strict_types=1);
|
||||
*
|
||||
* ... which it then applies implicitly to all future evaluated code, until it
|
||||
* is replaced by a new declaration.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* Otherwise, apply remembered strict types declaration to to the code until
|
||||
* a new declaration is encountered.
|
||||
*
|
||||
* @throws FatalErrorException if an invalid `strict_types` declaration is found
|
||||
*
|
||||
* @param array $nodes
|
||||
*/
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
if (!$this->atLeastPhp7) {
|
||||
return; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$prependStrictTypes = $this->strictTypes;
|
||||
|
||||
foreach ($nodes as $key => $node) {
|
||||
if ($node instanceof Declare_) {
|
||||
foreach ($node->declares as $declare) {
|
||||
// For PHP Parser 4.x
|
||||
$declareKey = $declare->key instanceof Identifier ? $declare->key->toString() : $declare->key;
|
||||
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());
|
||||
}
|
||||
|
||||
$this->strictTypes = $value->value === 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($prependStrictTypes) {
|
||||
$first = \reset($nodes);
|
||||
if (!$first instanceof Declare_) {
|
||||
$declare = new Declare_([new DeclareDeclare('strict_types', new LNumber(1))]);
|
||||
\array_unshift($nodes, $declare);
|
||||
}
|
||||
}
|
||||
|
||||
return $nodes;
|
||||
}
|
||||
}
|
126
vendor/psy/psysh/src/CodeCleaner/UseStatementPass.php
vendored
Normal file
126
vendor/psy/psysh/src/CodeCleaner/UseStatementPass.php
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
<?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\Name;
|
||||
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
|
||||
use PhpParser\Node\Stmt\GroupUse;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use PhpParser\Node\Stmt\Use_;
|
||||
use PhpParser\NodeTraverser;
|
||||
|
||||
/**
|
||||
* Provide implicit use statements for subsequent execution.
|
||||
*
|
||||
* The use statement pass remembers the last use statement line encountered:
|
||||
*
|
||||
* use Foo\Bar as Baz;
|
||||
*
|
||||
* ... which it then applies implicitly to all future evaluated code, until the
|
||||
* current namespace is replaced by another namespace.
|
||||
*/
|
||||
class UseStatementPass extends CodeCleanerPass
|
||||
{
|
||||
private $aliases = [];
|
||||
private $lastAliases = [];
|
||||
private $lastNamespace = null;
|
||||
|
||||
/**
|
||||
* Re-load the last set of use statements on re-entering a namespace.
|
||||
*
|
||||
* This isn't how namespaces normally work, but because PsySH has to spin
|
||||
* up a new namespace for every line of code, we do this to make things
|
||||
* work like you'd expect.
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
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)) {
|
||||
$this->aliases = $this->lastAliases;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If this statement is a namespace, forget all the aliases we had.
|
||||
*
|
||||
* If it's a use statement, remember the alias for later. Otherwise, apply
|
||||
* remembered aliases to the code.
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
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.
|
||||
foreach ($node->uses as $use) {
|
||||
$alias = $use->alias ?: \end($use->name->parts);
|
||||
$this->aliases[\strtolower($alias)] = Name::concat($node->prefix, $use->name, [
|
||||
'startLine' => $node->prefix->getAttribute('startLine'),
|
||||
'endLine' => $use->name->getAttribute('endLine'),
|
||||
]);
|
||||
}
|
||||
|
||||
return NodeTraverser::REMOVE_NODE;
|
||||
} elseif ($node instanceof Namespace_) {
|
||||
// Start fresh, since we're done with this 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find class/namespace aliases.
|
||||
*
|
||||
* @param Name $name
|
||||
*
|
||||
* @return FullyQualifiedName|null
|
||||
*/
|
||||
private function findAlias(Name $name)
|
||||
{
|
||||
$that = \strtolower($name);
|
||||
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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
411
vendor/psy/psysh/src/CodeCleaner/ValidClassNamePass.php
vendored
Normal file
411
vendor/psy/psysh/src/CodeCleaner/ValidClassNamePass.php
vendored
Normal file
@@ -0,0 +1,411 @@
|
||||
<?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\New_;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Do_;
|
||||
use PhpParser\Node\Stmt\If_;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use PhpParser\Node\Stmt\Switch_;
|
||||
use PhpParser\Node\Stmt\Trait_;
|
||||
use PhpParser\Node\Stmt\While_;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* Validate that classes exist.
|
||||
*
|
||||
* This pass throws a FatalErrorException rather than letting PHP run
|
||||
* headfirst into a real fatal error and die.
|
||||
*/
|
||||
class ValidClassNamePass extends NamespaceAwarePass
|
||||
{
|
||||
const CLASS_TYPE = 'class';
|
||||
const INTERFACE_TYPE = 'interface';
|
||||
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.
|
||||
*
|
||||
* Validate them upon entering the node, so that we know about their
|
||||
* presence and can validate constant fetches and static calls in class or
|
||||
* trait methods.
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
parent::enterNode($node);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private static function isConditional(Node $node)
|
||||
{
|
||||
return $node instanceof If_ ||
|
||||
$node instanceof While_ ||
|
||||
$node instanceof Do_ ||
|
||||
$node instanceof Switch_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a class definition statement.
|
||||
*
|
||||
* @param Class_ $stmt
|
||||
*/
|
||||
protected function validateClassStatement(Class_ $stmt)
|
||||
{
|
||||
$this->ensureCanDefine($stmt, self::CLASS_TYPE);
|
||||
if (isset($stmt->extends)) {
|
||||
$this->ensureClassExists($this->getFullyQualifiedName($stmt->extends), $stmt);
|
||||
}
|
||||
$this->ensureInterfacesExist($stmt->implements, $stmt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an interface definition statement.
|
||||
*
|
||||
* @param Interface_ $stmt
|
||||
*/
|
||||
protected function validateInterfaceStatement(Interface_ $stmt)
|
||||
{
|
||||
$this->ensureCanDefine($stmt, self::INTERFACE_TYPE);
|
||||
$this->ensureInterfacesExist($stmt->extends, $stmt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a trait definition statement.
|
||||
*
|
||||
* @param Trait_ $stmt
|
||||
*/
|
||||
protected function validateTraitStatement(Trait_ $stmt)
|
||||
{
|
||||
$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.
|
||||
*
|
||||
* @throws FatalErrorException
|
||||
*
|
||||
* @param Stmt $stmt
|
||||
* @param string $scopeType
|
||||
*/
|
||||
protected function ensureCanDefine(Stmt $stmt, $scopeType = self::CLASS_TYPE)
|
||||
{
|
||||
$name = $this->getFullyQualifiedName($stmt->name);
|
||||
|
||||
// check for name collisions
|
||||
$errorType = null;
|
||||
if ($this->classExists($name)) {
|
||||
$errorType = self::CLASS_TYPE;
|
||||
} elseif ($this->interfaceExists($name)) {
|
||||
$errorType = self::INTERFACE_TYPE;
|
||||
} elseif ($this->traitExists($name)) {
|
||||
$errorType = self::TRAIT_TYPE;
|
||||
}
|
||||
|
||||
if ($errorType !== null) {
|
||||
throw $this->createError(\sprintf('%s named %s already exists', \ucfirst($errorType), $name), $stmt);
|
||||
}
|
||||
|
||||
// Store creation for the rest of this code snippet so we can find local
|
||||
// issue too
|
||||
$this->currentScope[\strtolower($name)] = $scopeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that a referenced class exists.
|
||||
*
|
||||
* @throws FatalErrorException
|
||||
*
|
||||
* @param string $name
|
||||
* @param Stmt $stmt
|
||||
*/
|
||||
protected function ensureClassExists($name, $stmt)
|
||||
{
|
||||
if (!$this->classExists($name)) {
|
||||
throw $this->createError(\sprintf('Class \'%s\' not found', $name), $stmt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that a referenced class _or interface_ exists.
|
||||
*
|
||||
* @throws FatalErrorException
|
||||
*
|
||||
* @param string $name
|
||||
* @param Stmt $stmt
|
||||
*/
|
||||
protected function ensureClassOrInterfaceExists($name, $stmt)
|
||||
{
|
||||
if (!$this->classExists($name) && !$this->interfaceExists($name)) {
|
||||
throw $this->createError(\sprintf('Class \'%s\' not found', $name), $stmt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that a referenced class _or trait_ exists.
|
||||
*
|
||||
* @throws FatalErrorException
|
||||
*
|
||||
* @param string $name
|
||||
* @param Stmt $stmt
|
||||
*/
|
||||
protected function ensureClassOrTraitExists($name, $stmt)
|
||||
{
|
||||
if (!$this->classExists($name) && !$this->traitExists($name)) {
|
||||
throw $this->createError(\sprintf('Class \'%s\' not found', $name), $stmt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that a statically called method exists.
|
||||
*
|
||||
* @throws FatalErrorException
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $name
|
||||
* @param Stmt $stmt
|
||||
*/
|
||||
protected function ensureMethodExists($class, $name, $stmt)
|
||||
{
|
||||
$this->ensureClassOrTraitExists($class, $stmt);
|
||||
|
||||
// let's pretend all calls to self, parent and static are valid
|
||||
if (\in_array(\strtolower($class), ['self', 'parent', 'static'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ... and all calls to classes defined right now
|
||||
if ($this->findInScope($class) === self::CLASS_TYPE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if method name is an expression, give it a pass for now
|
||||
if ($name instanceof Expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!\method_exists($class, $name) && !\method_exists($class, '__callStatic')) {
|
||||
throw $this->createError(\sprintf('Call to undefined method %s::%s()', $class, $name), $stmt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that a referenced interface exists.
|
||||
*
|
||||
* @throws FatalErrorException
|
||||
*
|
||||
* @param Interface_[] $interfaces
|
||||
* @param Stmt $stmt
|
||||
*/
|
||||
protected function ensureInterfacesExist($interfaces, $stmt)
|
||||
{
|
||||
foreach ($interfaces as $interface) {
|
||||
/** @var string $name */
|
||||
$name = $this->getFullyQualifiedName($interface);
|
||||
if (!$this->interfaceExists($name)) {
|
||||
throw $this->createError(\sprintf('Interface \'%s\' not found', $name), $stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a symbol type key for storing in the scope name cache.
|
||||
*
|
||||
* @deprecated No longer used. Scope type should be passed into ensureCanDefine directly.
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param Stmt $stmt
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getScopeType(Stmt $stmt)
|
||||
{
|
||||
if ($stmt instanceof Class_) {
|
||||
return self::CLASS_TYPE;
|
||||
} elseif ($stmt instanceof Interface_) {
|
||||
return self::INTERFACE_TYPE;
|
||||
} elseif ($stmt instanceof Trait_) {
|
||||
return self::TRAIT_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a class exists, or has been defined in the current code snippet.
|
||||
*
|
||||
* Gives `self`, `static` and `parent` a free pass.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function classExists($name)
|
||||
{
|
||||
// Give `self`, `static` and `parent` a pass. This will actually let
|
||||
// some errors through, since we're not checking whether the keyword is
|
||||
// being used in a class scope.
|
||||
if (\in_array(\strtolower($name), ['self', 'static', 'parent'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return \class_exists($name) || $this->findInScope($name) === self::CLASS_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether an interface exists, or has been defined in the current code snippet.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function interfaceExists($name)
|
||||
{
|
||||
return \interface_exists($name) || $this->findInScope($name) === self::INTERFACE_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a trait exists, or has been defined in the current code snippet.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function traitExists($name)
|
||||
{
|
||||
return \trait_exists($name) || $this->findInScope($name) === self::TRAIT_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a symbol in the current code snippet scope.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function findInScope($name)
|
||||
{
|
||||
$name = \strtolower($name);
|
||||
if (isset($this->currentScope[$name])) {
|
||||
return $this->currentScope[$name];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error creation factory.
|
||||
*
|
||||
* @param string $msg
|
||||
* @param Stmt $stmt
|
||||
*
|
||||
* @return FatalErrorException
|
||||
*/
|
||||
protected function createError($msg, $stmt)
|
||||
{
|
||||
return new FatalErrorException($msg, 0, E_ERROR, null, $stmt->getLine());
|
||||
}
|
||||
}
|
90
vendor/psy/psysh/src/CodeCleaner/ValidConstantPass.php
vendored
Normal file
90
vendor/psy/psysh/src/CodeCleaner/ValidConstantPass.php
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
<?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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
112
vendor/psy/psysh/src/CodeCleaner/ValidConstructorPass.php
vendored
Normal file
112
vendor/psy/psysh/src/CodeCleaner/ValidConstructorPass.php
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
<?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\Identifier;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* Validate that the constructor method is not static, and does not have a
|
||||
* return type.
|
||||
*
|
||||
* Checks both explicit __construct methods as well as old-style constructor
|
||||
* methods with the same name as the class (for non-namespaced classes).
|
||||
*
|
||||
* As of PHP 5.3.3, methods with the same name as the last element of a
|
||||
* namespaced class name will no longer be treated as constructor. This change
|
||||
* doesn't affect non-namespaced classes.
|
||||
*
|
||||
* @author Martin Hasoň <martin.hason@gmail.com>
|
||||
*/
|
||||
class ValidConstructorPass extends CodeCleanerPass
|
||||
{
|
||||
private $namespace;
|
||||
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
$this->namespace = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the constructor is not static and does not have a return type.
|
||||
*
|
||||
* @throws FatalErrorException the constructor function is static
|
||||
* @throws FatalErrorException the constructor function has a return type
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Namespace_) {
|
||||
$this->namespace = isset($node->name) ? $node->name->parts : [];
|
||||
} elseif ($node instanceof Class_) {
|
||||
$constructor = null;
|
||||
foreach ($node->stmts as $stmt) {
|
||||
if ($stmt instanceof ClassMethod) {
|
||||
// If we find a new-style constructor, no need to look for the old-style
|
||||
if ('__construct' === \strtolower($stmt->name)) {
|
||||
$this->validateConstructor($stmt, $node);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We found a possible old-style constructor (unless there is also a __construct method)
|
||||
if (empty($this->namespace) && \strtolower($node->name) === \strtolower($stmt->name)) {
|
||||
$constructor = $stmt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($constructor) {
|
||||
$this->validateConstructor($constructor, $node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FatalErrorException the constructor function is static
|
||||
* @throws FatalErrorException the constructor function has a return type
|
||||
*
|
||||
* @param Node $constructor
|
||||
* @param Node $classNode
|
||||
*/
|
||||
private function validateConstructor(Node $constructor, Node $classNode)
|
||||
{
|
||||
if ($constructor->isStatic()) {
|
||||
// For PHP Parser 4.x
|
||||
$className = $classNode->name instanceof Identifier ? $classNode->name->toString() : $classNode->name;
|
||||
|
||||
$msg = \sprintf(
|
||||
'Constructor %s::%s() cannot be static',
|
||||
\implode('\\', \array_merge($this->namespace, (array) $className)),
|
||||
$constructor->name
|
||||
);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $classNode->getLine());
|
||||
}
|
||||
|
||||
if (\method_exists($constructor, 'getReturnType') && $constructor->getReturnType()) {
|
||||
// For PHP Parser 4.x
|
||||
$className = $classNode->name instanceof Identifier ? $classNode->name->toString() : $classNode->name;
|
||||
|
||||
$msg = \sprintf(
|
||||
'Constructor %s::%s() cannot declare a return type',
|
||||
\implode('\\', \array_merge($this->namespace, (array) $className)),
|
||||
$constructor->name
|
||||
);
|
||||
throw new FatalErrorException($msg, 0, E_ERROR, null, $classNode->getLine());
|
||||
}
|
||||
}
|
||||
}
|
97
vendor/psy/psysh/src/CodeCleaner/ValidFunctionNamePass.php
vendored
Normal file
97
vendor/psy/psysh/src/CodeCleaner/ValidFunctionNamePass.php
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
<?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\FuncCall;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Stmt\Do_;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\If_;
|
||||
use PhpParser\Node\Stmt\Switch_;
|
||||
use PhpParser\Node\Stmt\While_;
|
||||
use Psy\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* Validate that function calls will succeed.
|
||||
*
|
||||
* This pass throws a FatalErrorException rather than letting PHP run
|
||||
* headfirst into a real fatal error and die.
|
||||
*/
|
||||
class ValidFunctionNamePass extends NamespaceAwarePass
|
||||
{
|
||||
private $conditionalScopes = 0;
|
||||
|
||||
/**
|
||||
* Store newly defined function names on the way in, to allow recursion.
|
||||
*
|
||||
* @param Node $node
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
parent::enterNode($node);
|
||||
|
||||
if (self::isConditional($node)) {
|
||||
$this->conditionalScopes++;
|
||||
} elseif ($node instanceof Function_) {
|
||||
$name = $this->getFullyQualifiedName($node->name);
|
||||
|
||||
// @todo add an "else" here which adds a runtime check for instances where we can't tell
|
||||
// whether a function is being redefined by static analysis alone.
|
||||
if ($this->conditionalScopes === 0) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
$this->currentScope[\strtolower($name)] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function isConditional(Node $node)
|
||||
{
|
||||
return $node instanceof If_ ||
|
||||
$node instanceof While_ ||
|
||||
$node instanceof Do_ ||
|
||||
$node instanceof Switch_;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user