vendor/symfony/var-exporter/LazyProxyTrait.php line 95

  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\VarExporter;
  11. use Symfony\Component\VarExporter\Hydrator as PublicHydrator;
  12. use Symfony\Component\VarExporter\Internal\Hydrator;
  13. use Symfony\Component\VarExporter\Internal\LazyObjectRegistry as Registry;
  14. use Symfony\Component\VarExporter\Internal\LazyObjectState;
  15. use Symfony\Component\VarExporter\Internal\LazyObjectTrait;
  16. trait LazyProxyTrait
  17. {
  18.     use LazyObjectTrait;
  19.     /**
  20.      * Creates a lazy-loading virtual proxy.
  21.      *
  22.      * @param \Closure():object $initializer Returns the proxied object
  23.      * @param static|null       $instance
  24.      */
  25.     public static function createLazyProxy(\Closure $initializerobject $instance null): static
  26.     {
  27.         if (self::class !== $class $instance $instance::class : static::class) {
  28.             $skippedProperties = ["\0".self::class."\0lazyObjectState" => true];
  29.         } elseif (\defined($class.'::LAZY_OBJECT_PROPERTY_SCOPES')) {
  30.             Hydrator::$propertyScopes[$class] ??= $class::LAZY_OBJECT_PROPERTY_SCOPES;
  31.         }
  32.         $instance ??= (Registry::$classReflectors[$class] ??= new \ReflectionClass($class))->newInstanceWithoutConstructor();
  33.         $instance->lazyObjectState = new LazyObjectState($initializer);
  34.         foreach (Registry::$classResetters[$class] ??= Registry::getClassResetters($class) as $reset) {
  35.             $reset($instance$skippedProperties ??= []);
  36.         }
  37.         return $instance;
  38.     }
  39.     /**
  40.      * Returns whether the object is initialized.
  41.      *
  42.      * @param $partial Whether partially initialized objects should be considered as initialized
  43.      */
  44.     public function isLazyObjectInitialized(bool $partial false): bool
  45.     {
  46.         return !isset($this->lazyObjectState) || isset($this->lazyObjectState->realInstance) || Registry::$noInitializerState === $this->lazyObjectState->initializer;
  47.     }
  48.     /**
  49.      * Forces initialization of a lazy object and returns it.
  50.      */
  51.     public function initializeLazyObject(): parent
  52.     {
  53.         if ($state $this->lazyObjectState ?? null) {
  54.             return $state->realInstance ??= ($state->initializer)();
  55.         }
  56.         return $this;
  57.     }
  58.     /**
  59.      * @return bool Returns false when the object cannot be reset, ie when it's not a lazy object
  60.      */
  61.     public function resetLazyObject(): bool
  62.     {
  63.         if (!isset($this->lazyObjectState) || Registry::$noInitializerState === $this->lazyObjectState->initializer) {
  64.             return false;
  65.         }
  66.         unset($this->lazyObjectState->realInstance);
  67.         return true;
  68.     }
  69.     public function &__get($name): mixed
  70.     {
  71.         $propertyScopes Hydrator::$propertyScopes[$this::class] ??= Hydrator::getPropertyScopes($this::class);
  72.         $scope null;
  73.         $instance $this;
  74.         if ([$class, , $readonlyScope] = $propertyScopes[$name] ?? null) {
  75.             $scope Registry::getScope($propertyScopes$class$name);
  76.             if (null === $scope || isset($propertyScopes["\0$scope\0$name"])) {
  77.                 if ($state $this->lazyObjectState ?? null) {
  78.                     $instance $state->realInstance ??= ($state->initializer)();
  79.                 }
  80.                 $parent 2;
  81.                 goto get_in_scope;
  82.             }
  83.         }
  84.         $parent = (Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['get'];
  85.         if ($state $this->lazyObjectState ?? null) {
  86.             $instance $state->realInstance ??= ($state->initializer)();
  87.         } else {
  88.             if (=== $parent) {
  89.                 return parent::__get($name);
  90.             }
  91.             $value parent::__get($name);
  92.             return $value;
  93.         }
  94.         if (!$parent && null === $class && !\array_key_exists($name, (array) $instance)) {
  95.             $frame debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS1)[0];
  96.             trigger_error(sprintf('Undefined property: %s::$%s in %s on line %s'$instance::class, $name$frame['file'], $frame['line']), \E_USER_NOTICE);
  97.         }
  98.         get_in_scope:
  99.         try {
  100.             if (null === $scope) {
  101.                 if (null === $readonlyScope && !== $parent) {
  102.                     return $instance->$name;
  103.                 }
  104.                 $value $instance->$name;
  105.                 return $value;
  106.             }
  107.             $accessor Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
  108.             return $accessor['get']($instance$namenull !== $readonlyScope || === $parent);
  109.         } catch (\Error $e) {
  110.             if (\Error::class !== $e::class || !str_starts_with($e->getMessage(), 'Cannot access uninitialized non-nullable property')) {
  111.                 throw $e;
  112.             }
  113.             try {
  114.                 if (null === $scope) {
  115.                     $instance->$name = [];
  116.                     return $instance->$name;
  117.                 }
  118.                 $accessor['set']($instance$name, []);
  119.                 return $accessor['get']($instance$namenull !== $readonlyScope || === $parent);
  120.             } catch (\Error) {
  121.                 throw $e;
  122.             }
  123.         }
  124.     }
  125.     public function __set($name$value): void
  126.     {
  127.         $propertyScopes Hydrator::$propertyScopes[$this::class] ??= Hydrator::getPropertyScopes($this::class);
  128.         $scope null;
  129.         $instance $this;
  130.         if ([$class, , $readonlyScope] = $propertyScopes[$name] ?? null) {
  131.             $scope Registry::getScope($propertyScopes$class$name$readonlyScope);
  132.             if ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"])) {
  133.                 if ($state $this->lazyObjectState ?? null) {
  134.                     $instance $state->realInstance ??= ($state->initializer)();
  135.                 }
  136.                 goto set_in_scope;
  137.             }
  138.         }
  139.         if ($state $this->lazyObjectState ?? null) {
  140.             $instance $state->realInstance ??= ($state->initializer)();
  141.         } elseif ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['set']) {
  142.             parent::__set($name$value);
  143.             return;
  144.         }
  145.         set_in_scope:
  146.         if (null === $scope) {
  147.             $instance->$name $value;
  148.         } else {
  149.             $accessor Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
  150.             $accessor['set']($instance$name$value);
  151.         }
  152.     }
  153.     public function __isset($name): bool
  154.     {
  155.         $propertyScopes Hydrator::$propertyScopes[$this::class] ??= Hydrator::getPropertyScopes($this::class);
  156.         $scope null;
  157.         $instance $this;
  158.         if ([$class] = $propertyScopes[$name] ?? null) {
  159.             $scope Registry::getScope($propertyScopes$class$name);
  160.             if (null === $scope || isset($propertyScopes["\0$scope\0$name"])) {
  161.                 if ($state $this->lazyObjectState ?? null) {
  162.                     $instance $state->realInstance ??= ($state->initializer)();
  163.                 }
  164.                 goto isset_in_scope;
  165.             }
  166.         }
  167.         if ($state $this->lazyObjectState ?? null) {
  168.             $instance $state->realInstance ??= ($state->initializer)();
  169.         } elseif ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['isset']) {
  170.             return parent::__isset($name);
  171.         }
  172.         isset_in_scope:
  173.         if (null === $scope) {
  174.             return isset($instance->$name);
  175.         }
  176.         $accessor Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
  177.         return $accessor['isset']($instance$name);
  178.     }
  179.     public function __unset($name): void
  180.     {
  181.         $propertyScopes Hydrator::$propertyScopes[$this::class] ??= Hydrator::getPropertyScopes($this::class);
  182.         $scope null;
  183.         $instance $this;
  184.         if ([$class, , $readonlyScope] = $propertyScopes[$name] ?? null) {
  185.             $scope Registry::getScope($propertyScopes$class$name$readonlyScope);
  186.             if ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"])) {
  187.                 if ($state $this->lazyObjectState ?? null) {
  188.                     $instance $state->realInstance ??= ($state->initializer)();
  189.                 }
  190.                 goto unset_in_scope;
  191.             }
  192.         }
  193.         if ($state $this->lazyObjectState ?? null) {
  194.             $instance $state->realInstance ??= ($state->initializer)();
  195.         } elseif ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['unset']) {
  196.             parent::__unset($name);
  197.             return;
  198.         }
  199.         unset_in_scope:
  200.         if (null === $scope) {
  201.             unset($instance->$name);
  202.         } else {
  203.             $accessor Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
  204.             $accessor['unset']($instance$name);
  205.         }
  206.     }
  207.     public function __clone(): void
  208.     {
  209.         if (!isset($this->lazyObjectState)) {
  210.             if ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['clone']) {
  211.                 parent::__clone();
  212.             }
  213.             return;
  214.         }
  215.         $this->lazyObjectState = clone $this->lazyObjectState;
  216.         if (isset($this->lazyObjectState->realInstance)) {
  217.             $this->lazyObjectState->realInstance = clone $this->lazyObjectState->realInstance;
  218.         }
  219.     }
  220.     public function __serialize(): array
  221.     {
  222.         $class self::class;
  223.         $state $this->lazyObjectState ?? null;
  224.         if (!$state && (Registry::$parentMethods[$class] ??= Registry::getParentMethods($class))['serialize']) {
  225.             $properties parent::__serialize();
  226.         } else {
  227.             $properties = (array) $this;
  228.             if ($state) {
  229.                 unset($properties["\0$class\0lazyObjectState"]);
  230.                 $properties["\0$class\0lazyObjectReal"] = $state->realInstance ??= ($state->initializer)();
  231.             }
  232.         }
  233.         if ($state || Registry::$parentMethods[$class]['serialize'] || !Registry::$parentMethods[$class]['sleep']) {
  234.             return $properties;
  235.         }
  236.         $scope get_parent_class($class);
  237.         $data = [];
  238.         foreach (parent::__sleep() as $name) {
  239.             $value $properties[$k $name] ?? $properties[$k "\0*\0$name"] ?? $properties[$k "\0$scope\0$name"] ?? $k null;
  240.             if (null === $k) {
  241.                 trigger_error(sprintf('serialize(): "%s" returned as member variable from __sleep() but does not exist'$name), \E_USER_NOTICE);
  242.             } else {
  243.                 $data[$k] = $value;
  244.             }
  245.         }
  246.         return $data;
  247.     }
  248.     public function __unserialize(array $data): void
  249.     {
  250.         $class self::class;
  251.         if ($instance $data["\0$class\0lazyObjectReal"] ?? null) {
  252.             unset($data["\0$class\0lazyObjectReal"]);
  253.             foreach (Registry::$classResetters[$class] ??= Registry::getClassResetters($class) as $reset) {
  254.                 $reset($this$data);
  255.             }
  256.             if ($data) {
  257.                 PublicHydrator::hydrate($this$data);
  258.             }
  259.             $this->lazyObjectState = new LazyObjectState(Registry::$noInitializerState ??= static fn () => throw new \LogicException('Lazy proxy has no initializer.'));
  260.             $this->lazyObjectState->realInstance $instance;
  261.         } elseif ((Registry::$parentMethods[$class] ??= Registry::getParentMethods($class))['unserialize']) {
  262.             parent::__unserialize($data);
  263.         } else {
  264.             PublicHydrator::hydrate($this$data);
  265.             if (Registry::$parentMethods[$class]['wakeup']) {
  266.                 parent::__wakeup();
  267.             }
  268.         }
  269.     }
  270.     public function __destruct()
  271.     {
  272.         if (isset($this->lazyObjectState)) {
  273.             return;
  274.         }
  275.         if ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['destruct']) {
  276.             parent::__destruct();
  277.         }
  278.     }
  279. }