Mercurial > hg > cmmr2012-drupal-site
comparison vendor/psy/psysh/src/CodeCleaner/PassableByReferencePass.php @ 0:c75dbcec494b
Initial commit from drush-created site
author | Chris Cannam |
---|---|
date | Thu, 05 Jul 2018 14:24:15 +0000 |
parents | |
children | a9cd425dd02b |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c75dbcec494b |
---|---|
1 <?php | |
2 | |
3 /* | |
4 * This file is part of Psy Shell. | |
5 * | |
6 * (c) 2012-2018 Justin Hileman | |
7 * | |
8 * For the full copyright and license information, please view the LICENSE | |
9 * file that was distributed with this source code. | |
10 */ | |
11 | |
12 namespace Psy\CodeCleaner; | |
13 | |
14 use PhpParser\Node; | |
15 use PhpParser\Node\Expr; | |
16 use PhpParser\Node\Expr\ClassConstFetch; | |
17 use PhpParser\Node\Expr\FuncCall; | |
18 use PhpParser\Node\Expr\MethodCall; | |
19 use PhpParser\Node\Expr\PropertyFetch; | |
20 use PhpParser\Node\Expr\StaticCall; | |
21 use PhpParser\Node\Expr\Variable; | |
22 use Psy\Exception\FatalErrorException; | |
23 | |
24 /** | |
25 * Validate that only variables (and variable-like things) are passed by reference. | |
26 */ | |
27 class PassableByReferencePass extends CodeCleanerPass | |
28 { | |
29 const EXCEPTION_MESSAGE = 'Only variables can be passed by reference'; | |
30 | |
31 /** | |
32 * @throws FatalErrorException if non-variables are passed by reference | |
33 * | |
34 * @param Node $node | |
35 */ | |
36 public function enterNode(Node $node) | |
37 { | |
38 // @todo support MethodCall and StaticCall as well. | |
39 if ($node instanceof FuncCall) { | |
40 // if function name is an expression or a variable, give it a pass for now. | |
41 if ($node->name instanceof Expr || $node->name instanceof Variable) { | |
42 return; | |
43 } | |
44 | |
45 $name = (string) $node->name; | |
46 | |
47 if ($name === 'array_multisort') { | |
48 return $this->validateArrayMultisort($node); | |
49 } | |
50 | |
51 try { | |
52 $refl = new \ReflectionFunction($name); | |
53 } catch (\ReflectionException $e) { | |
54 // Well, we gave it a shot! | |
55 return; | |
56 } | |
57 | |
58 foreach ($refl->getParameters() as $key => $param) { | |
59 if (array_key_exists($key, $node->args)) { | |
60 $arg = $node->args[$key]; | |
61 if ($param->isPassedByReference() && !$this->isPassableByReference($arg)) { | |
62 throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, E_ERROR, null, $node->getLine()); | |
63 } | |
64 } | |
65 } | |
66 } | |
67 } | |
68 | |
69 private function isPassableByReference(Node $arg) | |
70 { | |
71 // FuncCall, MethodCall and StaticCall are all PHP _warnings_ not fatal errors, so we'll let | |
72 // PHP handle those ones :) | |
73 return $arg->value instanceof ClassConstFetch || | |
74 $arg->value instanceof PropertyFetch || | |
75 $arg->value instanceof Variable || | |
76 $arg->value instanceof FuncCall || | |
77 $arg->value instanceof MethodCall || | |
78 $arg->value instanceof StaticCall; | |
79 } | |
80 | |
81 /** | |
82 * Because array_multisort has a problematic signature... | |
83 * | |
84 * The argument order is all sorts of wonky, and whether something is passed | |
85 * by reference or not depends on the values of the two arguments before it. | |
86 * We'll do a good faith attempt at validating this, but err on the side of | |
87 * permissive. | |
88 * | |
89 * This is why you don't design languages where core code and extensions can | |
90 * implement APIs that wouldn't be possible in userland code. | |
91 * | |
92 * @throws FatalErrorException for clearly invalid arguments | |
93 * | |
94 * @param Node $node | |
95 */ | |
96 private function validateArrayMultisort(Node $node) | |
97 { | |
98 $nonPassable = 2; // start with 2 because the first one has to be passable by reference | |
99 foreach ($node->args as $arg) { | |
100 if ($this->isPassableByReference($arg)) { | |
101 $nonPassable = 0; | |
102 } elseif (++$nonPassable > 2) { | |
103 // There can be *at most* two non-passable-by-reference args in a row. This is about | |
104 // as close as we can get to validating the arguments for this function :-/ | |
105 throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, E_ERROR, null, $node->getLine()); | |
106 } | |
107 } | |
108 } | |
109 } |