Chris@13
|
1 <?php
|
Chris@13
|
2
|
Chris@13
|
3 /*
|
Chris@13
|
4 * This file is part of Psy Shell.
|
Chris@13
|
5 *
|
Chris@13
|
6 * (c) 2012-2018 Justin Hileman
|
Chris@13
|
7 *
|
Chris@13
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@13
|
9 * file that was distributed with this source code.
|
Chris@13
|
10 */
|
Chris@13
|
11
|
Chris@13
|
12 namespace Psy\CodeCleaner;
|
Chris@13
|
13
|
Chris@13
|
14 use PhpParser\Node;
|
Chris@13
|
15 use PhpParser\Node\Identifier;
|
Chris@13
|
16 use PhpParser\Node\Stmt\Class_;
|
Chris@13
|
17 use PhpParser\Node\Stmt\ClassMethod;
|
Chris@13
|
18 use PhpParser\Node\Stmt\Namespace_;
|
Chris@13
|
19 use Psy\Exception\FatalErrorException;
|
Chris@13
|
20
|
Chris@13
|
21 /**
|
Chris@13
|
22 * Validate that the constructor method is not static, and does not have a
|
Chris@13
|
23 * return type.
|
Chris@13
|
24 *
|
Chris@13
|
25 * Checks both explicit __construct methods as well as old-style constructor
|
Chris@13
|
26 * methods with the same name as the class (for non-namespaced classes).
|
Chris@13
|
27 *
|
Chris@13
|
28 * As of PHP 5.3.3, methods with the same name as the last element of a
|
Chris@13
|
29 * namespaced class name will no longer be treated as constructor. This change
|
Chris@13
|
30 * doesn't affect non-namespaced classes.
|
Chris@13
|
31 *
|
Chris@13
|
32 * @author Martin HasoĊ <martin.hason@gmail.com>
|
Chris@13
|
33 */
|
Chris@13
|
34 class ValidConstructorPass extends CodeCleanerPass
|
Chris@13
|
35 {
|
Chris@13
|
36 private $namespace;
|
Chris@13
|
37
|
Chris@13
|
38 public function beforeTraverse(array $nodes)
|
Chris@13
|
39 {
|
Chris@13
|
40 $this->namespace = [];
|
Chris@13
|
41 }
|
Chris@13
|
42
|
Chris@13
|
43 /**
|
Chris@13
|
44 * Validate that the constructor is not static and does not have a return type.
|
Chris@13
|
45 *
|
Chris@13
|
46 * @throws FatalErrorException the constructor function is static
|
Chris@13
|
47 * @throws FatalErrorException the constructor function has a return type
|
Chris@13
|
48 *
|
Chris@13
|
49 * @param Node $node
|
Chris@13
|
50 */
|
Chris@13
|
51 public function enterNode(Node $node)
|
Chris@13
|
52 {
|
Chris@13
|
53 if ($node instanceof Namespace_) {
|
Chris@13
|
54 $this->namespace = isset($node->name) ? $node->name->parts : [];
|
Chris@13
|
55 } elseif ($node instanceof Class_) {
|
Chris@13
|
56 $constructor = null;
|
Chris@13
|
57 foreach ($node->stmts as $stmt) {
|
Chris@13
|
58 if ($stmt instanceof ClassMethod) {
|
Chris@13
|
59 // If we find a new-style constructor, no need to look for the old-style
|
Chris@17
|
60 if ('__construct' === \strtolower($stmt->name)) {
|
Chris@13
|
61 $this->validateConstructor($stmt, $node);
|
Chris@13
|
62
|
Chris@13
|
63 return;
|
Chris@13
|
64 }
|
Chris@13
|
65
|
Chris@13
|
66 // We found a possible old-style constructor (unless there is also a __construct method)
|
Chris@17
|
67 if (empty($this->namespace) && \strtolower($node->name) === \strtolower($stmt->name)) {
|
Chris@13
|
68 $constructor = $stmt;
|
Chris@13
|
69 }
|
Chris@13
|
70 }
|
Chris@13
|
71 }
|
Chris@13
|
72
|
Chris@13
|
73 if ($constructor) {
|
Chris@13
|
74 $this->validateConstructor($constructor, $node);
|
Chris@13
|
75 }
|
Chris@13
|
76 }
|
Chris@13
|
77 }
|
Chris@13
|
78
|
Chris@13
|
79 /**
|
Chris@13
|
80 * @throws FatalErrorException the constructor function is static
|
Chris@13
|
81 * @throws FatalErrorException the constructor function has a return type
|
Chris@13
|
82 *
|
Chris@13
|
83 * @param Node $constructor
|
Chris@13
|
84 * @param Node $classNode
|
Chris@13
|
85 */
|
Chris@13
|
86 private function validateConstructor(Node $constructor, Node $classNode)
|
Chris@13
|
87 {
|
Chris@13
|
88 if ($constructor->isStatic()) {
|
Chris@13
|
89 // For PHP Parser 4.x
|
Chris@13
|
90 $className = $classNode->name instanceof Identifier ? $classNode->name->toString() : $classNode->name;
|
Chris@13
|
91
|
Chris@17
|
92 $msg = \sprintf(
|
Chris@13
|
93 'Constructor %s::%s() cannot be static',
|
Chris@17
|
94 \implode('\\', \array_merge($this->namespace, (array) $className)),
|
Chris@13
|
95 $constructor->name
|
Chris@13
|
96 );
|
Chris@13
|
97 throw new FatalErrorException($msg, 0, E_ERROR, null, $classNode->getLine());
|
Chris@13
|
98 }
|
Chris@13
|
99
|
Chris@17
|
100 if (\method_exists($constructor, 'getReturnType') && $constructor->getReturnType()) {
|
Chris@13
|
101 // For PHP Parser 4.x
|
Chris@13
|
102 $className = $classNode->name instanceof Identifier ? $classNode->name->toString() : $classNode->name;
|
Chris@13
|
103
|
Chris@17
|
104 $msg = \sprintf(
|
Chris@13
|
105 'Constructor %s::%s() cannot declare a return type',
|
Chris@17
|
106 \implode('\\', \array_merge($this->namespace, (array) $className)),
|
Chris@13
|
107 $constructor->name
|
Chris@13
|
108 );
|
Chris@13
|
109 throw new FatalErrorException($msg, 0, E_ERROR, null, $classNode->getLine());
|
Chris@13
|
110 }
|
Chris@13
|
111 }
|
Chris@13
|
112 }
|