![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/old/vendor/brick/varexporter/src/Internal/ObjectExporter/ |
<?php declare(strict_types=1); namespace Brick\VarExporter\Internal\ObjectExporter; use Brick\VarExporter\Internal\ObjectExporter; /** * Handles any class through direct property access and bound closures. * * @todo On PHP 7.4, we could remove unset() calls from the output for typed properties having no default value. * This doesn't hurt in the meantime. * * @internal This class is for internal use, and not part of the public API. It may change at any time without warning. */ class AnyObjectExporter extends ObjectExporter { /** * {@inheritDoc} */ public function supports(\ReflectionObject $reflectionObject) : bool { return true; } /** * {@inheritDoc} * * @psalm-suppress MixedAssignment */ public function export(object $object, \ReflectionObject $reflectionObject, array $path, array $parentIds) : array { $lines = $this->getCreateObjectCode($reflectionObject); $objectAsArray = (array) $object; $current = $this->exporter->skipDynamicProperties ? new \ReflectionClass($object) // properties from class definition only : $reflectionObject; // properties from class definition + dynamic properties $isParentClass = false; $returnNewObject = ($reflectionObject->getConstructor() === null); while ($current) { $publicNonReadonlyProperties = []; $nonPublicOrPublicReadonlyProperties = []; $unsetPublicNonReadonlyProperties = []; $unsetNonPublicOrPublicReadonlyProperties = []; foreach ($current->getProperties() as $property) { if ($property->isStatic()) { continue; } if ($isParentClass && ! $property->isPrivate()) { // property already handled in the child class. continue; } $name = $property->getName(); // getting the property value through the object to array cast, and not through reflection, as this is // currently the only way to know whether a declared property has been unset - at least before PHP 7.4, // which will bring ReflectionProperty::isInitialized(). $key = $this->getPropertyKey($property); if (array_key_exists($key, $objectAsArray)) { $value = $objectAsArray[$key]; if ($property->isPublic() && !(method_exists($property, 'isReadOnly') && $property->isReadOnly())) { $publicNonReadonlyProperties[$name] = $value; } else { $nonPublicOrPublicReadonlyProperties[$name] = $value; } } else { if ($property->isPublic() && !(method_exists($property, 'isReadOnly') && $property->isReadOnly())) { $unsetPublicNonReadonlyProperties[] = $name; } else { $unsetNonPublicOrPublicReadonlyProperties[] = $name; } } $returnNewObject = false; } if ($publicNonReadonlyProperties || $unsetPublicNonReadonlyProperties) { $lines[] = ''; foreach ($publicNonReadonlyProperties as $name => $value) { /** @psalm-suppress RedundantCast See: https://github.com/vimeo/psalm/issues/4891 */ $name = (string) $name; $newPath = $path; $newPath[] = $name; $newParentIds = $parentIds; $newParentIds[] = spl_object_id($object); $exportedValue = $this->exporter->export($value, $newPath, $newParentIds); $exportedValue = $this->exporter->wrap($exportedValue, '$object->' . $this->escapePropName($name) . ' = ', ';'); $lines = array_merge($lines, $exportedValue); } foreach ($unsetPublicNonReadonlyProperties as $name) { $lines[] = 'unset($object->' . $this->escapePropName($name) . ');'; } } if ($nonPublicOrPublicReadonlyProperties || $unsetNonPublicOrPublicReadonlyProperties) { $closureLines = []; if ($this->exporter->addTypeHints) { $closureLines[] = '/** @var \\' . $current->getName() . ' $this */'; } foreach ($nonPublicOrPublicReadonlyProperties as $name => $value) { $newPath = $path; $newPath[] = $name; $newParentIds = $parentIds; $newParentIds[] = spl_object_id($object); $exportedValue = $this->exporter->export($value, $newPath, $newParentIds); $exportedValue = $this->exporter->wrap($exportedValue, '$this->' . $this->escapePropName($name) . ' = ', ';'); $closureLines = array_merge($closureLines, $exportedValue); } foreach ($unsetNonPublicOrPublicReadonlyProperties as $name) { $closureLines[] = 'unset($this->' . $this->escapePropName($name) . ');'; } $lines[] = ''; $lines[] = '(function() {'; $lines = array_merge($lines, $this->exporter->indent($closureLines)); $lines[] = '})->bindTo($object, \\' . $current->getName() . '::class)();'; } $current = $current->getParentClass(); $isParentClass = true; } if ($returnNewObject) { // no constructor, no properties return ['new \\' . $reflectionObject->getName()]; } $lines[] = ''; $lines[] = 'return $object;'; return $this->wrapInClosure($lines); } /** * Returns the key of the given property in the object-to-array cast. * * @param \ReflectionProperty $property * * @return string */ private function getPropertyKey(\ReflectionProperty $property) : string { $name = $property->getName(); if ($property->isPrivate()) { return "\0" . $property->getDeclaringClass()->getName() . "\0" . $name; } if ($property->isProtected()) { return "\0*\0" . $name; } return $name; } /** * @param string $var * * @return string */ private function escapePropName(string $var) : string { if (preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $var) === 1) { return $var; } return '{' . var_export($var, true) . '}'; } }