Вход Регистрация
Файл: vendor/symfony/var-exporter/Internal/Exporter.php
Строк: 381
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SymfonyComponentVarExporterInternal;

use 
SymfonyComponentVarExporterExceptionNotInstantiableTypeException;

/**
 * @author Nicolas Grekas <p@tchwork.com>
 *
 * @internal
 */
class Exporter
{
    
/**
     * Prepares an array of values for VarExporter.
     *
     * For performance this method is public and has no type-hints.
     *
     * @param array             &$values
     * @param SplObjectStorage $objectsPool
     * @param array             &$refsPool
     * @param int               &$objectsCount
     * @param bool              &$valuesAreStatic
     *
     * @return array
     *
     * @throws NotInstantiableTypeException When a value cannot be serialized
     */
    
public static function prepare($values$objectsPool, &$refsPool, &$objectsCount, &$valuesAreStatic)
    {
        
$refs $values;
        foreach (
$values as $k => $value) {
            if (
is_resource($value)) {
                throw new 
NotInstantiableTypeException(get_resource_type($value).' resource');
            }
            
$refs[$k] = $objectsPool;

            if (
$isRef = !$valueIsStatic $values[$k] !== $objectsPool) {
                
$values[$k] = &$value// Break hard references to make $values completely
                
unset($value);         // independent from the original structure
                
$refs[$k] = $value $values[$k];
                if (
$value instanceof Reference && $value->id) {
                    
$valuesAreStatic false;
                    ++
$value->count;
                    continue;
                }
                
$refsPool[] = [&$refs[$k], $value, &$value];
                
$refs[$k] = $values[$k] = new Reference(-count($refsPool), $value);
            }

            if (
is_array($value)) {
                if (
$value) {
                    
$value self::prepare($value$objectsPool$refsPool$objectsCount$valueIsStatic);
                }
                goto 
handle_value;
            } elseif (!
is_object($value) || $value instanceof UnitEnum) {
                goto 
handle_value;
            }

            
$valueIsStatic false;
            if (isset(
$objectsPool[$value])) {
                ++
$objectsCount;
                
$value = new Reference($objectsPool[$value][0]);
                goto 
handle_value;
            }

            
$class $value::class;
            
$reflector Registry::$reflectors[$class] ??= Registry::getClassReflector($class);
            
$properties = [];

            if (
$reflector->hasMethod('__serialize')) {
                if (!
$reflector->getMethod('__serialize')->isPublic()) {
                    throw new 
Error(sprintf('Call to %s method "%s::__serialize()".'$reflector->getMethod('__serialize')->isProtected() ? 'protected' 'private'$class));
                }

                if (!
is_array($serializeProperties $value->__serialize())) {
                    throw new 
TypeError($class.'::__serialize() must return an array');
                }

                if (
$reflector->hasMethod('__unserialize')) {
                    
$properties $serializeProperties;
                } else {
                    foreach (
$serializeProperties as $n => $v) {
                        
$c $reflector->hasProperty($n) && ($p $reflector->getProperty($n))->isReadOnly() ? $p->class 'stdClass';
                        
$properties[$c][$n] = $v;
                    }
                }

                goto 
prepare_value;
            }

            
$sleep null;
            
$proto Registry::$prototypes[$class];

            if ((
$value instanceof ArrayIterator || $value instanceof ArrayObject) && null !== $proto) {
                
// ArrayIterator and ArrayObject need special care because their "flags"
                // option changes the behavior of the (array) casting operator.
                
[$arrayValue$properties] = self::getArrayObjectProperties($value$proto);

                
// populates Registry::$prototypes[$class] with a new instance
                
Registry::getClassReflector($classRegistry::$instantiableWithoutConstructor[$class], Registry::$cloneable[$class]);
            } elseif (
$value instanceof SplObjectStorage && Registry::$cloneable[$class] && null !== $proto) {
                
// By implementing Serializable, SplObjectStorage breaks
                // internal references; let's deal with it on our own.
                
foreach (clone $value as $v) {
                    
$properties[] = $v;
                    
$properties[] = $value[$v];
                }
                
$properties = ['SplObjectStorage' => ["" => $properties]];
                
$arrayValue = (array) $value;
            } elseif (
$value instanceof Serializable
                
|| $value instanceof __PHP_Incomplete_Class
                
|| PHP_VERSION_ID 80200 && $value instanceof DatePeriod
            
) {
                ++
$objectsCount;
                
$objectsPool[$value] = [$id count($objectsPool), serialize($value), [], 0];
                
$value = new Reference($id);
                goto 
handle_value;
            } else {
                if (
method_exists($class'__sleep')) {
                    if (!
is_array($sleep $value->__sleep())) {
                        
trigger_error('serialize(): __sleep should return an array only containing the names of instance-variables to serialize'E_USER_NOTICE);
                        
$value null;
                        goto 
handle_value;
                    }
                    
$sleep array_flip($sleep);
                }

                
$arrayValue = (array) $value;
            }

            
$proto = (array) $proto;

            foreach (
$arrayValue as $name => $v) {
                
$i 0;
                
$n = (string) $name;
                if (
'' === $n || "" !== $n[0]) {
                    
$c $reflector->hasProperty($n) && ($p $reflector->getProperty($n))->isReadOnly() ? $p->class 'stdClass';
                } elseif (
'*' === $n[1]) {
                    
$n substr($n3);
                    
$c $reflector->getProperty($n)->class;
                    if (
'Error' === $c) {
                        
$c 'TypeError';
                    } elseif (
'Exception' === $c) {
                        
$c 'ErrorException';
                    }
                } else {
                    
$i strpos($n""2);
                    
$c substr($n1$i 1);
                    
$n substr($n$i);
                }
                if (
null !== $sleep) {
                    if (!isset(
$sleep[$name]) && (!isset($sleep[$n]) || ($i && $c !== $class))) {
                        unset(
$arrayValue[$name]);
                        continue;
                    }
                    unset(
$sleep[$name], $sleep[$n]);
                }
                if (!
array_key_exists($name$proto) || $proto[$name] !== $v || "x00Errorx00trace" === $name || "x00Exceptionx00trace" === $name) {
                    
$properties[$c][$n] = $v;
                }
            }
            if (
$sleep) {
                foreach (
$sleep as $n => $v) {
                    
trigger_error(sprintf('serialize(): "%s" returned as member variable from __sleep() but does not exist'$n), E_USER_NOTICE);
                }
            }
            if (
method_exists($class'__unserialize')) {
                
$properties $arrayValue;
            }

            
prepare_value:
            
$objectsPool[$value] = [$id count($objectsPool)];
            
$properties self::prepare($properties$objectsPool$refsPool$objectsCount$valueIsStatic);
            ++
$objectsCount;
            
$objectsPool[$value] = [$id$class$propertiesmethod_exists($class'__unserialize') ? -$objectsCount : (method_exists($class'__wakeup') ? $objectsCount 0)];

            
$value = new Reference($id);

            
handle_value:
            if (
$isRef) {
                unset(
$value); // Break the hard reference created above
            
} elseif (!$valueIsStatic) {
                
$values[$k] = $value;
            }
            
$valuesAreStatic $valueIsStatic && $valuesAreStatic;
        }

        return 
$values;
    }

    public static function 
export($value$indent '')
    {
        switch (
true) {
            case 
is_int($value) || is_float($value): return var_export($valuetrue);
            case [] === 
$value: return '[]';
            case 
false === $value: return 'false';
            case 
true === $value: return 'true';
            case 
null === $value: return 'null';
            case 
'' === $value: return "''";
            case 
$value instanceof UnitEnum: return '\'.ltrim(var_export($value, true), '\');
        }

        if ($value instanceof Reference) {
            if (0 <= $value->id) {
                return '
$o['.$value->id.']';
            }
            if (!$value->count) {
                return self::export($value->value, $indent);
            }
            $value = -$value->id;

            return '
&$r['.$value.']';
        }
        $subIndent = $indent.'    ';

        if (is_string($value)) {
            $code = sprintf("'
%s'", addcslashes($value, "'\"));

            
$code = preg_replace_callback("/((?:[\0\r\n]|u{202A}|u{202B}|u{202D}|u{202E}|u{2066}|u{2067}|u{2068}|u{202C}|u{2069})++)(.)/", function ($m) use ($subIndent) {
                
$m[1] = sprintf(''."%s".'', str_replace(
                    ["
", "r", "n", "u{202A}", "u{202B}", "u{202D}", "u{202E}", "u{2066}", "u{2067}", "u{2068}", "u{202C}", "u{2069}", 'n\'],
                    ['', 'r', 'n', 'u{202A}', 'u{202B}', 'u{202D}', 'u{202E}', 'u{2066}', 'u{2067}', 'u{2068}', 'u{202C}', 'u{2069}', 'n"'."n".$subIndent.'
."\'],
                    
$m[1]
                ));

                if ("'" === $m[2]) {
                    return substr($m[1], 0, -2);
                }

                if (str_ends_with($m[1], '
n".'')) {
                    return substr_replace(
$m[1], "n".$subIndent.".'".$m[2], -2);
                }

                return $m[1].$m[2];
            }, $code, -1, $count);

            if ($count && str_starts_with($code, "''.")) {
                $code = substr($code, 3);
            }

            return $code;
        }

        if (is_array($value)) {
            $j = -1;
            $code = '';
            foreach ($value as $k => $v) {
                $code .= $subIndent;
                if (!is_int($k) || 1 !== $k - $j) {
                    $code .= self::export($k, $subIndent).' 
=> ';
                }
                if (is_int($k) && $k > $j) {
                    $j = $k;
                }
                $code .= self::export($v, $subIndent).",n";
            }

            return "[n".$code.$indent.'
]';
        }

        if ($value instanceof Values) {
            $code = $subIndent."$r = [],n";
            foreach ($value->values as $k => $v) {
                $code .= $subIndent.'
$r['.$k.'] = '.self::export($v, $subIndent).",n";
            }

            return "[n".$code.$indent.'
]';
        }

        if ($value instanceof Registry) {
            return self::exportRegistry($value, $indent, $subIndent);
        }

        if ($value instanceof Hydrator) {
            return self::exportHydrator($value, $indent, $subIndent);
        }

        throw new UnexpectedValueException(sprintf('
Cannot export value of type "%s".', get_debug_type($value)));
    }

    private static function exportRegistry(Registry $value, string $indent, string $subIndent): string
    {
        $code = '';
        $serializables = [];
        $seen = [];
        $prototypesAccess = 0;
        $factoriesAccess = 0;
        $r = '
\'.Registry::class;
        $j = -1;

        foreach ($value->classes as $k => $class) {
            if ('
:' === ($class[1] ?? null)) {
                $serializables[$k] = $class;
                continue;
            }
            if (!Registry::$instantiableWithoutConstructor[$class]) {
                if (is_subclass_of($class, '
Serializable') && !method_exists($class, '__unserialize')) {
                    $serializables[$k] = '
C:'.strlen($class).':"'.$class.'":0:{}';
                } else {
                    $serializables[$k] = '
O:'.strlen($class).':"'.$class.'":0:{}';
                }
                if (is_subclass_of($class, '
Throwable')) {
                    $eol = is_subclass_of($class, '
Error') ? "Error" : "Exception";
                    $serializables[$k] = substr_replace($serializables[$k], '
1:{s:'.(5 + strlen($eol)).':"'.$eol.'trace";a:0:{}}', -4);
                }
                continue;
            }
            $code .= $subIndent.(1 !== $k - $j ? $k.' 
=> ' : '');
            $j = $k;
            $eol = ",n";
            $c = '
['.self::export($class).']';

            if ($seen[$class] ?? false) {
                if (Registry::$cloneable[$class]) {
                    ++$prototypesAccess;
                    $code .= '
clone $p'.$c;
                } else {
                    ++$factoriesAccess;
                    $code .= '
$f'.$c.'()';
                }
            } else {
                $seen[$class] = true;
                if (Registry::$cloneable[$class]) {
                    $code .= '
clone ('.($prototypesAccess++ ? '$p' : '($p = &'.$r.'::$prototypes)').$c.' ?? '.$r.'::p';
                } else {
                    $code .= '
('.($factoriesAccess++ ? '$f' : '($f = &'.$r.'::$factories)').$c.' ?? '.$r.'::f';
                    $eol = '
()'.$eol;
                }
                $code .= '
('.substr($c, 1, -1).'))';
            }
            $code .= $eol;
        }

        if (1 === $prototypesAccess) {
            $code = str_replace('
($p = &'.$r.'::$prototypes)', $r.'::$prototypes', $code);
        }
        if (1 === $factoriesAccess) {
            $code = str_replace('
($f = &'.$r.'::$factories)', $r.'::$factories', $code);
        }
        if ('' !== $code) {
            $code = "n".$code.$indent;
        }

        if ($serializables) {
            $code = $r.'
::unserialize(['.$code.'], '.self::export($serializables, $indent).')';
        } else {
            $code = '
['.$code.']';
        }

        return '
$o '.$code;
    }

    private static function exportHydrator(Hydrator $value, string $indent, string $subIndent): string
    {
        $code = '';
        foreach ($value->properties as $class => $properties) {
            $code .= $subIndent.'    '.self::export($class).' 
=> '.self::export($properties, $subIndent.'    ').",n";
        }

        $code = [
            self::export($value->registry, $subIndent),
            self::export($value->values, $subIndent),
            '' !== $code ? "[n".$code.$subIndent.'
]' : '[]',
            self::export($value->value, $subIndent),
            self::export($value->wakeups, $subIndent),
        ];

        return '
\'.$value::class."::hydrate(n".$subIndent.implode(",n".$subIndent, $code)."n".$indent.')';
    }

    /**
     * @param ArrayIterator|ArrayObject $value
     * @param ArrayIterator|ArrayObject $proto
     */
    private static function getArrayObjectProperties($value, $proto): array
    {
        $reflector = $value instanceof ArrayIterator ? '
ArrayIterator' : 'ArrayObject';
        $reflector = Registry::$reflectors[$reflector] ??= Registry::getClassReflector($reflector);

        $properties = [
            $arrayValue = (array) $value,
            $reflector->getMethod('
getFlags')->invoke($value),
            $value instanceof ArrayObject ? $reflector->getMethod('
getIteratorClass')->invoke($value) : 'ArrayIterator',
        ];

        $reflector = $reflector->getMethod('
setFlags');
        $reflector->invoke($proto, ArrayObject::STD_PROP_LIST);

        if ($properties[1] & ArrayObject::STD_PROP_LIST) {
            $reflector->invoke($value, 0);
            $properties[0] = (array) $value;
        } else {
            $reflector->invoke($value, ArrayObject::STD_PROP_LIST);
            $arrayValue = (array) $value;
        }
        $reflector->invoke($value, $properties[1]);

        if ([[], 0, '
ArrayIterator'] === $properties) {
            $properties = [];
        } else {
            if ('
ArrayIterator' === $properties[2]) {
                unset($properties[2]);
            }
            $properties = [$reflector->class => ["" => $properties]];
        }

        return [$arrayValue, $properties];
    }
}
Онлайн: 5
Реклама