This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * @author Patsura Dmitry https://github.com/ovr <[email protected]> |
||
4 | */ |
||
5 | |||
6 | namespace PHPSA\Definition; |
||
7 | |||
8 | use PHPSA\CompiledExpression; |
||
9 | use PHPSA\Context; |
||
10 | use PhpParser\Node; |
||
11 | use PHPSA\Variable; |
||
12 | use PHPSA\Compiler\Event; |
||
13 | |||
14 | /** |
||
15 | * Class Definition |
||
16 | * |
||
17 | * @package PHPSA\Definition |
||
18 | */ |
||
19 | class ClassDefinition extends ParentDefinition |
||
0 ignored issues
–
show
|
|||
20 | { |
||
21 | /** |
||
22 | * @var int |
||
23 | */ |
||
24 | protected $type; |
||
25 | |||
26 | /** |
||
27 | * Class methods |
||
28 | * |
||
29 | * @var ClassMethod[] |
||
30 | */ |
||
31 | protected $methods = []; |
||
32 | |||
33 | /** |
||
34 | * Class properties |
||
35 | * |
||
36 | * @var Node\Stmt\PropertyProperty[] |
||
37 | */ |
||
38 | protected $properties = []; |
||
39 | |||
40 | /** |
||
41 | * Property Statements |
||
42 | * |
||
43 | * @var Node\Stmt\Property[] |
||
44 | */ |
||
45 | protected $propertyStatements = []; |
||
46 | |||
47 | /** |
||
48 | * Class constants |
||
49 | * |
||
50 | * @var Node\Stmt\ClassConst[] |
||
51 | */ |
||
52 | protected $constants = []; |
||
53 | |||
54 | /** |
||
55 | * @todo Use Finder |
||
56 | * |
||
57 | * @var string |
||
58 | */ |
||
59 | protected $filepath; |
||
60 | |||
61 | /** |
||
62 | * @var Node\Stmt\Class_|null |
||
63 | */ |
||
64 | protected $statement; |
||
65 | |||
66 | /** |
||
67 | * @var string|null |
||
68 | */ |
||
69 | protected $extendsClass; |
||
70 | |||
71 | /** |
||
72 | * @var ClassDefinition|null |
||
73 | */ |
||
74 | protected $extendsClassDefinition; |
||
0 ignored issues
–
show
|
|||
75 | |||
76 | /** |
||
77 | * @var array |
||
78 | */ |
||
79 | protected $interfaces = []; |
||
80 | |||
81 | /** |
||
82 | * @param string $name |
||
83 | * @param Node\Stmt\Class_ $statement |
||
84 | * @param integer $type |
||
85 | */ |
||
86 | public function __construct($name, Node\Stmt\Class_ $statement = null, $type = 0) |
||
87 | { |
||
88 | $this->name = $name; |
||
89 | $this->statement = $statement; |
||
90 | $this->type = $type; |
||
91 | } |
||
92 | |||
93 | /** |
||
94 | * @param ClassMethod $classMethod |
||
95 | * @param bool $overwrite Should we overwrite method if it already exists |
||
96 | * @return bool Did we overwrite method? |
||
97 | */ |
||
98 | public function addMethod(ClassMethod $classMethod, $overwrite = true) |
||
99 | { |
||
100 | if ($overwrite) { |
||
101 | $this->methods[$classMethod->getName()] = $classMethod; |
||
102 | } else { |
||
103 | $name = $classMethod->getName(); |
||
104 | if (isset($this->methods[$name])) { |
||
105 | return false; |
||
106 | } else { |
||
107 | $this->methods[$name] = $classMethod; |
||
108 | } |
||
109 | } |
||
110 | |||
111 | return true; |
||
112 | } |
||
113 | |||
114 | /** |
||
115 | * @param Node\Stmt\Property $property |
||
116 | */ |
||
117 | public function addProperty(Node\Stmt\Property $property) |
||
118 | { |
||
119 | foreach ($property->props as $propertyDefinition) { |
||
120 | $this->properties[$propertyDefinition->name] = $propertyDefinition; |
||
121 | $this->propertyStatements[$propertyDefinition->name] = $property; |
||
122 | } |
||
123 | } |
||
124 | |||
125 | /** |
||
126 | * @param Node\Stmt\ClassConst $const |
||
127 | */ |
||
128 | public function addConst(Node\Stmt\ClassConst $const) |
||
129 | { |
||
130 | $this->constants[$const->consts[0]->name] = $const; |
||
131 | } |
||
132 | |||
133 | /** |
||
134 | * @param Context $context |
||
135 | * @return $this |
||
136 | */ |
||
137 | public function compile(Context $context) |
||
0 ignored issues
–
show
This operation has 528 execution paths which exceeds the configured maximum of 200.
A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods. You can also find more information in the “Code” section of your repository. ![]() |
|||
138 | { |
||
139 | if ($this->compiled) { |
||
140 | return $this; |
||
0 ignored issues
–
show
The return type of
return $this; (PHPSA\Definition\ClassDefinition ) is incompatible with the return type declared by the abstract method PHPSA\Definition\AbstractDefinition::compile of type boolean .
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function ![]() |
|||
141 | } |
||
142 | |||
143 | $this->compiled = true; |
||
144 | $context->setFilepath($this->filepath); |
||
145 | $context->setScope($this); |
||
146 | |||
147 | $context->getEventManager()->fire( |
||
148 | Event\StatementBeforeCompile::EVENT_NAME, |
||
149 | new Event\StatementBeforeCompile( |
||
150 | $this->statement, |
||
0 ignored issues
–
show
It seems like
$this->statement can be null ; however, __construct() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
151 | $context |
||
152 | ) |
||
153 | ); |
||
154 | |||
155 | // Compile event for properties |
||
156 | foreach ($this->properties as $property) { |
||
157 | if (!$property->default) { |
||
158 | continue; |
||
159 | } |
||
160 | |||
161 | // fire expression event for property default |
||
162 | $context->getEventManager()->fire( |
||
163 | Event\ExpressionBeforeCompile::EVENT_NAME, |
||
164 | new Event\ExpressionBeforeCompile( |
||
165 | $property->default, |
||
166 | $context |
||
167 | ) |
||
168 | ); |
||
169 | } |
||
170 | |||
171 | // Compile event for PropertyProperty |
||
172 | foreach ($this->properties as $property) { |
||
173 | $context->getEventManager()->fire( |
||
174 | Event\StatementBeforeCompile::EVENT_NAME, |
||
175 | new Event\StatementBeforeCompile( |
||
176 | $property, |
||
177 | $context |
||
178 | ) |
||
179 | ); |
||
180 | } |
||
181 | |||
182 | // Compile event for constants |
||
183 | foreach ($this->constants as $const) { |
||
184 | $context->getEventManager()->fire( |
||
185 | Event\StatementBeforeCompile::EVENT_NAME, |
||
186 | new Event\StatementBeforeCompile( |
||
187 | $const, |
||
188 | $context |
||
189 | ) |
||
190 | ); |
||
191 | } |
||
192 | |||
193 | // Compiler event for property statements |
||
194 | foreach ($this->propertyStatements as $prop) { |
||
195 | $context->getEventManager()->fire( |
||
196 | Event\StatementBeforeCompile::EVENT_NAME, |
||
197 | new Event\StatementBeforeCompile( |
||
198 | $prop, |
||
199 | $context |
||
200 | ) |
||
201 | ); |
||
202 | } |
||
203 | |||
204 | // Compile each method |
||
205 | foreach ($this->methods as $method) { |
||
206 | $context->clearSymbols(); |
||
207 | |||
208 | if (!$method->isStatic()) { |
||
209 | $thisPtr = new Variable('this', $this, CompiledExpression::OBJECT); |
||
210 | $thisPtr->incGets(); |
||
211 | $context->addVariable($thisPtr); |
||
212 | } |
||
213 | |||
214 | $method->compile($context); |
||
215 | |||
216 | $symbols = $context->getSymbols(); |
||
217 | if (count($symbols) > 0) { |
||
218 | foreach ($symbols as $name => $variable) { |
||
219 | if (!$variable->isGlobal() && $variable->isUnused()) { |
||
220 | $context->warning( |
||
221 | 'unused-' . $variable->getSymbolType(), |
||
222 | sprintf( |
||
223 | 'Unused ' . $variable->getSymbolType() . ' $%s in method %s()', |
||
224 | $variable->getName(), |
||
225 | $method->getName() |
||
226 | ) |
||
227 | ); |
||
228 | } |
||
229 | } |
||
230 | } |
||
231 | } |
||
232 | |||
233 | return $this; |
||
0 ignored issues
–
show
The return type of
return $this; (PHPSA\Definition\ClassDefinition ) is incompatible with the return type declared by the abstract method PHPSA\Definition\AbstractDefinition::compile of type boolean .
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function ![]() |
|||
234 | } |
||
235 | |||
236 | /** |
||
237 | * @param string $name |
||
238 | * @param boolean|false $inherit |
||
239 | * @return bool |
||
240 | */ |
||
241 | public function hasMethod($name, $inherit = false) |
||
242 | { |
||
243 | if (isset($this->methods[$name])) { |
||
244 | return true; |
||
245 | } |
||
246 | |||
247 | if ($inherit && $this->extendsClassDefinition && $this->extendsClassDefinition->hasMethod($name, $inherit)) { |
||
248 | $method = $this->extendsClassDefinition->getMethod($name, $inherit); |
||
249 | return $method && ($method->isPublic() || $method->isProtected()); |
||
250 | } |
||
251 | |||
252 | return false; |
||
253 | } |
||
254 | |||
255 | /** |
||
256 | * @param string $name |
||
257 | * @param bool $inherit |
||
258 | * @return bool |
||
259 | */ |
||
260 | public function hasConst($name, $inherit = false) |
||
261 | { |
||
262 | if ($inherit && $this->extendsClassDefinition && $this->extendsClassDefinition->hasConst($name, $inherit)) { |
||
263 | return true; |
||
264 | } |
||
265 | |||
266 | return isset($this->constants[$name]); |
||
267 | } |
||
268 | |||
269 | /** |
||
270 | * @param $name |
||
271 | * @param boolean|false $inherit |
||
272 | * @return ClassMethod|null |
||
273 | */ |
||
274 | public function getMethod($name, $inherit = false) |
||
275 | { |
||
276 | if (isset($this->methods[$name])) { |
||
277 | return $this->methods[$name]; |
||
278 | } |
||
279 | |||
280 | if ($inherit && $this->extendsClassDefinition) { |
||
281 | return $this->extendsClassDefinition->getMethod($name, $inherit); |
||
282 | } |
||
283 | |||
284 | return null; |
||
285 | } |
||
286 | |||
287 | /** |
||
288 | * @param $name |
||
289 | * @param bool $inherit |
||
290 | * @param bool $isParent It's an internal parameter |
||
291 | * @return bool |
||
292 | */ |
||
293 | public function hasProperty($name, $inherit = false, $isParent = true) |
||
294 | { |
||
295 | if (isset($this->properties[$name])) { |
||
296 | if (!$isParent) { |
||
297 | $property = $this->propertyStatements[$name]; |
||
298 | if ($property->isPrivate()) { |
||
299 | return false; |
||
300 | } |
||
301 | } |
||
302 | |||
303 | return true; |
||
304 | } |
||
305 | |||
306 | if ($inherit && $this->extendsClassDefinition) { |
||
307 | return $this->extendsClassDefinition->hasProperty($name, true, false); |
||
308 | } |
||
309 | |||
310 | return false; |
||
311 | } |
||
312 | |||
313 | /** |
||
314 | * @param string $name |
||
315 | * @param bool $inherit |
||
316 | * @return Node\Stmt\PropertyProperty|null |
||
317 | */ |
||
318 | public function getProperty($name, $inherit = false) |
||
319 | { |
||
320 | if (isset($this->properties[$name])) { |
||
321 | return $this->properties[$name]; |
||
322 | } |
||
323 | |||
324 | if ($inherit && $this->extendsClassDefinition) { |
||
325 | return $this->extendsClassDefinition->getProperty($name, true); |
||
326 | } |
||
327 | |||
328 | return null; |
||
329 | } |
||
330 | |||
331 | /** |
||
332 | * @param string $name |
||
333 | * @param bool $inherit |
||
334 | * @return Node\Stmt\Property|null |
||
335 | */ |
||
336 | public function getPropertyStatement($name, $inherit = false) |
||
337 | { |
||
338 | if (isset($this->propertyStatements[$name])) { |
||
339 | return $this->propertyStatements[$name]; |
||
340 | } |
||
341 | |||
342 | if ($inherit && $this->extendsClassDefinition) { |
||
343 | return $this->extendsClassDefinition->getPropertyStatement($name, true); |
||
344 | } |
||
345 | |||
346 | return null; |
||
347 | } |
||
348 | |||
349 | /** |
||
350 | * @return string |
||
351 | */ |
||
352 | public function getFilepath() |
||
353 | { |
||
354 | return $this->filepath; |
||
355 | } |
||
356 | |||
357 | /** |
||
358 | * @param string $filepath |
||
359 | */ |
||
360 | public function setFilepath($filepath) |
||
361 | { |
||
362 | $this->filepath = $filepath; |
||
363 | } |
||
364 | |||
365 | /** |
||
366 | * @return bool |
||
367 | */ |
||
368 | public function isAbstract() |
||
369 | { |
||
370 | return (bool) ($this->type & Node\Stmt\Class_::MODIFIER_ABSTRACT); |
||
371 | } |
||
372 | |||
373 | /** |
||
374 | * @return bool |
||
375 | */ |
||
376 | public function isFinal() |
||
377 | { |
||
378 | return (bool) ($this->type & Node\Stmt\Class_::MODIFIER_FINAL); |
||
379 | } |
||
380 | |||
381 | /** |
||
382 | * @param null|string $extendsClass |
||
383 | */ |
||
384 | public function setExtendsClass($extendsClass) |
||
385 | { |
||
386 | $this->extendsClass = $extendsClass; |
||
387 | } |
||
388 | |||
389 | /** |
||
390 | * @return null|ClassDefinition |
||
391 | */ |
||
392 | public function getExtendsClassDefinition() |
||
393 | { |
||
394 | return $this->extendsClassDefinition; |
||
395 | } |
||
396 | |||
397 | /** |
||
398 | * @param ClassDefinition $extendsClassDefinition |
||
399 | */ |
||
400 | public function setExtendsClassDefinition(ClassDefinition $extendsClassDefinition) |
||
0 ignored issues
–
show
|
|||
401 | { |
||
402 | $this->extendsClassDefinition = $extendsClassDefinition; |
||
403 | } |
||
404 | |||
405 | /** |
||
406 | * @param string $interface |
||
407 | */ |
||
408 | public function addInterface($interface) |
||
409 | { |
||
410 | $this->interfaces[] = $interface; |
||
411 | } |
||
412 | |||
413 | /** |
||
414 | * @return null|string |
||
415 | */ |
||
416 | public function getExtendsClass() |
||
417 | { |
||
418 | return $this->extendsClass; |
||
419 | } |
||
420 | |||
421 | /** |
||
422 | * @param TraitDefinition $definition |
||
423 | * @param Node\Stmt\TraitUseAdaptation\Alias[] $adaptations |
||
424 | */ |
||
425 | public function mergeTrait(TraitDefinition $definition, array $adaptations) |
||
426 | { |
||
427 | $methods = $definition->getMethods(); |
||
428 | if ($methods) { |
||
0 ignored issues
–
show
The expression
$methods of type PHPSA\Definition\ClassMethod[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
429 | foreach ($adaptations as $adaptation) { |
||
430 | // We don't support Trait name for now |
||
431 | if (!$adaptation->trait) { |
||
432 | $methodNameFromTrait = $adaptation->method; |
||
433 | if (isset($methods[$methodNameFromTrait])) { |
||
434 | /** @var ClassMethod $method Method from Trait */ |
||
435 | $method = $methods[$methodNameFromTrait]; |
||
436 | if ($adaptation->newName |
||
437 | || ($adaptation->newModifier && $method->getModifier() != $adaptation->newModifier)) { |
||
0 ignored issues
–
show
The expression
$adaptation->newModifier of type null|integer is loosely compared to true ; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
![]() |
|||
438 | // Don't modify original method from Trait |
||
439 | $method = clone $method; |
||
440 | $method->setName($adaptation->newName); |
||
0 ignored issues
–
show
$adaptation->newName is of type null|object<PhpParser\Node\Identifier> , but the function expects a string .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
441 | $method->setModifier($adaptation->newModifier); |
||
442 | |||
443 | $methods[$methodNameFromTrait] = $method; |
||
444 | } |
||
445 | } |
||
446 | } |
||
447 | } |
||
448 | |||
449 | foreach ($methods as $method) { |
||
450 | $this->addMethod($method, false); |
||
451 | } |
||
452 | } |
||
453 | } |
||
454 | |||
455 | /** |
||
456 | * @return ClassMethod[] |
||
457 | */ |
||
458 | public function getMethods() |
||
459 | { |
||
460 | return $this->methods; |
||
461 | } |
||
462 | } |
||
463 |
The class complexity is the sum of the complexity of all methods. A very high value is usually an indication that your class does not follow the single reponsibility principle and does more than one job.
Some resources for further reading:
You can also find more detailed suggestions for refactoring in the “Code” section of your repository.