TYPO3  7.6
Instantiator.php
Go to the documentation of this file.
1 <?php
2 /*
3  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14  *
15  * This software consists of voluntary contributions made by many individuals
16  * and is licensed under the MIT license. For more information, see
17  * <http://www.doctrine-project.org>.
18  */
19 
20 namespace Doctrine\Instantiator;
21 
22 use Closure;
25 use Exception;
26 use ReflectionClass;
27 
33 final class Instantiator implements InstantiatorInterface
34 {
42 
46  private static $cachedInstantiators = array();
47 
51  private static $cachedCloneables = array();
52 
56  public function instantiate($className)
57  {
58  if (isset(self::$cachedCloneables[$className])) {
59  return clone self::$cachedCloneables[$className];
60  }
61 
62  if (isset(self::$cachedInstantiators[$className])) {
63  $factory = self::$cachedInstantiators[$className];
64 
65  return $factory();
66  }
67 
68  return $this->buildAndCacheFromFactory($className);
69  }
70 
78  private function buildAndCacheFromFactory($className)
79  {
80  $factory = self::$cachedInstantiators[$className] = $this->buildFactory($className);
81  $instance = $factory();
82 
83  if ($this->isSafeToClone(new ReflectionClass($instance))) {
84  self::$cachedCloneables[$className] = clone $instance;
85  }
86 
87  return $instance;
88  }
89 
98  private function buildFactory($className)
99  {
100  $reflectionClass = $this->getReflectionClass($className);
101 
102  if ($this->isInstantiableViaReflection($reflectionClass)) {
103  return function () use ($reflectionClass) {
104  return $reflectionClass->newInstanceWithoutConstructor();
105  };
106  }
107 
108  $serializedString = sprintf(
109  '%s:%d:"%s":0:{}',
110  $this->getSerializationFormat($reflectionClass),
111  strlen($className),
112  $className
113  );
114 
115  $this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString);
116 
117  return function () use ($serializedString) {
118  return unserialize($serializedString);
119  };
120  }
121 
129  private function getReflectionClass($className)
130  {
131  if (! class_exists($className)) {
133  }
134 
135  $reflection = new ReflectionClass($className);
136 
137  if ($reflection->isAbstract()) {
139  }
140 
141  return $reflection;
142  }
143 
152  private function checkIfUnSerializationIsSupported(ReflectionClass $reflectionClass, $serializedString)
153  {
154  set_error_handler(function ($code, $message, $file, $line) use ($reflectionClass, & $error) {
156  $reflectionClass,
157  $message,
158  $code,
159  $file,
160  $line
161  );
162  });
163 
164  $this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString);
165 
166  restore_error_handler();
167 
168  if ($error) {
169  throw $error;
170  }
171  }
172 
181  private function attemptInstantiationViaUnSerialization(ReflectionClass $reflectionClass, $serializedString)
182  {
183  try {
184  unserialize($serializedString);
185  } catch (Exception $exception) {
186  restore_error_handler();
187 
188  throw UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $exception);
189  }
190  }
191 
197  private function isInstantiableViaReflection(ReflectionClass $reflectionClass)
198  {
199  if (\PHP_VERSION_ID >= 50600) {
200  return ! ($this->hasInternalAncestors($reflectionClass) && $reflectionClass->isFinal());
201  }
202 
203  return \PHP_VERSION_ID >= 50400 && ! $this->hasInternalAncestors($reflectionClass);
204  }
205 
213  private function hasInternalAncestors(ReflectionClass $reflectionClass)
214  {
215  do {
216  if ($reflectionClass->isInternal()) {
217  return true;
218  }
219  } while ($reflectionClass = $reflectionClass->getParentClass());
220 
221  return false;
222  }
223 
236  private function getSerializationFormat(ReflectionClass $reflectionClass)
237  {
239  && $reflectionClass->implementsInterface('Serializable')
240  ) {
241  return self::SERIALIZATION_FORMAT_USE_UNSERIALIZER;
242  }
243 
244  return self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER;
245  }
246 
253  {
254  return PHP_VERSION_ID === 50429 || PHP_VERSION_ID === 50513;
255  }
256 
264  private function isSafeToClone(ReflectionClass $reflection)
265  {
266  if (method_exists($reflection, 'isCloneable') && ! $reflection->isCloneable()) {
267  return false;
268  }
269 
270  // not cloneable if it implements `__clone`, as we want to avoid calling it
271  return ! $reflection->hasMethod('__clone');
272  }
273 }