Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. Instantiator.php

class Instantiator

Same name in this branch
  1. 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\Instantiator
View 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&lt;T&gt; $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&lt;T&gt; $reflectionClass
Instantiator::getReflectionClass private function @phpstan-param class-string&lt;T&gt; $className
Instantiator::hasInternalAncestors private function Verifies whether the given class is to be considered internal
Instantiator::instantiate public function @phpstan-param class-string&lt;T&gt; $className Overrides InstantiatorInterface::instantiate
Instantiator::isInstantiableViaReflection private function @phpstan-param ReflectionClass&lt;T&gt; $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 {
RSS feed
Powered by Drupal