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 function get_class; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* Dynamic closure method invocation is responsible to call dynamic methods via closure |
18
|
|
|
*/ |
19
|
|
|
final class DynamicClosureMethodInvocation extends AbstractMethodInvocation |
20
|
|
|
{ |
21
|
|
|
/** |
22
|
|
|
* Closure to use |
23
|
|
|
* |
24
|
|
|
* @var \Closure |
25
|
|
|
*/ |
26
|
|
|
protected $closureToCall; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* Previous instance of invocation |
30
|
|
|
* |
31
|
|
|
* @var null|object |
32
|
|
|
*/ |
33
|
|
|
protected $previousInstance; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Invokes original method and return result from it |
37
|
|
|
* |
38
|
7 |
|
* @return mixed |
39
|
|
|
*/ |
40
|
7 |
|
public function proceed() |
41
|
1 |
|
{ |
42
|
|
|
if (isset($this->advices[$this->current])) { |
43
|
1 |
|
$currentInterceptor = $this->advices[$this->current++]; |
44
|
|
|
|
45
|
|
|
return $currentInterceptor->invoke($this); |
46
|
|
|
} |
47
|
7 |
|
|
48
|
7 |
|
// Fill the closure only once if it's empty |
49
|
7 |
|
if ($this->closureToCall === null) { |
50
|
|
|
$this->closureToCall = $this->reflectionMethod->getClosure($this->instance); |
51
|
|
|
$this->previousInstance = $this->instance; |
|
|
|
|
52
|
|
|
} |
53
|
7 |
|
|
54
|
|
|
// Rebind the closure if instance was changed since last time |
55
|
|
|
if ($this->previousInstance !== $this->instance) { |
56
|
|
|
$this->closureToCall = $this->closureToCall->bindTo($this->instance, $this->reflectionMethod->class); |
57
|
|
|
$this->previousInstance = $this->instance; |
58
|
7 |
|
} |
59
|
|
|
|
60
|
|
|
return ($this->closureToCall)(...$this->arguments); |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Returns the object for which current joinpoint is invoked |
65
|
|
|
* |
66
|
|
|
* @return object Instance of object or null for static call/unavailable context |
67
|
|
|
*/ |
68
|
|
|
final public function getThis(): ?object |
69
|
|
|
{ |
70
|
|
|
return $this->instance; |
|
|
|
|
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Checks if the current joinpoint is dynamic or static |
75
|
|
|
* |
76
|
|
|
* Dynamic joinpoint contains a reference to an object that can be received via getThis() method call |
77
|
|
|
* @see ClassJoinpoint::getThis() |
78
|
|
|
*/ |
79
|
|
|
final public function isDynamic(): bool |
80
|
|
|
{ |
81
|
|
|
return true; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Returns the static scope name (class name) of this joinpoint. |
86
|
|
|
*/ |
87
|
|
|
final public function getScope(): string |
88
|
|
|
{ |
89
|
|
|
// $this->instance always contains an object, so we can take it's name as a scope name |
90
|
|
|
return get_class($this->instance); |
91
|
|
|
} |
92
|
|
|
} |
93
|
|
|
|
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.