1 | <?php |
||
22 | class MagicSet extends MagicMethodGenerator |
||
23 | { |
||
24 | private string $callParentTemplate = <<<'PHP' |
||
|
|||
25 | %s |
||
26 | |||
27 | if (isset(self::$%s[$name])) { |
||
28 | return ($this->$name = $value); |
||
29 | } |
||
30 | |||
31 | if (isset(self::$%s[$name])) { |
||
32 | // check protected property access via compatible class |
||
33 | $callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2); |
||
34 | $caller = isset($callers[1]) ? $callers[1] : []; |
||
35 | $object = isset($caller['object']) ? $caller['object'] : ''; |
||
36 | $expectedType = self::$%s[$name]; |
||
37 | |||
38 | if ($object instanceof $expectedType) { |
||
39 | return ($this->$name = $value); |
||
40 | } |
||
41 | |||
42 | $class = isset($caller['class']) ? $caller['class'] : ''; |
||
43 | |||
44 | if ($class === $expectedType || is_subclass_of($class, $expectedType) || $class === 'ReflectionProperty') { |
||
45 | return ($this->$name = $value); |
||
46 | } |
||
47 | } elseif (isset(self::$%s[$name])) { |
||
48 | // check private property access via same class |
||
49 | $callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2); |
||
50 | $caller = isset($callers[1]) ? $callers[1] : []; |
||
51 | $class = isset($caller['class']) ? $caller['class'] : ''; |
||
52 | |||
53 | static $accessorCache = []; |
||
54 | |||
55 | if (isset(self::$%s[$name][$class])) { |
||
56 | $cacheKey = $class . '#' . $name; |
||
57 | $accessor = isset($accessorCache[$cacheKey]) |
||
58 | ? $accessorCache[$cacheKey] |
||
59 | : $accessorCache[$cacheKey] = \Closure::bind(function ($instance, $value) use ($name) { |
||
60 | return ($instance->$name = $value); |
||
61 | }, null, $class); |
||
62 | |||
63 | return $accessor($this, $value); |
||
64 | } |
||
65 | |||
66 | if ('ReflectionProperty' === $class) { |
||
67 | $tmpClass = key(self::$%s[$name]); |
||
68 | $cacheKey = $tmpClass . '#' . $name; |
||
69 | $accessor = isset($accessorCache[$cacheKey]) |
||
70 | ? $accessorCache[$cacheKey] |
||
71 | : $accessorCache[$cacheKey] = \Closure::bind(function ($instance, $value) use ($name) { |
||
72 | return ($instance->$name = $value); |
||
73 | }, null, $tmpClass); |
||
74 | |||
75 | return $accessor($this, $value); |
||
76 | } |
||
77 | } |
||
78 | |||
79 | %s |
||
80 | PHP; |
||
81 | |||
82 | /** |
||
83 | * @throws InvalidArgumentException |
||
84 | */ |
||
85 | public function __construct( |
||
86 | ReflectionClass $originalClass, |
||
87 | PropertyGenerator $initializerProperty, |
||
88 | MethodGenerator $callInitializer, |
||
89 | 2 | PublicPropertiesMap $publicProperties, |
|
90 | ProtectedPropertiesMap $protectedProperties, |
||
91 | PrivatePropertiesMap $privateProperties |
||
92 | ) { |
||
93 | parent::__construct( |
||
94 | $originalClass, |
||
95 | '__set', |
||
96 | [new ParameterGenerator('name'), new ParameterGenerator('value')] |
||
97 | 2 | ); |
|
98 | 2 | ||
99 | 2 | $override = $originalClass->hasMethod('__set'); |
|
100 | 2 | ||
101 | $parentAccess = 'return parent::__set($name, $value);'; |
||
102 | |||
103 | 2 | if (! $override) { |
|
104 | $parentAccess = PublicScopeSimulator::getPublicAccessSimulationCode( |
||
105 | 2 | PublicScopeSimulator::OPERATION_SET, |
|
106 | 'name', |
||
107 | 2 | 'value' |
|
108 | 1 | ); |
|
109 | 1 | } |
|
110 | 1 | ||
111 | 1 | $this->setBody(sprintf( |
|
112 | $this->callParentTemplate, |
||
113 | '$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName() |
||
114 | . '(\'__set\', array(\'name\' => $name, \'value\' => $value));', |
||
115 | 2 | $publicProperties->getName(), |
|
116 | 2 | $protectedProperties->getName(), |
|
117 | 2 | $protectedProperties->getName(), |
|
118 | 2 | $privateProperties->getName(), |
|
119 | 2 | $privateProperties->getName(), |
|
120 | 2 | $privateProperties->getName(), |
|
121 | 2 | $parentAccess |
|
122 | 2 | )); |
|
123 | 2 | } |
|
124 | } |
||
125 |