1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Component\VarExporter\Internal; 13 14use Symfony\Component\VarExporter\Exception\ClassNotFoundException; 15 16/** 17 * @author Nicolas Grekas <p@tchwork.com> 18 * 19 * @internal 20 */ 21class Hydrator 22{ 23 public static $hydrators = []; 24 25 public $registry; 26 public $values; 27 public $properties; 28 public $value; 29 public $wakeups; 30 31 public function __construct(?Registry $registry, ?Values $values, array $properties, $value, array $wakeups) 32 { 33 $this->registry = $registry; 34 $this->values = $values; 35 $this->properties = $properties; 36 $this->value = $value; 37 $this->wakeups = $wakeups; 38 } 39 40 public static function hydrate($objects, $values, $properties, $value, $wakeups) 41 { 42 foreach ($properties as $class => $vars) { 43 (self::$hydrators[$class] ?? self::getHydrator($class))($vars, $objects); 44 } 45 foreach ($wakeups as $k => $v) { 46 if (\is_array($v)) { 47 $objects[-$k]->__unserialize($v); 48 } else { 49 $objects[$v]->__wakeup(); 50 } 51 } 52 53 return $value; 54 } 55 56 public static function getHydrator($class) 57 { 58 if ('stdClass' === $class) { 59 return self::$hydrators[$class] = static function ($properties, $objects) { 60 foreach ($properties as $name => $values) { 61 foreach ($values as $i => $v) { 62 $objects[$i]->$name = $v; 63 } 64 } 65 }; 66 } 67 68 if (!class_exists($class) && !interface_exists($class, false) && !trait_exists($class, false)) { 69 throw new ClassNotFoundException($class); 70 } 71 $classReflector = new \ReflectionClass($class); 72 73 if (!$classReflector->isInternal()) { 74 return self::$hydrators[$class] = (self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'))->bindTo(null, $class); 75 } 76 77 if ($classReflector->name !== $class) { 78 return self::$hydrators[$classReflector->name] ?? self::getHydrator($classReflector->name); 79 } 80 81 switch ($class) { 82 case 'ArrayIterator': 83 case 'ArrayObject': 84 $constructor = \Closure::fromCallable([$classReflector->getConstructor(), 'invokeArgs']); 85 86 return self::$hydrators[$class] = static function ($properties, $objects) use ($constructor) { 87 foreach ($properties as $name => $values) { 88 if ("\0" !== $name) { 89 foreach ($values as $i => $v) { 90 $objects[$i]->$name = $v; 91 } 92 } 93 } 94 foreach ($properties["\0"] ?? [] as $i => $v) { 95 $constructor($objects[$i], $v); 96 } 97 }; 98 99 case 'ErrorException': 100 return self::$hydrators[$class] = (self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'))->bindTo(null, new class() extends \ErrorException { 101 }); 102 103 case 'TypeError': 104 return self::$hydrators[$class] = (self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'))->bindTo(null, new class() extends \Error { 105 }); 106 107 case 'SplObjectStorage': 108 return self::$hydrators[$class] = static function ($properties, $objects) { 109 foreach ($properties as $name => $values) { 110 if ("\0" === $name) { 111 foreach ($values as $i => $v) { 112 for ($j = 0; $j < \count($v); ++$j) { 113 $objects[$i]->attach($v[$j], $v[++$j]); 114 } 115 } 116 continue; 117 } 118 foreach ($values as $i => $v) { 119 $objects[$i]->$name = $v; 120 } 121 } 122 }; 123 } 124 125 $propertySetters = []; 126 foreach ($classReflector->getProperties() as $propertyReflector) { 127 if (!$propertyReflector->isStatic()) { 128 $propertyReflector->setAccessible(true); 129 $propertySetters[$propertyReflector->name] = \Closure::fromCallable([$propertyReflector, 'setValue']); 130 } 131 } 132 133 if (!$propertySetters) { 134 return self::$hydrators[$class] = self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'); 135 } 136 137 return self::$hydrators[$class] = static function ($properties, $objects) use ($propertySetters) { 138 foreach ($properties as $name => $values) { 139 if ($setValue = $propertySetters[$name] ?? null) { 140 foreach ($values as $i => $v) { 141 $setValue($objects[$i], $v); 142 } 143 continue; 144 } 145 foreach ($values as $i => $v) { 146 $objects[$i]->$name = $v; 147 } 148 } 149 }; 150 } 151} 152