class Instantiator
Same name in this branch
- 11.1.x vendor/symfony/var-exporter/Instantiator.php \Symfony\Component\VarExporter\Instantiator
Hierarchy
- class \Doctrine\Instantiator\Instantiator implements \Doctrine\Instantiator\InstantiatorInterface
Expanded class hierarchy of Instantiator
2 files declare their use of Instantiator
- Doubler.php in vendor/
phpspec/ prophecy/ src/ Prophecy/ Doubler/ Doubler.php - ThrowPromise.php in vendor/
phpspec/ prophecy/ src/ Prophecy/ Promise/ ThrowPromise.php
File
-
vendor/
doctrine/ instantiator/ src/ Doctrine/ Instantiator/ Instantiator.php, line 25
Namespace
Doctrine\InstantiatorView source
final class Instantiator implements InstantiatorInterface {
/**
* Markers used internally by PHP to define whether {@see \unserialize} should invoke
* the method {@see \Serializable::unserialize()} when dealing with classes implementing
* the {@see \Serializable} interface.
*
* @deprecated This constant will be private in 2.0
*/
private const SERIALIZATION_FORMAT_USE_UNSERIALIZER = 'C';
private const SERIALIZATION_FORMAT_AVOID_UNSERIALIZER = 'O';
/**
* Used to instantiate specific classes, indexed by class name.
*
* @var callable[]
*/
private static array $cachedInstantiators = [];
/**
* Array of objects that can directly be cloned, indexed by class name.
*
* @var object[]
*/
private static array $cachedCloneables = [];
/**
* @phpstan-param class-string<T> $className
*
* @phpstan-return T
*
* @throws ExceptionInterface
*
* @template T of object
*/
public function instantiate(string $className) : object {
if (isset(self::$cachedCloneables[$className])) {
/** @phpstan-var T */
$cachedCloneable = self::$cachedCloneables[$className];
return clone $cachedCloneable;
}
if (isset(self::$cachedInstantiators[$className])) {
$factory = self::$cachedInstantiators[$className];
return $factory();
}
return $this->buildAndCacheFromFactory($className);
}
/**
* Builds the requested object and caches it in static properties for performance
*
* @phpstan-param class-string<T> $className
*
* @phpstan-return T
*
* @template T of object
*/
private function buildAndCacheFromFactory(string $className) : object {
$factory = self::$cachedInstantiators[$className] = $this->buildFactory($className);
$instance = $factory();
if ($this->isSafeToClone(new ReflectionClass($instance))) {
self::$cachedCloneables[$className] = clone $instance;
}
return $instance;
}
/**
* Builds a callable capable of instantiating the given $className without
* invoking its constructor.
*
* @phpstan-param class-string<T> $className
*
* @phpstan-return callable(): T
*
* @throws InvalidArgumentException
* @throws UnexpectedValueException
* @throws ReflectionException
*
* @template T of object
*/
private function buildFactory(string $className) : callable {
$reflectionClass = $this->getReflectionClass($className);
if ($this->isInstantiableViaReflection($reflectionClass)) {
return [
$reflectionClass,
'newInstanceWithoutConstructor',
];
}
$serializedString = sprintf('%s:%d:"%s":0:{}', is_subclass_of($className, Serializable::class) ? self::SERIALIZATION_FORMAT_USE_UNSERIALIZER : self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER, strlen($className), $className);
$this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString);
return static fn() => unserialize($serializedString);
}
/**
* @phpstan-param class-string<T> $className
*
* @phpstan-return ReflectionClass<T>
*
* @throws InvalidArgumentException
* @throws ReflectionException
*
* @template T of object
*/
private function getReflectionClass(string $className) : ReflectionClass {
if (!class_exists($className)) {
throw InvalidArgumentException::fromNonExistingClass($className);
}
if (enum_exists($className, false)) {
throw InvalidArgumentException::fromEnum($className);
}
$reflection = new ReflectionClass($className);
if ($reflection->isAbstract()) {
throw InvalidArgumentException::fromAbstractClass($reflection);
}
return $reflection;
}
/**
* @phpstan-param ReflectionClass<T> $reflectionClass
*
* @throws UnexpectedValueException
*
* @template T of object
*/
private function checkIfUnSerializationIsSupported(ReflectionClass $reflectionClass, string $serializedString) : void {
set_error_handler(static function (int $code, string $message, string $file, int $line) use ($reflectionClass, &$error) : bool {
$error = UnexpectedValueException::fromUncleanUnSerialization($reflectionClass, $message, $code, $file, $line);
return true;
});
try {
$this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString);
} finally {
restore_error_handler();
}
if ($error) {
throw $error;
}
}
/**
* @phpstan-param ReflectionClass<T> $reflectionClass
*
* @throws UnexpectedValueException
*
* @template T of object
*/
private function attemptInstantiationViaUnSerialization(ReflectionClass $reflectionClass, string $serializedString) : void {
try {
unserialize($serializedString);
} catch (Exception $exception) {
throw UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $exception);
}
}
/**
* @phpstan-param ReflectionClass<T> $reflectionClass
*
* @template T of object
*/
private function isInstantiableViaReflection(ReflectionClass $reflectionClass) : bool {
return !($this->hasInternalAncestors($reflectionClass) && $reflectionClass->isFinal());
}
/**
* Verifies whether the given class is to be considered internal
*
* @phpstan-param ReflectionClass<T> $reflectionClass
*
* @template T of object
*/
private function hasInternalAncestors(ReflectionClass $reflectionClass) : bool {
do {
if ($reflectionClass->isInternal()) {
return true;
}
$reflectionClass = $reflectionClass->getParentClass();
} while ($reflectionClass);
return false;
}
/**
* Checks if a class is cloneable
*
* Classes implementing `__clone` cannot be safely cloned, as that may cause side-effects.
*
* @phpstan-param ReflectionClass<T> $reflectionClass
*
* @template T of object
*/
private function isSafeToClone(ReflectionClass $reflectionClass) : bool {
return $reflectionClass->isCloneable() && !$reflectionClass->hasMethod('__clone') && !$reflectionClass->isSubclassOf(ArrayIterator::class);
}
}
Members
Title Sort descending | Deprecated | Modifiers | Object type | Summary | Overriden Title |
---|---|---|---|---|---|
Instantiator::$cachedCloneables | private static | property | Array of objects that can directly be cloned, indexed by class name. | ||
Instantiator::$cachedInstantiators | private static | property | Used to instantiate specific classes, indexed by class name. | ||
Instantiator::attemptInstantiationViaUnSerialization | private | function | @phpstan-param ReflectionClass<T> $reflectionClass | ||
Instantiator::buildAndCacheFromFactory | private | function | Builds the requested object and caches it in static properties for performance | ||
Instantiator::buildFactory | private | function | Builds a callable capable of instantiating the given $className without invoking its constructor. |
||
Instantiator::checkIfUnSerializationIsSupported | private | function | @phpstan-param ReflectionClass<T> $reflectionClass | ||
Instantiator::getReflectionClass | private | function | @phpstan-param class-string<T> $className | ||
Instantiator::hasInternalAncestors | private | function | Verifies whether the given class is to be considered internal | ||
Instantiator::instantiate | public | function | @phpstan-param class-string<T> $className | Overrides InstantiatorInterface::instantiate | |
Instantiator::isInstantiableViaReflection | private | function | @phpstan-param ReflectionClass<T> $reflectionClass | ||
Instantiator::isSafeToClone | private | function | Checks if a class is cloneable | ||
Instantiator::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER | private | constant | |||
Instantiator::SERIALIZATION_FORMAT_USE_UNSERIALIZER | Deprecated | private | constant | Markers used internally by PHP to define whether {the method {the { |