From 4d7c89bfaf8d5897d67880fc643974d7850688b9 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Mon, 4 Apr 2016 14:34:08 +0200 Subject: [PATCH 01/12] [ValueExporter] extracted ValueExporter in its own component --- .../Component/ValueExporter/.gitignore | 3 + .../Component/ValueExporter/CHANGELOG.md | 7 ++ .../Exception/InvalidFormatterException.php | 31 +++++++ .../Exporter/AbstractValueExporter.php | 74 +++++++++++++++ .../Exporter/ValueExporterInterface.php | 53 +++++++++++ .../Exporter/ValueToStringExporter.php | 93 +++++++++++++++++++ .../Formatter/DateTimeToStringFormatter.php | 39 ++++++++ .../Formatter/FormatterInterface.php | 31 +++++++ .../PhpIncompleteClassToStringFormatter.php | 44 +++++++++ .../Formatter/StringFormatterInterface.php | 31 +++++++ .../Component/ValueExporter/LICENSE.txt | 19 ++++ src/Symfony/Component/ValueExporter/README.md | 16 ++++ .../Resources/functions/to_string.php | 23 +++++ .../ValueExporter/Tests/ValueExporterTest.php | 82 ++++++++++++++++ .../Component/ValueExporter/ValueExporter.php | 93 +++++++++++++++++++ .../Component/ValueExporter/composer.json | 49 ++++++++++ .../Component/ValueExporter/phpunit.xml.dist | 31 +++++++ 17 files changed, 719 insertions(+) create mode 100644 src/Symfony/Component/ValueExporter/.gitignore create mode 100644 src/Symfony/Component/ValueExporter/CHANGELOG.md create mode 100644 src/Symfony/Component/ValueExporter/Exception/InvalidFormatterException.php create mode 100644 src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php create mode 100644 src/Symfony/Component/ValueExporter/Exporter/ValueExporterInterface.php create mode 100644 src/Symfony/Component/ValueExporter/Exporter/ValueToStringExporter.php create mode 100644 src/Symfony/Component/ValueExporter/Formatter/DateTimeToStringFormatter.php create mode 100644 src/Symfony/Component/ValueExporter/Formatter/FormatterInterface.php create mode 100644 src/Symfony/Component/ValueExporter/Formatter/PhpIncompleteClassToStringFormatter.php create mode 100644 src/Symfony/Component/ValueExporter/Formatter/StringFormatterInterface.php create mode 100644 src/Symfony/Component/ValueExporter/LICENSE.txt create mode 100644 src/Symfony/Component/ValueExporter/README.md create mode 100644 src/Symfony/Component/ValueExporter/Resources/functions/to_string.php create mode 100644 src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php create mode 100644 src/Symfony/Component/ValueExporter/ValueExporter.php create mode 100644 src/Symfony/Component/ValueExporter/composer.json create mode 100644 src/Symfony/Component/ValueExporter/phpunit.xml.dist diff --git a/src/Symfony/Component/ValueExporter/.gitignore b/src/Symfony/Component/ValueExporter/.gitignore new file mode 100644 index 0000000000000..5414c2c655e72 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/.gitignore @@ -0,0 +1,3 @@ +composer.lock +phpunit.xml +vendor/ diff --git a/src/Symfony/Component/ValueExporter/CHANGELOG.md b/src/Symfony/Component/ValueExporter/CHANGELOG.md new file mode 100644 index 0000000000000..58eb13c207df2 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +3.2.0 +----- + + * introducing the component diff --git a/src/Symfony/Component/ValueExporter/Exception/InvalidFormatterException.php b/src/Symfony/Component/ValueExporter/Exception/InvalidFormatterException.php new file mode 100644 index 0000000000000..a8ae16aa43680 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Exception/InvalidFormatterException.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Exception; + +/** + * Thrown when a {@link \Symfony\Component\ValueExporter\Formatter\FormatterInterface} + * is not supported by the {@link \Symfony\Component\ValueExporter\Exporter\ValueExporterInterface}. + * + * @author Jules Pietri + */ +class InvalidFormatterException extends \InvalidArgumentException +{ + /** + * @param string $formatterClass The invalid formatter class + * @param string $exporterClass The exporter class + * @param string $expectedInterface The expected formatter interface + */ + public function __construct($formatterClass, $exporterClass, $expectedInterface) + { + parent::__construct(sprintf('The exporter "%s" expects formatters implementing "%", but was given "%s" class.', $exporterClass, $expectedInterface, $formatterClass)); + } +} diff --git a/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php b/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php new file mode 100644 index 0000000000000..82d98f05a5cb1 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Exporter; + +use Symfony\Component\ValueExporter\Exception\InvalidFormatterException; +use Symfony\Component\ValueExporter\Formatter\FormatterInterface; + +/** + * ValueExporterInterface implementations export PHP values. + * + * @author Jules Pietri + */ +abstract class AbstractValueExporter implements ValueExporterInterface +{ + /** + * The supported formatter interface. + * + * @var string + */ + protected $formatterInterface = FormatterInterface::class; + + /** + * An array of formatters. + * + * @var FormatterInterface[] + */ + private $formatters = array(); + + /** + * Takes {@link FormatterInterface} as arguments. + * + * They will be called in the given order. + */ + final public function __construct() + { + $this->addFormatters(func_get_args()); + } + + /** + * {@inheritdoc} + */ + final public function addFormatters(array $appends, array $prepends = array()) + { + foreach ($appends as $append) { + if (!$append instanceof $this->formatterInterface) { + throw new InvalidFormatterException(get_class($append), self::class, $this->formatterInterface); + } + $this->formatters[] = $append; + } + foreach (array_reverse($prepends) as $prepend) { + if (!$prepend instanceof $this->formatterInterface) { + throw new InvalidFormatterException(get_class($prepend), self::class, $this->formatterInterface); + } + array_unshift($this->formatters, $prepend); + } + } + + /** + * @return FormatterInterface[] + */ + final protected function formatters() + { + return $this->formatters; + } +} diff --git a/src/Symfony/Component/ValueExporter/Exporter/ValueExporterInterface.php b/src/Symfony/Component/ValueExporter/Exporter/ValueExporterInterface.php new file mode 100644 index 0000000000000..d055059fa9536 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Exporter/ValueExporterInterface.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Exporter; + +use Symfony\Component\ValueExporter\Exception\InvalidFormatterException; +use Symfony\Component\ValueExporter\Exporter; +use Symfony\Component\ValueExporter\Formatter\FormatterInterface; + +/** + * ValueExporterInterface implementations export PHP values. + * + * An implementation can rely on {@link FormatterInterface} implementations + * to handle specific types of value. + * + * @author Jules Pietri + */ +interface ValueExporterInterface +{ + /** + * Exports a PHP value. + * + * ValueExporter instance should always deal with array or \Traversable + * values first in order to handle depth and expand arguments. + * + * Usually you don't need to define the depth but it will be incremented + * in recursive calls. When expand is false any expandable values such as + * arrays or objects should be inline in their exported representation. + * + * @param mixed $value The PHP value to export + * @param int $depth The level of indentation + * @param bool $expand Whether to inline or expand nested values + */ + public function exportValue($value, $depth, $expand); + + /** + * Adds {@link FormatterInterface} that will be called in the given order. + * + * @param FormatterInterface[] $appends The formatters to execute at last + * @param FormatterInterface[] $prepends The formatters to execute first + * + * @throws InvalidFormatterException If the exporter does not support a given formatter + */ + public function addFormatters(array $appends, array $prepends); +} diff --git a/src/Symfony/Component/ValueExporter/Exporter/ValueToStringExporter.php b/src/Symfony/Component/ValueExporter/Exporter/ValueToStringExporter.php new file mode 100644 index 0000000000000..71663d822eaef --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Exporter/ValueToStringExporter.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Exporter; + +use Symfony\Component\ValueExporter\Formatter\StringFormatterInterface; + +/** + * @author Fabien Potencier + * @author Bernhard Schussek + * @author Quentin Schuler + * @author Jules Pietri + */ +class ValueToStringExporter extends AbstractValueExporter +{ + protected $formatterInterface = StringFormatterInterface::class; + + public function exportValue($value, $depth = 1, $expand = false) + { + // Arrays have to be handled first to deal with nested level and depth, + // this implementation intentionally ignores \Traversable values. + // Therefor, \Traversable instances might be treated as objects unless + // implementing a {@link StringFormatterInterface} and passing it to + // the exporter in order to support them. + if (is_array($value)) { + if (empty($value)) { + return 'array()'; + } + $indent = str_repeat(' ', $depth); + + $a = array(); + foreach ($value as $k => $v) { + if (is_array($v) && !empty($v)) { + $expand = true; + } + $a[] = sprintf('%s => %s', is_string($k) ? sprintf("'%s'", $k) : $k, $this->exportValue($v, $depth + 1, $expand)); + } + if ($expand) { + return sprintf("array(\n%s%s\n%s)", $indent, implode(sprintf(", \n%s", $indent), $a), str_repeat(' ', $depth - 1)); + } + + $s = sprintf('array(%s)', implode(', ', $a)); + + if (80 > strlen($s)) { + return $s; + } + + return sprintf("array(\n%s%s\n)", $indent, implode(sprintf(",\n%s", $indent), $a)); + } + // Not an array, test each formatter + foreach ($this->formatters() as $formatter) { + /** @var StringFormatterInterface $formatter */ + if ($formatter->supports($value)) { + return $formatter->formatToString($value); + } + } + // Fallback on default + if (is_object($value)) { + return sprintf('object(%s)', get_class($value)); + } + if (is_resource($value)) { + return sprintf('resource(%s#%d)', get_resource_type($value), $value); + } + if (is_float($value)) { + return sprintf('(float) %s', $value); + } + if (is_int($value)) { + return sprintf('(int) %d', $value); + } + if (is_string($value)) { + return sprintf('"%s"', $value); + } + if (null === $value) { + return 'null'; + } + if (false === $value) { + return 'false'; + } + if (true === $value) { + return 'true'; + } + + return (string) $value; + } +} diff --git a/src/Symfony/Component/ValueExporter/Formatter/DateTimeToStringFormatter.php b/src/Symfony/Component/ValueExporter/Formatter/DateTimeToStringFormatter.php new file mode 100644 index 0000000000000..efa4b0b6ccc26 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Formatter/DateTimeToStringFormatter.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Formatter; + +/** + * Returns a string representation of a DateTimeInterface instance. + * + * Based on the contribution by @scuben (https://github.com/scuben) + * https://github.com/symfony/symfony/commit/a1762fb65423dc94d69c5fb6abaed37f2ad576e6 + * + * @author Jules Pietri + */ +class DateTimeToStringFormatter implements StringFormatterInterface +{ + /** + * {@inheritdoc} + */ + public function supports($value) + { + return $value instanceof \DateTimeInterface; + } + + /** + * {@inheritdoc} + */ + public function formatToString($value) + { + return sprintf('object(%s) - %s', get_class($value), $value->format(\DateTime::ISO8601)); + } +} diff --git a/src/Symfony/Component/ValueExporter/Formatter/FormatterInterface.php b/src/Symfony/Component/ValueExporter/Formatter/FormatterInterface.php new file mode 100644 index 0000000000000..e32504905b05a --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Formatter/FormatterInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Formatter; + +/** + * FormatterInterface. + * + * Returns a formatted representation of (a) supported type(s) of PHP value. + * + * @author Jules Pietri + */ +interface FormatterInterface +{ + /** + * Returns whether the formatter can format the type(s) of the given value. + * + * @param mixed $value The given value to format + * + * @return bool Whether the given value can be formatted + */ + public function supports($value); +} diff --git a/src/Symfony/Component/ValueExporter/Formatter/PhpIncompleteClassToStringFormatter.php b/src/Symfony/Component/ValueExporter/Formatter/PhpIncompleteClassToStringFormatter.php new file mode 100644 index 0000000000000..b3ba602d1bed5 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Formatter/PhpIncompleteClassToStringFormatter.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Formatter; + +/** + * Returns a string representation of a __PHP_Incomplete_Class instance. + * + * @author Yonel Ceruto González + * @author Jules Pietri + */ +class PhpIncompleteClassToStringFormatter implements StringFormatterInterface +{ + /** + * {@inheritdoc} + */ + public function supports($value) + { + return $value instanceof \__PHP_Incomplete_Class; + } + + /** + * {@inheritdoc} + */ + public function formatToString($value) + { + return sprintf('__PHP_Incomplete_Class(%s)', $this->getClassNameFromIncomplete($value)); + } + + private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value) + { + $array = new \ArrayObject($value); + + return $array['__PHP_Incomplete_Class_Name']; + } +} diff --git a/src/Symfony/Component/ValueExporter/Formatter/StringFormatterInterface.php b/src/Symfony/Component/ValueExporter/Formatter/StringFormatterInterface.php new file mode 100644 index 0000000000000..8c6fa260e98a3 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Formatter/StringFormatterInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Formatter; + +/** + * StringFormatter. + * + * Returns a string representation of a given value. + * + * @author Jules Pietri + */ +interface StringFormatterInterface extends FormatterInterface +{ + /** + * Returns a given value formatted to string. + * + * @param mixed $value The given value to format to string + * + * @return string A string representation of the given value + */ + public function formatToString($value); +} diff --git a/src/Symfony/Component/ValueExporter/LICENSE.txt b/src/Symfony/Component/ValueExporter/LICENSE.txt new file mode 100644 index 0000000000000..0564c5a9b7f1f --- /dev/null +++ b/src/Symfony/Component/ValueExporter/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2016 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/ValueExporter/README.md b/src/Symfony/Component/ValueExporter/README.md new file mode 100644 index 0000000000000..cde83ef943a37 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/README.md @@ -0,0 +1,16 @@ +ValueExporter Component +======================= + +The ValueExporter component provides mechanisms to export any arbitrary +PHP variable in a desired format. Built on top, it provides a `to_string()` +function that you can safely use instead of casting `(string) $value`, finely +represented thanks to formatters. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/value_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/ValueExporter/Resources/functions/to_string.php b/src/Symfony/Component/ValueExporter/Resources/functions/to_string.php new file mode 100644 index 0000000000000..367fca038e77b --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Resources/functions/to_string.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Component\ValueExporter\ValueExporter; + +if (!function_exists('to_string')) { + /** + * @author Nicolas Grekas + * @author Jules Pietri + */ + function to_string($value, $depth = 1, $expand = false) + { + return ValueExporter::export($value, $depth, $expand); + } +} diff --git a/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php b/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php new file mode 100644 index 0000000000000..ecb4b5fcfa6b7 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Tests; + +use Symfony\Component\ValueExporter\ValueExporter; + +class ValueExporterTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider valueProvider + */ + public function testExportValue($value, $string) + { + $this->assertSame($string, ValueExporter::export($value)); + } + + /** + * @dataProvider valueProvider + */ + public function testToStringFunctionWrapper($value, $string) + { + $this->assertSame($string, to_string($value)); + } + + public function testExportValueExpanded() + { + $value = array( + array(ValueExporter::class, 'export'), + ); + + $exportedValue = << array(0 => "Symfony\Component\ValueExporter\ValueExporter", 1 => "export") +) +EOT; + + $this->assertSame($exportedValue, ValueExporter::export($value, 1, true)); + } + + public function valueProvider() + { + $foo = new \__PHP_Incomplete_Class(); + $array = new \ArrayObject($foo); + $array['__PHP_Incomplete_Class_Name'] = 'AppBundle/Foo'; + + return array( + 'null' => array(null, 'null'), + 'true' => array(true, 'true'), + 'false' => array(false, 'false'), + 'int' => array(4, '(int) 4'), + 'float' => array(4.5, '(float) 4.5'), + 'string' => array('test', '"test"'), + 'empty array' => array(array(), 'array()'), + 'numeric array' => array( + array(0 => null, 1 => true, 2 => 1, 3 => '2', 4 => new \stdClass()), + 'array(0 => null, 1 => true, 2 => (int) 1, 3 => "2", 4 => object(stdClass))', + ), + 'mixed keys array' => array( + array(0 => 0, '1' => 'un', 'key' => 4.5), + 'array(0 => (int) 0, 1 => "un", \'key\' => (float) 4.5)', + ), + 'datetime' => array( + new \DateTime('2014-06-10 07:35:40', new \DateTimeZone('UTC')), + 'object(DateTime) - 2014-06-10T07:35:40+0000', + ), + 'datetime immutable' => array( + new \DateTimeImmutable('2014-06-10 07:35:40', new \DateTimeZone('UTC')), + 'object(DateTimeImmutable) - 2014-06-10T07:35:40+0000', + ), + 'php incomplete class' => array($foo, '__PHP_Incomplete_Class(AppBundle/Foo)'), + ); + } +} diff --git a/src/Symfony/Component/ValueExporter/ValueExporter.php b/src/Symfony/Component/ValueExporter/ValueExporter.php new file mode 100644 index 0000000000000..cbb242984b780 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/ValueExporter.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter; + +use Symfony\Component\ValueExporter\Exporter\ValueExporterInterface; +use Symfony\Component\ValueExporter\Exporter\ValueToStringExporter; +use Symfony\Component\ValueExporter\Formatter\DateTimeToStringFormatter; +use Symfony\Component\ValueExporter\Formatter\FormatterInterface; +use Symfony\Component\ValueExporter\Formatter\PhpIncompleteClassToStringFormatter; + +// Load the global to_string() function +require_once __DIR__.'/Resources/functions/to_string.php'; + +/** + * @author Nicolas Grekas + * @author Jules Pietri + */ +class ValueExporter +{ + private static $handler; + private static $exporter; + private static $appends = array(); + private static $prepends = array(); + + public static function export($value, $depth = 1, $expand = false) + { + if (null === self::$handler) { + $exporter = self::$exporter ?: new ValueToStringExporter( + new DateTimeToStringFormatter(), + new PhpIncompleteClassToStringFormatter() + ); + $exporter->addFormatters(self::$appends, self::$prepends); + // Clear extra formatters + self::$appends = self::$prepends = array(); + self::$handler = function ($value, $depth = 1, $expand = false) use ($exporter) { + return $exporter->exportValue($value, $depth, $expand); + }; + } + + return call_user_func(self::$handler, $value, $depth, $expand); + } + + public static function setHandler(callable $callable = null) + { + $prevHandler = self::$handler; + self::$handler = $callable; + + return $prevHandler; + } + + /** + * Sets a new {@link ValueExporterInterface} instance as exporter. + * + * @param ValueExporterInterface $exporter The exporter instance + */ + public static function setExporter(ValueExporterInterface $exporter) + { + self::$handler = null; + self::$exporter = $exporter; + self::$appends = self:: $prepends = array(); + } + + /** + * Appends a {@link FormatterInterface} to the {@link ValueExporterInterface}. + * + * @param FormatterInterface $formatter + */ + public static function appendFormatter(FormatterInterface $formatter) + { + self::$handler = null; + self::$appends[] = $formatter; + } + + /** + * Prepends a {@link FormatterInterface} to the {@link ValueExporterInterface}. + * + * @param FormatterInterface $formatter + */ + public static function prependFormatter(FormatterInterface $formatter) + { + self::$handler = null; + self::$prepends[] = $formatter; + } +} diff --git a/src/Symfony/Component/ValueExporter/composer.json b/src/Symfony/Component/ValueExporter/composer.json new file mode 100644 index 0000000000000..48a6b310ecf79 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/composer.json @@ -0,0 +1,49 @@ +{ + "name": "symfony/value-exporter", + "type": "library", + "description": "Symfony mechanism for formatting PHP variables", + "keywords": ["export", "php values", "logs"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Jules Pietri", + "email": "jules@heahprod.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9" + }, + "suggest": { + "symfony/var-dumper": "To dump PHP values" + }, + "autoload": { + "files": [ "Resources/functions/to_string.php" ], + "psr-4": { "Symfony\\Component\\ValueExporter\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + } +} diff --git a/src/Symfony/Component/ValueExporter/phpunit.xml.dist b/src/Symfony/Component/ValueExporter/phpunit.xml.dist new file mode 100644 index 0000000000000..4e1bb0a660cd4 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + From afadb5bb3c4bce726147455bc90a7386cda0d345 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Tue, 5 Apr 2016 19:42:38 +0200 Subject: [PATCH 02/12] [ValueExporter] added CallableToStringFormatter --- .../Exporter/ValueToStringExporter.php | 2 +- .../Formatter/CallableToStringFormatter.php | 50 +++++++++++++++++++ .../ValueExporter/Tests/ValueExporterTest.php | 14 +++++- .../Component/ValueExporter/ValueExporter.php | 2 + 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Component/ValueExporter/Formatter/CallableToStringFormatter.php diff --git a/src/Symfony/Component/ValueExporter/Exporter/ValueToStringExporter.php b/src/Symfony/Component/ValueExporter/Exporter/ValueToStringExporter.php index 71663d822eaef..5a0374caf4054 100644 --- a/src/Symfony/Component/ValueExporter/Exporter/ValueToStringExporter.php +++ b/src/Symfony/Component/ValueExporter/Exporter/ValueToStringExporter.php @@ -30,7 +30,7 @@ public function exportValue($value, $depth = 1, $expand = false) // Therefor, \Traversable instances might be treated as objects unless // implementing a {@link StringFormatterInterface} and passing it to // the exporter in order to support them. - if (is_array($value)) { + if (is_array($value) && !is_callable($value)) { if (empty($value)) { return 'array()'; } diff --git a/src/Symfony/Component/ValueExporter/Formatter/CallableToStringFormatter.php b/src/Symfony/Component/ValueExporter/Formatter/CallableToStringFormatter.php new file mode 100644 index 0000000000000..075d2b8d77a1a --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Formatter/CallableToStringFormatter.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Formatter; + +/** + * Returns a string representation of a string or array callable. + * + * @author Jules Pietri + */ +class CallableToStringFormatter implements StringFormatterInterface +{ + /** + * {@inheritdoc} + */ + public function supports($value) + { + return is_callable($value) && !$value instanceof \Closure; + } + + /** + * {@inheritdoc} + */ + public function formatToString($value) + { + if (is_string($value)) { + return sprintf('(function) "%s"', $value); + } + + $caller = is_object($value) ? get_class($value) : (is_object($value[0]) ? get_class($value[0]) : $value[0]); + if (is_object($value)) { + return sprintf('(invokable) "%s"', $caller); + } + + $method = $value[1]; + if (false !== $cut = strpos($method, $caller)) { + $method = substr($method, $cut); + } + + return sprintf('(callable) "%s::%s"', $caller, $method); + } +} diff --git a/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php b/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php index ecb4b5fcfa6b7..26c6af7397e5f 100644 --- a/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php +++ b/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php @@ -39,7 +39,7 @@ public function testExportValueExpanded() $exportedValue = << array(0 => "Symfony\Component\ValueExporter\ValueExporter", 1 => "export") + 0 => (callable) "Symfony\Component\ValueExporter\ValueExporter::export" ) EOT; @@ -68,6 +68,13 @@ public function valueProvider() array(0 => 0, '1' => 'un', 'key' => 4.5), 'array(0 => (int) 0, 1 => "un", \'key\' => (float) 4.5)', ), + 'closure' => array(function() {}, 'object(Closure)'), + 'callable string' => array('strlen', '(function) "strlen"'), + 'callable array' => array( + array($this, 'testExportValue'), + '(callable) "Symfony\Component\ValueExporter\Tests\ValueExporterTest::testExportValue"', + ), + 'invokable object' => array($this, '(invokable) "Symfony\Component\ValueExporter\Tests\ValueExporterTest"'), 'datetime' => array( new \DateTime('2014-06-10 07:35:40', new \DateTimeZone('UTC')), 'object(DateTime) - 2014-06-10T07:35:40+0000', @@ -79,4 +86,9 @@ public function valueProvider() 'php incomplete class' => array($foo, '__PHP_Incomplete_Class(AppBundle/Foo)'), ); } + + public function __invoke() + { + return 'TEST'; + } } diff --git a/src/Symfony/Component/ValueExporter/ValueExporter.php b/src/Symfony/Component/ValueExporter/ValueExporter.php index cbb242984b780..6460cb33d8e11 100644 --- a/src/Symfony/Component/ValueExporter/ValueExporter.php +++ b/src/Symfony/Component/ValueExporter/ValueExporter.php @@ -13,6 +13,7 @@ use Symfony\Component\ValueExporter\Exporter\ValueExporterInterface; use Symfony\Component\ValueExporter\Exporter\ValueToStringExporter; +use Symfony\Component\ValueExporter\Formatter\CallableToStringFormatter; use Symfony\Component\ValueExporter\Formatter\DateTimeToStringFormatter; use Symfony\Component\ValueExporter\Formatter\FormatterInterface; use Symfony\Component\ValueExporter\Formatter\PhpIncompleteClassToStringFormatter; @@ -35,6 +36,7 @@ public static function export($value, $depth = 1, $expand = false) { if (null === self::$handler) { $exporter = self::$exporter ?: new ValueToStringExporter( + new CallableToStringFormatter(), new DateTimeToStringFormatter(), new PhpIncompleteClassToStringFormatter() ); From 4eeb80070317583b1eb6f8b55b29d6b6ef9b7467 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Sat, 9 Apr 2016 04:31:32 +0200 Subject: [PATCH 03/12] [ValueExporter] added abstract ExpandedFormatter --- .../Exporter/AbstractValueExporter.php | 7 +++ .../Formatter/ExpandedFormatter.php | 44 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/Symfony/Component/ValueExporter/Formatter/ExpandedFormatter.php diff --git a/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php b/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php index 82d98f05a5cb1..745068a2690a3 100644 --- a/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php +++ b/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php @@ -12,6 +12,7 @@ namespace Symfony\Component\ValueExporter\Exporter; use Symfony\Component\ValueExporter\Exception\InvalidFormatterException; +use Symfony\Component\ValueExporter\Formatter\ExpandedFormatter; use Symfony\Component\ValueExporter\Formatter\FormatterInterface; /** @@ -54,12 +55,18 @@ final public function addFormatters(array $appends, array $prepends = array()) if (!$append instanceof $this->formatterInterface) { throw new InvalidFormatterException(get_class($append), self::class, $this->formatterInterface); } + if ($append instanceof ExpandedFormatter) { + $append->setExporter($this); + } $this->formatters[] = $append; } foreach (array_reverse($prepends) as $prepend) { if (!$prepend instanceof $this->formatterInterface) { throw new InvalidFormatterException(get_class($prepend), self::class, $this->formatterInterface); } + if ($prepend instanceof ExpandedFormatter) { + $prepend->setExporter($this); + } array_unshift($this->formatters, $prepend); } } diff --git a/src/Symfony/Component/ValueExporter/Formatter/ExpandedFormatter.php b/src/Symfony/Component/ValueExporter/Formatter/ExpandedFormatter.php new file mode 100644 index 0000000000000..0157f98a536a7 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Formatter/ExpandedFormatter.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Formatter; + +use Symfony\Component\ValueExporter\Exporter\ValueExporterInterface; + +/** + * ExpandedFormatter. + * + * An abstract class holding the {@link ValueExporterInterface} to export nested values. + * + * @author Jules Pietri + */ +abstract class ExpandedFormatter +{ + /** + * @var ValueExporterInterface + */ + private $exporter; + + /** + * Sets the exporter to call on nested values. + * + * @param ValueExporterInterface $exporter The exporter + */ + final public function setExporter(ValueExporterInterface $exporter) + { + $this->exporter = $exporter; + } + + final protected function export($value) + { + return $this->exporter->exportValue($value, 1, false); + } +} From 845aea1f65b20ab895e0aa61872dff3c08a02968 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Sat, 9 Apr 2016 12:56:37 +0200 Subject: [PATCH 04/12] [ValueExporter] added TraversableToStringFormatter --- .../TraversableToStringFormatter.php | 41 +++++++++++++++++++ .../Tests/Fixtures/TraversableInstance.php | 31 ++++++++++++++ .../ValueExporter/Tests/ValueExporterTest.php | 17 ++++++++ 3 files changed, 89 insertions(+) create mode 100644 src/Symfony/Component/ValueExporter/Formatter/TraversableToStringFormatter.php create mode 100644 src/Symfony/Component/ValueExporter/Tests/Fixtures/TraversableInstance.php diff --git a/src/Symfony/Component/ValueExporter/Formatter/TraversableToStringFormatter.php b/src/Symfony/Component/ValueExporter/Formatter/TraversableToStringFormatter.php new file mode 100644 index 0000000000000..96e6f40a245a7 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Formatter/TraversableToStringFormatter.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Formatter; + +/** + * Returns a string representation of an instance implementing \Traversable. + * + * @author Jules Pietri + */ +class TraversableToStringFormatter extends ExpandedFormatter implements StringFormatterInterface +{ + /** + * {@inheritdoc} + */ + public function supports($value) + { + return $value instanceof \Traversable; + } + + /** + * {@inheritdoc} + */ + public function formatToString($value) + { + $nested = array(); + foreach ($value as $k => $v) { + $nested[] = sprintf('%s => %s', is_string($k) ? sprintf("'%s'", $k) : $k, $this->export($v)); + } + + return sprintf("Traversable:\"%s\"(\n %s\n)", get_class($value), implode(",\n ", $nested)); + } +} diff --git a/src/Symfony/Component/ValueExporter/Tests/Fixtures/TraversableInstance.php b/src/Symfony/Component/ValueExporter/Tests/Fixtures/TraversableInstance.php new file mode 100644 index 0000000000000..6a3bfc32a2aed --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Tests/Fixtures/TraversableInstance.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Tests\Fixtures; + +/** + * TraversableInstance. + * + * @author Jules Pietri + */ +class TraversableInstance implements \IteratorAggregate +{ + public $property1 = 'value1'; + public $property2 = 'value2'; + + /** + * {@inheritdoc} + */ + public function getIterator() + { + return new \ArrayIterator($this); + } +} diff --git a/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php b/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php index 26c6af7397e5f..ea04899be4d66 100644 --- a/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php +++ b/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\ValueExporter\Tests; +use Symfony\Component\ValueExporter\Formatter\TraversableToStringFormatter; +use Symfony\Component\ValueExporter\Tests\Fixtures\TraversableInstance; use Symfony\Component\ValueExporter\ValueExporter; class ValueExporterTest extends \PHPUnit_Framework_TestCase @@ -46,6 +48,21 @@ public function testExportValueExpanded() $this->assertSame($exportedValue, ValueExporter::export($value, 1, true)); } + public function testExportTraversable() + { + ValueExporter::appendFormatter(new TraversableToStringFormatter()); + + $value = new TraversableInstance(); + $exportedValue = << "value1", + 'property2' => "value2" +) +EOT; + + $this->assertSame($exportedValue, ValueExporter::export($value)); + } + public function valueProvider() { $foo = new \__PHP_Incomplete_Class(); From 438b845a58c8a6daa56acb7513776893d83a6a6b Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Mon, 18 Apr 2016 08:41:44 +0200 Subject: [PATCH 05/12] [ValueExporter] added EntityToStringFormatter --- .../Formatter/EntityToStringFormatter.php | 44 +++++++++++++++++++ .../ValueExporter/Tests/Fixtures/Entity.php | 32 ++++++++++++++ .../Tests/Fixtures/PublicEntity.php | 27 ++++++++++++ .../ValueExporter/Tests/ValueExporterTest.php | 4 ++ .../Component/ValueExporter/ValueExporter.php | 2 + 5 files changed, 109 insertions(+) create mode 100644 src/Symfony/Component/ValueExporter/Formatter/EntityToStringFormatter.php create mode 100644 src/Symfony/Component/ValueExporter/Tests/Fixtures/Entity.php create mode 100644 src/Symfony/Component/ValueExporter/Tests/Fixtures/PublicEntity.php diff --git a/src/Symfony/Component/ValueExporter/Formatter/EntityToStringFormatter.php b/src/Symfony/Component/ValueExporter/Formatter/EntityToStringFormatter.php new file mode 100644 index 0000000000000..45f5bbf957833 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Formatter/EntityToStringFormatter.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Formatter; + +/** + * Returns a string representation of a DateTimeInterface instance. + * + * Based on the contribution by @scuben (https://github.com/scuben) + * https://github.com/symfony/symfony/commit/a1762fb65423dc94d69c5fb6abaed37f2ad576e6 + * + * @author Jules Pietri + */ +class EntityToStringFormatter implements StringFormatterInterface +{ + /** + * {@inheritdoc} + */ + public function supports($value) + { + return is_object($value) + && !$value instanceof \Closure + && (isset($value->id) || is_callable(array($value, 'id')) || is_callable(array($value, 'getId'))) + ; + } + + /** + * {@inheritdoc} + */ + public function formatToString($value) + { + $id = isset($value->id) ? $value->id : (is_callable(array($value, 'id')) ? $value->id() : $value->getId()); + + return sprintf('entity:%s(%s)', $id, get_class($value)); + } +} diff --git a/src/Symfony/Component/ValueExporter/Tests/Fixtures/Entity.php b/src/Symfony/Component/ValueExporter/Tests/Fixtures/Entity.php new file mode 100644 index 0000000000000..06c38e984552a --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Tests/Fixtures/Entity.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Tests\Fixtures; + +/** + * Entity with an id getter. + * + * @author Jules Pietri + */ +class Entity +{ + private $id; + + public function __construct($id) + { + $this->id = $id; + } + + public function getId() + { + return $this->id; + } +} diff --git a/src/Symfony/Component/ValueExporter/Tests/Fixtures/PublicEntity.php b/src/Symfony/Component/ValueExporter/Tests/Fixtures/PublicEntity.php new file mode 100644 index 0000000000000..310dd32b716a6 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Tests/Fixtures/PublicEntity.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Tests\Fixtures; + +/** + * Entity with a public id. + * + * @author Jules Pietri + */ +class PublicEntity +{ + public $id; + + public function __construct($id) + { + $this->id = $id; + } +} diff --git a/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php b/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php index ea04899be4d66..34983e16276eb 100644 --- a/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php +++ b/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php @@ -12,6 +12,8 @@ namespace Symfony\Component\ValueExporter\Tests; use Symfony\Component\ValueExporter\Formatter\TraversableToStringFormatter; +use Symfony\Component\ValueExporter\Tests\Fixtures\Entity; +use Symfony\Component\ValueExporter\Tests\Fixtures\PublicEntity; use Symfony\Component\ValueExporter\Tests\Fixtures\TraversableInstance; use Symfony\Component\ValueExporter\ValueExporter; @@ -101,6 +103,8 @@ public function valueProvider() 'object(DateTimeImmutable) - 2014-06-10T07:35:40+0000', ), 'php incomplete class' => array($foo, '__PHP_Incomplete_Class(AppBundle/Foo)'), + 'entity' => array(new Entity(23), 'entity:23(Symfony\Component\ValueExporter\Tests\Fixtures\Entity)'), + 'public entity' => array(new PublicEntity(23), 'entity:23(Symfony\Component\ValueExporter\Tests\Fixtures\PublicEntity)'), ); } diff --git a/src/Symfony/Component/ValueExporter/ValueExporter.php b/src/Symfony/Component/ValueExporter/ValueExporter.php index 6460cb33d8e11..067620acfebe6 100644 --- a/src/Symfony/Component/ValueExporter/ValueExporter.php +++ b/src/Symfony/Component/ValueExporter/ValueExporter.php @@ -15,6 +15,7 @@ use Symfony\Component\ValueExporter\Exporter\ValueToStringExporter; use Symfony\Component\ValueExporter\Formatter\CallableToStringFormatter; use Symfony\Component\ValueExporter\Formatter\DateTimeToStringFormatter; +use Symfony\Component\ValueExporter\Formatter\EntityToStringFormatter; use Symfony\Component\ValueExporter\Formatter\FormatterInterface; use Symfony\Component\ValueExporter\Formatter\PhpIncompleteClassToStringFormatter; @@ -38,6 +39,7 @@ public static function export($value, $depth = 1, $expand = false) $exporter = self::$exporter ?: new ValueToStringExporter( new CallableToStringFormatter(), new DateTimeToStringFormatter(), + new EntityToStringFormatter(), new PhpIncompleteClassToStringFormatter() ); $exporter->addFormatters(self::$appends, self::$prepends); From eb1a34d4b3e50d32c679208cc6b878d5b6b50fc8 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Thu, 21 Apr 2016 13:58:35 +0200 Subject: [PATCH 06/12] [ValueExporter] added priority to formatters --- .../Exporter/AbstractValueExporter.php | 49 ++++++++++++------- .../Exporter/ValueExporterInterface.php | 7 ++- .../ValueExporter/Tests/ValueExporterTest.php | 2 +- .../Component/ValueExporter/ValueExporter.php | 40 +++++++-------- 4 files changed, 55 insertions(+), 43 deletions(-) diff --git a/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php b/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php index 745068a2690a3..65a439086ec84 100644 --- a/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php +++ b/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php @@ -30,12 +30,19 @@ abstract class AbstractValueExporter implements ValueExporterInterface protected $formatterInterface = FormatterInterface::class; /** - * An array of formatters. + * An array of arrays of formatters by priority. * - * @var FormatterInterface[] + * @var array[] */ private $formatters = array(); + /** + * A sorted array of formatters. + * + * @var FormatterInterface[] + */ + private $sortedFormatters; + /** * Takes {@link FormatterInterface} as arguments. * @@ -49,25 +56,26 @@ final public function __construct() /** * {@inheritdoc} */ - final public function addFormatters(array $appends, array $prepends = array()) + final public function addFormatters(array $formatters) { - foreach ($appends as $append) { - if (!$append instanceof $this->formatterInterface) { - throw new InvalidFormatterException(get_class($append), self::class, $this->formatterInterface); - } - if ($append instanceof ExpandedFormatter) { - $append->setExporter($this); + $this->sortedFormatters = null; + + foreach ($formatters as $formatter) { + if (is_array($formatter)) { + $priority = (int) $formatter[1]; + $formatter = $formatter[0]; + } else { + $priority = 0; } - $this->formatters[] = $append; - } - foreach (array_reverse($prepends) as $prepend) { - if (!$prepend instanceof $this->formatterInterface) { - throw new InvalidFormatterException(get_class($prepend), self::class, $this->formatterInterface); + if (!$formatter instanceof $this->formatterInterface) { + throw new InvalidFormatterException(get_class($formatter), self::class, $this->formatterInterface); } - if ($prepend instanceof ExpandedFormatter) { - $prepend->setExporter($this); + if ($formatter instanceof ExpandedFormatter) { + $formatter->setExporter($this); } - array_unshift($this->formatters, $prepend); + + // Using the class as key prevents duplicate + $this->formatters[$priority][get_class($formatter)] = $formatter; } } @@ -76,6 +84,11 @@ final public function addFormatters(array $appends, array $prepends = array()) */ final protected function formatters() { - return $this->formatters; + if (null === $this->sortedFormatters) { + krsort($this->formatters); + $this->sortedFormatters = call_user_func_array('array_merge', $this->formatters); + } + + return $this->sortedFormatters; } } diff --git a/src/Symfony/Component/ValueExporter/Exporter/ValueExporterInterface.php b/src/Symfony/Component/ValueExporter/Exporter/ValueExporterInterface.php index d055059fa9536..8342f9e820d7b 100644 --- a/src/Symfony/Component/ValueExporter/Exporter/ValueExporterInterface.php +++ b/src/Symfony/Component/ValueExporter/Exporter/ValueExporterInterface.php @@ -42,12 +42,11 @@ interface ValueExporterInterface public function exportValue($value, $depth, $expand); /** - * Adds {@link FormatterInterface} that will be called in the given order. + * Adds {@link FormatterInterface} that will be called by priority. * - * @param FormatterInterface[] $appends The formatters to execute at last - * @param FormatterInterface[] $prepends The formatters to execute first + * @param (FormatterInterface|array)[] $formatters * * @throws InvalidFormatterException If the exporter does not support a given formatter */ - public function addFormatters(array $appends, array $prepends); + public function addFormatters(array $formatters); } diff --git a/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php b/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php index 34983e16276eb..fbff728b6a15e 100644 --- a/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php +++ b/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php @@ -52,7 +52,7 @@ public function testExportValueExpanded() public function testExportTraversable() { - ValueExporter::appendFormatter(new TraversableToStringFormatter()); + ValueExporter::addFormatters(array(new TraversableToStringFormatter())); $value = new TraversableInstance(); $exportedValue = <<addFormatters(self::$appends, self::$prepends); - // Clear extra formatters - self::$appends = self::$prepends = array(); + $exporter->addFormatters(self::$formatters); + // Clear formatters + self::$formatters = array(); self::$handler = function ($value, $depth = 1, $expand = false) use ($exporter) { return $exporter->exportValue($value, $depth, $expand); }; @@ -70,28 +69,29 @@ public static function setExporter(ValueExporterInterface $exporter) { self::$handler = null; self::$exporter = $exporter; - self::$appends = self:: $prepends = array(); + self::$formatters = array(); } /** - * Appends a {@link FormatterInterface} to the {@link ValueExporterInterface}. + * Adds {@link FormatterInterface} to the {@link ValueExporterInterface}. * - * @param FormatterInterface $formatter - */ - public static function appendFormatter(FormatterInterface $formatter) - { - self::$handler = null; - self::$appends[] = $formatter; - } - - /** - * Prepends a {@link FormatterInterface} to the {@link ValueExporterInterface}. + * You can simple pass an instance or an array with the instance and the priority: * - * @param FormatterInterface $formatter + * + * ValueExporter::addFormatters(array( + * new AcmeFormatter, + * array(new AcmeOtherFormatter(), 10) + * ); + * + * + * @param mixed $formatters An array of FormatterInterface instances and/or + * arrays holding an instance and its priority */ - public static function prependFormatter(FormatterInterface $formatter) + public static function addFormatters($formatters) { self::$handler = null; - self::$prepends[] = $formatter; + foreach ($formatters as $formatter) { + self::$formatters[] = $formatter; + } } } From 0e066a14aa3c2af5d90429a371418be92bd66d04 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Thu, 21 Apr 2016 17:09:02 +0200 Subject: [PATCH 07/12] [ValueExporter] made ExpandedFormatter a trait --- .../Exporter/AbstractValueExporter.php | 21 +++++++++++++++---- .../Exporter/ValueExporterInterface.php | 2 +- .../Exporter/ValueToStringExporter.php | 10 +++++++-- ...rmatter.php => ExpandedFormatterTrait.php} | 11 +++++++--- .../TraversableToStringFormatter.php | 4 +++- 5 files changed, 37 insertions(+), 11 deletions(-) rename src/Symfony/Component/ValueExporter/Formatter/{ExpandedFormatter.php => ExpandedFormatterTrait.php} (74%) diff --git a/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php b/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php index 65a439086ec84..634b1cbf2b1bb 100644 --- a/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php +++ b/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php @@ -12,7 +12,7 @@ namespace Symfony\Component\ValueExporter\Exporter; use Symfony\Component\ValueExporter\Exception\InvalidFormatterException; -use Symfony\Component\ValueExporter\Formatter\ExpandedFormatter; +use Symfony\Component\ValueExporter\Formatter\ExpandedFormatterTrait; use Symfony\Component\ValueExporter\Formatter\FormatterInterface; /** @@ -22,6 +22,15 @@ */ abstract class AbstractValueExporter implements ValueExporterInterface { + /** + * @var int + */ + protected $depth; + /** + * @var bool + */ + protected $expand; + /** * The supported formatter interface. * @@ -67,15 +76,19 @@ final public function addFormatters(array $formatters) } else { $priority = 0; } + + $formatterClass = get_class($formatter); + if (!$formatter instanceof $this->formatterInterface) { - throw new InvalidFormatterException(get_class($formatter), self::class, $this->formatterInterface); + throw new InvalidFormatterException($formatterClass, self::class, $this->formatterInterface); } - if ($formatter instanceof ExpandedFormatter) { + + if (in_array(ExpandedFormatterTrait::class, class_uses($formatterClass), true)) { $formatter->setExporter($this); } // Using the class as key prevents duplicate - $this->formatters[$priority][get_class($formatter)] = $formatter; + $this->formatters[$priority][$formatterClass] = $formatter; } } diff --git a/src/Symfony/Component/ValueExporter/Exporter/ValueExporterInterface.php b/src/Symfony/Component/ValueExporter/Exporter/ValueExporterInterface.php index 8342f9e820d7b..cdc9275a72637 100644 --- a/src/Symfony/Component/ValueExporter/Exporter/ValueExporterInterface.php +++ b/src/Symfony/Component/ValueExporter/Exporter/ValueExporterInterface.php @@ -39,7 +39,7 @@ interface ValueExporterInterface * @param int $depth The level of indentation * @param bool $expand Whether to inline or expand nested values */ - public function exportValue($value, $depth, $expand); + public function exportValue($value, $depth = 1, $expand = false); /** * Adds {@link FormatterInterface} that will be called by priority. diff --git a/src/Symfony/Component/ValueExporter/Exporter/ValueToStringExporter.php b/src/Symfony/Component/ValueExporter/Exporter/ValueToStringExporter.php index 5a0374caf4054..e6fbcc9f124a2 100644 --- a/src/Symfony/Component/ValueExporter/Exporter/ValueToStringExporter.php +++ b/src/Symfony/Component/ValueExporter/Exporter/ValueToStringExporter.php @@ -25,6 +25,9 @@ class ValueToStringExporter extends AbstractValueExporter public function exportValue($value, $depth = 1, $expand = false) { + // Use set properties for recursive calls + $depth = null === $this->depth ? $depth : $this->depth; + $expand = null === $this->expand ? $expand : $this->expand; // Arrays have to be handled first to deal with nested level and depth, // this implementation intentionally ignores \Traversable values. // Therefor, \Traversable instances might be treated as objects unless @@ -39,9 +42,12 @@ public function exportValue($value, $depth = 1, $expand = false) $a = array(); foreach ($value as $k => $v) { if (is_array($v) && !empty($v)) { - $expand = true; + $this->expand = true; + $this->depth = $depth + 1; } - $a[] = sprintf('%s => %s', is_string($k) ? sprintf("'%s'", $k) : $k, $this->exportValue($v, $depth + 1, $expand)); + $a[] = sprintf('%s => %s', is_string($k) ? sprintf("'%s'", $k) : $k, $this->exportValue($v)); + $this->depth = null; + $this->expand = null; } if ($expand) { return sprintf("array(\n%s%s\n%s)", $indent, implode(sprintf(", \n%s", $indent), $a), str_repeat(' ', $depth - 1)); diff --git a/src/Symfony/Component/ValueExporter/Formatter/ExpandedFormatter.php b/src/Symfony/Component/ValueExporter/Formatter/ExpandedFormatterTrait.php similarity index 74% rename from src/Symfony/Component/ValueExporter/Formatter/ExpandedFormatter.php rename to src/Symfony/Component/ValueExporter/Formatter/ExpandedFormatterTrait.php index 0157f98a536a7..24816b818d25d 100644 --- a/src/Symfony/Component/ValueExporter/Formatter/ExpandedFormatter.php +++ b/src/Symfony/Component/ValueExporter/Formatter/ExpandedFormatterTrait.php @@ -16,11 +16,11 @@ /** * ExpandedFormatter. * - * An abstract class holding the {@link ValueExporterInterface} to export nested values. + * A trait holding the {@link ValueExporterInterface} to export nested values. * * @author Jules Pietri */ -abstract class ExpandedFormatter +trait ExpandedFormatterTrait { /** * @var ValueExporterInterface @@ -37,8 +37,13 @@ final public function setExporter(ValueExporterInterface $exporter) $this->exporter = $exporter; } + /** + * @param mixed $value The nested value to export + * + * @return mixed The exported nested value + */ final protected function export($value) { - return $this->exporter->exportValue($value, 1, false); + return $this->exporter->exportValue($value); } } diff --git a/src/Symfony/Component/ValueExporter/Formatter/TraversableToStringFormatter.php b/src/Symfony/Component/ValueExporter/Formatter/TraversableToStringFormatter.php index 96e6f40a245a7..db45b5d193637 100644 --- a/src/Symfony/Component/ValueExporter/Formatter/TraversableToStringFormatter.php +++ b/src/Symfony/Component/ValueExporter/Formatter/TraversableToStringFormatter.php @@ -16,8 +16,10 @@ * * @author Jules Pietri */ -class TraversableToStringFormatter extends ExpandedFormatter implements StringFormatterInterface +class TraversableToStringFormatter implements StringFormatterInterface { + use ExpandedFormatterTrait; + /** * {@inheritdoc} */ From 677c3c267c6f73d23d7589dde221509d6afd1324 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Thu, 21 Apr 2016 17:10:43 +0200 Subject: [PATCH 08/12] [ValueExporter] fixed class name's late static bind in AbstractValueExporter --- .../Component/ValueExporter/Exporter/AbstractValueExporter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php b/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php index 634b1cbf2b1bb..52f613ecd4679 100644 --- a/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php +++ b/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php @@ -80,7 +80,7 @@ final public function addFormatters(array $formatters) $formatterClass = get_class($formatter); if (!$formatter instanceof $this->formatterInterface) { - throw new InvalidFormatterException($formatterClass, self::class, $this->formatterInterface); + throw new InvalidFormatterException($formatterClass, static::class, $this->formatterInterface); } if (in_array(ExpandedFormatterTrait::class, class_uses($formatterClass), true)) { From 8f63b0786164a7f456d8b6ba5ffd10b9d69c8796 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Thu, 21 Apr 2016 19:47:51 +0200 Subject: [PATCH 09/12] [ValueExporter] added support for formatters FQCN instead of instances --- .../Exporter/AbstractValueExporter.php | 49 +++++++++++++------ .../ValueExporter/Tests/ValueExporterTest.php | 2 +- .../Component/ValueExporter/ValueExporter.php | 8 +-- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php b/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php index 52f613ecd4679..f7ee40e18295e 100644 --- a/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php +++ b/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php @@ -39,19 +39,26 @@ abstract class AbstractValueExporter implements ValueExporterInterface protected $formatterInterface = FormatterInterface::class; /** - * An array of arrays of formatters by priority. + * An array of priorities by formatter class. * * @var array[] */ private $formatters = array(); /** - * A sorted array of formatters. + * An array of formatters instances sorted by priority or null. * - * @var FormatterInterface[] + * @var FormatterInterface[]|null */ private $sortedFormatters; + /** + * An array of cached formatters instances by class. + * + * @var FormatterInterface[] + */ + private $cachedFormatters = array(); + /** * Takes {@link FormatterInterface} as arguments. * @@ -72,23 +79,19 @@ final public function addFormatters(array $formatters) foreach ($formatters as $formatter) { if (is_array($formatter)) { $priority = (int) $formatter[1]; - $formatter = $formatter[0]; + $formatterClass = $formatter[0]; } else { $priority = 0; + $formatterClass = $formatter; } - $formatterClass = get_class($formatter); - - if (!$formatter instanceof $this->formatterInterface) { + if (!in_array($this->formatterInterface, class_implements($formatterClass), true)) { throw new InvalidFormatterException($formatterClass, static::class, $this->formatterInterface); } - if (in_array(ExpandedFormatterTrait::class, class_uses($formatterClass), true)) { - $formatter->setExporter($this); - } - - // Using the class as key prevents duplicate - $this->formatters[$priority][$formatterClass] = $formatter; + // Using the class as key prevents duplicate and allows to + // dynamically change the priority + $this->formatters[$formatterClass] = $priority; } } @@ -98,8 +101,24 @@ final public function addFormatters(array $formatters) final protected function formatters() { if (null === $this->sortedFormatters) { - krsort($this->formatters); - $this->sortedFormatters = call_user_func_array('array_merge', $this->formatters); + arsort($this->formatters); + + foreach (array_keys($this->formatters) as $formatterClass) { + if (isset($this->cachedFormatters[$formatterClass])) { + $this->sortedFormatters[] = $this->cachedFormatters[$formatterClass]; + + continue; + } + + $formatter = new $formatterClass(); + + if (in_array(ExpandedFormatterTrait::class, class_uses($formatterClass), true)) { + /* @var ExpandedFormatterTrait $formatter */ + $formatter->setExporter($this); + } + + $this->sortedFormatters[] = $this->cachedFormatters[$formatterClass] = $formatter; + } } return $this->sortedFormatters; diff --git a/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php b/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php index fbff728b6a15e..831ee4e9001ae 100644 --- a/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php +++ b/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php @@ -52,7 +52,7 @@ public function testExportValueExpanded() public function testExportTraversable() { - ValueExporter::addFormatters(array(new TraversableToStringFormatter())); + ValueExporter::addFormatters(array(TraversableToStringFormatter::class)); $value = new TraversableInstance(); $exportedValue = <<addFormatters(self::$formatters); // Clear formatters From a83684c6d0643f2c97c4da02973a9fec4a9200c7 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Sat, 23 Apr 2016 08:15:48 +0200 Subject: [PATCH 10/12] [ValueExporter] added support for objects implementing __toString() --- .../Exporter/ValueToStringExporter.php | 4 +++ .../Formatter/EntityToStringFormatter.php | 4 +++ .../Fixtures/EntityImplementingToString.php | 34 +++++++++++++++++++ .../Fixtures/ObjectImplementingToString.php | 32 +++++++++++++++++ .../ValueExporter/Tests/ValueExporterTest.php | 10 ++++++ 5 files changed, 84 insertions(+) create mode 100644 src/Symfony/Component/ValueExporter/Tests/Fixtures/EntityImplementingToString.php create mode 100644 src/Symfony/Component/ValueExporter/Tests/Fixtures/ObjectImplementingToString.php diff --git a/src/Symfony/Component/ValueExporter/Exporter/ValueToStringExporter.php b/src/Symfony/Component/ValueExporter/Exporter/ValueToStringExporter.php index e6fbcc9f124a2..480c6bf7a5abb 100644 --- a/src/Symfony/Component/ValueExporter/Exporter/ValueToStringExporter.php +++ b/src/Symfony/Component/ValueExporter/Exporter/ValueToStringExporter.php @@ -70,6 +70,10 @@ public function exportValue($value, $depth = 1, $expand = false) } // Fallback on default if (is_object($value)) { + if (method_exists($value, '__toString')) { + return sprintf('object(%s) "%s"', get_class($value), $value); + } + return sprintf('object(%s)', get_class($value)); } if (is_resource($value)) { diff --git a/src/Symfony/Component/ValueExporter/Formatter/EntityToStringFormatter.php b/src/Symfony/Component/ValueExporter/Formatter/EntityToStringFormatter.php index 45f5bbf957833..162fc2fe0dd22 100644 --- a/src/Symfony/Component/ValueExporter/Formatter/EntityToStringFormatter.php +++ b/src/Symfony/Component/ValueExporter/Formatter/EntityToStringFormatter.php @@ -39,6 +39,10 @@ public function formatToString($value) { $id = isset($value->id) ? $value->id : (is_callable(array($value, 'id')) ? $value->id() : $value->getId()); + if (method_exists($value, '__toString')) { + return sprintf('entity:%s(%s) "%s"', $id, get_class($value), $value); + } + return sprintf('entity:%s(%s)', $id, get_class($value)); } } diff --git a/src/Symfony/Component/ValueExporter/Tests/Fixtures/EntityImplementingToString.php b/src/Symfony/Component/ValueExporter/Tests/Fixtures/EntityImplementingToString.php new file mode 100644 index 0000000000000..370981ce0f73c --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Tests/Fixtures/EntityImplementingToString.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Tests\Fixtures; + +/** + * Entity with an id getter. + * + * @author Jules Pietri + */ +class EntityImplementingToString +{ + public $id; + private $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } + + public function __toString() + { + return (string) $this->name; + } +} diff --git a/src/Symfony/Component/ValueExporter/Tests/Fixtures/ObjectImplementingToString.php b/src/Symfony/Component/ValueExporter/Tests/Fixtures/ObjectImplementingToString.php new file mode 100644 index 0000000000000..9455301fd5c90 --- /dev/null +++ b/src/Symfony/Component/ValueExporter/Tests/Fixtures/ObjectImplementingToString.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ValueExporter\Tests\Fixtures; + +/** + * Entity with an id getter. + * + * @author Jules Pietri + */ +class ObjectImplementingToString +{ + private $name; + + public function __construct($name) + { + $this->name = $name; + } + + public function __toString() + { + return (string) $this->name; + } +} diff --git a/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php b/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php index 831ee4e9001ae..1ffa0274400b4 100644 --- a/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php +++ b/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php @@ -13,6 +13,8 @@ use Symfony\Component\ValueExporter\Formatter\TraversableToStringFormatter; use Symfony\Component\ValueExporter\Tests\Fixtures\Entity; +use Symfony\Component\ValueExporter\Tests\Fixtures\EntityImplementingToString; +use Symfony\Component\ValueExporter\Tests\Fixtures\ObjectImplementingToString; use Symfony\Component\ValueExporter\Tests\Fixtures\PublicEntity; use Symfony\Component\ValueExporter\Tests\Fixtures\TraversableInstance; use Symfony\Component\ValueExporter\ValueExporter; @@ -87,6 +89,10 @@ public function valueProvider() array(0 => 0, '1' => 'un', 'key' => 4.5), 'array(0 => (int) 0, 1 => "un", \'key\' => (float) 4.5)', ), + 'object implementing to string' => array( + new ObjectImplementingToString('test'), + 'object(Symfony\Component\ValueExporter\Tests\Fixtures\ObjectImplementingToString) "test"', + ), 'closure' => array(function() {}, 'object(Closure)'), 'callable string' => array('strlen', '(function) "strlen"'), 'callable array' => array( @@ -105,6 +111,10 @@ public function valueProvider() 'php incomplete class' => array($foo, '__PHP_Incomplete_Class(AppBundle/Foo)'), 'entity' => array(new Entity(23), 'entity:23(Symfony\Component\ValueExporter\Tests\Fixtures\Entity)'), 'public entity' => array(new PublicEntity(23), 'entity:23(Symfony\Component\ValueExporter\Tests\Fixtures\PublicEntity)'), + 'entity implementing to string' => array( + new EntityImplementingToString(23, 'test'), + 'entity:23(Symfony\Component\ValueExporter\Tests\Fixtures\EntityImplementingToString) "test"', + ), ); } From ab119f088ef9d8f8a9465768fe8c6b9c937be427 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Sat, 23 Apr 2016 08:34:55 +0200 Subject: [PATCH 11/12] [ValueExporter] tweaked CallableToStringFormatter --- .../ValueExporter/Formatter/CallableToStringFormatter.php | 6 +++++- .../Component/ValueExporter/Tests/ValueExporterTest.php | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/ValueExporter/Formatter/CallableToStringFormatter.php b/src/Symfony/Component/ValueExporter/Formatter/CallableToStringFormatter.php index 075d2b8d77a1a..8240919bf01eb 100644 --- a/src/Symfony/Component/ValueExporter/Formatter/CallableToStringFormatter.php +++ b/src/Symfony/Component/ValueExporter/Formatter/CallableToStringFormatter.php @@ -36,7 +36,7 @@ public function formatToString($value) } $caller = is_object($value) ? get_class($value) : (is_object($value[0]) ? get_class($value[0]) : $value[0]); - if (is_object($value)) { + if (is_object($value) || (is_object($value[0]) && isset($value[1]) && '__invoke' === $value[1])) { return sprintf('(invokable) "%s"', $caller); } @@ -45,6 +45,10 @@ public function formatToString($value) $method = substr($method, $cut); } + if ((new \ReflectionMethod($caller, $method))->isStatic()) { + return sprintf('(static) "%s::%s"', $caller, $method); + } + return sprintf('(callable) "%s::%s"', $caller, $method); } } diff --git a/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php b/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php index 1ffa0274400b4..ec544c6000112 100644 --- a/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php +++ b/src/Symfony/Component/ValueExporter/Tests/ValueExporterTest.php @@ -45,7 +45,7 @@ public function testExportValueExpanded() $exportedValue = << (callable) "Symfony\Component\ValueExporter\ValueExporter::export" + 0 => (static) "Symfony\Component\ValueExporter\ValueExporter::export" ) EOT; @@ -100,6 +100,7 @@ public function valueProvider() '(callable) "Symfony\Component\ValueExporter\Tests\ValueExporterTest::testExportValue"', ), 'invokable object' => array($this, '(invokable) "Symfony\Component\ValueExporter\Tests\ValueExporterTest"'), + 'invokable object as array' => array(array($this, '__invoke'), '(invokable) "Symfony\Component\ValueExporter\Tests\ValueExporterTest"'), 'datetime' => array( new \DateTime('2014-06-10 07:35:40', new \DateTimeZone('UTC')), 'object(DateTime) - 2014-06-10T07:35:40+0000', From 826e8fb5bdd5fda0298f7fdc66011778631bf748 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Sat, 23 Apr 2016 09:15:44 +0200 Subject: [PATCH 12/12] [ValueExporter] fixed some doc blocks in AbstractValueExporter --- .../ValueExporter/Exporter/AbstractValueExporter.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php b/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php index f7ee40e18295e..11c0bffef0da4 100644 --- a/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php +++ b/src/Symfony/Component/ValueExporter/Exporter/AbstractValueExporter.php @@ -39,9 +39,9 @@ abstract class AbstractValueExporter implements ValueExporterInterface protected $formatterInterface = FormatterInterface::class; /** - * An array of priorities by formatter class. + * An array indexed by formatter FQCN with a corresponding priority as value. * - * @var array[] + * @var int[] */ private $formatters = array(); @@ -53,16 +53,18 @@ abstract class AbstractValueExporter implements ValueExporterInterface private $sortedFormatters; /** - * An array of cached formatters instances by class. + * An array of cached formatters instances by their FQCN. * * @var FormatterInterface[] */ private $cachedFormatters = array(); /** - * Takes {@link FormatterInterface} as arguments. + * Takes {@link FormatterInterface} FQCN as arguments. * * They will be called in the given order. + * Alternatively, instead of a class, you can pass an array with + * a class and its priority {@see self::addFormatters}. */ final public function __construct() {