diff --git a/.php_cs.dist b/.php_cs.dist index f9f786895f..ef19f6f131 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -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 diff --git a/composer.json b/composer.json index fc42f7b955..50788784a0 100644 --- a/composer.json +++ b/composer.json @@ -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", diff --git a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/array-iterator.php b/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/array-iterator.php deleted file mode 100644 index 47eb76e09d..0000000000 --- a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/array-iterator.php +++ /dev/null @@ -1,29 +0,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 ( - ), -)); diff --git a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/array-object-custom.php b/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/array-object-custom.php deleted file mode 100644 index 3f99f018e5..0000000000 --- a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/array-object-custom.php +++ /dev/null @@ -1,29 +0,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 ( - ), -)); diff --git a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/array-object.php b/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/array-object.php deleted file mode 100644 index fc4b0d48c3..0000000000 --- a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/array-object.php +++ /dev/null @@ -1,37 +0,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 ( - ), -)); diff --git a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/bool.php b/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/bool.php deleted file mode 100644 index 7c0eaedf00..0000000000 --- a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/bool.php +++ /dev/null @@ -1 +0,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 ( - ), -)); diff --git a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/datetime.php b/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/datetime.php deleted file mode 100644 index fa2cb9d6c1..0000000000 --- a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/datetime.php +++ /dev/null @@ -1,31 +0,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, - ), -)); diff --git a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/hard-references-recursive.php b/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/hard-references-recursive.php deleted file mode 100644 index 84c6a8a009..0000000000 --- a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/hard-references-recursive.php +++ /dev/null @@ -1,22 +0,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 ( - ), -)); diff --git a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/hard-references.php b/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/hard-references.php deleted file mode 100644 index 00b8f6a7e8..0000000000 --- a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/hard-references.php +++ /dev/null @@ -1,26 +0,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 ( - ), -)); diff --git a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/incomplete-class.php b/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/incomplete-class.php deleted file mode 100644 index ea92f52ab0..0000000000 --- a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/incomplete-class.php +++ /dev/null @@ -1,15 +0,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 ( - ), -)); diff --git a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/private.php b/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/private.php deleted file mode 100644 index bda368b6be..0000000000 --- a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/private.php +++ /dev/null @@ -1,40 +0,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 ( - ), -)); diff --git a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/serializable.php b/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/serializable.php deleted file mode 100644 index b86ddc86f2..0000000000 --- a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/serializable.php +++ /dev/null @@ -1,20 +0,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 ( - ), -)); diff --git a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/simple-array.php b/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/simple-array.php deleted file mode 100644 index 8dd73eb31d..0000000000 --- a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/simple-array.php +++ /dev/null @@ -1,7 +0,0 @@ - 123, - 1 => - array ( - 0 => 'abc', - ), -); diff --git a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/spl-object-storage.php b/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/spl-object-storage.php deleted file mode 100644 index 4c72a10971..0000000000 --- a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/spl-object-storage.php +++ /dev/null @@ -1,28 +0,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 ( - ), -)); diff --git a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/wakeup.php b/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/wakeup.php deleted file mode 100644 index fdf2218512..0000000000 --- a/src/Symfony/Component/Cache/Tests/Marshaller/Fixtures/wakeup.php +++ /dev/null @@ -1,31 +0,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, - ), -)); diff --git a/src/Symfony/Component/Cache/Traits/PhpArrayTrait.php b/src/Symfony/Component/Cache/Traits/PhpArrayTrait.php index 37b03c2fb9..587da5a498 100644 --- a/src/Symfony/Component/Cache/Traits/PhpArrayTrait.php +++ b/src/Symfony/Component/Cache/Traits/PhpArrayTrait.php @@ -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 @@ -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); diff --git a/src/Symfony/Component/Cache/Traits/PhpFilesTrait.php b/src/Symfony/Component/Cache/Traits/PhpFilesTrait.php index dcf428f23c..2d021f0f79 100644 --- a/src/Symfony/Component/Cache/Traits/PhpFilesTrait.php +++ b/src/Symfony/Component/Cache/Traits/PhpFilesTrait.php @@ -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 @@ -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); diff --git a/src/Symfony/Component/Cache/composer.json b/src/Symfony/Component/Cache/composer.json index e683ae21f6..ab642debc7 100644 --- a/src/Symfony/Component/Cache/composer.json +++ b/src/Symfony/Component/Cache/composer.json @@ -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", diff --git a/src/Symfony/Component/VarExporter/.gitignore b/src/Symfony/Component/VarExporter/.gitignore new file mode 100644 index 0000000000..5414c2c655 --- /dev/null +++ b/src/Symfony/Component/VarExporter/.gitignore @@ -0,0 +1,3 @@ +composer.lock +phpunit.xml +vendor/ diff --git a/src/Symfony/Component/VarExporter/CHANGELOG.md b/src/Symfony/Component/VarExporter/CHANGELOG.md new file mode 100644 index 0000000000..9aa4a8b310 --- /dev/null +++ b/src/Symfony/Component/VarExporter/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +4.2.0 +----- + + * added the component diff --git a/src/Symfony/Component/Cache/Marshaller/PhpMarshaller/Configurator.php b/src/Symfony/Component/VarExporter/Internal/Configurator.php similarity index 90% rename from src/Symfony/Component/Cache/Marshaller/PhpMarshaller/Configurator.php rename to src/Symfony/Component/VarExporter/Internal/Configurator.php index c2398b1409..f1925ad59d 100644 --- a/src/Symfony/Component/Cache/Marshaller/PhpMarshaller/Configurator.php +++ b/src/Symfony/Component/VarExporter/Internal/Configurator.php @@ -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 @@ -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); diff --git a/src/Symfony/Component/Cache/Marshaller/PhpMarshaller/Marshaller.php b/src/Symfony/Component/VarExporter/Internal/Exporter.php similarity index 58% rename from src/Symfony/Component/Cache/Marshaller/PhpMarshaller/Marshaller.php rename to src/Symfony/Component/VarExporter/Internal/Exporter.php index 3fe24baf94..5ba406d800 100644 --- a/src/Symfony/Component/Cache/Marshaller/PhpMarshaller/Marshaller.php +++ b/src/Symfony/Component/VarExporter/Internal/Exporter.php @@ -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 * * @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) { diff --git a/src/Symfony/Component/Cache/Marshaller/PhpMarshaller/Reference.php b/src/Symfony/Component/VarExporter/Internal/Reference.php similarity index 87% rename from src/Symfony/Component/Cache/Marshaller/PhpMarshaller/Reference.php rename to src/Symfony/Component/VarExporter/Internal/Reference.php index f5dd01fd80..4f39c1ff30 100644 --- a/src/Symfony/Component/Cache/Marshaller/PhpMarshaller/Reference.php +++ b/src/Symfony/Component/VarExporter/Internal/Reference.php @@ -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 diff --git a/src/Symfony/Component/Cache/Marshaller/PhpMarshaller/Registry.php b/src/Symfony/Component/VarExporter/Internal/Registry.php similarity index 60% rename from src/Symfony/Component/Cache/Marshaller/PhpMarshaller/Registry.php rename to src/Symfony/Component/VarExporter/Internal/Registry.php index 4bf5290e11..46c57b5c3f 100644 --- a/src/Symfony/Component/Cache/Marshaller/PhpMarshaller/Registry.php +++ b/src/Symfony/Component/VarExporter/Internal/Registry.php @@ -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 @@ -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; diff --git a/src/Symfony/Component/Cache/Marshaller/PhpMarshaller/Values.php b/src/Symfony/Component/VarExporter/Internal/Values.php similarity index 56% rename from src/Symfony/Component/Cache/Marshaller/PhpMarshaller/Values.php rename to src/Symfony/Component/VarExporter/Internal/Values.php index 3aa404dd5f..21ae04e68b 100644 --- a/src/Symfony/Component/Cache/Marshaller/PhpMarshaller/Values.php +++ b/src/Symfony/Component/VarExporter/Internal/Values.php @@ -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 @@ -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; } } diff --git a/src/Symfony/Component/VarExporter/LICENSE b/src/Symfony/Component/VarExporter/LICENSE new file mode 100644 index 0000000000..ad399a798d --- /dev/null +++ b/src/Symfony/Component/VarExporter/LICENSE @@ -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. diff --git a/src/Symfony/Component/VarExporter/README.md b/src/Symfony/Component/VarExporter/README.md new file mode 100644 index 0000000000..7ac1b7dc2b --- /dev/null +++ b/src/Symfony/Component/VarExporter/README.md @@ -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) diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/array-iterator.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/array-iterator.php new file mode 100644 index 0000000000..8cdd560349 --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/array-iterator.php @@ -0,0 +1,25 @@ + [ + "\0" => [ + [ + [ + 123, + ], + 1, + ], + ], + ], + ], + \Symfony\Component\VarExporter\Internal\Registry::$objects[0], + [] +); diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object-custom.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object-custom.php new file mode 100644 index 0000000000..0a67a15268 --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object-custom.php @@ -0,0 +1,25 @@ + [ + "\0" => [ + [ + [ + 234, + ], + 1, + ], + ], + ], + ], + \Symfony\Component\VarExporter\Internal\Registry::$objects[0], + [] +); diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object.php new file mode 100644 index 0000000000..97c51427ee --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/array-object.php @@ -0,0 +1,30 @@ + [ + "\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], + [] +); diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/bool.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/bool.php new file mode 100644 index 0000000000..438d99ecc0 --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/bool.php @@ -0,0 +1,3 @@ +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], + ], + [] +); diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime.php new file mode 100644 index 0000000000..10ef69412c --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/datetime.php @@ -0,0 +1,28 @@ + [ + 'date' => [ + '1970-01-01 00:00:00.000000', + ], + 'timezone_type' => [ + 1, + ], + 'timezone' => [ + '+00:00', + ], + ], + ], + \Symfony\Component\VarExporter\Internal\Registry::$objects[0], + [ + 1 => 0, + ] +); diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/hard-references-recursive.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/hard-references-recursive.php new file mode 100644 index 0000000000..e17f4f93cb --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/hard-references-recursive.php @@ -0,0 +1,15 @@ + 'B'."\r" + .'C'."\n" + ."\n", +]; diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/private.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/private.php new file mode 100644 index 0000000000..36040abc35 --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/private.php @@ -0,0 +1,34 @@ + [ + '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], + ], + [] +); diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/serializable.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/serializable.php new file mode 100644 index 0000000000..da735d6e2e --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/serializable.php @@ -0,0 +1,14 @@ + [ + "\0" => [ + [ + \Symfony\Component\VarExporter\Internal\Registry::$objects[1], + 345, + ], + ], + ], + ], + \Symfony\Component\VarExporter\Internal\Registry::$objects[0], + [] +); diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/wakeup.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/wakeup.php new file mode 100644 index 0000000000..236203ae3d --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/wakeup.php @@ -0,0 +1,28 @@ + [ + 'sub' => [ + \Symfony\Component\VarExporter\Internal\Registry::$objects[1], + 123, + ], + 'baz' => [ + 1 => 123, + ], + ], + ], + \Symfony\Component\VarExporter\Internal\Registry::$objects[0], + [ + 1 => 1, + 2 => 0, + ] +); diff --git a/src/Symfony/Component/Cache/Tests/Marshaller/PhpMarshallerTest.php b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php similarity index 91% rename from src/Symfony/Component/Cache/Tests/Marshaller/PhpMarshallerTest.php rename to src/Symfony/Component/VarExporter/Tests/VarExporterTest.php index 05c64d27c0..b497170533 100644 --- a/src/Symfony/Component/Cache/Tests/Marshaller/PhpMarshallerTest.php +++ b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php @@ -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 = '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)); diff --git a/src/Symfony/Component/Cache/Marshaller/PhpMarshaller.php b/src/Symfony/Component/VarExporter/VarExporter.php similarity index 52% rename from src/Symfony/Component/Cache/Marshaller/PhpMarshaller.php rename to src/Symfony/Component/VarExporter/VarExporter.php index a3d43ac945..2bed1532f3 100644 --- a/src/Symfony/Component/Cache/Marshaller/PhpMarshaller.php +++ b/src/Symfony/Component/VarExporter/VarExporter.php @@ -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 + * 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 */ -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); } } diff --git a/src/Symfony/Component/VarExporter/composer.json b/src/Symfony/Component/VarExporter/composer.json new file mode 100644 index 0000000000..35290469a0 --- /dev/null +++ b/src/Symfony/Component/VarExporter/composer.json @@ -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" + } + } +} diff --git a/src/Symfony/Component/VarExporter/phpunit.xml.dist b/src/Symfony/Component/VarExporter/phpunit.xml.dist new file mode 100644 index 0000000000..8f54f6811a --- /dev/null +++ b/src/Symfony/Component/VarExporter/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + +