1 | <?php |
||
32 | final class Instantiator implements InstantiatorInterface |
||
33 | { |
||
34 | /** |
||
35 | * Markers used internally by PHP to define whether {@see \unserialize} should invoke |
||
36 | * the method {@see \Serializable::unserialize()} when dealing with classes implementing |
||
37 | * the {@see \Serializable} interface. |
||
38 | */ |
||
39 | const SERIALIZATION_FORMAT_USE_UNSERIALIZER = 'C'; |
||
40 | const SERIALIZATION_FORMAT_AVOID_UNSERIALIZER = 'O'; |
||
41 | |||
42 | /** |
||
43 | * @var \callable[] used to instantiate specific classes, indexed by class name |
||
44 | */ |
||
45 | private static $cachedInstantiators = []; |
||
46 | |||
47 | /** |
||
48 | * @var object[] of objects that can directly be cloned, indexed by class name |
||
49 | */ |
||
50 | private static $cachedCloneables = []; |
||
51 | |||
52 | /** |
||
53 | * {@inheritDoc} |
||
54 | */ |
||
55 | 40 | public function instantiate($className) |
|
69 | |||
70 | /** |
||
71 | * Builds the requested object and caches it in static properties for performance |
||
72 | * |
||
73 | * @return object |
||
74 | */ |
||
75 | 22 | private function buildAndCacheFromFactory(string $className) |
|
86 | |||
87 | /** |
||
88 | * Builds a callable capable of instantiating the given $className without |
||
89 | * invoking its constructor. |
||
90 | * |
||
91 | * @throws InvalidArgumentException |
||
92 | * @throws UnexpectedValueException |
||
93 | * @throws \ReflectionException |
||
94 | */ |
||
95 | 22 | private function buildFactory(string $className) : callable |
|
96 | { |
||
97 | 22 | $reflectionClass = $this->getReflectionClass($className); |
|
98 | |||
99 | 18 | if ($this->isInstantiableViaReflection($reflectionClass)) { |
|
100 | 16 | return [$reflectionClass, 'newInstanceWithoutConstructor']; |
|
101 | } |
||
102 | |||
103 | 2 | $serializedString = sprintf( |
|
104 | 2 | '%s:%d:"%s":0:{}', |
|
105 | 2 | self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER, |
|
106 | 2 | strlen($className), |
|
107 | 2 | $className |
|
108 | ); |
||
109 | |||
110 | 2 | $this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString); |
|
111 | |||
112 | 2 | return function () use ($serializedString) { |
|
113 | 2 | return unserialize($serializedString); |
|
114 | 1 | }; |
|
115 | } |
||
116 | |||
117 | /** |
||
118 | * @param string $className |
||
119 | * |
||
120 | * @return ReflectionClass |
||
121 | * |
||
122 | * @throws InvalidArgumentException |
||
123 | * @throws \ReflectionException |
||
124 | */ |
||
125 | 22 | private function getReflectionClass($className) : ReflectionClass |
|
139 | |||
140 | /** |
||
141 | * @param ReflectionClass $reflectionClass |
||
142 | * @param string $serializedString |
||
143 | * |
||
144 | * @throws UnexpectedValueException |
||
145 | * |
||
146 | * @return void |
||
147 | */ |
||
148 | private function checkIfUnSerializationIsSupported(ReflectionClass $reflectionClass, $serializedString) : void |
||
168 | |||
169 | /** |
||
170 | * @param ReflectionClass $reflectionClass |
||
171 | * @param string $serializedString |
||
172 | * |
||
173 | * @throws UnexpectedValueException |
||
174 | * |
||
175 | * @return void |
||
176 | */ |
||
177 | 2 | private function attemptInstantiationViaUnSerialization(ReflectionClass $reflectionClass, $serializedString) : void |
|
187 | |||
188 | 18 | private function isInstantiableViaReflection(ReflectionClass $reflectionClass) : bool |
|
192 | |||
193 | /** |
||
194 | * Verifies whether the given class is to be considered internal |
||
195 | */ |
||
196 | 18 | private function hasInternalAncestors(ReflectionClass $reflectionClass) : bool |
|
206 | |||
207 | /** |
||
208 | * Checks if a class is cloneable |
||
209 | * |
||
210 | * Classes implementing `__clone` cannot be safely cloned, as that may cause side-effects. |
||
211 | */ |
||
212 | 17 | private function isSafeToClone(ReflectionClass $reflection) : bool |
|
216 | } |
||
217 |