[VarExporter] a new component to serialize values to plain PHP code
This commit is contained in:
parent
2df7320c1c
commit
7831ad75e5
|
@ -24,7 +24,6 @@ return PhpCsFixer\Config::create()
|
|||
->exclude(array(
|
||||
'Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures',
|
||||
// directories containing files with content that is autogenerated by `var_export`, which breaks CS in output code
|
||||
'Symfony/Component/Cache/Tests/Marshaller/Fixtures',
|
||||
'Symfony/Component/DependencyInjection/Tests/Fixtures',
|
||||
'Symfony/Component/Routing/Tests/Fixtures/dumper',
|
||||
// fixture templates
|
||||
|
@ -33,6 +32,7 @@ return PhpCsFixer\Config::create()
|
|||
'Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom',
|
||||
// generated fixtures
|
||||
'Symfony/Component/VarDumper/Tests/Fixtures',
|
||||
'Symfony/Component/VarExporter/Tests/Fixtures',
|
||||
// resource templates
|
||||
'Symfony/Bundle/FrameworkBundle/Resources/views/Form',
|
||||
// explicit trigger_error tests
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
"symfony/twig-bundle": "self.version",
|
||||
"symfony/validator": "self.version",
|
||||
"symfony/var-dumper": "self.version",
|
||||
"symfony/var-exporter": "self.version",
|
||||
"symfony/web-link": "self.version",
|
||||
"symfony/web-profiler-bundle": "self.version",
|
||||
"symfony/web-server-bundle": "self.version",
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'ArrayIterator',
|
||||
)),
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
'ArrayIterator' =>
|
||||
array (
|
||||
'' . "\0" . '' =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
0 => 123,
|
||||
),
|
||||
1 => 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'3' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -1,29 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyArrayObject',
|
||||
)),
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
'ArrayObject' =>
|
||||
array (
|
||||
'' . "\0" . '' =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
0 => 234,
|
||||
),
|
||||
1 => 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'3' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -1,37 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'ArrayObject',
|
||||
'1' => 'ArrayObject',
|
||||
)),
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
'ArrayObject' =>
|
||||
array (
|
||||
'' . "\0" . '' =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
0 => 1,
|
||||
1 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
),
|
||||
1 => 0,
|
||||
),
|
||||
),
|
||||
'foo' =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[1],
|
||||
),
|
||||
),
|
||||
),
|
||||
'3' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -1 +0,0 @@
|
|||
<?php return true;
|
|
@ -1,21 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyCloneable',
|
||||
'1' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyNotCloneable',
|
||||
)),
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
),
|
||||
'3' =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
1 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[1],
|
||||
),
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -1,31 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'DateTime',
|
||||
)),
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
'DateTime' =>
|
||||
array (
|
||||
'date' =>
|
||||
array (
|
||||
0 => '1970-01-01 00:00:00.000000',
|
||||
),
|
||||
'timezone_type' =>
|
||||
array (
|
||||
0 => 1,
|
||||
),
|
||||
'timezone' =>
|
||||
array (
|
||||
0 => '+00:00',
|
||||
),
|
||||
),
|
||||
),
|
||||
'3' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'4' =>
|
||||
array (
|
||||
1 => 0,
|
||||
),
|
||||
));
|
|
@ -1,22 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' => NULL,
|
||||
'1' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Values::__set_state(array(
|
||||
'1' =>
|
||||
array (
|
||||
0 =>
|
||||
&Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$references[1],
|
||||
),
|
||||
)),
|
||||
'2' =>
|
||||
array (
|
||||
),
|
||||
'3' =>
|
||||
array (
|
||||
0 =>
|
||||
&Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$references[1],
|
||||
),
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -1,26 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'stdClass',
|
||||
)),
|
||||
'1' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Values::__set_state(array(
|
||||
'1' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
)),
|
||||
'2' =>
|
||||
array (
|
||||
),
|
||||
'3' =>
|
||||
array (
|
||||
0 =>
|
||||
&Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$references[1],
|
||||
1 =>
|
||||
&Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$references[1],
|
||||
2 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
),
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -1,15 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'O:20:"SomeNotExistingClass":0:{}',
|
||||
)),
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
),
|
||||
'3' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -1,40 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyPrivateValue',
|
||||
'1' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyPrivateChildValue',
|
||||
)),
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyPrivateValue' =>
|
||||
array (
|
||||
'prot' =>
|
||||
array (
|
||||
0 => 123,
|
||||
),
|
||||
'priv' =>
|
||||
array (
|
||||
0 => 234,
|
||||
1 => 234,
|
||||
),
|
||||
),
|
||||
'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyPrivateChildValue' =>
|
||||
array (
|
||||
'prot' =>
|
||||
array (
|
||||
1 => 123,
|
||||
),
|
||||
),
|
||||
),
|
||||
'3' =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
1 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[1],
|
||||
),
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -1,20 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'C:55:"Symfony\\Component\\Cache\\Tests\\Marshaller\\MySerializable":3:{123}',
|
||||
)),
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
),
|
||||
'3' =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
1 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
),
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -1,7 +0,0 @@
|
|||
<?php return array (
|
||||
0 => 123,
|
||||
1 =>
|
||||
array (
|
||||
0 => 'abc',
|
||||
),
|
||||
);
|
|
@ -1,28 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'SplObjectStorage',
|
||||
'1' => 'stdClass',
|
||||
)),
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
'SplObjectStorage' =>
|
||||
array (
|
||||
'' . "\0" . '' =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[1],
|
||||
1 => 345,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'3' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'4' =>
|
||||
array (
|
||||
),
|
||||
));
|
|
@ -1,31 +0,0 @@
|
|||
<?php return Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator::__set_state(array(
|
||||
'0' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::__set_state(array(
|
||||
'0' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyWakeup',
|
||||
'1' => 'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyWakeup',
|
||||
)),
|
||||
'1' => NULL,
|
||||
'2' =>
|
||||
array (
|
||||
'Symfony\\Component\\Cache\\Tests\\Marshaller\\MyWakeup' =>
|
||||
array (
|
||||
'sub' =>
|
||||
array (
|
||||
0 =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[1],
|
||||
1 => 123,
|
||||
),
|
||||
'baz' =>
|
||||
array (
|
||||
1 => 123,
|
||||
),
|
||||
),
|
||||
),
|
||||
'3' =>
|
||||
Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry::$objects[0],
|
||||
'4' =>
|
||||
array (
|
||||
1 => 1,
|
||||
2 => 0,
|
||||
),
|
||||
));
|
|
@ -13,7 +13,7 @@ namespace Symfony\Component\Cache\Traits;
|
|||
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
||||
use Symfony\Component\VarExporter\VarExporter;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
|
@ -76,7 +76,7 @@ EOF;
|
|||
$value = "'N;'";
|
||||
} elseif (\is_object($value) || \is_array($value)) {
|
||||
try {
|
||||
$value = PhpMarshaller::marshall($value, $isStaticValue);
|
||||
$value = VarExporter::export($value, $isStaticValue);
|
||||
} catch (\Exception $e) {
|
||||
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \is_object($value) ? \get_class($value) : 'array'), 0, $e);
|
||||
}
|
||||
|
@ -93,7 +93,8 @@ EOF;
|
|||
}
|
||||
|
||||
if (!$isStaticValue) {
|
||||
$value = "static function () {\nreturn {$value};\n}";
|
||||
$value = str_replace("\n", "\n ", $value);
|
||||
$value = "static function () {\n return {$value};\n}";
|
||||
}
|
||||
$hash = hash('md5', $value);
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace Symfony\Component\Cache\Traits;
|
|||
|
||||
use Symfony\Component\Cache\Exception\CacheException;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
||||
use Symfony\Component\VarExporter\VarExporter;
|
||||
|
||||
/**
|
||||
* @author Piotr Stankowski <git@trakos.pl>
|
||||
|
@ -166,7 +166,7 @@ trait PhpFilesTrait
|
|||
$value = "'N;'";
|
||||
} elseif (\is_object($value) || \is_array($value)) {
|
||||
try {
|
||||
$value = PhpMarshaller::marshall($value, $isStaticValue);
|
||||
$value = VarExporter::export($value, $isStaticValue);
|
||||
} catch (\Exception $e) {
|
||||
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \is_object($value) ? \get_class($value) : 'array'), 0, $e);
|
||||
}
|
||||
|
@ -183,7 +183,8 @@ trait PhpFilesTrait
|
|||
}
|
||||
|
||||
if (!$isStaticValue) {
|
||||
$value = "static function () {\n\nreturn {$value};\n\n}";
|
||||
$value = str_replace("\n", "\n ", $value);
|
||||
$value = "static function () {\n\n return {$value};\n\n}";
|
||||
}
|
||||
|
||||
$file = $this->files[$key] = $this->getFile($key, true);
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
"psr/cache": "~1.0",
|
||||
"psr/log": "~1.0",
|
||||
"psr/simple-cache": "^1.0",
|
||||
"symfony/contracts": "^1.0"
|
||||
"symfony/contracts": "^1.0",
|
||||
"symfony/var-exporter": "^4.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"cache/integration-tests": "dev-master",
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
composer.lock
|
||||
phpunit.xml
|
||||
vendor/
|
|
@ -0,0 +1,7 @@
|
|||
CHANGELOG
|
||||
=========
|
||||
|
||||
4.2.0
|
||||
-----
|
||||
|
||||
* added the component
|
|
@ -9,7 +9,7 @@
|
|||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
||||
namespace Symfony\Component\VarExporter\Internal;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
|
@ -20,20 +20,24 @@ class Configurator
|
|||
{
|
||||
public static $configurators = array();
|
||||
|
||||
public $registry;
|
||||
public $values;
|
||||
public $properties;
|
||||
public $value;
|
||||
public $wakeups;
|
||||
|
||||
public function __construct(?Registry $registry, ?Values $values, array $properties, $value, array $wakeups)
|
||||
{
|
||||
$this->{0} = $registry;
|
||||
$this->{1} = $values;
|
||||
$this->{2} = $properties;
|
||||
$this->{3} = $value;
|
||||
$this->{4} = $wakeups;
|
||||
$this->registry = $registry;
|
||||
$this->values = $values;
|
||||
$this->properties = $properties;
|
||||
$this->value = $value;
|
||||
$this->wakeups = $wakeups;
|
||||
}
|
||||
|
||||
public static function __set_state($state)
|
||||
public static function pop($objects, $values, $properties, $value, $wakeups)
|
||||
{
|
||||
$objects = Registry::$objects;
|
||||
list(Registry::$objects, Registry::$references) = \array_pop(Registry::$stack);
|
||||
list(, , $properties, $value, $wakeups) = $state;
|
||||
|
||||
foreach ($properties as $class => $vars) {
|
||||
(self::$configurators[$class] ?? self::getConfigurator($class))($vars, $objects);
|
|
@ -9,17 +9,17 @@
|
|||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
||||
namespace Symfony\Component\VarExporter\Internal;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Marshaller
|
||||
class Exporter
|
||||
{
|
||||
/**
|
||||
* Prepares an array of values for PhpMarshaller.
|
||||
* Prepares an array of values for VarExporter.
|
||||
*
|
||||
* For performance this method is public and has no type-hints.
|
||||
*
|
||||
|
@ -32,10 +32,8 @@ class Marshaller
|
|||
* @return int
|
||||
*
|
||||
* @throws \Exception When a value cannot be serialized
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function marshall($values, $objectsPool, &$refsPool, &$objectsCount, &$valuesAreStatic)
|
||||
public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount, &$valuesAreStatic)
|
||||
{
|
||||
$refs = $values;
|
||||
foreach ($values as $k => $value) {
|
||||
|
@ -58,7 +56,7 @@ class Marshaller
|
|||
|
||||
if (\is_array($value)) {
|
||||
if ($value) {
|
||||
$value = self::marshall($value, $objectsPool, $refsPool, $objectsCount, $valueIsStatic);
|
||||
$value = self::prepare($value, $objectsPool, $refsPool, $objectsCount, $valueIsStatic);
|
||||
}
|
||||
goto handle_value;
|
||||
} elseif (!\is_object($value) && !$value instanceof \__PHP_Incomplete_Class) {
|
||||
|
@ -144,7 +142,7 @@ class Marshaller
|
|||
}
|
||||
|
||||
$objectsPool[$value] = array($id = \count($objectsPool));
|
||||
$properties = self::marshall($properties, $objectsPool, $refsPool, $objectsCount, $valueIsStatic);
|
||||
$properties = self::prepare($properties, $objectsPool, $refsPool, $objectsCount, $valueIsStatic);
|
||||
++$objectsCount;
|
||||
$objectsPool[$value] = array($id, $class, $properties, \method_exists($class, '__wakeup') ? $objectsCount : 0);
|
||||
|
||||
|
@ -162,6 +160,134 @@ class Marshaller
|
|||
return $values;
|
||||
}
|
||||
|
||||
public static function export($value, $indent = '')
|
||||
{
|
||||
switch (true) {
|
||||
case \is_int($value) || \is_float($value): return var_export($value, true);
|
||||
case array() === $value: return '[]';
|
||||
case false === $value: return 'false';
|
||||
case true === $value: return 'true';
|
||||
case null === $value: return 'null';
|
||||
case '' === $value: return "''";
|
||||
}
|
||||
|
||||
if ($value instanceof Reference) {
|
||||
if (0 <= $value->id) {
|
||||
return '\\'.Registry::class.'::$objects['.$value->id.']';
|
||||
}
|
||||
$value = -$value->id;
|
||||
|
||||
return '&\\'.Registry::class.'::$references['.$value.']';
|
||||
}
|
||||
$subIndent = $indent.' ';
|
||||
|
||||
if (\is_string($value)) {
|
||||
$code = var_export($value, true);
|
||||
|
||||
if (false !== strpos($value, "\n") || false !== strpos($value, "\r")) {
|
||||
$code = strtr($code, array(
|
||||
"\r\n" => "'.\"\\r\\n\"\n".$subIndent.".'",
|
||||
"\r" => "'.\"\\r\"\n".$subIndent.".'",
|
||||
"\n" => "'.\"\\n\"\n".$subIndent.".'",
|
||||
));
|
||||
}
|
||||
|
||||
if (false !== strpos($value, "\0")) {
|
||||
$code = str_replace('\' . "\0" . \'', '\'."\0".\'', $code);
|
||||
$code = str_replace('".\'\'."', '', $code);
|
||||
}
|
||||
|
||||
if (false !== strpos($code, "''.")) {
|
||||
$code = str_replace("''.", '', $code);
|
||||
}
|
||||
|
||||
if (".''" === substr($code, -3)) {
|
||||
$code = rtrim(substr($code, 0, -3));
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
if (\is_array($value)) {
|
||||
$j = -1;
|
||||
$code = '';
|
||||
foreach ($value as $k => $v) {
|
||||
$code .= $subIndent;
|
||||
if ($k !== ++$j) {
|
||||
$code .= self::export($k, $subIndent).' => ';
|
||||
$j = INF;
|
||||
}
|
||||
$code .= self::export($v, $subIndent).",\n";
|
||||
}
|
||||
|
||||
return "[\n".$code.$indent.']';
|
||||
}
|
||||
|
||||
if ($value instanceof Values) {
|
||||
$code = '';
|
||||
foreach ($value->values as $k => $v) {
|
||||
$code .= $subIndent.'\\'.Registry::class.'::$references['.$k.'] = '.self::export($v, $subIndent).",\n";
|
||||
}
|
||||
|
||||
return "[\n".$code.$indent.']';
|
||||
}
|
||||
|
||||
if ($value instanceof Registry) {
|
||||
$code = '';
|
||||
$reflectors = array();
|
||||
$serializables = array();
|
||||
|
||||
foreach ($value as $k => $class) {
|
||||
if (':' === ($class[1] ?? null)) {
|
||||
$serializables[$k] = $class;
|
||||
continue;
|
||||
}
|
||||
$c = '\\'.$class.'::class';
|
||||
$reflectors[$class] = '\\'.Registry::class.'::$reflectors['.$c.'] ?? \\'.Registry::class.'::getClassReflector('.$c.', '
|
||||
.self::export(Registry::$instantiableWithoutConstructor[$class]).', '
|
||||
.self::export(Registry::$cloneable[$class])
|
||||
.')';
|
||||
|
||||
if (Registry::$cloneable[$class]) {
|
||||
$code .= $subIndent.'clone \\'.Registry::class.'::$prototypes['.$c."],\n";
|
||||
} elseif (Registry::$instantiableWithoutConstructor[$class]) {
|
||||
$code .= $subIndent.'\\'.Registry::class.'::$reflectors['.$c."]->newInstanceWithoutConstructor(),\n";
|
||||
} else {
|
||||
$code .= $subIndent.'\\'.Registry::class.'::$reflectors['.$c."]->newInstance(),\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($reflectors) {
|
||||
$code = "[\n".$subIndent.implode(",\n".$subIndent, $reflectors).",\n".$indent."], [\n".$code.$indent.'], ';
|
||||
$code .= !$serializables ? "[\n".$indent.']' : self::export($serializables, $indent);
|
||||
} else {
|
||||
$code = '[], []';
|
||||
$code .= ', '.self::export($serializables, $indent);
|
||||
}
|
||||
|
||||
return '\\'.Registry::class.'::push('.$code.')';
|
||||
}
|
||||
|
||||
if ($value instanceof Configurator) {
|
||||
$code = '';
|
||||
foreach ($value->properties as $class => $properties) {
|
||||
$code .= $subIndent.' \\'.$class.'::class => '.self::export($properties, $subIndent.' ').",\n";
|
||||
}
|
||||
|
||||
$code = array(
|
||||
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 '\\'.\get_class($value)."::pop(\n".$subIndent.implode(",\n".$subIndent, $code)."\n".$indent.')';
|
||||
}
|
||||
|
||||
throw new \UnexpectedValueException(sprintf('Cannot export value of type "%s".', \is_object($value) ? \get_class($value) : \gettype($value)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the state of an ArrayIterator or ArrayObject instance.
|
||||
*
|
||||
|
@ -172,8 +298,6 @@ class Marshaller
|
|||
* @param object $proto
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function getArrayObjectProperties($value, &$arrayValue, $proto)
|
||||
{
|
|
@ -9,7 +9,7 @@
|
|||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
||||
namespace Symfony\Component\VarExporter\Internal;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
|
@ -9,7 +9,7 @@
|
|||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
||||
namespace Symfony\Component\VarExporter\Internal;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
|
@ -33,44 +33,35 @@ class Registry
|
|||
}
|
||||
}
|
||||
|
||||
public static function __set_state($classes)
|
||||
public static function push($reflectors, $objects, $serializables)
|
||||
{
|
||||
$unserializeCallback = null;
|
||||
self::$stack[] = array(self::$objects, self::$references);
|
||||
self::$objects = $classes;
|
||||
self::$references = array();
|
||||
try {
|
||||
foreach (self::$objects as &$class) {
|
||||
if (':' === ($class[1] ?? null)) {
|
||||
if (null === $unserializeCallback) {
|
||||
$unserializeCallback = ini_set('unserialize_callback_func', __CLASS__.'::getClassReflector');
|
||||
}
|
||||
$class = \unserialize($class);
|
||||
continue;
|
||||
}
|
||||
$r = self::$reflectors[$class] ?? self::getClassReflector($class);
|
||||
|
||||
if (self::$cloneable[$class]) {
|
||||
$class = clone self::$prototypes[$class];
|
||||
} else {
|
||||
$class = self::$instantiableWithoutConstructor[$class] ? $r->newInstanceWithoutConstructor() : $r->newInstance();
|
||||
}
|
||||
if (!$serializables) {
|
||||
return self::$objects = $objects;
|
||||
}
|
||||
$unserializeCallback = ini_set('unserialize_callback_func', __CLASS__.'::getClassReflector');
|
||||
|
||||
try {
|
||||
foreach ($serializables as $k => $v) {
|
||||
$objects[$k] = unserialize($v);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
list(self::$objects, self::$references) = \array_pop(self::$stack);
|
||||
list(self::$objects, self::$references) = array_pop(self::$stack);
|
||||
throw $e;
|
||||
} finally {
|
||||
if (null !== $unserializeCallback) {
|
||||
ini_set('unserialize_callback_func', $unserializeCallback);
|
||||
}
|
||||
ini_set('unserialize_callback_func', $unserializeCallback);
|
||||
}
|
||||
|
||||
return self::$objects = $objects;
|
||||
}
|
||||
|
||||
public static function getClassReflector($class)
|
||||
public static function getClassReflector($class, $instantiableWithoutConstructor = null, $cloneable = null)
|
||||
{
|
||||
$reflector = new \ReflectionClass($class);
|
||||
|
||||
if (self::$instantiableWithoutConstructor[$class] = !$reflector->isFinal() || !$reflector->isInternal()) {
|
||||
if (self::$instantiableWithoutConstructor[$class] = $instantiableWithoutConstructor ?? (!$reflector->isFinal() || !$reflector->isInternal())) {
|
||||
$proto = $reflector->newInstanceWithoutConstructor();
|
||||
} else {
|
||||
try {
|
||||
|
@ -80,14 +71,23 @@ class Registry
|
|||
}
|
||||
}
|
||||
|
||||
if ($proto instanceof \Reflector || $proto instanceof \ReflectionGenerator || $proto instanceof \ReflectionType) {
|
||||
if (null !== $cloneable) {
|
||||
self::$prototypes[$class] = $proto;
|
||||
self::$cloneable[$class] = $cloneable;
|
||||
|
||||
return self::$reflectors[$class] = $reflector;
|
||||
}
|
||||
|
||||
if ($proto instanceof \Reflector || $proto instanceof \ReflectionGenerator || $proto instanceof \ReflectionType || $proto instanceof \IteratorIterator || $proto instanceof \RecursiveIteratorIterator) {
|
||||
if (!$proto instanceof \Serializable && !\method_exists($proto, '__wakeup')) {
|
||||
throw new \Exception(sprintf("Serialization of '%s' is not allowed", $class));
|
||||
}
|
||||
self::$cloneable[$class] = false;
|
||||
} else {
|
||||
self::$cloneable[$class] = !$reflector->hasMethod('__clone');
|
||||
}
|
||||
|
||||
self::$prototypes[$class] = $proto;
|
||||
self::$cloneable[$class] = !$reflector->hasMethod('__clone');
|
||||
|
||||
if ($proto instanceof \Throwable) {
|
||||
static $trace;
|
|
@ -9,7 +9,7 @@
|
|||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
||||
namespace Symfony\Component\VarExporter\Internal;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
|
@ -18,17 +18,10 @@ namespace Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
|||
*/
|
||||
class Values
|
||||
{
|
||||
public $values;
|
||||
|
||||
public function __construct(array $values)
|
||||
{
|
||||
foreach ($values as $i => $v) {
|
||||
$this->$i = $v;
|
||||
}
|
||||
}
|
||||
|
||||
public static function __set_state($values)
|
||||
{
|
||||
foreach ($values as $i => $v) {
|
||||
Registry::$references[$i] = $v;
|
||||
}
|
||||
$this->values = $values;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2018 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,34 @@
|
|||
VarExporter Component
|
||||
=====================
|
||||
|
||||
The VarExporter component allows exporting any serializable PHP data structure to
|
||||
plain PHP code. While doing so, it preserves all the semantics associated with
|
||||
the serialization mechanism of PHP (`__wakeup`, `__sleep`, `Serializable`).
|
||||
|
||||
The reason to use this component *vs* `serialize()` or
|
||||
[igbinary](https://github.com/igbinary/igbinary) is performance: thanks to
|
||||
OPcache, the resulting code is significantly faster and more memory efficient
|
||||
than using `unserialize()` or `igbinary_unserialize()`.
|
||||
|
||||
Unlike `var_export()`, this works on any serializable PHP value.
|
||||
|
||||
It also provides a few improvements over `var_export()`/`serialize()`:
|
||||
|
||||
* the output is PSR-2 compatible;
|
||||
* the output can be re-indented without messing up with `\r` or `\n` in the data
|
||||
* missing classes throw a `ReflectionException` instead of being unserialized to
|
||||
`PHP_Incomplete_Class` objects;
|
||||
* references involving `SplObjectStorage`, `ArrayObject` or `ArrayIterator`
|
||||
instances are preserved;
|
||||
* `Reflection*`, `IteratorIterator` and `RecursiveIteratorIterator` classes
|
||||
throw an exception when being serialized (their unserialized version is broken
|
||||
anyway, see https://bugs.php.net/76737).
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Documentation](https://symfony.com/doc/current/components/var_exporter/introduction.html)
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
|
||||
\Symfony\Component\VarExporter\Internal\Registry::push([
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\ArrayIterator::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\ArrayIterator::class, true, true),
|
||||
], [
|
||||
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\ArrayIterator::class],
|
||||
], [
|
||||
]),
|
||||
null,
|
||||
[
|
||||
\ArrayIterator::class => [
|
||||
"\0" => [
|
||||
[
|
||||
[
|
||||
123,
|
||||
],
|
||||
1,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
|
||||
[]
|
||||
);
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
|
||||
\Symfony\Component\VarExporter\Internal\Registry::push([
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\Symfony\Component\VarExporter\Tests\MyArrayObject::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\Symfony\Component\VarExporter\Tests\MyArrayObject::class, true, true),
|
||||
], [
|
||||
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\Symfony\Component\VarExporter\Tests\MyArrayObject::class],
|
||||
], [
|
||||
]),
|
||||
null,
|
||||
[
|
||||
\ArrayObject::class => [
|
||||
"\0" => [
|
||||
[
|
||||
[
|
||||
234,
|
||||
],
|
||||
1,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
|
||||
[]
|
||||
);
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
|
||||
\Symfony\Component\VarExporter\Internal\Registry::push([
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\ArrayObject::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\ArrayObject::class, true, true),
|
||||
], [
|
||||
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\ArrayObject::class],
|
||||
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\ArrayObject::class],
|
||||
], [
|
||||
]),
|
||||
null,
|
||||
[
|
||||
\ArrayObject::class => [
|
||||
"\0" => [
|
||||
[
|
||||
[
|
||||
1,
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
|
||||
],
|
||||
0,
|
||||
],
|
||||
],
|
||||
'foo' => [
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[1],
|
||||
],
|
||||
],
|
||||
],
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
|
||||
[]
|
||||
);
|
|
@ -0,0 +1,3 @@
|
|||
<?php
|
||||
|
||||
return true;
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
|
||||
\Symfony\Component\VarExporter\Internal\Registry::push([
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\Symfony\Component\VarExporter\Tests\MyCloneable::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\Symfony\Component\VarExporter\Tests\MyCloneable::class, true, false),
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\Symfony\Component\VarExporter\Tests\MyNotCloneable::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\Symfony\Component\VarExporter\Tests\MyNotCloneable::class, true, false),
|
||||
], [
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\Symfony\Component\VarExporter\Tests\MyCloneable::class]->newInstanceWithoutConstructor(),
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\Symfony\Component\VarExporter\Tests\MyNotCloneable::class]->newInstanceWithoutConstructor(),
|
||||
], [
|
||||
]),
|
||||
null,
|
||||
[],
|
||||
[
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[1],
|
||||
],
|
||||
[]
|
||||
);
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
|
||||
\Symfony\Component\VarExporter\Internal\Registry::push([
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\DateTime::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\DateTime::class, true, true),
|
||||
], [
|
||||
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\DateTime::class],
|
||||
], [
|
||||
]),
|
||||
null,
|
||||
[
|
||||
\DateTime::class => [
|
||||
'date' => [
|
||||
'1970-01-01 00:00:00.000000',
|
||||
],
|
||||
'timezone_type' => [
|
||||
1,
|
||||
],
|
||||
'timezone' => [
|
||||
'+00:00',
|
||||
],
|
||||
],
|
||||
],
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
|
||||
[
|
||||
1 => 0,
|
||||
]
|
||||
);
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
|
||||
\Symfony\Component\VarExporter\Internal\Registry::push([], [], []),
|
||||
[
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$references[1] = [
|
||||
&\Symfony\Component\VarExporter\Internal\Registry::$references[1],
|
||||
],
|
||||
],
|
||||
[],
|
||||
[
|
||||
&\Symfony\Component\VarExporter\Internal\Registry::$references[1],
|
||||
],
|
||||
[]
|
||||
);
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
|
||||
\Symfony\Component\VarExporter\Internal\Registry::push([
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\stdClass::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\stdClass::class, true, true),
|
||||
], [
|
||||
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\stdClass::class],
|
||||
], [
|
||||
]),
|
||||
[
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$references[1] = \Symfony\Component\VarExporter\Internal\Registry::$objects[0],
|
||||
],
|
||||
[],
|
||||
[
|
||||
&\Symfony\Component\VarExporter\Internal\Registry::$references[1],
|
||||
&\Symfony\Component\VarExporter\Internal\Registry::$references[1],
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
|
||||
],
|
||||
[]
|
||||
);
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
|
||||
\Symfony\Component\VarExporter\Internal\Registry::push([], [], [
|
||||
'O:20:"SomeNotExistingClass":0:{}',
|
||||
]),
|
||||
null,
|
||||
[],
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
|
||||
[]
|
||||
);
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
"\0\0\r\n"
|
||||
.'A' => 'B'."\r"
|
||||
.'C'."\n"
|
||||
."\n",
|
||||
];
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
|
||||
\Symfony\Component\VarExporter\Internal\Registry::push([
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\Symfony\Component\VarExporter\Tests\MyPrivateValue::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\Symfony\Component\VarExporter\Tests\MyPrivateValue::class, true, true),
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\Symfony\Component\VarExporter\Tests\MyPrivateChildValue::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\Symfony\Component\VarExporter\Tests\MyPrivateChildValue::class, true, true),
|
||||
], [
|
||||
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\Symfony\Component\VarExporter\Tests\MyPrivateValue::class],
|
||||
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\Symfony\Component\VarExporter\Tests\MyPrivateChildValue::class],
|
||||
], [
|
||||
]),
|
||||
null,
|
||||
[
|
||||
\Symfony\Component\VarExporter\Tests\MyPrivateValue::class => [
|
||||
'prot' => [
|
||||
123,
|
||||
],
|
||||
'priv' => [
|
||||
234,
|
||||
234,
|
||||
],
|
||||
],
|
||||
\Symfony\Component\VarExporter\Tests\MyPrivateChildValue::class => [
|
||||
'prot' => [
|
||||
1 => 123,
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[1],
|
||||
],
|
||||
[]
|
||||
);
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
|
||||
\Symfony\Component\VarExporter\Internal\Registry::push([], [], [
|
||||
'C:50:"Symfony\\Component\\VarExporter\\Tests\\MySerializable":3:{123}',
|
||||
]),
|
||||
null,
|
||||
[],
|
||||
[
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
|
||||
],
|
||||
[]
|
||||
);
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
123,
|
||||
[
|
||||
'abc',
|
||||
],
|
||||
];
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
|
||||
\Symfony\Component\VarExporter\Internal\Registry::push([
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\SplObjectStorage::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\SplObjectStorage::class, true, true),
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\stdClass::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\stdClass::class, true, true),
|
||||
], [
|
||||
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\SplObjectStorage::class],
|
||||
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\stdClass::class],
|
||||
], [
|
||||
]),
|
||||
null,
|
||||
[
|
||||
\SplObjectStorage::class => [
|
||||
"\0" => [
|
||||
[
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[1],
|
||||
345,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
|
||||
[]
|
||||
);
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
return \Symfony\Component\VarExporter\Internal\Configurator::pop(
|
||||
\Symfony\Component\VarExporter\Internal\Registry::push([
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$reflectors[\Symfony\Component\VarExporter\Tests\MyWakeup::class] ?? \Symfony\Component\VarExporter\Internal\Registry::getClassReflector(\Symfony\Component\VarExporter\Tests\MyWakeup::class, true, true),
|
||||
], [
|
||||
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\Symfony\Component\VarExporter\Tests\MyWakeup::class],
|
||||
clone \Symfony\Component\VarExporter\Internal\Registry::$prototypes[\Symfony\Component\VarExporter\Tests\MyWakeup::class],
|
||||
], [
|
||||
]),
|
||||
null,
|
||||
[
|
||||
\Symfony\Component\VarExporter\Tests\MyWakeup::class => [
|
||||
'sub' => [
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[1],
|
||||
123,
|
||||
],
|
||||
'baz' => [
|
||||
1 => 123,
|
||||
],
|
||||
],
|
||||
],
|
||||
\Symfony\Component\VarExporter\Internal\Registry::$objects[0],
|
||||
[
|
||||
1 => 1,
|
||||
2 => 0,
|
||||
]
|
||||
);
|
|
@ -9,14 +9,14 @@
|
|||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Cache\Tests\Marshaller;
|
||||
namespace Symfony\Component\VarExporter\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller;
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry;
|
||||
use Symfony\Component\VarExporter\Internal\Registry;
|
||||
use Symfony\Component\VarExporter\VarExporter;
|
||||
use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
|
||||
|
||||
class DoctrineProviderTest extends TestCase
|
||||
class VarExporterTest extends TestCase
|
||||
{
|
||||
use VarDumperTestTrait;
|
||||
|
||||
|
@ -28,7 +28,7 @@ class DoctrineProviderTest extends TestCase
|
|||
{
|
||||
$unserializeCallback = ini_set('unserialize_callback_func', 'var_dump');
|
||||
try {
|
||||
Registry::__set_state(array('O:20:"SomeNotExistingClass":0:{}'));
|
||||
Registry::push(array(), array(), array('O:20:"SomeNotExistingClass":0:{}'));
|
||||
} finally {
|
||||
$this->assertSame('var_dump', ini_set('unserialize_callback_func', $unserializeCallback));
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ class DoctrineProviderTest extends TestCase
|
|||
{
|
||||
$expectedDump = $this->getDump($value);
|
||||
try {
|
||||
PhpMarshaller::marshall($value);
|
||||
VarExporter::export($value);
|
||||
} finally {
|
||||
$this->assertDumpEquals(rtrim($expectedDump), $value);
|
||||
}
|
||||
|
@ -74,12 +74,12 @@ class DoctrineProviderTest extends TestCase
|
|||
{
|
||||
$serializedValue = serialize($value);
|
||||
$isStaticValue = true;
|
||||
$marshalledValue = PhpMarshaller::marshall($value, $isStaticValue);
|
||||
$marshalledValue = VarExporter::export($value, $isStaticValue);
|
||||
|
||||
$this->assertSame($staticValueExpected, $isStaticValue);
|
||||
$this->assertSame($serializedValue, serialize($value));
|
||||
|
||||
$dump = '<?php return '.$marshalledValue.";\n";
|
||||
$dump = "<?php\n\nreturn ".$marshalledValue.";\n";
|
||||
$fixtureFile = __DIR__.'/Fixtures/'.$testName.'.php';
|
||||
$this->assertStringEqualsFile($fixtureFile, $dump);
|
||||
|
||||
|
@ -97,6 +97,8 @@ class DoctrineProviderTest extends TestCase
|
|||
|
||||
public function provideMarshall()
|
||||
{
|
||||
yield array('multiline-string', array("\0\0\r\nA" => "B\rC\n\n"), true);
|
||||
|
||||
yield array('bool', true, true);
|
||||
yield array('simple-array', array(123, array('abc')), true);
|
||||
yield array('datetime', \DateTime::createFromFormat('U', 0));
|
|
@ -9,32 +9,42 @@
|
|||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Cache\Marshaller;
|
||||
namespace Symfony\Component\VarExporter;
|
||||
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Configurator;
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Marshaller;
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Reference;
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Registry;
|
||||
use Symfony\Component\Cache\Marshaller\PhpMarshaller\Values;
|
||||
use Symfony\Component\VarExporter\Internal\Configurator;
|
||||
use Symfony\Component\VarExporter\Internal\Exporter;
|
||||
use Symfony\Component\VarExporter\Internal\Reference;
|
||||
use Symfony\Component\VarExporter\Internal\Registry;
|
||||
use Symfony\Component\VarExporter\Internal\Values;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* Exports serializable PHP values to PHP code.
|
||||
*
|
||||
* PhpMarshaller allows serializing PHP data structures using var_export()
|
||||
* while preserving all the semantics associated to serialize().
|
||||
* VarExporter allows serializing PHP data structures to plain PHP code (like var_export())
|
||||
* while preserving all the semantics associated with serialize() (unlike var_export()).
|
||||
*
|
||||
* By leveraging OPcache, the generated PHP code is faster than doing the same with unserialize().
|
||||
*
|
||||
* @internal
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class PhpMarshaller
|
||||
final class VarExporter
|
||||
{
|
||||
public static function marshall($value, bool &$isStaticValue = null): string
|
||||
/**
|
||||
* Exports a serializable PHP value to PHP code.
|
||||
*
|
||||
* @param mixed $value The value to export
|
||||
* @param bool &$isStaticValue Set to true after execution if the provided value is static, false otherwise
|
||||
*
|
||||
* @return string The value exported as PHP code
|
||||
*
|
||||
* @throws \Exception When the provided value cannot be serialized
|
||||
*/
|
||||
public static function export($value, bool &$isStaticValue = null): string
|
||||
{
|
||||
$isStaticValue = true;
|
||||
|
||||
if (!\is_object($value) && !(\is_array($value) && $value) && !$value instanceof \__PHP_Incomplete_Class && !\is_resource($value)) {
|
||||
return var_export($value, true);
|
||||
return Exporter::export($value);
|
||||
}
|
||||
|
||||
$objectsPool = new \SplObjectStorage();
|
||||
|
@ -42,7 +52,7 @@ class PhpMarshaller
|
|||
$objectsCount = 0;
|
||||
|
||||
try {
|
||||
$value = Marshaller::marshall(array($value), $objectsPool, $refsPool, $objectsCount, $isStaticValue)[0];
|
||||
$value = Exporter::prepare(array($value), $objectsPool, $refsPool, $objectsCount, $isStaticValue)[0];
|
||||
} finally {
|
||||
$references = array();
|
||||
foreach ($refsPool as $i => $v) {
|
||||
|
@ -52,7 +62,7 @@ class PhpMarshaller
|
|||
}
|
||||
|
||||
if ($isStaticValue) {
|
||||
return var_export($value, true);
|
||||
return Exporter::export($value);
|
||||
}
|
||||
|
||||
$classes = array();
|
||||
|
@ -75,13 +85,8 @@ class PhpMarshaller
|
|||
}
|
||||
}
|
||||
|
||||
$value = new Configurator($classes ? new Registry($classes) : null, $references ? new Values($references) : null, $properties, $value, $wakeups);
|
||||
$value = var_export($value, true);
|
||||
$value = new Configurator(new Registry($classes), $references ? new Values($references) : null, $properties, $value, $wakeups);
|
||||
|
||||
$regexp = sprintf("{%s::__set_state\(array\(\s++'id' => %%s(\d+),\s++\)\)}", preg_quote(Reference::class));
|
||||
$value = preg_replace(sprintf($regexp, ''), Registry::class.'::$objects[$1]', $value);
|
||||
$value = preg_replace(sprintf($regexp, '-'), '&'.Registry::class.'::$references[$1]', $value);
|
||||
|
||||
return $value;
|
||||
return Exporter::export($value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"name": "symfony/var-exporter",
|
||||
"type": "library",
|
||||
"description": "A blend of var_export() + serialize() to turn any serializable data structure to plain PHP code",
|
||||
"keywords": ["export", "serialize"],
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.1.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/var-dumper": "^4.1.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\VarExporter\\": "" },
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.2-dev"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
colors="true"
|
||||
bootstrap="vendor/autoload.php"
|
||||
failOnRisky="true"
|
||||
failOnWarning="true"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Symfony VarExporter Component Test Suite">
|
||||
<directory>./Tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./</directory>
|
||||
<exclude>
|
||||
<directory>./Resources</directory>
|
||||
<directory>./Tests</directory>
|
||||
<directory>./vendor</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
Reference in New Issue