1
|
|
|
<?php |
2
|
|
|
declare(strict_types = 1); |
3
|
|
|
/* |
4
|
|
|
* Go! AOP framework |
5
|
|
|
* |
6
|
|
|
* @copyright Copyright 2015, Lisachenko Alexander <[email protected]> |
7
|
|
|
* |
8
|
|
|
* This source file is subject to the license that is bundled |
9
|
|
|
* with this source code in the file LICENSE. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Go\Aop\Framework; |
13
|
|
|
|
14
|
|
|
use Closure; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* Static closure method invocation is responsible to call static methods via closure |
18
|
|
|
*/ |
19
|
|
|
final class StaticClosureMethodInvocation extends AbstractMethodInvocation |
20
|
|
|
{ |
21
|
|
|
/** |
22
|
|
|
* Closure to use |
23
|
|
|
* |
24
|
|
|
* @var Closure |
25
|
|
|
*/ |
26
|
|
|
protected $closureToCall; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* Previous scope of invocation |
30
|
|
|
* |
31
|
|
|
* @var null|string |
32
|
|
|
*/ |
33
|
|
|
protected $previousScope; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Proceeds all registered advices for the static method and returns an invocation result |
37
|
|
|
*/ |
38
|
13 |
|
public function proceed() |
39
|
|
|
{ |
40
|
13 |
|
if (isset($this->advices[$this->current])) { |
41
|
1 |
|
$currentInterceptor = $this->advices[$this->current++]; |
42
|
|
|
|
43
|
1 |
|
return $currentInterceptor->invoke($this); |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
// Rebind the closure if scope (class name) was changed since last time |
47
|
13 |
|
if ($this->previousScope !== $this->instance) { |
48
|
13 |
|
if ($this->closureToCall === null) { |
49
|
13 |
|
$this->closureToCall = static::getStaticInvoker($this->className, $this->reflectionMethod->name); |
50
|
|
|
} |
51
|
13 |
|
$this->closureToCall = $this->closureToCall->bindTo(null, $this->instance); |
52
|
13 |
|
$this->previousScope = $this->instance; |
|
|
|
|
53
|
|
|
} |
54
|
|
|
|
55
|
13 |
|
return ($this->closureToCall)($this->arguments); |
56
|
|
|
|
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Returns static method invoker for the concrete method in the class |
61
|
|
|
*/ |
62
|
13 |
|
protected static function getStaticInvoker(string $className, string $methodName): Closure |
63
|
|
|
{ |
64
|
|
|
return function (array $args) use ($className, $methodName) { |
65
|
13 |
|
return forward_static_call_array([$className, $methodName], $args); |
66
|
13 |
|
}; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Checks if the current joinpoint is dynamic or static |
71
|
|
|
* |
72
|
|
|
* Dynamic joinpoint contains a reference to an object that can be received via getThis() method call |
73
|
|
|
* |
74
|
|
|
* @see ClassJoinpoint::getThis() |
75
|
|
|
*/ |
76
|
|
|
final public function isDynamic(): bool |
77
|
|
|
{ |
78
|
|
|
return false; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Returns the object for which current joinpoint is invoked |
83
|
|
|
* |
84
|
|
|
* @return object Instance of object or null for static call/unavailable context |
85
|
|
|
*/ |
86
|
|
|
final public function getThis(): ?object |
87
|
|
|
{ |
88
|
|
|
return null; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Returns the static scope name (class name) of this joinpoint. |
93
|
|
|
*/ |
94
|
|
|
final public function getScope(): string |
95
|
|
|
{ |
96
|
|
|
// $this->instance contains the current class scope that was received via static::class |
97
|
|
|
return $this->instance; |
98
|
|
|
} |
99
|
|
|
} |
100
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.