PluginProbe
WindPress – Tailwind CSS integration for WordPress / 3.0.5
WindPress – Tailwind CSS integration for WordPress v3.0.5
3.2.90 3.2.89 3.2.88 3.2.87 3.2.86 3.2.85 3.2.84 3.2.83 3.2.82 3.2.81 trunk 3.0.0 3.0.1 3.0.10 3.0.11 3.0.12 3.0.13 3.0.14 3.0.15 3.0.16 3.0.17 3.0.2 3.0.3 3.0.4 3.0.5 All 144 releases
windpress / vendor / symfony / property-access / PropertyAccessor.php

PropertyAccessor.php in WindPress – Tailwind CSS integration for WordPress 3.0.5, at vendor/symfony/property-access/PropertyAccessor.php

605 lines 31.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 namespace WindPressPackages\Symfony\Component\PropertyAccess;
12
13 use WindPressPackages\Psr\Cache\CacheItemPoolInterface;
14 use WindPressPackages\Psr\Log\LoggerInterface;
15 use WindPressPackages\Psr\Log\NullLogger;
16 use WindPressPackages\Symfony\Component\Cache\Adapter\AdapterInterface;
17 use WindPressPackages\Symfony\Component\Cache\Adapter\ApcuAdapter;
18 use WindPressPackages\Symfony\Component\Cache\Adapter\NullAdapter;
19 use WindPressPackages\Symfony\Component\PropertyAccess\Exception\AccessException;
20 use WindPressPackages\Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
21 use WindPressPackages\Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
22 use WindPressPackages\Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
23 use WindPressPackages\Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
24 use WindPressPackages\Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
25 use WindPressPackages\Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
26 use WindPressPackages\Symfony\Component\PropertyInfo\PropertyReadInfo;
27 use WindPressPackages\Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface;
28 use WindPressPackages\Symfony\Component\PropertyInfo\PropertyWriteInfo;
29 use WindPressPackages\Symfony\Component\PropertyInfo\PropertyWriteInfoExtractorInterface;
30 /**
31 * Default implementation of {@link PropertyAccessorInterface}.
32 *
33 * @author Bernhard Schussek <bschussek@gmail.com>
34 * @author Kévin Dunglas <dunglas@gmail.com>
35 * @author Nicolas Grekas <p@tchwork.com>
36 */
37 class PropertyAccessor implements PropertyAccessorInterface
38 {
39 /** @var int Allow none of the magic methods */
40 public const DISALLOW_MAGIC_METHODS = ReflectionExtractor::DISALLOW_MAGIC_METHODS;
41 /** @var int Allow magic __get methods */
42 public const MAGIC_GET = ReflectionExtractor::ALLOW_MAGIC_GET;
43 /** @var int Allow magic __set methods */
44 public const MAGIC_SET = ReflectionExtractor::ALLOW_MAGIC_SET;
45 /** @var int Allow magic __call methods */
46 public const MAGIC_CALL = ReflectionExtractor::ALLOW_MAGIC_CALL;
47 public const DO_NOT_THROW = 0;
48 public const THROW_ON_INVALID_INDEX = 1;
49 public const THROW_ON_INVALID_PROPERTY_PATH = 2;
50 private const VALUE = 0;
51 private const REF = 1;
52 private const IS_REF_CHAINED = 2;
53 private const CACHE_PREFIX_READ = 'r';
54 private const CACHE_PREFIX_WRITE = 'w';
55 private const CACHE_PREFIX_PROPERTY_PATH = 'p';
56 private $magicMethodsFlags;
57 private $ignoreInvalidIndices;
58 private $ignoreInvalidProperty;
59 /**
60 * @var CacheItemPoolInterface
61 */
62 private $cacheItemPool;
63 private $propertyPathCache = [];
64 /**
65 * @var PropertyReadInfoExtractorInterface
66 */
67 private $readInfoExtractor;
68 /**
69 * @var PropertyWriteInfoExtractorInterface
70 */
71 private $writeInfoExtractor;
72 private $readPropertyCache = [];
73 private $writePropertyCache = [];
74 private const RESULT_PROTO = [self::VALUE => null];
75 /**
76 * Should not be used by application code. Use
77 * {@link PropertyAccess::createPropertyAccessor()} instead.
78 *
79 * @param int $magicMethods A bitwise combination of the MAGIC_* constants
80 * to specify the allowed magic methods (__get, __set, __call)
81 * or self::DISALLOW_MAGIC_METHODS for none
82 * @param int $throw A bitwise combination of the THROW_* constants
83 * to specify when exceptions should be thrown
84 * @param PropertyReadInfoExtractorInterface $readInfoExtractor
85 * @param PropertyWriteInfoExtractorInterface $writeInfoExtractor
86 */
87 public function __construct($magicMethods = self::MAGIC_GET | self::MAGIC_SET, $throw = self::THROW_ON_INVALID_PROPERTY_PATH, ?CacheItemPoolInterface $cacheItemPool = null, $readInfoExtractor = null, $writeInfoExtractor = null)
88 {
89 if (\is_bool($magicMethods)) {
90 trigger_deprecation('symfony/property-access', '5.2', 'Passing a boolean as the first argument to "%s()" is deprecated. Pass a combination of bitwise flags instead (i.e an integer).', __METHOD__);
91 $magicMethods = ($magicMethods ? self::MAGIC_CALL : 0) | self::MAGIC_GET | self::MAGIC_SET;
92 } elseif (!\is_int($magicMethods)) {
93 throw new \TypeError(\sprintf('Argument 1 passed to "%s()" must be an integer, "%s" given.', __METHOD__, \get_debug_type($readInfoExtractor)));
94 }
95 if (\is_bool($throw)) {
96 trigger_deprecation('symfony/property-access', '5.3', 'Passing a boolean as the second argument to "%s()" is deprecated. Pass a combination of bitwise flags instead (i.e an integer).', __METHOD__);
97 $throw = $throw ? self::THROW_ON_INVALID_INDEX : self::DO_NOT_THROW;
98 if (!\is_bool($readInfoExtractor)) {
99 $throw |= self::THROW_ON_INVALID_PROPERTY_PATH;
100 }
101 }
102 if (\is_bool($readInfoExtractor)) {
103 trigger_deprecation('symfony/property-access', '5.3', 'Passing a boolean as the fourth argument to "%s()" is deprecated. Pass a combination of bitwise flags as the second argument instead (i.e an integer).', __METHOD__);
104 if ($readInfoExtractor) {
105 $throw |= self::THROW_ON_INVALID_PROPERTY_PATH;
106 }
107 $readInfoExtractor = $writeInfoExtractor;
108 $writeInfoExtractor = 4 < \func_num_args() ? \func_get_arg(4) : null;
109 }
110 if (null !== $readInfoExtractor && !$readInfoExtractor instanceof PropertyReadInfoExtractorInterface) {
111 throw new \TypeError(\sprintf('Argument 4 passed to "%s()" must be null or an instance of "%s", "%s" given.', __METHOD__, PropertyReadInfoExtractorInterface::class, \get_debug_type($readInfoExtractor)));
112 }
113 if (null !== $writeInfoExtractor && !$writeInfoExtractor instanceof PropertyWriteInfoExtractorInterface) {
114 throw new \TypeError(\sprintf('Argument 5 passed to "%s()" must be null or an instance of "%s", "%s" given.', __METHOD__, PropertyWriteInfoExtractorInterface::class, \get_debug_type($writeInfoExtractor)));
115 }
116 $this->magicMethodsFlags = $magicMethods;
117 $this->ignoreInvalidIndices = 0 === ($throw & self::THROW_ON_INVALID_INDEX);
118 $this->cacheItemPool = $cacheItemPool instanceof NullAdapter ? null : $cacheItemPool;
119 // Replace the NullAdapter by the null value
120 $this->ignoreInvalidProperty = 0 === ($throw & self::THROW_ON_INVALID_PROPERTY_PATH);
121 $this->readInfoExtractor = $readInfoExtractor ?? new ReflectionExtractor([], null, null, \false);
122 $this->writeInfoExtractor = $writeInfoExtractor ?? new ReflectionExtractor(['set'], null, null, \false);
123 }
124 /**
125 * {@inheritdoc}
126 */
127 public function getValue($objectOrArray, $propertyPath)
128 {
129 $zval = [self::VALUE => $objectOrArray];
130 if (\is_object($objectOrArray) && \false === \strpbrk((string) $propertyPath, '.[')) {
131 return $this->readProperty($zval, $propertyPath, $this->ignoreInvalidProperty)[self::VALUE];
132 }
133 $propertyPath = $this->getPropertyPath($propertyPath);
134 $propertyValues = $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength(), $this->ignoreInvalidIndices);
135 return $propertyValues[\count($propertyValues) - 1][self::VALUE];
136 }
137 /**
138 * {@inheritdoc}
139 */
140 public function setValue(&$objectOrArray, $propertyPath, $value)
141 {
142 if (\is_object($objectOrArray) && \false === \strpbrk((string) $propertyPath, '.[')) {
143 $zval = [self::VALUE => $objectOrArray];
144 try {
145 $this->writeProperty($zval, $propertyPath, $value);
146 return;
147 } catch (\TypeError $e) {
148 self::throwInvalidArgumentException($e->getMessage(), $e->getTrace(), 0, $propertyPath, $e);
149 // It wasn't thrown in this class so rethrow it
150 throw $e;
151 }
152 }
153 $propertyPath = $this->getPropertyPath($propertyPath);
154 $zval = [self::VALUE => $objectOrArray, self::REF => &$objectOrArray];
155 $propertyValues = $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength() - 1);
156 $overwrite = \true;
157 try {
158 for ($i = \count($propertyValues) - 1; 0 <= $i; --$i) {
159 $zval = $propertyValues[$i];
160 unset($propertyValues[$i]);
161 // You only need set value for current element if:
162 // 1. it's the parent of the last index element
163 // OR
164 // 2. its child is not passed by reference
165 //
166 // This may avoid unnecessary value setting process for array elements.
167 // For example:
168 // '[a][b][c]' => 'old-value'
169 // If you want to change its value to 'new-value',
170 // you only need set value for '[a][b][c]' and it's safe to ignore '[a][b]' and '[a]'
171 if ($overwrite) {
172 $property = $propertyPath->getElement($i);
173 if ($propertyPath->isIndex($i)) {
174 if ($overwrite = !isset($zval[self::REF])) {
175 $ref =& $zval[self::REF];
176 $ref = $zval[self::VALUE];
177 }
178 $this->writeIndex($zval, $property, $value);
179 if ($overwrite) {
180 $zval[self::VALUE] = $zval[self::REF];
181 }
182 } else {
183 $this->writeProperty($zval, $property, $value);
184 }
185 // if current element is an object
186 // OR
187 // if current element's reference chain is not broken - current element
188 // as well as all its ancients in the property path are all passed by reference,
189 // then there is no need to continue the value setting process
190 if (\is_object($zval[self::VALUE]) || isset($zval[self::IS_REF_CHAINED])) {
191 break;
192 }
193 }
194 $value = $zval[self::VALUE];
195 }
196 } catch (\TypeError $e) {
197 self::throwInvalidArgumentException($e->getMessage(), $e->getTrace(), 0, $propertyPath, $e);
198 // It wasn't thrown in this class so rethrow it
199 throw $e;
200 }
201 }
202 private static function throwInvalidArgumentException(string $message, array $trace, int $i, string $propertyPath, ?\Throwable $previous = null) : void
203 {
204 if (!isset($trace[$i]['file']) || __FILE__ !== $trace[$i]['file']) {
205 return;
206 }
207 if (\PHP_VERSION_ID < 80000) {
208 if (\preg_match('/^Typed property \\S+::\\$\\S+ must be (\\S+), (\\S+) used$/', $message, $matches)) {
209 [, $expectedType, $actualType] = $matches;
210 throw new InvalidArgumentException(\sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $expectedType, 'NULL' === $actualType ? 'null' : $actualType, $propertyPath), 0, $previous);
211 }
212 if (!\str_starts_with($message, 'Argument ')) {
213 return;
214 }
215 $pos = \strpos($message, $delim = 'must be of the type ') ?: (\strpos($message, $delim = 'must be an instance of ') ?: \strpos($message, $delim = 'must implement interface '));
216 $pos += \strlen($delim);
217 $j = \strpos($message, ',', $pos);
218 $type = \substr($message, 2 + $j, \strpos($message, ' given', $j) - $j - 2);
219 $message = \substr($message, $pos, $j - $pos);
220 throw new InvalidArgumentException(\sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $message, 'NULL' === $type ? 'null' : $type, $propertyPath), 0, $previous);
221 }
222 if (\preg_match('/^\\S+::\\S+\\(\\): Argument #\\d+ \\(\\$\\S+\\) must be of type (\\S+), (\\S+) given/', $message, $matches)) {
223 [, $expectedType, $actualType] = $matches;
224 throw new InvalidArgumentException(\sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $expectedType, 'NULL' === $actualType ? 'null' : $actualType, $propertyPath), 0, $previous);
225 }
226 if (\preg_match('/^Cannot assign (\\S+) to property \\S+::\\$\\S+ of type (\\S+)$/', $message, $matches)) {
227 [, $actualType, $expectedType] = $matches;
228 throw new InvalidArgumentException(\sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $expectedType, 'NULL' === $actualType ? 'null' : $actualType, $propertyPath), 0, $previous);
229 }
230 }
231 /**
232 * {@inheritdoc}
233 */
234 public function isReadable($objectOrArray, $propertyPath)
235 {
236 if (!$propertyPath instanceof PropertyPathInterface) {
237 $propertyPath = new PropertyPath($propertyPath);
238 }
239 try {
240 $zval = [self::VALUE => $objectOrArray];
241 $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength(), $this->ignoreInvalidIndices);
242 return \true;
243 } catch (AccessException $e) {
244 return \false;
245 } catch (UnexpectedTypeException $e) {
246 return \false;
247 }
248 }
249 /**
250 * {@inheritdoc}
251 */
252 public function isWritable($objectOrArray, $propertyPath)
253 {
254 $propertyPath = $this->getPropertyPath($propertyPath);
255 try {
256 $zval = [self::VALUE => $objectOrArray];
257 $propertyValues = $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength() - 1);
258 for ($i = \count($propertyValues) - 1; 0 <= $i; --$i) {
259 $zval = $propertyValues[$i];
260 unset($propertyValues[$i]);
261 if ($propertyPath->isIndex($i)) {
262 if (!$zval[self::VALUE] instanceof \ArrayAccess && !\is_array($zval[self::VALUE])) {
263 return \false;
264 }
265 } elseif (!\is_object($zval[self::VALUE]) || !$this->isPropertyWritable($zval[self::VALUE], $propertyPath->getElement($i))) {
266 return \false;
267 }
268 if (\is_object($zval[self::VALUE])) {
269 return \true;
270 }
271 }
272 return \true;
273 } catch (AccessException $e) {
274 return \false;
275 } catch (UnexpectedTypeException $e) {
276 return \false;
277 }
278 }
279 /**
280 * Reads the path from an object up to a given path index.
281 *
282 * @throws UnexpectedTypeException if a value within the path is neither object nor array
283 * @throws NoSuchIndexException If a non-existing index is accessed
284 */
285 private function readPropertiesUntil(array $zval, PropertyPathInterface $propertyPath, int $lastIndex, bool $ignoreInvalidIndices = \true) : array
286 {
287 if (!\is_object($zval[self::VALUE]) && !\is_array($zval[self::VALUE])) {
288 throw new UnexpectedTypeException($zval[self::VALUE], $propertyPath, 0);
289 }
290 // Add the root object to the list
291 $propertyValues = [$zval];
292 for ($i = 0; $i < $lastIndex; ++$i) {
293 $property = $propertyPath->getElement($i);
294 $isIndex = $propertyPath->isIndex($i);
295 if ($isIndex) {
296 // Create missing nested arrays on demand
297 if ($zval[self::VALUE] instanceof \ArrayAccess && !$zval[self::VALUE]->offsetExists($property) || \is_array($zval[self::VALUE]) && !isset($zval[self::VALUE][$property]) && !\array_key_exists($property, $zval[self::VALUE])) {
298 if (!$ignoreInvalidIndices) {
299 if (!\is_array($zval[self::VALUE])) {
300 if (!$zval[self::VALUE] instanceof \Traversable) {
301 throw new NoSuchIndexException(\sprintf('Cannot read index "%s" while trying to traverse path "%s".', $property, (string) $propertyPath));
302 }
303 $zval[self::VALUE] = \iterator_to_array($zval[self::VALUE]);
304 }
305 throw new NoSuchIndexException(\sprintf('Cannot read index "%s" while trying to traverse path "%s". Available indices are "%s".', $property, (string) $propertyPath, \print_r(\array_keys($zval[self::VALUE]), \true)));
306 }
307 if ($i + 1 < $propertyPath->getLength()) {
308 if (isset($zval[self::REF])) {
309 $zval[self::VALUE][$property] = [];
310 $zval[self::REF] = $zval[self::VALUE];
311 } else {
312 $zval[self::VALUE] = [$property => []];
313 }
314 }
315 }
316 $zval = $this->readIndex($zval, $property);
317 } else {
318 $zval = $this->readProperty($zval, $property, $this->ignoreInvalidProperty);
319 }
320 // the final value of the path must not be validated
321 if ($i + 1 < $propertyPath->getLength() && !\is_object($zval[self::VALUE]) && !\is_array($zval[self::VALUE])) {
322 throw new UnexpectedTypeException($zval[self::VALUE], $propertyPath, $i + 1);
323 }
324 if (isset($zval[self::REF]) && (0 === $i || isset($propertyValues[$i - 1][self::IS_REF_CHAINED]))) {
325 // Set the IS_REF_CHAINED flag to true if:
326 // current property is passed by reference and
327 // it is the first element in the property path or
328 // the IS_REF_CHAINED flag of its parent element is true
329 // Basically, this flag is true only when the reference chain from the top element to current element is not broken
330 $zval[self::IS_REF_CHAINED] = \true;
331 }
332 $propertyValues[] = $zval;
333 }
334 return $propertyValues;
335 }
336 /**
337 * Reads a key from an array-like structure.
338 *
339 * @param string|int $index The key to read
340 *
341 * @throws NoSuchIndexException If the array does not implement \ArrayAccess or it is not an array
342 */
343 private function readIndex(array $zval, $index) : array
344 {
345 if (!$zval[self::VALUE] instanceof \ArrayAccess && !\is_array($zval[self::VALUE])) {
346 throw new NoSuchIndexException(\sprintf('Cannot read index "%s" from object of type "%s" because it doesn\'t implement \\ArrayAccess.', $index, \get_debug_type($zval[self::VALUE])));
347 }
348 $result = self::RESULT_PROTO;
349 if (isset($zval[self::VALUE][$index])) {
350 $result[self::VALUE] = $zval[self::VALUE][$index];
351 if (!isset($zval[self::REF])) {
352 // Save creating references when doing read-only lookups
353 } elseif (\is_array($zval[self::VALUE])) {
354 $result[self::REF] =& $zval[self::REF][$index];
355 } elseif (\is_object($result[self::VALUE])) {
356 $result[self::REF] = $result[self::VALUE];
357 }
358 }
359 return $result;
360 }
361 /**
362 * Reads the value of a property from an object.
363 *
364 * @throws NoSuchPropertyException If $ignoreInvalidProperty is false and the property does not exist or is not public
365 */
366 private function readProperty(array $zval, string $property, bool $ignoreInvalidProperty = \false) : array
367 {
368 if (!\is_object($zval[self::VALUE])) {
369 throw new NoSuchPropertyException(\sprintf('Cannot read property "%s" from an array. Maybe you intended to write the property path as "[%1$s]" instead.', $property));
370 }
371 $result = self::RESULT_PROTO;
372 $object = $zval[self::VALUE];
373 $class = \get_class($object);
374 $access = $this->getReadInfo($class, $property);
375 if (null !== $access) {
376 $name = $access->getName();
377 $type = $access->getType();
378 try {
379 if (PropertyReadInfo::TYPE_METHOD === $type) {
380 try {
381 $result[self::VALUE] = $object->{$name}();
382 } catch (\TypeError $e) {
383 [$trace] = $e->getTrace();
384 // handle uninitialized properties in PHP >= 7
385 if (__FILE__ === ($trace['file'] ?? null) && $name === $trace['function'] && $object instanceof $trace['class'] && \preg_match('/Return value (?:of .*::\\w+\\(\\) )?must be of (?:the )?type (\\w+), null returned$/', $e->getMessage(), $matches)) {
386 throw new UninitializedPropertyException(\sprintf('The method "%s::%s()" returned "null", but expected type "%3$s". Did you forget to initialize a property or to make the return type nullable using "?%3$s"?', \get_debug_type($object), $name, $matches[1]), 0, $e);
387 }
388 throw $e;
389 }
390 } elseif (PropertyReadInfo::TYPE_PROPERTY === $type) {
391 if ($access->canBeReference() && !isset($object->{$name}) && !\array_key_exists($name, (array) $object) && (\PHP_VERSION_ID < 70400 || !(new \ReflectionProperty($class, $name))->hasType())) {
392 throw new UninitializedPropertyException(\sprintf('The property "%s::$%s" is not initialized.', $class, $name));
393 }
394 $result[self::VALUE] = $object->{$name};
395 if (isset($zval[self::REF]) && $access->canBeReference()) {
396 $result[self::REF] =& $object->{$name};
397 }
398 }
399 } catch (\Error $e) {
400 // handle uninitialized properties in PHP >= 7.4
401 if (\PHP_VERSION_ID >= 70400 && \preg_match('/^Typed property ([\\w\\\\@]+)::\\$(\\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) {
402 $r = new \ReflectionProperty(\str_contains($matches[1], '@anonymous') ? $class : $matches[1], $matches[2]);
403 $type = ($type = $r->getType()) instanceof \ReflectionNamedType ? $type->getName() : (string) $type;
404 throw new UninitializedPropertyException(\sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $matches[1], $r->getName(), $type), 0, $e);
405 }
406 throw $e;
407 }
408 } elseif (\property_exists($object, $property) && \array_key_exists($property, (array) $object)) {
409 $result[self::VALUE] = $object->{$property};
410 if (isset($zval[self::REF])) {
411 $result[self::REF] =& $object->{$property};
412 }
413 } elseif (!$ignoreInvalidProperty) {
414 throw new NoSuchPropertyException(\sprintf('Can\'t get a way to read the property "%s" in class "%s".', $property, $class));
415 }
416 // Objects are always passed around by reference
417 if (isset($zval[self::REF]) && \is_object($result[self::VALUE])) {
418 $result[self::REF] = $result[self::VALUE];
419 }
420 return $result;
421 }
422 /**
423 * Guesses how to read the property value.
424 */
425 private function getReadInfo(string $class, string $property) : ?PropertyReadInfo
426 {
427 $key = \str_replace('\\', '.', $class) . '..' . $property;
428 if (isset($this->readPropertyCache[$key])) {
429 return $this->readPropertyCache[$key];
430 }
431 if ($this->cacheItemPool) {
432 $item = $this->cacheItemPool->getItem(self::CACHE_PREFIX_READ . \rawurlencode($key));
433 if ($item->isHit()) {
434 return $this->readPropertyCache[$key] = $item->get();
435 }
436 }
437 $accessor = $this->readInfoExtractor->getReadInfo($class, $property, ['enable_getter_setter_extraction' => \true, 'enable_magic_methods_extraction' => $this->magicMethodsFlags, 'enable_constructor_extraction' => \false]);
438 if (isset($item)) {
439 $this->cacheItemPool->save($item->set($accessor));
440 }
441 return $this->readPropertyCache[$key] = $accessor;
442 }
443 /**
444 * Sets the value of an index in a given array-accessible value.
445 *
446 * @param string|int $index The index to write at
447 * @param mixed $value The value to write
448 *
449 * @throws NoSuchIndexException If the array does not implement \ArrayAccess or it is not an array
450 */
451 private function writeIndex(array $zval, $index, $value)
452 {
453 if (!$zval[self::VALUE] instanceof \ArrayAccess && !\is_array($zval[self::VALUE])) {
454 throw new NoSuchIndexException(\sprintf('Cannot modify index "%s" in object of type "%s" because it doesn\'t implement \\ArrayAccess.', $index, \get_debug_type($zval[self::VALUE])));
455 }
456 $zval[self::REF][$index] = $value;
457 }
458 /**
459 * Sets the value of a property in the given object.
460 *
461 * @param mixed $value The value to write
462 *
463 * @throws NoSuchPropertyException if the property does not exist or is not public
464 */
465 private function writeProperty(array $zval, string $property, $value)
466 {
467 if (!\is_object($zval[self::VALUE])) {
468 throw new NoSuchPropertyException(\sprintf('Cannot write property "%s" to an array. Maybe you should write the property path as "[%1$s]" instead?', $property));
469 }
470 $object = $zval[self::VALUE];
471 $class = \get_class($object);
472 $mutator = $this->getWriteInfo($class, $property, $value);
473 if (PropertyWriteInfo::TYPE_NONE !== $mutator->getType()) {
474 $type = $mutator->getType();
475 if (PropertyWriteInfo::TYPE_METHOD === $type) {
476 $object->{$mutator->getName()}($value);
477 } elseif (PropertyWriteInfo::TYPE_PROPERTY === $type) {
478 $object->{$mutator->getName()} = $value;
479 } elseif (PropertyWriteInfo::TYPE_ADDER_AND_REMOVER === $type) {
480 $this->writeCollection($zval, $property, $value, $mutator->getAdderInfo(), $mutator->getRemoverInfo());
481 }
482 } elseif ($object instanceof \stdClass && \property_exists($object, $property)) {
483 $object->{$property} = $value;
484 } elseif (!$this->ignoreInvalidProperty) {
485 if ($mutator->hasErrors()) {
486 throw new NoSuchPropertyException(\implode('. ', $mutator->getErrors()) . '.');
487 }
488 throw new NoSuchPropertyException(\sprintf('Could not determine access type for property "%s" in class "%s".', $property, \get_debug_type($object)));
489 }
490 }
491 /**
492 * Adjusts a collection-valued property by calling add*() and remove*() methods.
493 */
494 private function writeCollection(array $zval, string $property, iterable $collection, PropertyWriteInfo $addMethod, PropertyWriteInfo $removeMethod)
495 {
496 // At this point the add and remove methods have been found
497 $previousValue = $this->readProperty($zval, $property);
498 $previousValue = $previousValue[self::VALUE];
499 $removeMethodName = $removeMethod->getName();
500 $addMethodName = $addMethod->getName();
501 if ($previousValue instanceof \Traversable) {
502 $previousValue = \iterator_to_array($previousValue);
503 }
504 if ($previousValue && \is_array($previousValue)) {
505 if (\is_object($collection)) {
506 $collection = \iterator_to_array($collection);
507 }
508 foreach ($previousValue as $key => $item) {
509 if (!\in_array($item, $collection, \true)) {
510 unset($previousValue[$key]);
511 $zval[self::VALUE]->{$removeMethodName}($item);
512 }
513 }
514 } else {
515 $previousValue = \false;
516 }
517 foreach ($collection as $item) {
518 if (!$previousValue || !\in_array($item, $previousValue, \true)) {
519 $zval[self::VALUE]->{$addMethodName}($item);
520 }
521 }
522 }
523 private function getWriteInfo(string $class, string $property, $value) : PropertyWriteInfo
524 {
525 $useAdderAndRemover = \is_iterable($value);
526 $key = \str_replace('\\', '.', $class) . '..' . $property . '..' . (int) $useAdderAndRemover;
527 if (isset($this->writePropertyCache[$key])) {
528 return $this->writePropertyCache[$key];
529 }
530 if ($this->cacheItemPool) {
531 $item = $this->cacheItemPool->getItem(self::CACHE_PREFIX_WRITE . \rawurlencode($key));
532 if ($item->isHit()) {
533 return $this->writePropertyCache[$key] = $item->get();
534 }
535 }
536 $mutator = $this->writeInfoExtractor->getWriteInfo($class, $property, ['enable_getter_setter_extraction' => \true, 'enable_magic_methods_extraction' => $this->magicMethodsFlags, 'enable_constructor_extraction' => \false, 'enable_adder_remover_extraction' => $useAdderAndRemover]);
537 if (isset($item)) {
538 $this->cacheItemPool->save($item->set($mutator));
539 }
540 return $this->writePropertyCache[$key] = $mutator;
541 }
542 /**
543 * Returns whether a property is writable in the given object.
544 */
545 private function isPropertyWritable(object $object, string $property) : bool
546 {
547 $mutatorForArray = $this->getWriteInfo(\get_class($object), $property, []);
548 if (PropertyWriteInfo::TYPE_NONE !== $mutatorForArray->getType() || $object instanceof \stdClass && \property_exists($object, $property)) {
549 return \true;
550 }
551 $mutator = $this->getWriteInfo(\get_class($object), $property, '');
552 return PropertyWriteInfo::TYPE_NONE !== $mutator->getType() || $object instanceof \stdClass && \property_exists($object, $property);
553 }
554 /**
555 * Gets a PropertyPath instance and caches it.
556 *
557 * @param string|PropertyPath $propertyPath
558 */
559 private function getPropertyPath($propertyPath) : PropertyPath
560 {
561 if ($propertyPath instanceof PropertyPathInterface) {
562 // Don't call the copy constructor has it is not needed here
563 return $propertyPath;
564 }
565 if (isset($this->propertyPathCache[$propertyPath])) {
566 return $this->propertyPathCache[$propertyPath];
567 }
568 if ($this->cacheItemPool) {
569 $item = $this->cacheItemPool->getItem(self::CACHE_PREFIX_PROPERTY_PATH . \rawurlencode($propertyPath));
570 if ($item->isHit()) {
571 return $this->propertyPathCache[$propertyPath] = $item->get();
572 }
573 }
574 $propertyPathInstance = new PropertyPath($propertyPath);
575 if (isset($item)) {
576 $item->set($propertyPathInstance);
577 $this->cacheItemPool->save($item);
578 }
579 return $this->propertyPathCache[$propertyPath] = $propertyPathInstance;
580 }
581 /**
582 * Creates the APCu adapter if applicable.
583 *
584 * @return AdapterInterface
585 *
586 * @throws \LogicException When the Cache Component isn't available
587 */
588 public static function createCache(string $namespace, int $defaultLifetime, string $version, ?LoggerInterface $logger = null)
589 {
590 if (!\class_exists(ApcuAdapter::class)) {
591 throw new \LogicException(\sprintf('The Symfony Cache component must be installed to use "%s()".', __METHOD__));
592 }
593 if (!ApcuAdapter::isSupported()) {
594 return new NullAdapter();
595 }
596 $apcu = new ApcuAdapter($namespace, $defaultLifetime / 5, $version);
597 if ('cli' === \PHP_SAPI && !\filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) {
598 $apcu->setLogger(new NullLogger());
599 } elseif (null !== $logger) {
600 $apcu->setLogger($logger);
601 }
602 return $apcu;
603 }
604 }
605