diff --git a/.travis.yml b/.travis.yml
index 7829e9f4df..92ca12215f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,8 +9,10 @@ matrix:
env: deps=low
- php: 5.6
env: deps=high
+ - php: nightly
- php: hhvm-nightly
allow_failures:
+ - php: nightly
- php: hhvm-nightly
fast_finish: true
@@ -24,12 +26,12 @@ env:
before_install:
- travis_retry sudo apt-get install parallel
- composer self-update
- - if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then phpenv config-rm xdebug.ini; fi;
- - if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
- - if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ] && [ $(php -r "echo PHP_MINOR_VERSION;") -le 4 ]; then echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
- - if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then pecl install -f memcached-2.1.0; fi;
- - if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
- - if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then php -i; fi;
+ - if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]]; then phpenv config-rm xdebug.ini; fi;
+ - if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]]; then echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
+ - if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]] && [ $(php -r "echo PHP_MINOR_VERSION;") -le 4 ]; then echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
+ - if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]]; then pecl install -f memcached-2.1.0; fi;
+ - if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]]; then echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
+ - if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]]; then php -i; fi;
- sudo locale-gen fr_FR.UTF-8 && sudo update-locale
# Set the COMPOSER_ROOT_VERSION to the right version according to the branch being built
- if [ "$TRAVIS_BRANCH" = "master" ]; then export COMPOSER_ROOT_VERSION=dev-master; else export COMPOSER_ROOT_VERSION="$TRAVIS_BRANCH".x-dev; fi;
@@ -38,8 +40,9 @@ install:
- if [ "$deps" = "no" ]; then composer --prefer-source install; fi;
script:
+ - components=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n')
- if [ "$deps" = "no" ]; then export SYMFONY_DEPRECATIONS_HELPER=strict; fi;
- - if [ "$deps" = "no" ]; then ls -d src/Symfony/*/* | parallel --gnu --keep-order 'echo -e "\\nRunning {} tests"; phpunit --exclude-group tty,benchmark,intl-data {} || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi;
+ - if [ "$deps" = "no" ]; then echo "$components" | parallel --gnu --keep-order 'echo -e "\\nRunning {} tests"; phpunit --exclude-group tty,benchmark,intl-data {} || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi;
- if [ "$deps" = "no" ]; then echo -e "\\nRunning tests requiring tty"; phpunit --group tty || (echo -e "\\e[41mKO\\e[0m tty group" && $(exit 1)); fi;
- - if [ "$deps" = "high" ]; then find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist | sed 's#\(.*\)/.*#\1#' | parallel --gnu --keep-order -j25% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source update; phpunit --exclude-group tty,benchmark,intl-data,legacy || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi;
- - if [ "$deps" = "low" ]; then find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist | sed 's#\(.*\)/.*#\1#' | parallel --gnu --keep-order -j25% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source --prefer-lowest --prefer-stable update; phpunit --exclude-group tty,benchmark,intl-data || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi;
+ - if [ "$deps" = "high" ]; then echo "$components" | parallel --gnu --keep-order -j25% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source update; phpunit --exclude-group tty,benchmark,intl-data,legacy || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi;
+ - if [ "$deps" = "low" ]; then echo "$components" | parallel --gnu --keep-order -j25% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source --prefer-lowest --prefer-stable update; phpunit --exclude-group tty,benchmark,intl-data || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi;
diff --git a/UPGRADE-2.7.md b/UPGRADE-2.7.md
index 137773d647..60ad352fa6 100644
--- a/UPGRADE-2.7.md
+++ b/UPGRADE-2.7.md
@@ -84,6 +84,9 @@ Serializer
$normalizer = new GetSetMethodNormalizer(null, $nameConverter);
```
+ * `Symfony\Component\Serializer\Exception\ExceptionInterface` is the new name for the now
+ deprecated `Symfony\Component\Serializer\Exception\Exception` interface.
+
PropertyAccess
--------------
diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig
index f82979cd62..6135d1016a 100644
--- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig
+++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig
@@ -1,4 +1,4 @@
-{% extends "form_div_layout.html.twig" %}
+{% use "form_div_layout.html.twig" %}
{# Widgets #}
@@ -164,12 +164,12 @@
{% if parent_label_class is defined %}
{% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ parent_label_class)|trim}) %}
{% endif %}
- {% if label is empty %}
+ {% if label is not sameas(false) and label is empty %}
{% set label = name|humanize %}
{% endif %}
{% endif %}
{% endblock checkbox_radio_label %}
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index a070e19ae5..2fde6a02e6 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -303,7 +303,7 @@ class FrameworkExtension extends Extension
'memcached' => 'Symfony\Component\HttpKernel\Profiler\MemcachedProfilerStorage',
'redis' => 'Symfony\Component\HttpKernel\Profiler\RedisProfilerStorage',
);
- list($class, ) = explode(':', $config['dsn'], 2);
+ list($class) = explode(':', $config['dsn'], 2);
if (!isset($supported[$class])) {
throw new \LogicException(sprintf('Driver "%s" is not supported for the profiler.', $class));
}
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig
index 2d0a56e668..c3bf01c342 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig
@@ -90,6 +90,9 @@
rows.appendChild(row);
var methodCell = document.createElement('td');
+ if (request.error) {
+ methodCell.className = 'sf-ajax-request-error';
+ }
methodCell.textContent = request.method;
row.appendChild(methodCell);
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig
index c7f84d5773..109b4dd8cd 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig
@@ -315,7 +315,7 @@
text-align: right;
}
.sf-ajax-request-error {
- color: #a33;
+ color: #a33 !important;
}
.sf-ajax-request-loading {
-webkit-animation: sf-blink .5s ease-in-out infinite;
diff --git a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php
index ac49ffd224..fb74795736 100644
--- a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php
+++ b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php
@@ -472,7 +472,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
);
}
- if (null !== $this->key && (null === $this->addDefaultChildren || is_integer($this->addDefaultChildren) && $this->addDefaultChildren > 0)) {
+ if (null !== $this->key && (null === $this->addDefaultChildren || is_int($this->addDefaultChildren) && $this->addDefaultChildren > 0)) {
throw new InvalidDefinitionException(
sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s"', $path)
);
diff --git a/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php b/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php
index fe12ff3b21..ee90be9c39 100644
--- a/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php
+++ b/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php
@@ -117,7 +117,7 @@ class PrototypedArrayNode extends ArrayNode
if (null === $children) {
$this->defaultChildren = array('defaults');
} else {
- $this->defaultChildren = is_integer($children) && $children > 0 ? range(1, $children) : (array) $children;
+ $this->defaultChildren = is_int($children) && $children > 0 ? range(1, $children) : (array) $children;
}
}
diff --git a/src/Symfony/Component/Config/Util/XmlUtils.php b/src/Symfony/Component/Config/Util/XmlUtils.php
index f2e2b2c4e5..d0107c1a33 100644
--- a/src/Symfony/Component/Config/Util/XmlUtils.php
+++ b/src/Symfony/Component/Config/Util/XmlUtils.php
@@ -222,7 +222,7 @@ class XmlUtils
LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
$error->code,
trim($error->message),
- $error->file ? $error->file : 'n/a',
+ $error->file ?: 'n/a',
$error->line,
$error->column
);
diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php b/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php
index 38be37a850..b3f274a8b0 100644
--- a/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php
+++ b/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php
@@ -149,7 +149,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
));
}
- if (false === array_search(static::$availableOptions[$option], $this->options)) {
+ if (!in_array(static::$availableOptions[$option], $this->options)) {
$this->options[] = static::$availableOptions[$option];
}
}
diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php
index bbe7747b57..ccd889b0aa 100644
--- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php
+++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php
@@ -274,7 +274,7 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase
protected function getInputStream($input)
{
$stream = fopen('php://memory', 'r+', false);
- fputs($stream, $input);
+ fwrite($stream, $input);
rewind($stream);
return $stream;
diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
index 72bb25e586..6e441880e9 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
@@ -1175,7 +1175,7 @@ EOF;
$behavior[$id] = $argument->getInvalidBehavior();
}
- $calls[$id] += 1;
+ ++$calls[$id];
}
}
}
@@ -1486,12 +1486,12 @@ EOF;
}
while ($i > 0) {
- $i -= 1;
+ --$i;
$name .= $nonFirstChars[$i%$nonFirstCharsLength];
$i = intval($i/$nonFirstCharsLength);
}
- $this->variableCount += 1;
+ ++$this->variableCount;
// check that the name is not reserved
if (in_array($name, $this->reservedVariables, true)) {
diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
index cd1a90056d..ad6d3bdfc5 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
@@ -260,7 +260,8 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$builder->setResourceTracking(false);
$builderCompilerPasses = $builder->getCompiler()->getPassConfig()->getPasses();
$builder->addCompilerPass($this->getMock('Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface'));
- $this->assertEquals(sizeof($builderCompilerPasses) + 1, sizeof($builder->getCompiler()->getPassConfig()->getPasses()));
+
+ $this->assertCount(count($builder->getCompiler()->getPassConfig()->getPasses()) - 1, $builderCompilerPasses);
}
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
index 01e5710fce..84f210bfb7 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
@@ -74,8 +74,8 @@ class PhpDumperTest extends \PHPUnit_Framework_TestCase
$container = new ContainerBuilder();
$container->setDefinition('test', $definition);
- $container->setParameter('foo', 'wiz'.dirname(dirname(__FILE__)));
- $container->setParameter('bar', dirname(__FILE__));
+ $container->setParameter('foo', 'wiz'.dirname(__DIR__));
+ $container->setParameter('bar', __DIR__);
$container->setParameter('baz', '%bar%/PhpDumperTest.php');
$container->setParameter('buz', dirname(dirname(__DIR__)));
$container->compile();
diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php
index 4f52f218b6..e5341b3ffa 100644
--- a/src/Symfony/Component/DomCrawler/Crawler.php
+++ b/src/Symfony/Component/DomCrawler/Crawler.php
@@ -45,9 +45,10 @@ class Crawler extends \SplObjectStorage
/**
* Constructor.
*
- * @param mixed $node A Node to use as the base for the crawling
+ * @param mixed $node A Node to use as the base for the crawling
* @param string $currentUri The current URI
- * @param string $baseHref The base href value
+ * @param string $baseHref The base href value
+ *
* @api
*/
public function __construct($node = null, $currentUri = null, $baseHref = null)
diff --git a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php
index 82045a1dd5..1431908591 100755
--- a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php
+++ b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php
@@ -327,7 +327,7 @@ EOF
{
$crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li');
$nodes = $crawler->reduce(function ($node, $i) {
- return $i == 1 ? false : true;
+ return $i !== 1;
});
$this->assertNotSame($nodes, $crawler, '->reduce() returns a new instance of a crawler');
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $nodes, '->reduce() returns a new instance of a crawler');
diff --git a/src/Symfony/Component/Form/AbstractRendererEngine.php b/src/Symfony/Component/Form/AbstractRendererEngine.php
index 00bf85401c..8202cbf97b 100644
--- a/src/Symfony/Component/Form/AbstractRendererEngine.php
+++ b/src/Symfony/Component/Form/AbstractRendererEngine.php
@@ -67,8 +67,7 @@ abstract class AbstractRendererEngine implements FormRendererEngineInterface
// Unset instead of resetting to an empty array, in order to allow
// implementations (like TwigRendererEngine) to check whether $cacheKey
// is set at all.
- unset($this->resources[$cacheKey]);
- unset($this->resourceHierarchyLevels[$cacheKey]);
+ unset($this->resources[$cacheKey], $this->resourceHierarchyLevels[$cacheKey]);
}
/**
diff --git a/src/Symfony/Component/Form/FormBuilder.php b/src/Symfony/Component/Form/FormBuilder.php
index 9437dad1c1..8b5b919413 100644
--- a/src/Symfony/Component/Form/FormBuilder.php
+++ b/src/Symfony/Component/Form/FormBuilder.php
@@ -138,8 +138,7 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
- unset($this->unresolvedChildren[$name]);
- unset($this->children[$name]);
+ unset($this->unresolvedChildren[$name], $this->children[$name]);
return $this;
}
diff --git a/src/Symfony/Component/Form/FormEvents.php b/src/Symfony/Component/Form/FormEvents.php
index cd2bfb4322..021117f490 100644
--- a/src/Symfony/Component/Form/FormEvents.php
+++ b/src/Symfony/Component/Form/FormEvents.php
@@ -12,10 +12,10 @@ namespace Symfony\Component\Form;
/**
* To learn more about how form events work check the documentation
- * entry at {@link http://symfony.com/doc/any/components/form/form_events.html}
+ * entry at {@link http://symfony.com/doc/any/components/form/form_events.html}.
*
* To learn how to dynamically modify forms using events check the cookbook
- * entry at {@link http://symfony.com/doc/any/cookbook/form/dynamic_form_modification.html}
+ * entry at {@link http://symfony.com/doc/any/cookbook/form/dynamic_form_modification.html}.
*
* @author Bernhard Schussek
*/
diff --git a/src/Symfony/Component/Form/FormRenderer.php b/src/Symfony/Component/Form/FormRenderer.php
index c5faee1548..b286ffdf41 100644
--- a/src/Symfony/Component/Form/FormRenderer.php
+++ b/src/Symfony/Component/Form/FormRenderer.php
@@ -296,8 +296,7 @@ class FormRenderer implements FormRendererInterface
// Clear the caches if they were filled for the first time within
// this function call
if ($hierarchyInit) {
- unset($this->blockNameHierarchyMap[$viewAndSuffixCacheKey]);
- unset($this->hierarchyLevelMap[$viewAndSuffixCacheKey]);
+ unset($this->blockNameHierarchyMap[$viewAndSuffixCacheKey], $this->hierarchyLevelMap[$viewAndSuffixCacheKey]);
}
if ($varInit) {
diff --git a/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php b/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php
index 3060472975..090eb8bc4f 100644
--- a/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php
+++ b/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php
@@ -54,7 +54,7 @@ abstract class FormPerformanceTestCase extends FormIntegrationTestCase
*/
public function setMaxRunningTime($maxRunningTime)
{
- if (is_integer($maxRunningTime) && $maxRunningTime >= 0) {
+ if (is_int($maxRunningTime) && $maxRunningTime >= 0) {
$this->maxRunningTime = $maxRunningTime;
} else {
throw new \InvalidArgumentException();
diff --git a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php
index 0e7b2f8419..9a49e2f3c8 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php
@@ -147,8 +147,7 @@ class RouterListener implements EventSubscriberInterface
}
$request->attributes->add($parameters);
- unset($parameters['_route']);
- unset($parameters['_controller']);
+ unset($parameters['_route'], $parameters['_controller']);
$request->attributes->set('_route_params', $parameters);
} catch (ResourceNotFoundException $e) {
$message = sprintf('No route found for "%s %s"', $request->getMethod(), $request->getPathInfo());
diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php
index 86d8812530..4303ab1343 100644
--- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php
@@ -29,7 +29,7 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase
$this->assertSame('logger', $c->getName());
$this->assertSame($nb, $c->countErrors());
- $this->assertSame($expectedLogs ? $expectedLogs : $logs, $c->getLogs());
+ $this->assertSame($expectedLogs ?: $logs, $c->getLogs());
$this->assertSame($expectedDeprecationCount, $c->countDeprecations());
$this->assertSame($expectedScreamCount, $c->countScreams());
diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php
index 25e97a72e1..2140fbf582 100644
--- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php
+++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php
@@ -258,7 +258,7 @@ class OptionsResolver implements Options
throw new AccessException('Options cannot be made required from a lazy option or normalizer.');
}
- foreach ((array) $optionNames as $key => $option) {
+ foreach ((array) $optionNames as $option) {
$this->defined[$option] = true;
$this->required[$option] = true;
}
@@ -339,7 +339,7 @@ class OptionsResolver implements Options
throw new AccessException('Options cannot be defined from a lazy option or normalizer.');
}
- foreach ((array) $optionNames as $key => $option) {
+ foreach ((array) $optionNames as $option) {
$this->defined[$option] = true;
}
@@ -617,14 +617,8 @@ class OptionsResolver implements Options
}
foreach ((array) $optionNames as $option) {
- unset($this->defined[$option]);
- unset($this->defaults[$option]);
- unset($this->required[$option]);
- unset($this->resolved[$option]);
- unset($this->lazy[$option]);
- unset($this->normalizers[$option]);
- unset($this->allowedTypes[$option]);
- unset($this->allowedValues[$option]);
+ unset($this->defined[$option], $this->defaults[$option], $this->required[$option], $this->resolved[$option]);
+ unset($this->lazy[$option], $this->normalizers[$option], $this->allowedTypes[$option], $this->allowedValues[$option]);
}
return $this;
diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
index 8a2e375410..3b3c642bf6 100644
--- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
+++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
@@ -70,7 +70,6 @@ class PropertyAccessor implements PropertyAccessorInterface
}
$propertyValues = & $this->readPropertiesUntil($objectOrArray, $propertyPath, $propertyPath->getLength() - 1);
- $overwrite = true;
// Add the root object to the list
array_unshift($propertyValues, array(
@@ -81,18 +80,19 @@ class PropertyAccessor implements PropertyAccessorInterface
for ($i = count($propertyValues) - 1; $i >= 0; --$i) {
$objectOrArray = & $propertyValues[$i][self::VALUE];
- if ($overwrite) {
- $property = $propertyPath->getElement($i);
+ $property = $propertyPath->getElement($i);
- if ($propertyPath->isIndex($i)) {
- $this->writeIndex($objectOrArray, $property, $value);
- } else {
- $this->writeProperty($objectOrArray, $property, $value);
- }
+ if ($propertyPath->isIndex($i)) {
+ $this->writeIndex($objectOrArray, $property, $value);
+ } else {
+ $this->writeProperty($objectOrArray, $property, $value);
+ }
+
+ if ($propertyValues[$i][self::IS_REF]) {
+ return;
}
$value = & $objectOrArray;
- $overwrite = !$propertyValues[$i][self::IS_REF];
}
}
@@ -127,7 +127,6 @@ class PropertyAccessor implements PropertyAccessorInterface
try {
$propertyValues = $this->readPropertiesUntil($objectOrArray, $propertyPath, $propertyPath->getLength() - 1);
- $overwrite = true;
// Add the root object to the list
array_unshift($propertyValues, array(
@@ -138,21 +137,21 @@ class PropertyAccessor implements PropertyAccessorInterface
for ($i = count($propertyValues) - 1; $i >= 0; --$i) {
$objectOrArray = $propertyValues[$i][self::VALUE];
- if ($overwrite) {
- $property = $propertyPath->getElement($i);
+ $property = $propertyPath->getElement($i);
- if ($propertyPath->isIndex($i)) {
- if (!$objectOrArray instanceof \ArrayAccess && !is_array($objectOrArray)) {
- return false;
- }
- } else {
- if (!$this->isPropertyWritable($objectOrArray, $property)) {
- return false;
- }
+ if ($propertyPath->isIndex($i)) {
+ if (!$objectOrArray instanceof \ArrayAccess && !is_array($objectOrArray)) {
+ return false;
+ }
+ } else {
+ if (!$this->isPropertyWritable($objectOrArray, $property)) {
+ return false;
}
}
- $overwrite = !$propertyValues[$i][self::IS_REF];
+ if ($propertyValues[$i][self::IS_REF]) {
+ return true;
+ }
}
return true;
diff --git a/src/Symfony/Component/PropertyAccess/PropertyPathBuilder.php b/src/Symfony/Component/PropertyAccess/PropertyPathBuilder.php
index 079da283f2..5d7fdac6fd 100644
--- a/src/Symfony/Component/PropertyAccess/PropertyPathBuilder.php
+++ b/src/Symfony/Component/PropertyAccess/PropertyPathBuilder.php
@@ -264,8 +264,7 @@ class PropertyPathBuilder
// All remaining elements should be removed
for (; $i < $length; ++$i) {
- unset($this->elements[$i]);
- unset($this->isIndex[$i]);
+ unset($this->elements[$i], $this->isIndex[$i]);
}
} else {
$diff = $insertionLength - $cutLength;
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php
index 5cd605b164..7b1b927529 100644
--- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php
+++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php
@@ -25,6 +25,7 @@ class TestClass
private $publicAccessorWithMoreRequiredParameters;
private $publicIsAccessor;
private $publicHasAccessor;
+ private $publicGetter;
public function __construct($value)
{
@@ -37,6 +38,7 @@ class TestClass
$this->publicAccessorWithMoreRequiredParameters = $value;
$this->publicIsAccessor = $value;
$this->publicHasAccessor = $value;
+ $this->publicGetter = $value;
}
public function setPublicAccessor($value)
@@ -166,4 +168,9 @@ class TestClass
{
return 'foobar';
}
+
+ public function getPublicGetter()
+ {
+ return $this->publicGetter;
+ }
}
diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
index 6b55d449ae..690fcd262d 100644
--- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
+++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
@@ -419,6 +419,14 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
array(array('index' => array('%!@$§.' => 'Bernhard')), '[index][%!@$§.]', 'Bernhard'),
array((object) array('%!@$§' => 'Bernhard'), '%!@$§', 'Bernhard'),
array((object) array('property' => (object) array('%!@$§' => 'Bernhard')), 'property.%!@$§', 'Bernhard'),
+
+ // nested objects and arrays
+ array(array('foo' => new TestClass('bar')), '[foo].publicGetSetter', 'bar'),
+ array(new TestClass(array('foo' => 'bar')), 'publicGetSetter[foo]', 'bar'),
+ array(new TestClass(new TestClass('bar')), 'publicGetter.publicGetSetter', 'bar'),
+ array(new TestClass(array('foo' => new TestClass('bar'))), 'publicGetter[foo].publicGetSetter', 'bar'),
+ array(new TestClass(new TestClass(new TestClass('bar'))), 'publicGetter.publicGetter.publicGetSetter', 'bar'),
+ array(new TestClass(array('foo' => array('baz' => new TestClass('bar')))), 'publicGetter[foo][baz].publicGetSetter', 'bar'),
);
}
diff --git a/src/Symfony/Component/Security/Acl/Dbal/AclProvider.php b/src/Symfony/Component/Security/Acl/Dbal/AclProvider.php
index 705b4ffd8d..fd6f5baab4 100644
--- a/src/Symfony/Component/Security/Acl/Dbal/AclProvider.php
+++ b/src/Symfony/Component/Security/Acl/Dbal/AclProvider.php
@@ -173,7 +173,8 @@ class AclProvider implements AclProviderInterface
}
// Is it time to load the current batch?
- if ((self::MAX_BATCH_SIZE === count($currentBatch) || ($i + 1) === $c) && count($currentBatch) > 0) {
+ $currentBatchesCount = count($currentBatch);
+ if ($currentBatchesCount > 0 && (self::MAX_BATCH_SIZE === $currentBatchesCount || ($i + 1) === $c)) {
try {
$loadedBatch = $this->lookupObjectIdentities($currentBatch, $sids, $oidLookup);
} catch (AclNotFoundException $aclNotFoundException) {
@@ -559,10 +560,11 @@ QUERY;
// attach ACL to the result set; even though we do not enforce that every
// object identity has only one instance, we must make sure to maintain
// referential equality with the oids passed to findAcls()
- if (!isset($oidCache[$objectIdentifier.$classType])) {
- $oidCache[$objectIdentifier.$classType] = $acl->getObjectIdentity();
+ $oidCacheKey = $objectIdentifier.$classType;
+ if (!isset($oidCache[$oidCacheKey])) {
+ $oidCache[$oidCacheKey] = $acl->getObjectIdentity();
}
- $result->attach($oidCache[$objectIdentifier.$classType], $acl);
+ $result->attach($oidCache[$oidCacheKey], $acl);
// so, this hasn't been hydrated yet
} else {
// create object identity if we haven't done so yet
@@ -670,7 +672,7 @@ QUERY;
// let's see if we have already hydrated this
if (isset($acls[$parentId])) {
$aclParentAclProperty->setValue($acl, $acls[$parentId]);
- $processed += 1;
+ ++$processed;
continue;
}
diff --git a/src/Symfony/Component/Security/Acl/Tests/Dbal/AclProviderBenchmarkTest.php b/src/Symfony/Component/Security/Acl/Tests/Dbal/AclProviderBenchmarkTest.php
index dab90d46fd..46a695afa8 100644
--- a/src/Symfony/Component/Security/Acl/Tests/Dbal/AclProviderBenchmarkTest.php
+++ b/src/Symfony/Component/Security/Acl/Tests/Dbal/AclProviderBenchmarkTest.php
@@ -122,7 +122,7 @@ class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase
if ($id === 1000 || ($id < 1500 && rand(0, 1))) {
$this->insertClassStmt->execute(array($id, $this->getRandomString(rand(20, 100), 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\\_')));
- $id += 1;
+ ++$id;
return $id-1;
} else {
@@ -148,7 +148,7 @@ class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase
}
$this->generateAces($classId, $id);
- $id += 1;
+ ++$id;
return $id-1;
}
@@ -163,7 +163,7 @@ class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase
$this->getRandomString(rand(5, 30)),
rand(0, 1),
));
- $id += 1;
+ ++$id;
return $id-1;
} else {
@@ -215,7 +215,7 @@ class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase
rand(0, 1),
));
- $id += 1;
+ ++$id;
}
}
diff --git a/src/Symfony/Component/Security/Acl/Tests/Voter/AclVoterTest.php b/src/Symfony/Component/Security/Acl/Tests/Voter/AclVoterTest.php
index c4c0b4edc5..2148135f4b 100644
--- a/src/Symfony/Component/Security/Acl/Tests/Voter/AclVoterTest.php
+++ b/src/Symfony/Component/Security/Acl/Tests/Voter/AclVoterTest.php
@@ -27,7 +27,7 @@ class AclVoterTest extends \PHPUnit_Framework_TestCase
*/
public function testSupportsAttribute($attribute, $supported)
{
- list($voter, , $permissionMap, ,) = $this->getVoter(true, false);
+ list($voter, , $permissionMap) = $this->getVoter(true, false);
$permissionMap
->expects($this->once())
@@ -44,7 +44,7 @@ class AclVoterTest extends \PHPUnit_Framework_TestCase
*/
public function testSupportsAttributeNonString($attribute)
{
- list($voter, , , , ,) = $this->getVoter(true, false);
+ list($voter) = $this->getVoter(true, false);
$this->assertFalse($voter->supportsAttribute($attribute));
}
@@ -72,7 +72,7 @@ class AclVoterTest extends \PHPUnit_Framework_TestCase
*/
public function testSupportsClass($class)
{
- list($voter, , , ,) = $this->getVoter();
+ list($voter) = $this->getVoter();
$this->assertTrue($voter->supportsClass($class));
}
@@ -88,7 +88,7 @@ class AclVoterTest extends \PHPUnit_Framework_TestCase
public function testVote()
{
- list($voter, , $permissionMap, ,) = $this->getVoter();
+ list($voter, , $permissionMap) = $this->getVoter();
$permissionMap
->expects($this->atLeastOnce())
->method('getMasks')
@@ -103,7 +103,7 @@ class AclVoterTest extends \PHPUnit_Framework_TestCase
*/
public function testVoteWhenNoObjectIsPassed($allowIfObjectIdentityUnavailable)
{
- list($voter, , $permissionMap, ,) = $this->getVoter($allowIfObjectIdentityUnavailable);
+ list($voter, , $permissionMap) = $this->getVoter($allowIfObjectIdentityUnavailable);
$permissionMap
->expects($this->once())
->method('getMasks')
@@ -124,7 +124,7 @@ class AclVoterTest extends \PHPUnit_Framework_TestCase
*/
public function testVoteWhenOidStrategyReturnsNull($allowIfUnavailable)
{
- list($voter, , $permissionMap, $oidStrategy,) = $this->getVoter($allowIfUnavailable);
+ list($voter, , $permissionMap, $oidStrategy) = $this->getVoter($allowIfUnavailable);
$permissionMap
->expects($this->once())
->method('getMasks')
diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php
index 4f732542ea..c17a9545a0 100644
--- a/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php
+++ b/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php
@@ -60,7 +60,7 @@ class PreAuthenticatedAuthenticationProvider implements AuthenticationProviderIn
throw new BadCredentialsException('No pre-authenticated principal found in request.');
}
- $user = $this->userProvider->loadUserByUsername($user);
+ $user = $this->userProvider->loadUserByUsername($user);
$this->userChecker->checkPostAuth($user);
diff --git a/src/Symfony/Component/Security/Core/Tests/Util/SecureRandomTest.php b/src/Symfony/Component/Security/Core/Tests/Util/SecureRandomTest.php
index 6e12ef2dd8..590efd3a74 100644
--- a/src/Symfony/Component/Security/Core/Tests/Util/SecureRandomTest.php
+++ b/src/Symfony/Component/Security/Core/Tests/Util/SecureRandomTest.php
@@ -41,7 +41,7 @@ class SecureRandomTest extends \PHPUnit_Framework_TestCase
for ($j = 1; $j <= 5000; $j++) {
$k = 4 * $j - 1;
- $c[8 * $b[$k - 3] + 4 * $b[$k - 2] + 2 * $b[$k - 1] + $b[$k]] += 1;
+ ++$c[8 * $b[$k - 3] + 4 * $b[$k - 2] + 2 * $b[$k - 1] + $b[$k]];
}
$f = 0;
@@ -73,14 +73,14 @@ class SecureRandomTest extends \PHPUnit_Framework_TestCase
$run = 6;
}
- $runs[$run] += 1;
+ ++$runs[$run];
};
$currentRun = 0;
$lastBit = null;
for ($i = 0; $i < 20000; $i++) {
if ($lastBit === $b[$i]) {
- $currentRun += 1;
+ ++$currentRun;
} else {
if ($currentRun > 0) {
$addRun($currentRun);
@@ -115,7 +115,7 @@ class SecureRandomTest extends \PHPUnit_Framework_TestCase
$lastBit = null;
for ($i = 0; $i < 20000; $i++) {
if ($lastBit === $b[$i]) {
- $currentRun += 1;
+ ++$currentRun;
} else {
if ($currentRun > $longestRun) {
$longestRun = $currentRun;
diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/ExceptionListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/ExceptionListenerTest.php
index d7d1826cb8..3d409e5457 100644
--- a/src/Symfony/Component/Security/Http/Tests/Firewall/ExceptionListenerTest.php
+++ b/src/Symfony/Component/Security/Http/Tests/Firewall/ExceptionListenerTest.php
@@ -172,9 +172,9 @@ class ExceptionListenerTest extends \PHPUnit_Framework_TestCase
private function createExceptionListener(TokenStorageInterface $tokenStorage = null, AuthenticationTrustResolverInterface $trustResolver = null, HttpUtils $httpUtils = null, AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, AccessDeniedHandlerInterface $accessDeniedHandler = null)
{
return new ExceptionListener(
- $tokenStorage ? $tokenStorage : $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'),
- $trustResolver ? $trustResolver : $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface'),
- $httpUtils ? $httpUtils : $this->getMock('Symfony\Component\Security\Http\HttpUtils'),
+ $tokenStorage ?: $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'),
+ $trustResolver ?: $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface'),
+ $httpUtils ?: $this->getMock('Symfony\Component\Security\Http\HttpUtils'),
'key',
$authenticationEntryPoint,
$errorPage,
diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php
index f6c30b8334..ec1c35d708 100644
--- a/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php
+++ b/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php
@@ -20,7 +20,7 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase
{
public function testOnCoreSecurityDoesNotTryToPopulateNonEmptyTokenStorage()
{
- list($listener, $tokenStorage, , , ,) = $this->getListener();
+ list($listener, $tokenStorage) = $this->getListener();
$tokenStorage
->expects($this->once())
@@ -38,7 +38,7 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase
public function testOnCoreSecurityDoesNothingWhenNoCookieIsSet()
{
- list($listener, $tokenStorage, $service, ,) = $this->getListener();
+ list($listener, $tokenStorage, $service) = $this->getListener();
$tokenStorage
->expects($this->once())
@@ -64,7 +64,7 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase
public function testOnCoreSecurityIgnoresAuthenticationExceptionThrownByAuthenticationManagerImplementation()
{
- list($listener, $tokenStorage, $service, $manager,) = $this->getListener();
+ list($listener, $tokenStorage, $service, $manager) = $this->getListener();
$tokenStorage
->expects($this->once())
@@ -144,7 +144,7 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase
public function testOnCoreSecurity()
{
- list($listener, $tokenStorage, $service, $manager,) = $this->getListener();
+ list($listener, $tokenStorage, $service, $manager) = $this->getListener();
$tokenStorage
->expects($this->once())
diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md
index 64be9caced..ceeeeb1e7a 100644
--- a/src/Symfony/Component/Serializer/CHANGELOG.md
+++ b/src/Symfony/Component/Serializer/CHANGELOG.md
@@ -14,6 +14,9 @@ CHANGELOG
* [DEPRECATION] `GetSetMethodNormalizer::setCamelizedAttributes()` and
`PropertyNormalizer::setCamelizedAttributes()` are replaced by
`CamelCaseToSnakeCaseNameConverter`
+ * [DEPRECATION] the `Exception` interface has been renamed to `ExceptionInterface`
+ * added `ObjectNormalizer` leveraging the `PropertyAccess` component to normalize
+ objects containing both properties and getters / setters / issers / hassers methods.
2.6.0
-----
diff --git a/src/Symfony/Component/Serializer/Exception/Exception.php b/src/Symfony/Component/Serializer/Exception/Exception.php
index dc2b8fd1c3..fc0606e2ff 100644
--- a/src/Symfony/Component/Serializer/Exception/Exception.php
+++ b/src/Symfony/Component/Serializer/Exception/Exception.php
@@ -14,7 +14,7 @@ namespace Symfony\Component\Serializer\Exception;
/**
* Base exception.
*
- * @author Johannes M. Schmitt
+ * @deprecated since version 2.7, to be removed in 3.0. Use ExceptionInterface instead.
*/
interface Exception
{
diff --git a/src/Symfony/Component/Serializer/Exception/ExceptionInterface.php b/src/Symfony/Component/Serializer/Exception/ExceptionInterface.php
new file mode 100644
index 0000000000..ff67edbb02
--- /dev/null
+++ b/src/Symfony/Component/Serializer/Exception/ExceptionInterface.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Serializer\Exception;
+
+/**
+ * Base exception interface.
+ *
+ * @author Johannes M. Schmitt
+ */
+interface ExceptionInterface extends Exception
+{
+}
diff --git a/src/Symfony/Component/Serializer/Exception/InvalidArgumentException.php b/src/Symfony/Component/Serializer/Exception/InvalidArgumentException.php
index 8cad373a1c..f4e645c448 100644
--- a/src/Symfony/Component/Serializer/Exception/InvalidArgumentException.php
+++ b/src/Symfony/Component/Serializer/Exception/InvalidArgumentException.php
@@ -16,6 +16,6 @@ namespace Symfony\Component\Serializer\Exception;
*
* @author Johannes M. Schmitt
*/
-class InvalidArgumentException extends \InvalidArgumentException implements Exception
+class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}
diff --git a/src/Symfony/Component/Serializer/Exception/LogicException.php b/src/Symfony/Component/Serializer/Exception/LogicException.php
index a372bf9471..08c53e0ff7 100644
--- a/src/Symfony/Component/Serializer/Exception/LogicException.php
+++ b/src/Symfony/Component/Serializer/Exception/LogicException.php
@@ -16,6 +16,6 @@ namespace Symfony\Component\Serializer\Exception;
*
* @author Lukas Kahwe Smith
*/
-class LogicException extends \LogicException implements Exception
+class LogicException extends \LogicException implements ExceptionInterface
{
}
diff --git a/src/Symfony/Component/Serializer/Exception/RuntimeException.php b/src/Symfony/Component/Serializer/Exception/RuntimeException.php
index 0272865489..a287d97ad4 100644
--- a/src/Symfony/Component/Serializer/Exception/RuntimeException.php
+++ b/src/Symfony/Component/Serializer/Exception/RuntimeException.php
@@ -16,6 +16,6 @@ namespace Symfony\Component\Serializer\Exception;
*
* @author Johannes M. Schmitt
*/
-class RuntimeException extends \RuntimeException implements Exception
+class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}
diff --git a/src/Symfony/Component/Serializer/Exception/UnexpectedValueException.php b/src/Symfony/Component/Serializer/Exception/UnexpectedValueException.php
index 86cf204038..2a63c5bd3f 100644
--- a/src/Symfony/Component/Serializer/Exception/UnexpectedValueException.php
+++ b/src/Symfony/Component/Serializer/Exception/UnexpectedValueException.php
@@ -16,6 +16,6 @@ namespace Symfony\Component\Serializer\Exception;
*
* @author Lukas Kahwe Smith
*/
-class UnexpectedValueException extends \UnexpectedValueException implements Exception
+class UnexpectedValueException extends \UnexpectedValueException implements ExceptionInterface
{
}
diff --git a/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php
new file mode 100644
index 0000000000..7a1d3db94a
--- /dev/null
+++ b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php
@@ -0,0 +1,94 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Serializer\Mapping;
+
+/**
+ * {@inheritdoc}
+ *
+ * @author Kévin Dunglas
+ */
+class AttributeMetadata implements AttributeMetadataInterface
+{
+ /**
+ * @var string
+ *
+ * @internal This property is public in order to reduce the size of the
+ * class' serialized representation. Do not access it. Use
+ * {@link getName()} instead.
+ */
+ public $name;
+
+ /**
+ * @var array
+ *
+ * @internal This property is public in order to reduce the size of the
+ * class' serialized representation. Do not access it. Use
+ * {@link getGroups()} instead.
+ */
+ public $groups = array();
+
+ /**
+ * Constructs a metadata for the given attribute.
+ *
+ * @param string $name
+ */
+ public function __construct($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addGroup($group)
+ {
+ if (!in_array($group, $this->groups)) {
+ $this->groups[] = $group;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getGroups()
+ {
+ return $this->groups;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function merge(AttributeMetadataInterface $attributeMetadata)
+ {
+ foreach ($attributeMetadata->getGroups() as $group) {
+ $this->addGroup($group);
+ }
+ }
+
+ /**
+ * Returns the names of the properties that should be serialized.
+ *
+ * @return string[]
+ */
+ public function __sleep()
+ {
+ return array('name', 'groups');
+ }
+}
diff --git a/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php b/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php
new file mode 100644
index 0000000000..0701a58b56
--- /dev/null
+++ b/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.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\Serializer\Mapping;
+
+/**
+ * Stores metadata needed for serializing and deserializing attributes.
+ *
+ * Primarily, the metadata stores serialization groups.
+ *
+ * @author Kévin Dunglas
+ */
+interface AttributeMetadataInterface
+{
+ /**
+ * Gets the attribute name.
+ *
+ * @return string
+ */
+ public function getName();
+
+ /**
+ * Adds this attribute to the given group.
+ *
+ * @param string $group
+ */
+ public function addGroup($group);
+
+ /**
+ * Gets groups of this attribute.
+ *
+ * @return string[]
+ */
+ public function getGroups();
+
+ /**
+ * Merges an {@see AttributeMetadataInterface} with in the current one.
+ *
+ * @param AttributeMetadataInterface $attributeMetadata
+ */
+ public function merge(AttributeMetadataInterface $attributeMetadata);
+}
diff --git a/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php b/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php
index ff24c3cabc..3794a7451b 100644
--- a/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php
+++ b/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php
@@ -12,31 +12,29 @@
namespace Symfony\Component\Serializer\Mapping;
/**
- * Stores all metadata needed for serializing objects of specific class.
- *
- * Primarily, the metadata stores serialization groups.
+ * {@inheritdoc}
*
* @author Kévin Dunglas
*/
-class ClassMetadata
+class ClassMetadata implements ClassMetadataInterface
{
/**
* @var string
*
* @internal This property is public in order to reduce the size of the
* class' serialized representation. Do not access it. Use
- * {@link getClassName()} instead.
+ * {@link getName()} instead.
*/
public $name;
/**
- * @var array
+ * @var AttributeMetadataInterface[]
*
* @internal This property is public in order to reduce the size of the
* class' serialized representation. Do not access it. Use
- * {@link getGroups()} instead.
+ * {@link getAttributesMetadata()} instead.
*/
- public $attributesGroups = array();
+ public $attributesMetadata = array();
/**
* @var \ReflectionClass
@@ -54,66 +52,50 @@ class ClassMetadata
}
/**
- * Returns the name of the backing PHP class.
- *
- * @return string The name of the backing class.
+ * {@inheritdoc}
*/
- public function getClassName()
+ public function getName()
{
return $this->name;
}
/**
- * Gets serialization groups.
- *
- * @return array
+ * {@inheritdoc}
*/
- public function getAttributesGroups()
+ public function addAttributeMetadata(AttributeMetadataInterface $attributeMetadata)
{
- return $this->attributesGroups;
+ $this->attributesMetadata[$attributeMetadata->getName()] = $attributeMetadata;
}
/**
- * Adds an attribute to a serialization group
- *
- * @param string $attribute
- * @param string $group
- * @throws \InvalidArgumentException
+ * {@inheritdoc}
*/
- public function addAttributeGroup($attribute, $group)
+ public function getAttributesMetadata()
{
- if (!is_string($attribute) || !is_string($group)) {
- throw new \InvalidArgumentException('Arguments must be strings.');
- }
-
- if (!isset($this->groups[$group]) || !in_array($attribute, $this->attributesGroups[$group])) {
- $this->attributesGroups[$group][] = $attribute;
- }
+ return $this->attributesMetadata;
}
/**
- * Merges attributes' groups.
- *
- * @param ClassMetadata $classMetadata
+ * {@inheritdoc}
*/
- public function mergeAttributesGroups(ClassMetadata $classMetadata)
+ public function merge(ClassMetadataInterface $classMetadata)
{
- foreach ($classMetadata->getAttributesGroups() as $group => $attributes) {
- foreach ($attributes as $attribute) {
- $this->addAttributeGroup($attribute, $group);
+ foreach ($classMetadata->getAttributesMetadata() as $attributeMetadata) {
+ if (isset($this->attributesMetadata[$attributeMetadata->getName()])) {
+ $this->attributesMetadata[$attributeMetadata->getName()]->merge($attributeMetadata);
+ } else {
+ $this->addAttributeMetadata($attributeMetadata);
}
}
}
/**
- * Returns a ReflectionClass instance for this class.
- *
- * @return \ReflectionClass
+ * {@inheritdoc}
*/
public function getReflectionClass()
{
if (!$this->reflClass) {
- $this->reflClass = new \ReflectionClass($this->getClassName());
+ $this->reflClass = new \ReflectionClass($this->getName());
}
return $this->reflClass;
@@ -128,7 +110,7 @@ class ClassMetadata
{
return array(
'name',
- 'attributesGroups',
+ 'attributes',
);
}
}
diff --git a/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php b/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php
new file mode 100644
index 0000000000..c967666bd7
--- /dev/null
+++ b/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php
@@ -0,0 +1,57 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Serializer\Mapping;
+
+/**
+ * Stores metadata needed for serializing and deserializing objects of specific class.
+ *
+ * Primarily, the metadata stores the list of attributes to serialize or deserialize.
+ *
+ * @author Kévin Dunglas
+ */
+interface ClassMetadataInterface
+{
+ /**
+ * Returns the name of the backing PHP class.
+ *
+ * @return string The name of the backing class.
+ */
+ public function getName();
+
+ /**
+ * Adds an {@link AttributeMetadataInterface}.
+ *
+ * @param AttributeMetadataInterface $attributeMetadata
+ */
+ public function addAttributeMetadata(AttributeMetadataInterface $attributeMetadata);
+
+ /**
+ * Gets the list of {@link AttributeMetadataInterface}.
+ *
+ * @return AttributeMetadataInterface[]
+ */
+ public function getAttributesMetadata();
+
+ /**
+ * Merges a {@link ClassMetadataInterface} in the current one.
+ *
+ * @param ClassMetadataInterface $classMetadata
+ */
+ public function merge(ClassMetadataInterface $classMetadata);
+
+ /**
+ * Returns a {@link \ReflectionClass} instance for this class.
+ *
+ * @return \ReflectionClass
+ */
+ public function getReflectionClass();
+}
diff --git a/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactory.php b/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactory.php
index c462dd8666..344d633b26 100644
--- a/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactory.php
+++ b/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactory.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Serializer\Mapping\Factory;
use Doctrine\Common\Cache\Cache;
+use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\Loader\LoaderInterface;
@@ -20,7 +21,7 @@ use Symfony\Component\Serializer\Mapping\Loader\LoaderInterface;
*
* @author Kévin Dunglas
*/
-class ClassMetadataFactory
+class ClassMetadataFactory implements ClassMetadataFactoryInterface
{
/**
* @var LoaderInterface
@@ -46,28 +47,13 @@ class ClassMetadataFactory
}
/**
- * If the method was called with the same class name (or an object of that
- * class) before, the same metadata instance is returned.
- *
- * If the factory was configured with a cache, this method will first look
- * for an existing metadata instance in the cache. If an existing instance
- * is found, it will be returned without further ado.
- *
- * Otherwise, a new metadata instance is created. If the factory was
- * configured with a loader, the metadata is passed to the
- * {@link LoaderInterface::loadClassMetadata()} method for further
- * configuration. At last, the new object is returned.
- *
- * @param string|object $value
- * @return ClassMetadata
- * @throws \InvalidArgumentException
-
+ * {@inheritdoc}
*/
public function getMetadataFor($value)
{
$class = $this->getClass($value);
if (!$class) {
- throw new \InvalidArgumentException(sprintf('Cannot create metadata for non-objects. Got: %s', gettype($value)));
+ throw new InvalidArgumentException(sprintf('Cannot create metadata for non-objects. Got: "%s"', gettype($value)));
}
if (isset($this->loadedClasses[$class])) {
@@ -79,39 +65,33 @@ class ClassMetadataFactory
}
if (!class_exists($class) && !interface_exists($class)) {
- throw new \InvalidArgumentException(sprintf('The class or interface "%s" does not exist.', $class));
+ throw new InvalidArgumentException(sprintf('The class or interface "%s" does not exist.', $class));
}
- $metadata = new ClassMetadata($class);
+ $classMetadata = new ClassMetadata($class);
+ $this->loader->loadClassMetadata($classMetadata);
- $reflClass = $metadata->getReflectionClass();
+ $reflectionClass = $classMetadata->getReflectionClass();
- // Include groups from the parent class
- if ($parent = $reflClass->getParentClass()) {
- $metadata->mergeAttributesGroups($this->getMetadataFor($parent->name));
+ // Include metadata from the parent class
+ if ($parent = $reflectionClass->getParentClass()) {
+ $classMetadata->merge($this->getMetadataFor($parent->name));
}
- // Include groups from all implemented interfaces
- foreach ($reflClass->getInterfaces() as $interface) {
- $metadata->mergeAttributesGroups($this->getMetadataFor($interface->name));
- }
-
- if ($this->loader) {
- $this->loader->loadClassMetadata($metadata);
+ // Include metadata from all implemented interfaces
+ foreach ($reflectionClass->getInterfaces() as $interface) {
+ $classMetadata->merge($this->getMetadataFor($interface->name));
}
if ($this->cache) {
- $this->cache->save($class, $metadata);
+ $this->cache->save($class, $classMetadata);
}
- return $this->loadedClasses[$class] = $metadata;
+ return $this->loadedClasses[$class] = $classMetadata;
}
/**
- * Checks if class has metadata.
- *
- * @param mixed $value
- * @return bool
+ * {@inheritdoc}
*/
public function hasMetadataFor($value)
{
@@ -123,7 +103,8 @@ class ClassMetadataFactory
/**
* Gets a class name for a given class or instance.
*
- * @param $value
+ * @param mixed $value
+ *
* @return string|bool
*/
private function getClass($value)
diff --git a/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactoryInterface.php b/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactoryInterface.php
new file mode 100644
index 0000000000..a03298a06b
--- /dev/null
+++ b/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactoryInterface.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\Serializer\Mapping\Factory;
+
+use Symfony\Component\Serializer\Exception\InvalidArgumentException;
+use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
+
+/**
+ * Returns a {@see ClassMetadataInterface}.
+ *
+ * @author Kévin Dunglas
+ */
+interface ClassMetadataFactoryInterface
+{
+ /**
+ * If the method was called with the same class name (or an object of that
+ * class) before, the same metadata instance is returned.
+ *
+ * If the factory was configured with a cache, this method will first look
+ * for an existing metadata instance in the cache. If an existing instance
+ * is found, it will be returned without further ado.
+ *
+ * Otherwise, a new metadata instance is created. If the factory was
+ * configured with a loader, the metadata is passed to the
+ * {@link LoaderInterface::loadClassMetadata()} method for further
+ * configuration. At last, the new object is returned.
+ *
+ * @param string|object $value
+ *
+ * @return ClassMetadataInterface
+ *
+ * @throws InvalidArgumentException
+ */
+ public function getMetadataFor($value);
+
+ /**
+ * Checks if class has metadata.
+ *
+ * @param mixed $value
+ *
+ * @return bool
+ */
+ public function hasMetadataFor($value);
+}
diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php
index 3b25948924..2db9b9d3d3 100644
--- a/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php
+++ b/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php
@@ -13,7 +13,9 @@ namespace Symfony\Component\Serializer\Mapping\Loader;
use Doctrine\Common\Annotations\Reader;
use Symfony\Component\Serializer\Annotation\Groups;
-use Symfony\Component\Serializer\Mapping\ClassMetadata;
+use Symfony\Component\Serializer\Exception\MappingException;
+use Symfony\Component\Serializer\Mapping\AttributeMetadata;
+use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
/**
* Annotation loader.
@@ -38,18 +40,25 @@ class AnnotationLoader implements LoaderInterface
/**
* {@inheritdoc}
*/
- public function loadClassMetadata(ClassMetadata $metadata)
+ public function loadClassMetadata(ClassMetadataInterface $classMetadata)
{
- $reflClass = $metadata->getReflectionClass();
- $className = $reflClass->name;
+ $reflectionClass = $classMetadata->getReflectionClass();
+ $className = $reflectionClass->name;
$loaded = false;
- foreach ($reflClass->getProperties() as $property) {
+ $attributesMetadata = $classMetadata->getAttributesMetadata();
+
+ foreach ($reflectionClass->getProperties() as $property) {
+ if (!isset($attributeMetadata[$property->name])) {
+ $attributesMetadata[$property->name] = new AttributeMetadata($property->name);
+ $classMetadata->addAttributeMetadata($attributesMetadata[$property->name]);
+ }
+
if ($property->getDeclaringClass()->name == $className) {
foreach ($this->reader->getPropertyAnnotations($property) as $groups) {
if ($groups instanceof Groups) {
foreach ($groups->getGroups() as $group) {
- $metadata->addAttributeGroup($property->name, $group);
+ $attributesMetadata[$property->name]->addGroup($group);
}
}
@@ -58,16 +67,25 @@ class AnnotationLoader implements LoaderInterface
}
}
- foreach ($reflClass->getMethods() as $method) {
+ foreach ($reflectionClass->getMethods() as $method) {
if ($method->getDeclaringClass()->name == $className) {
foreach ($this->reader->getMethodAnnotations($method) as $groups) {
if ($groups instanceof Groups) {
if (preg_match('/^(get|is)(.+)$/i', $method->name, $matches)) {
+ $attributeName = lcfirst($matches[2]);
+
+ if (isset($attributesMetadata[$attributeName])) {
+ $attributeMetadata = $attributesMetadata[$attributeName];
+ } else {
+ $attributesMetadata[$attributeName] = $attributeMetadata = new AttributeMetadata($attributeName);
+ $classMetadata->addAttributeMetadata($attributeMetadata);
+ }
+
foreach ($groups->getGroups() as $group) {
- $metadata->addAttributeGroup(lcfirst($matches[2]), $group);
+ $attributeMetadata->addGroup($group);
}
} else {
- throw new \BadMethodCallException(sprintf('Groups on "%s::%s" cannot be added. Groups can only be added on methods beginning with "get" or "is".', $className, $method->name));
+ throw new MappingException(sprintf('Groups on "%s::%s" cannot be added. Groups can only be added on methods beginning with "get" or "is".', $className, $method->name));
}
}
diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/FileLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/FileLoader.php
index a583757ba6..38bb59389a 100644
--- a/src/Symfony/Component/Serializer/Mapping/Loader/FileLoader.php
+++ b/src/Symfony/Component/Serializer/Mapping/Loader/FileLoader.php
@@ -13,8 +13,16 @@ namespace Symfony\Component\Serializer\Mapping\Loader;
use Symfony\Component\Serializer\Exception\MappingException;
+/**
+ * Base class for all file based loaders.
+ *
+ * @author Kévin Dunglas
+ */
abstract class FileLoader implements LoaderInterface
{
+ /**
+ * @var string
+ */
protected $file;
/**
@@ -22,8 +30,7 @@ abstract class FileLoader implements LoaderInterface
*
* @param string $file The mapping file to load
*
- * @throws MappingException if the mapping file does not exist
- * @throws MappingException if the mapping file is not readable
+ * @throws MappingException if the mapping file does not exist or is not readable
*/
public function __construct($file)
{
diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/LoaderChain.php b/src/Symfony/Component/Serializer/Mapping/Loader/LoaderChain.php
index 572d78aeca..8bf1c17da9 100644
--- a/src/Symfony/Component/Serializer/Mapping/Loader/LoaderChain.php
+++ b/src/Symfony/Component/Serializer/Mapping/Loader/LoaderChain.php
@@ -12,24 +12,28 @@
namespace Symfony\Component\Serializer\Mapping\Loader;
use Symfony\Component\Serializer\Exception\MappingException;
-use Symfony\Component\Serializer\Mapping\ClassMetadata;
+use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
/**
- * Calls multiple LoaderInterface instances in a chain
+ * Calls multiple {@link LoaderInterface} instances in a chain.
*
* This class accepts multiple instances of LoaderInterface to be passed to the
- * constructor. When loadClassMetadata() is called, the same method is called
+ * constructor. When {@link loadClassMetadata()} is called, the same method is called
* in all of these loaders, regardless of whether any of them was
* successful or not.
*
* @author Bernhard Schussek
+ * @author Kévin Dunglas
*/
class LoaderChain implements LoaderInterface
{
- protected $loaders;
+ /**
+ * @var LoaderInterface[]
+ */
+ private $loaders;
/**
- * Accepts a list of LoaderInterface instances
+ * Accepts a list of LoaderInterface instances.
*
* @param LoaderInterface[] $loaders An array of LoaderInterface instances
*
@@ -49,7 +53,7 @@ class LoaderChain implements LoaderInterface
/**
* {@inheritdoc}
*/
- public function loadClassMetadata(ClassMetadata $metadata)
+ public function loadClassMetadata(ClassMetadataInterface $metadata)
{
$success = false;
diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/LoaderInterface.php b/src/Symfony/Component/Serializer/Mapping/Loader/LoaderInterface.php
index d81f11e017..ebf84b6a96 100644
--- a/src/Symfony/Component/Serializer/Mapping/Loader/LoaderInterface.php
+++ b/src/Symfony/Component/Serializer/Mapping/Loader/LoaderInterface.php
@@ -11,10 +11,10 @@
namespace Symfony\Component\Serializer\Mapping\Loader;
-use Symfony\Component\Serializer\Mapping\ClassMetadata;
+use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
/**
- * Loads class metadata.
+ * Loads {@link ClassMetadataInterface}.
*
* @author Kévin Dunglas
*/
@@ -23,9 +23,9 @@ interface LoaderInterface
/**
* Load class metadata.
*
- * @param ClassMetadata $metadata A metadata
+ * @param ClassMetadataInterface $classMetadata A metadata
*
* @return bool
*/
- public function loadClassMetadata(ClassMetadata $metadata);
+ public function loadClassMetadata(ClassMetadataInterface $classMetadata);
}
diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php
index 6e47e99a63..0da2f7d690 100644
--- a/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php
+++ b/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php
@@ -13,7 +13,8 @@ namespace Symfony\Component\Serializer\Mapping\Loader;
use Symfony\Component\Config\Util\XmlUtils;
use Symfony\Component\Serializer\Exception\MappingException;
-use Symfony\Component\Serializer\Mapping\ClassMetadata;
+use Symfony\Component\Serializer\Mapping\AttributeMetadata;
+use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
/**
* Loads XML mapping files.
@@ -23,7 +24,7 @@ use Symfony\Component\Serializer\Mapping\ClassMetadata;
class XmlFileLoader extends FileLoader
{
/**
- * An array of SimpleXMLElement instances.
+ * An array of {@class \SimpleXMLElement} instances.
*
* @var \SimpleXMLElement[]|null
*/
@@ -32,7 +33,7 @@ class XmlFileLoader extends FileLoader
/**
* {@inheritdoc}
*/
- public function loadClassMetadata(ClassMetadata $metadata)
+ public function loadClassMetadata(ClassMetadataInterface $classMetadata)
{
if (null === $this->classes) {
$this->classes = array();
@@ -43,12 +44,23 @@ class XmlFileLoader extends FileLoader
}
}
- if (isset($this->classes[$metadata->getClassName()])) {
- $xml = $this->classes[$metadata->getClassName()];
+ $attributesMetadata = $classMetadata->getAttributesMetadata();
+
+ if (isset($this->classes[$classMetadata->getName()])) {
+ $xml = $this->classes[$classMetadata->getName()];
foreach ($xml->attribute as $attribute) {
+ $attributeName = (string) $attribute['name'];
+
+ if (isset($attributesMetadata[$attributeName])) {
+ $attributeMetadata = $attributesMetadata[$attributeName];
+ } else {
+ $attributeMetadata = new AttributeMetadata($attributeName);
+ $classMetadata->addAttributeMetadata($attributeMetadata);
+ }
+
foreach ($attribute->group as $group) {
- $metadata->addAttributeGroup((string) $attribute['name'], (string) $group);
+ $attributeMetadata->addGroup((string) $group);
}
}
@@ -59,7 +71,7 @@ class XmlFileLoader extends FileLoader
}
/**
- * Parse a XML File.
+ * Parses a XML File.
*
* @param string $file Path of file
*
diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php
index f72aa5a1d1..1c5d5e82e6 100644
--- a/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php
+++ b/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php
@@ -12,11 +12,12 @@
namespace Symfony\Component\Serializer\Mapping\Loader;
use Symfony\Component\Serializer\Exception\MappingException;
-use Symfony\Component\Serializer\Mapping\ClassMetadata;
+use Symfony\Component\Serializer\Mapping\AttributeMetadata;
+use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
use Symfony\Component\Yaml\Parser;
/**
- * YAML File Loader
+ * YAML File Loader.
*
* @author Kévin Dunglas
*/
@@ -34,7 +35,7 @@ class YamlFileLoader extends FileLoader
/**
* {@inheritdoc}
*/
- public function loadClassMetadata(ClassMetadata $metadata)
+ public function loadClassMetadata(ClassMetadataInterface $classMetadata)
{
if (null === $this->classes) {
if (!stream_is_local($this->file)) {
@@ -59,14 +60,22 @@ class YamlFileLoader extends FileLoader
$this->classes = $classes;
}
- if (isset($this->classes[$metadata->getClassName()])) {
- $yaml = $this->classes[$metadata->getClassName()];
+ if (isset($this->classes[$classMetadata->getName()])) {
+ $yaml = $this->classes[$classMetadata->getName()];
if (isset($yaml['attributes']) && is_array($yaml['attributes'])) {
+ $attributesMetadata = $classMetadata->getAttributesMetadata();
foreach ($yaml['attributes'] as $attribute => $data) {
+ if (isset($attributesMetadata[$attribute])) {
+ $attributeMetadata = $attributesMetadata[$attribute];
+ } else {
+ $attributeMetadata = new AttributeMetadata($attribute);
+ $classMetadata->addAttributeMetadata($attributeMetadata);
+ }
+
if (isset($data['groups'])) {
foreach ($data['groups'] as $group) {
- $metadata->addAttributeGroup($attribute, $group);
+ $attributeMetadata->addGroup($group);
}
}
}
diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
index 9ea4d374bf..dfd21b4644 100644
--- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
@@ -15,7 +15,8 @@ use Symfony\Component\Serializer\Exception\CircularReferenceException;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Exception\RuntimeException;
-use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
+use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
+use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface;
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
@@ -26,21 +27,42 @@ use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
*/
abstract class AbstractNormalizer extends SerializerAwareNormalizer implements NormalizerInterface, DenormalizerInterface
{
+ /**
+ * @var int
+ */
protected $circularReferenceLimit = 1;
+ /**
+ * @var callable
+ */
protected $circularReferenceHandler;
+ /**
+ * @var ClassMetadataFactoryInterface|null
+ */
protected $classMetadataFactory;
+ /**
+ * @var NameConverterInterface|null
+ */
protected $nameConverter;
+ /**
+ * @var array
+ */
protected $callbacks = array();
+ /**
+ * @var array
+ */
protected $ignoredAttributes = array();
+ /**
+ * @var array
+ */
protected $camelizedAttributes = array();
/**
- * Sets the {@link ClassMetadataFactory} to use.
+ * Sets the {@link ClassMetadataFactoryInterface} to use.
*
- * @param ClassMetadataFactory|null $classMetadataFactory
+ * @param ClassMetadataFactoryInterface|null $classMetadataFactory
* @param NameConverterInterface|null $nameConverter
*/
- public function __construct(ClassMetadataFactory $classMetadataFactory = null, NameConverterInterface $nameConverter = null)
+ public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null)
{
$this->classMetadataFactory = $classMetadataFactory;
$this->nameConverter = $nameConverter;
@@ -219,19 +241,21 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N
* Gets attributes to normalize using groups.
*
* @param string|object $classOrObject
- * @param array $context
- * @return array|bool
+ * @param array $context
+ * @param bool $attributesAsString If false, return an array of {@link AttributeMetadataInterface}
+ *
+ * @return string[]|AttributeMetadataInterface[]|bool
*/
- protected function getAllowedAttributes($classOrObject, array $context)
+ protected function getAllowedAttributes($classOrObject, array $context, $attributesAsString = false)
{
if (!$this->classMetadataFactory || !isset($context['groups']) || !is_array($context['groups'])) {
return false;
}
$allowedAttributes = array();
- foreach ($this->classMetadataFactory->getMetadataFor($classOrObject)->getAttributesGroups() as $group => $attributes) {
- if (in_array($group, $context['groups'])) {
- $allowedAttributes = array_merge($allowedAttributes, $attributes);
+ foreach ($this->classMetadataFactory->getMetadataFor($classOrObject)->getAttributesMetadata() as $attributeMetadata) {
+ if (count(array_intersect($attributeMetadata->getGroups(), $context['groups']))) {
+ $allowedAttributes[] = $attributesAsString ? $attributeMetadata->getName() : $attributeMetadata;
}
}
diff --git a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php
index 348ab78bb2..6ccfb984b5 100644
--- a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php
@@ -52,7 +52,7 @@ class GetSetMethodNormalizer extends AbstractNormalizer
$reflectionObject = new \ReflectionObject($object);
$reflectionMethods = $reflectionObject->getMethods(\ReflectionMethod::IS_PUBLIC);
- $allowedAttributes = $this->getAllowedAttributes($object, $context);
+ $allowedAttributes = $this->getAllowedAttributes($object, $context, true);
$attributes = array();
foreach ($reflectionMethods as $method) {
@@ -68,7 +68,7 @@ class GetSetMethodNormalizer extends AbstractNormalizer
}
$attributeValue = $method->invoke($object);
- if (array_key_exists($attributeName, $this->callbacks)) {
+ if (isset($this->callbacks[$attributeName])) {
$attributeValue = call_user_func($this->callbacks[$attributeName], $attributeValue);
}
if (null !== $attributeValue && !is_scalar($attributeValue)) {
@@ -97,7 +97,7 @@ class GetSetMethodNormalizer extends AbstractNormalizer
*/
public function denormalize($data, $class, $format = null, array $context = array())
{
- $allowedAttributes = $this->getAllowedAttributes($class, $context);
+ $allowedAttributes = $this->getAllowedAttributes($class, $context, true);
$normalizedData = $this->prepareForDenormalization($data);
$reflectionClass = new \ReflectionClass($class);
diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php
new file mode 100644
index 0000000000..92628cb531
--- /dev/null
+++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php
@@ -0,0 +1,162 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Serializer\Normalizer;
+
+use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
+use Symfony\Component\PropertyAccess\PropertyAccess;
+use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
+use Symfony\Component\Serializer\Exception\CircularReferenceException;
+use Symfony\Component\Serializer\Exception\LogicException;
+use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
+use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
+
+/**
+ * Converts between objects and arrays using the PropertyAccess component.
+ *
+ * @author Kévin Dunglas
+ */
+class ObjectNormalizer extends AbstractNormalizer
+{
+ /**
+ * @var PropertyAccessorInterface
+ */
+ protected $propertyAccessor;
+
+ public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyAccessorInterface $propertyAccessor = null)
+ {
+ parent::__construct($classMetadataFactory, $nameConverter);
+
+ $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function supportsNormalization($data, $format = null)
+ {
+ return is_object($data);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @throws CircularReferenceException
+ */
+ public function normalize($object, $format = null, array $context = array())
+ {
+ if ($this->isCircularReference($object, $context)) {
+ return $this->handleCircularReference($object);
+ }
+
+ $data = array();
+ $attributes = $this->getAllowedAttributes($object, $context, true);
+
+ // If not using groups, detect manually
+ if (false === $attributes) {
+ $attributes = array();
+
+ // methods
+ $reflClass = new \ReflectionClass($object);
+ foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflMethod) {
+ if (
+ !$reflMethod->isConstructor() &&
+ !$reflMethod->isDestructor() &&
+ 0 === $reflMethod->getNumberOfRequiredParameters()
+ ) {
+ $name = $reflMethod->getName();
+
+ if (strpos($name, 'get') === 0 || strpos($name, 'has') === 0) {
+ // getters and hassers
+ $attributes[lcfirst(substr($name, 3))] = true;
+ } elseif (strpos($name, 'is') === 0) {
+ // issers
+ $attributes[lcfirst(substr($name, 2))] = true;
+ }
+ }
+ }
+
+ // properties
+ foreach ($reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $reflProperty) {
+ $attributes[$reflProperty->getName()] = true;
+ }
+
+ $attributes = array_keys($attributes);
+ }
+
+ foreach ($attributes as $attribute) {
+ if (in_array($attribute, $this->ignoredAttributes)) {
+ continue;
+ }
+
+ $attributeValue = $this->propertyAccessor->getValue($object, $attribute);
+
+ if (isset($this->callbacks[$attribute])) {
+ $attributeValue = call_user_func($this->callbacks[$attribute], $attributeValue);
+ }
+
+ if (null !== $attributeValue && !is_scalar($attributeValue)) {
+ if (!$this->serializer instanceof NormalizerInterface) {
+ throw new LogicException(sprintf('Cannot normalize attribute "%s" because injected serializer is not a normalizer', $attribute));
+ }
+
+ $attributeValue = $this->serializer->normalize($attributeValue, $format, $context);
+ }
+
+ if ($this->nameConverter) {
+ $attribute = $this->nameConverter->normalize($attribute);
+ }
+
+ $data[$attribute] = $attributeValue;
+ }
+
+ return $data;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function supportsDenormalization($data, $type, $format = null)
+ {
+ return class_exists($type);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function denormalize($data, $class, $format = null, array $context = array())
+ {
+ $allowedAttributes = $this->getAllowedAttributes($class, $context, true);
+ $normalizedData = $this->prepareForDenormalization($data);
+
+ $reflectionClass = new \ReflectionClass($class);
+ $object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes);
+
+ foreach ($normalizedData as $attribute => $value) {
+ $allowed = $allowedAttributes === false || in_array($attribute, $allowedAttributes);
+ $ignored = in_array($attribute, $this->ignoredAttributes);
+
+ if ($allowed && !$ignored) {
+ if ($this->nameConverter) {
+ $attribute = $this->nameConverter->normalize($attribute);
+ }
+
+ try {
+ $this->propertyAccessor->setValue($object, $attribute, $value);
+ } catch (NoSuchPropertyException $exception) {
+ // Properties not found are ignored
+ }
+ }
+ }
+
+ return $object;
+ }
+}
diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php
index b54187abb2..970d0661cd 100644
--- a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php
@@ -46,7 +46,7 @@ class PropertyNormalizer extends AbstractNormalizer
$reflectionObject = new \ReflectionObject($object);
$attributes = array();
- $allowedAttributes = $this->getAllowedAttributes($object, $context);
+ $allowedAttributes = $this->getAllowedAttributes($object, $context, true);
foreach ($reflectionObject->getProperties() as $property) {
if (in_array($property->name, $this->ignoredAttributes)) {
@@ -64,7 +64,7 @@ class PropertyNormalizer extends AbstractNormalizer
$attributeValue = $property->getValue($object);
- if (array_key_exists($property->name, $this->callbacks)) {
+ if (isset($this->callbacks[$property->name])) {
$attributeValue = call_user_func($this->callbacks[$property->name], $attributeValue);
}
if (null !== $attributeValue && !is_scalar($attributeValue)) {
@@ -89,7 +89,7 @@ class PropertyNormalizer extends AbstractNormalizer
*/
public function denormalize($data, $class, $format = null, array $context = array())
{
- $allowedAttributes = $this->getAllowedAttributes($class, $context);
+ $allowedAttributes = $this->getAllowedAttributes($class, $context, true);
$data = $this->prepareForDenormalization($data);
$reflectionClass = new \ReflectionClass($class);
diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php
new file mode 100644
index 0000000000..f22746bc5d
--- /dev/null
+++ b/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php
@@ -0,0 +1,57 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Serializer\Tests\Mapping;
+
+use Symfony\Component\Serializer\Mapping\AttributeMetadata;
+
+/**
+ * @author Kévin Dunglas
+ */
+class AttributeMetadataTest extends \PHPUnit_Framework_TestCase
+{
+ public function testInterface()
+ {
+ $attributeMetadata = new AttributeMetadata('name');
+ $this->assertInstanceOf('Symfony\Component\Serializer\Mapping\AttributeMetadataInterface', $attributeMetadata);
+ }
+
+ public function testGetName()
+ {
+ $attributeMetadata = new AttributeMetadata('name');
+ $this->assertEquals('name', $attributeMetadata->getName());
+ }
+
+ public function testGroups()
+ {
+ $attributeMetadata = new AttributeMetadata('group');
+ $attributeMetadata->addGroup('a');
+ $attributeMetadata->addGroup('a');
+ $attributeMetadata->addGroup('b');
+
+ $this->assertEquals(array('a', 'b'), $attributeMetadata->getGroups());
+ }
+
+ public function testMerge()
+ {
+ $attributeMetadata1 = new AttributeMetadata('a1');
+ $attributeMetadata1->addGroup('a');
+ $attributeMetadata1->addGroup('b');
+
+ $attributeMetadata2 = new AttributeMetadata('a2');
+ $attributeMetadata2->addGroup('a');
+ $attributeMetadata2->addGroup('c');
+
+ $attributeMetadata1->merge($attributeMetadata2);
+
+ $this->assertEquals(array('a', 'b', 'c'), $attributeMetadata1->getGroups());
+ }
+}
diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/ClassMetadataTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/ClassMetadataTest.php
new file mode 100644
index 0000000000..90017580ac
--- /dev/null
+++ b/src/Symfony/Component/Serializer/Tests/Mapping/ClassMetadataTest.php
@@ -0,0 +1,65 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Serializer\Tests\Mapping;
+
+use Symfony\Component\Serializer\Mapping\ClassMetadata;
+
+/**
+ * @author Kévin Dunglas
+ */
+class ClassMetadataTest extends \PHPUnit_Framework_TestCase
+{
+ public function testInterface()
+ {
+ $classMetadata = new ClassMetadata('name');
+ $this->assertInstanceOf('Symfony\Component\Serializer\Mapping\ClassMetadataInterface', $classMetadata);
+ }
+
+ public function testAttributeMetadata()
+ {
+ $classMetadata = new ClassMetadata('c');
+
+ $a1 = $this->getMock('Symfony\Component\Serializer\Mapping\AttributeMetadataInterface');
+ $a1->method('getName')->willReturn('a1');
+
+ $a2 = $this->getMock('Symfony\Component\Serializer\Mapping\AttributeMetadataInterface');
+ $a2->method('getName')->willReturn('a2');
+
+ $classMetadata->addAttributeMetadata($a1);
+ $classMetadata->addAttributeMetadata($a2);
+
+ $this->assertEquals(array('a1' => $a1, 'a2' => $a2), $classMetadata->getAttributesMetadata());
+ }
+
+ public function testMerge()
+ {
+ $classMetadata1 = new ClassMetadata('c1');
+ $classMetadata2 = new ClassMetadata('c2');
+
+ $ac1 = $this->getMock('Symfony\Component\Serializer\Mapping\AttributeMetadataInterface');
+ $ac1->method('getName')->willReturn('a1');
+ $ac1->method('getGroups')->willReturn(array('a', 'b'));
+
+ $ac2 = $this->getMock('Symfony\Component\Serializer\Mapping\AttributeMetadataInterface');
+ $ac2->method('getName')->willReturn('a1');
+ $ac2->method('getGroups')->willReturn(array('b', 'c'));
+
+ $classMetadata1->addAttributeMetadata($ac1);
+ $classMetadata2->addAttributeMetadata($ac2);
+
+ $classMetadata1->merge($classMetadata2);
+
+ $ac1->method('getGroups')->willReturn('a', 'b', 'c');
+
+ $this->assertEquals(array('a1' => $ac1), $classMetadata2->getAttributesMetadata());
+ }
+}
diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Factory/ClassMetadataFactoryTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Factory/ClassMetadataFactoryTest.php
index 0573f2889a..2e2ba22dce 100644
--- a/src/Symfony/Component/Serializer/Tests/Mapping/Factory/ClassMetadataFactoryTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Mapping/Factory/ClassMetadataFactoryTest.php
@@ -14,21 +14,26 @@ namespace Symfony\Component\Serializer\Tests\Mapping\Factory;
use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
+use Symfony\Component\Serializer\Mapping\Loader\LoaderChain;
use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory;
-require_once __DIR__.'/../../../Annotation/Groups.php';
-
/**
* @author Kévin Dunglas
*/
class ClassMetadataFactoryTest extends \PHPUnit_Framework_TestCase
{
+ public function testInterface()
+ {
+ $classMetadata = new ClassMetadataFactory(new LoaderChain(array()));
+ $this->assertInstanceOf('Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory', $classMetadata);
+ }
+
public function testGetMetadataFor()
{
$factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
- $metadata = $factory->getMetadataFor('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
+ $classMetadata = $factory->getMetadataFor('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
- $this->assertEquals(TestClassMetadataFactory::createClassMetadata(true, true), $metadata);
+ $this->assertEquals(TestClassMetadataFactory::createClassMetadata(true, true), $classMetadata);
}
public function testHasMetadataFor()
diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php
index 8a34108cbd..484d062f22 100644
--- a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php
@@ -16,42 +16,51 @@ use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory;
-require_once __DIR__.'/../../../Annotation/Groups.php';
-
/**
* @author Kévin Dunglas
*/
class AnnotationLoaderTest extends \PHPUnit_Framework_TestCase
{
+ /**
+ * @var AnnotationLoader
+ */
+ private $loader;
+
+ protected function setUp()
+ {
+ $this->loader = new AnnotationLoader(new AnnotationReader());
+ }
+
+ public function testInterface()
+ {
+ $this->assertInstanceOf('Symfony\Component\Serializer\Mapping\Loader\LoaderInterface', $this->loader);
+ }
+
public function testLoadClassMetadataReturnsTrueIfSuccessful()
{
- $loader = new AnnotationLoader(new AnnotationReader());
- $metadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
+ $classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
- $this->assertTrue($loader->loadClassMetadata($metadata));
+ $this->assertTrue($this->loader->loadClassMetadata($classMetadata));
}
public function testLoadClassMetadata()
{
- $loader = new AnnotationLoader(new AnnotationReader());
- $metadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
+ $classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
+ $this->loader->loadClassMetadata($classMetadata);
- $loader->loadClassMetadata($metadata);
-
- $this->assertEquals(TestClassMetadataFactory::createClassMetadata(), $metadata);
+ $this->assertEquals(TestClassMetadataFactory::createClassMetadata(), $classMetadata);
}
public function testLoadClassMetadataAndMerge()
{
- $loader = new AnnotationLoader(new AnnotationReader());
- $metadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
- $parentMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummyParent');
+ $classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
+ $parentClassMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummyParent');
- $loader->loadClassMetadata($parentMetadata);
- $metadata->mergeAttributesGroups($parentMetadata);
+ $this->loader->loadClassMetadata($parentClassMetadata);
+ $classMetadata->merge($parentClassMetadata);
- $loader->loadClassMetadata($metadata);
+ $this->loader->loadClassMetadata($classMetadata);
- $this->assertEquals(TestClassMetadataFactory::createClassMetadata(true), $metadata);
+ $this->assertEquals(TestClassMetadataFactory::createClassMetadata(true), $classMetadata);
}
}
diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php
index 35888626e0..418e6b1538 100644
--- a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php
@@ -20,7 +20,13 @@ use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory;
*/
class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
{
+ /**
+ * @var XmlFileLoader
+ */
private $loader;
+ /**
+ * @var ClassMetadata
+ */
private $metadata;
public function setUp()
@@ -29,6 +35,11 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->metadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
}
+ public function testInterface()
+ {
+ $this->assertInstanceOf('Symfony\Component\Serializer\Mapping\Loader\LoaderInterface', $this->loader);
+ }
+
public function testLoadClassMetadataReturnsTrueIfSuccessful()
{
$this->assertTrue($this->loader->loadClassMetadata($this->metadata));
diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php
index ec6434cf1d..37c9bcbdf9 100644
--- a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php
@@ -20,7 +20,13 @@ use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory;
*/
class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
{
+ /**
+ * @var YamlFileLoader
+ */
private $loader;
+ /**
+ * @var ClassMetadata
+ */
private $metadata;
public function setUp()
@@ -29,6 +35,11 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->metadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
}
+ public function testInterface()
+ {
+ $this->assertInstanceOf('Symfony\Component\Serializer\Mapping\Loader\LoaderInterface', $this->loader);
+ }
+
public function testLoadClassMetadataReturnsTrueIfSuccessful()
{
$this->assertTrue($this->loader->loadClassMetadata($this->metadata));
diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/TestClassMetadataFactory.php b/src/Symfony/Component/Serializer/Tests/Mapping/TestClassMetadataFactory.php
index c253e72eaa..07b38c024b 100644
--- a/src/Symfony/Component/Serializer/Tests/Mapping/TestClassMetadataFactory.php
+++ b/src/Symfony/Component/Serializer/Tests/Mapping/TestClassMetadataFactory.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\Serializer\Tests\Mapping;
+use Symfony\Component\Serializer\Mapping\AttributeMetadata;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
/**
@@ -22,22 +23,38 @@ class TestClassMetadataFactory
{
$expected = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
+ $foo = new AttributeMetadata('foo');
+ $foo->addGroup('a');
+ $expected->addAttributeMetadata($foo);
+
+ $bar = new AttributeMetadata('bar');
+ $bar->addGroup('b');
+ $bar->addGroup('c');
+ $expected->addAttributeMetadata($bar);
+
+ $fooBar = new AttributeMetadata('fooBar');
+ $fooBar->addGroup('a');
+ $fooBar->addGroup('b');
+ $expected->addAttributeMetadata($fooBar);
+
+ $symfony = new AttributeMetadata('symfony');
+ $expected->addAttributeMetadata($symfony);
+
if ($withParent) {
- $expected->addAttributeGroup('kevin', 'a');
- $expected->addAttributeGroup('coopTilleuls', 'a');
- $expected->addAttributeGroup('coopTilleuls', 'b');
+ $kevin = new AttributeMetadata('kevin');
+ $kevin->addGroup('a');
+ $expected->addAttributeMetadata($kevin);
+
+ $coopTilleuls = new AttributeMetadata('coopTilleuls');
+ $coopTilleuls->addGroup('a');
+ $coopTilleuls->addGroup('b');
+ $expected->addAttributeMetadata($coopTilleuls);
}
if ($withInterface) {
- $expected->addAttributeGroup('symfony', 'a');
+ $symfony->addGroup('a');
}
- $expected->addAttributeGroup('foo', 'a');
- $expected->addAttributeGroup('bar', 'b');
- $expected->addAttributeGroup('bar', 'c');
- $expected->addAttributeGroup('fooBar', 'a');
- $expected->addAttributeGroup('fooBar', 'b');
-
// load reflection class so that the comparison passes
$expected->getReflectionClass();
@@ -47,9 +64,15 @@ class TestClassMetadataFactory
public static function createXmlCLassMetadata()
{
$expected = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');
- $expected->addAttributeGroup('foo', 'group1');
- $expected->addAttributeGroup('foo', 'group2');
- $expected->addAttributeGroup('bar', 'group2');
+
+ $foo = new AttributeMetadata('foo');
+ $foo->addGroup('group1');
+ $foo->addGroup('group2');
+ $expected->addAttributeMetadata($foo);
+
+ $bar = new AttributeMetadata('bar');
+ $bar->addGroup('group2');
+ $expected->addAttributeMetadata($bar);
return $expected;
}
diff --git a/src/Symfony/Component/Serializer/Tests/NameConverter/CamelCaseToSnakeCaseNameConverterTest.php b/src/Symfony/Component/Serializer/Tests/NameConverter/CamelCaseToSnakeCaseNameConverterTest.php
index 7d677181b3..2d57017340 100644
--- a/src/Symfony/Component/Serializer/Tests/NameConverter/CamelCaseToSnakeCaseNameConverterTest.php
+++ b/src/Symfony/Component/Serializer/Tests/NameConverter/CamelCaseToSnakeCaseNameConverterTest.php
@@ -18,6 +18,12 @@ use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter
*/
class CamelCaseToSnakeCaseNameConverterTest extends \PHPUnit_Framework_TestCase
{
+ public function testInterface()
+ {
+ $attributeMetadata = new CamelCaseToSnakeCaseNameConverter();
+ $this->assertInstanceOf('Symfony\Component\Serializer\NameConverter\NameConverterInterface', $attributeMetadata);
+ }
+
/**
* @dataProvider attributeProvider
*/
diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/CustomNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/CustomNormalizerTest.php
index 0aeaba472a..86ae003120 100644
--- a/src/Symfony/Component/Serializer/Tests/Normalizer/CustomNormalizerTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Normalizer/CustomNormalizerTest.php
@@ -17,12 +17,23 @@ use Symfony\Component\Serializer\Serializer;
class CustomNormalizerTest extends \PHPUnit_Framework_TestCase
{
+ /**
+ * @var CustomNormalizer
+ */
+ private $normalizer;
+
protected function setUp()
{
$this->normalizer = new CustomNormalizer();
$this->normalizer->setSerializer(new Serializer());
}
+ public function testInterface()
+ {
+ $this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\NormalizerInterface', $this->normalizer);
+ $this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\DenormalizerInterface', $this->normalizer);
+ }
+
public function testSerialize()
{
$obj = new ScalarDummy();
diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php
index bdfbfa0629..82854fa0a7 100644
--- a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php
@@ -40,6 +40,12 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
$this->normalizer->setSerializer($this->serializer);
}
+ public function testInterface()
+ {
+ $this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\NormalizerInterface', $this->normalizer);
+ $this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\DenormalizerInterface', $this->normalizer);
+ }
+
public function testNormalize()
{
$obj = new GetSetDummy();
@@ -426,6 +432,14 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('foo', $obj->getFoo());
$this->assertEquals('bar', $obj->getBar());
}
+
+ public function testDenormalizeNonExistingAttribute()
+ {
+ $this->assertEquals(
+ new PropertyDummy(),
+ $this->normalizer->denormalize(array('non_existing' => true), __NAMESPACE__.'\PropertyDummy')
+ );
+ }
}
class GetSetDummy
diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php
new file mode 100644
index 0000000000..e90209f357
--- /dev/null
+++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php
@@ -0,0 +1,497 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Serializer\Tests\Normalizer;
+
+use Doctrine\Common\Annotations\AnnotationReader;
+use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
+use Symfony\Component\Serializer\Serializer;
+use Symfony\Component\Serializer\SerializerInterface;
+use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
+use Symfony\Component\Serializer\Tests\Fixtures\CircularReferenceDummy;
+use Symfony\Component\Serializer\Tests\Fixtures\SiblingHolder;
+use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
+use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
+use Symfony\Component\Serializer\Tests\Fixtures\GroupDummy;
+
+/**
+ * @author Kévin Dunglas
+ */
+class ObjectNormalizerTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @var ObjectNormalizerTest
+ */
+ private $normalizer;
+ /**
+ * @var SerializerInterface
+ */
+ private $serializer;
+
+ protected function setUp()
+ {
+ $this->serializer = $this->getMock(__NAMESPACE__.'\ObjectSerializerNormalizer');
+ $this->normalizer = new ObjectNormalizer();
+ $this->normalizer->setSerializer($this->serializer);
+ }
+
+ public function testNormalize()
+ {
+ $obj = new ObjectDummy();
+ $object = new \stdClass();
+ $obj->setFoo('foo');
+ $obj->bar = 'bar';
+ $obj->setBaz(true);
+ $obj->setCamelCase('camelcase');
+ $obj->setObject($object);
+
+ $this->serializer
+ ->expects($this->once())
+ ->method('normalize')
+ ->with($object, 'any')
+ ->will($this->returnValue('string_object'))
+ ;
+
+ $this->assertEquals(
+ array(
+ 'foo' => 'foo',
+ 'bar' => 'bar',
+ 'baz' => true,
+ 'fooBar' => 'foobar',
+ 'camelCase' => 'camelcase',
+ 'object' => 'string_object',
+ ),
+ $this->normalizer->normalize($obj, 'any')
+ );
+ }
+
+ public function testDenormalize()
+ {
+ $obj = $this->normalizer->denormalize(
+ array('foo' => 'foo', 'bar' => 'bar', 'baz' => true, 'fooBar' => 'foobar'),
+ __NAMESPACE__.'\ObjectDummy',
+ 'any'
+ );
+ $this->assertEquals('foo', $obj->getFoo());
+ $this->assertEquals('bar', $obj->bar);
+ $this->assertTrue($obj->isBaz());
+ }
+
+ public function testDenormalizeWithObject()
+ {
+ $data = new \stdClass();
+ $data->foo = 'foo';
+ $data->bar = 'bar';
+ $data->fooBar = 'foobar';
+ $obj = $this->normalizer->denormalize($data, __NAMESPACE__.'\ObjectDummy', 'any');
+ $this->assertEquals('foo', $obj->getFoo());
+ $this->assertEquals('bar', $obj->bar);
+ }
+
+ public function testLegacyDenormalizeOnCamelCaseFormat()
+ {
+ $this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
+
+ $this->normalizer->setCamelizedAttributes(array('camel_case'));
+ $obj = $this->normalizer->denormalize(
+ array('camel_case' => 'camelCase'),
+ __NAMESPACE__.'\ObjectDummy'
+ );
+ $this->assertEquals('camelCase', $obj->getCamelCase());
+ }
+
+ public function testDenormalizeNull()
+ {
+ $this->assertEquals(new ObjectDummy(), $this->normalizer->denormalize(null, __NAMESPACE__.'\ObjectDummy'));
+ }
+
+ public function testConstructorDenormalize()
+ {
+ $obj = $this->normalizer->denormalize(
+ array('foo' => 'foo', 'bar' => 'bar', 'baz' => true, 'fooBar' => 'foobar'),
+ __NAMESPACE__.'\ObjectConstructorDummy', 'any');
+ $this->assertEquals('foo', $obj->getFoo());
+ $this->assertEquals('bar', $obj->bar);
+ $this->assertTrue($obj->isBaz());
+ }
+
+ public function testConstructorDenormalizeWithMissingOptionalArgument()
+ {
+ $obj = $this->normalizer->denormalize(
+ array('foo' => 'test', 'baz' => array(1, 2, 3)),
+ __NAMESPACE__.'\ObjectConstructorOptionalArgsDummy', 'any');
+ $this->assertEquals('test', $obj->getFoo());
+ $this->assertEquals(array(), $obj->bar);
+ $this->assertEquals(array(1, 2, 3), $obj->getBaz());
+ }
+
+ public function testConstructorWithObjectDenormalize()
+ {
+ $data = new \stdClass();
+ $data->foo = 'foo';
+ $data->bar = 'bar';
+ $data->baz = true;
+ $data->fooBar = 'foobar';
+ $obj = $this->normalizer->denormalize($data, __NAMESPACE__.'\ObjectConstructorDummy', 'any');
+ $this->assertEquals('foo', $obj->getFoo());
+ $this->assertEquals('bar', $obj->bar);
+ }
+
+ public function testGroupsNormalize()
+ {
+ $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
+ $this->normalizer = new ObjectNormalizer($classMetadataFactory);
+ $this->normalizer->setSerializer($this->serializer);
+
+ $obj = new GroupDummy();
+ $obj->setFoo('foo');
+ $obj->setBar('bar');
+ $obj->setFooBar('fooBar');
+ $obj->setSymfony('symfony');
+ $obj->setKevin('kevin');
+ $obj->setCoopTilleuls('coopTilleuls');
+
+ $this->assertEquals(array(
+ 'bar' => 'bar',
+ ), $this->normalizer->normalize($obj, null, array('groups' => array('c'))));
+
+ $this->assertEquals(array(
+ 'symfony' => 'symfony',
+ 'foo' => 'foo',
+ 'fooBar' => 'fooBar',
+ 'bar' => 'bar',
+ 'kevin' => 'kevin',
+ 'coopTilleuls' => 'coopTilleuls',
+ ), $this->normalizer->normalize($obj, null, array('groups' => array('a', 'c'))));
+ }
+
+ public function testGroupsDenormalize()
+ {
+ $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
+ $this->normalizer = new ObjectNormalizer($classMetadataFactory);
+ $this->normalizer->setSerializer($this->serializer);
+
+ $obj = new GroupDummy();
+ $obj->setFoo('foo');
+
+ $toNormalize = array('foo' => 'foo', 'bar' => 'bar');
+
+ $normalized = $this->normalizer->denormalize(
+ $toNormalize,
+ 'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy',
+ null,
+ array('groups' => array('a'))
+ );
+ $this->assertEquals($obj, $normalized);
+
+ $obj->setBar('bar');
+
+ $normalized = $this->normalizer->denormalize(
+ $toNormalize,
+ 'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy',
+ null,
+ array('groups' => array('a', 'b'))
+ );
+ $this->assertEquals($obj, $normalized);
+ }
+
+ /**
+ * @dataProvider provideCallbacks
+ */
+ public function testCallbacks($callbacks, $value, $result, $message)
+ {
+ $this->normalizer->setCallbacks($callbacks);
+
+ $obj = new ObjectConstructorDummy('', $value, true);
+
+ $this->assertEquals(
+ $result,
+ $this->normalizer->normalize($obj, 'any'),
+ $message
+ );
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testUncallableCallbacks()
+ {
+ $this->normalizer->setCallbacks(array('bar' => null));
+
+ $obj = new ObjectConstructorDummy('baz', 'quux', true);
+
+ $this->normalizer->normalize($obj, 'any');
+ }
+
+ public function testIgnoredAttributes()
+ {
+ $this->normalizer->setIgnoredAttributes(array('foo', 'bar', 'baz', 'camelCase', 'object'));
+
+ $obj = new ObjectDummy();
+ $obj->setFoo('foo');
+ $obj->bar = 'bar';
+ $obj->setBaz(true);
+
+ $this->assertEquals(
+ array('fooBar' => 'foobar'),
+ $this->normalizer->normalize($obj, 'any')
+ );
+ }
+
+ public function provideCallbacks()
+ {
+ return array(
+ array(
+ array(
+ 'bar' => function ($bar) {
+ return 'baz';
+ },
+ ),
+ 'baz',
+ array('foo' => '', 'bar' => 'baz', 'baz' => true),
+ 'Change a string',
+ ),
+ array(
+ array(
+ 'bar' => function ($bar) {
+ return;
+ },
+ ),
+ 'baz',
+ array('foo' => '', 'bar' => null, 'baz' => true),
+ 'Null an item',
+ ),
+ array(
+ array(
+ 'bar' => function ($bar) {
+ return $bar->format('d-m-Y H:i:s');
+ },
+ ),
+ new \DateTime('2011-09-10 06:30:00'),
+ array('foo' => '', 'bar' => '10-09-2011 06:30:00', 'baz' => true),
+ 'Format a date',
+ ),
+ array(
+ array(
+ 'bar' => function ($bars) {
+ $foos = '';
+ foreach ($bars as $bar) {
+ $foos .= $bar->getFoo();
+ }
+
+ return $foos;
+ },
+ ),
+ array(new ObjectConstructorDummy('baz', '', false), new ObjectConstructorDummy('quux', '', false)),
+ array('foo' => '', 'bar' => 'bazquux', 'baz' => true),
+ 'Collect a property',
+ ),
+ array(
+ array(
+ 'bar' => function ($bars) {
+ return count($bars);
+ },
+ ),
+ array(new ObjectConstructorDummy('baz', '', false), new ObjectConstructorDummy('quux', '', false)),
+ array('foo' => '', 'bar' => 2, 'baz' => true),
+ 'Count a property',
+ ),
+ );
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Serializer\Exception\LogicException
+ * @expectedExceptionMessage Cannot normalize attribute "object" because injected serializer is not a normalizer
+ */
+ public function testUnableToNormalizeObjectAttribute()
+ {
+ $serializer = $this->getMock('Symfony\Component\Serializer\SerializerInterface');
+ $this->normalizer->setSerializer($serializer);
+
+ $obj = new ObjectDummy();
+ $object = new \stdClass();
+ $obj->setObject($object);
+
+ $this->normalizer->normalize($obj, 'any');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Serializer\Exception\CircularReferenceException
+ */
+ public function testUnableToNormalizeCircularReference()
+ {
+ $serializer = new Serializer(array($this->normalizer));
+ $this->normalizer->setSerializer($serializer);
+ $this->normalizer->setCircularReferenceLimit(2);
+
+ $obj = new CircularReferenceDummy();
+
+ $this->normalizer->normalize($obj);
+ }
+
+ public function testSiblingReference()
+ {
+ $serializer = new Serializer(array($this->normalizer));
+ $this->normalizer->setSerializer($serializer);
+
+ $siblingHolder = new SiblingHolder();
+
+ $expected = array(
+ 'sibling0' => array('coopTilleuls' => 'Les-Tilleuls.coop'),
+ 'sibling1' => array('coopTilleuls' => 'Les-Tilleuls.coop'),
+ 'sibling2' => array('coopTilleuls' => 'Les-Tilleuls.coop'),
+ );
+ $this->assertEquals($expected, $this->normalizer->normalize($siblingHolder));
+ }
+
+ public function testCircularReferenceHandler()
+ {
+ $serializer = new Serializer(array($this->normalizer));
+ $this->normalizer->setSerializer($serializer);
+ $this->normalizer->setCircularReferenceHandler(function ($obj) {
+ return get_class($obj);
+ });
+
+ $obj = new CircularReferenceDummy();
+
+ $expected = array('me' => 'Symfony\Component\Serializer\Tests\Fixtures\CircularReferenceDummy');
+ $this->assertEquals($expected, $this->normalizer->normalize($obj));
+ }
+
+ public function testDenormalizeNonExistingAttribute()
+ {
+ $this->assertEquals(
+ new ObjectDummy(),
+ $this->normalizer->denormalize(array('non_existing' => true), __NAMESPACE__.'\ObjectDummy')
+ );
+ }
+}
+
+class ObjectDummy
+{
+ protected $foo;
+ public $bar;
+ private $baz;
+ protected $camelCase;
+ protected $object;
+
+ public function getFoo()
+ {
+ return $this->foo;
+ }
+
+ public function setFoo($foo)
+ {
+ $this->foo = $foo;
+ }
+
+ public function isBaz()
+ {
+ return $this->baz;
+ }
+
+ public function setBaz($baz)
+ {
+ $this->baz = $baz;
+ }
+
+ public function getFooBar()
+ {
+ return $this->foo.$this->bar;
+ }
+
+ public function getCamelCase()
+ {
+ return $this->camelCase;
+ }
+
+ public function setCamelCase($camelCase)
+ {
+ $this->camelCase = $camelCase;
+ }
+
+ public function otherMethod()
+ {
+ throw new \RuntimeException("Dummy::otherMethod() should not be called");
+ }
+
+ public function setObject($object)
+ {
+ $this->object = $object;
+ }
+
+ public function getObject()
+ {
+ return $this->object;
+ }
+}
+
+class ObjectConstructorDummy
+{
+ protected $foo;
+ public $bar;
+ private $baz;
+
+ public function __construct($foo, $bar, $baz)
+ {
+ $this->foo = $foo;
+ $this->bar = $bar;
+ $this->baz = $baz;
+ }
+
+ public function getFoo()
+ {
+ return $this->foo;
+ }
+
+ public function isBaz()
+ {
+ return $this->baz;
+ }
+
+ public function otherMethod()
+ {
+ throw new \RuntimeException("Dummy::otherMethod() should not be called");
+ }
+}
+
+abstract class ObjectSerializerNormalizer implements SerializerInterface, NormalizerInterface
+{
+}
+
+class ObjectConstructorOptionalArgsDummy
+{
+ protected $foo;
+ public $bar;
+ private $baz;
+
+ public function __construct($foo, $bar = array(), $baz = array())
+ {
+ $this->foo = $foo;
+ $this->bar = $bar;
+ $this->baz = $baz;
+ }
+
+ public function getFoo()
+ {
+ return $this->foo;
+ }
+
+ public function getBaz()
+ {
+ return $this->baz;
+ }
+
+ public function otherMethod()
+ {
+ throw new \RuntimeException("Dummy::otherMethod() should not be called");
+ }
+}
diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php
index 68266a7bcd..95e469f147 100644
--- a/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php
@@ -333,6 +333,14 @@ class PropertyNormalizerTest extends \PHPUnit_Framework_TestCase
$expected = array('me' => 'Symfony\Component\Serializer\Tests\Fixtures\PropertyCircularReferenceDummy');
$this->assertEquals($expected, $this->normalizer->normalize($obj));
}
+
+ public function testDenormalizeNonExistingAttribute()
+ {
+ $this->assertEquals(
+ new PropertyDummy(),
+ $this->normalizer->denormalize(array('non_existing' => true), __NAMESPACE__.'\PropertyDummy')
+ );
+ }
}
class PropertyDummy
diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php
index 3a6b1ae4c5..68f70fcfe5 100644
--- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php
+++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php
@@ -22,6 +22,17 @@ use Symfony\Component\Serializer\Tests\Normalizer\TestDenormalizer;
class SerializerTest extends \PHPUnit_Framework_TestCase
{
+ public function testInterface()
+ {
+ $serializer = new Serializer();
+
+ $this->assertInstanceOf('Symfony\Component\Serializer\SerializerInterface', $serializer);
+ $this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\NormalizerInterface', $serializer);
+ $this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\DenormalizerInterface', $serializer);
+ $this->assertInstanceOf('Symfony\Component\Serializer\Encoder\EncoderInterface', $serializer);
+ $this->assertInstanceOf('Symfony\Component\Serializer\Encoder\DecoderInterface', $serializer);
+ }
+
/**
* @expectedException \Symfony\Component\Serializer\Exception\UnexpectedValueException
*/
diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json
index 0944c69731..13ee4bdcc0 100644
--- a/src/Symfony/Component/Serializer/composer.json
+++ b/src/Symfony/Component/Serializer/composer.json
@@ -22,6 +22,7 @@
"symfony/phpunit-bridge": "~2.7|~3.0",
"symfony/yaml": "~2.7|~3.0",
"symfony/config": "~2.7|~3.0",
+ "symfony/property-access": "~2.7|~3.0.0",
"doctrine/annotations": "~1.0",
"doctrine/cache": "~1.0"
},
@@ -29,7 +30,8 @@
"doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.",
"doctrine/cache": "For using the default cached annotation reader and metadata cache.",
"symfony/yaml": "For using the default YAML mapping loader.",
- "symfony/config": "For using the XML mapping loader."
+ "symfony/config": "For using the XML mapping loader.",
+ "symfony/property-access": "For using the ObjectNormalizer."
},
"autoload": {
"psr-0": { "Symfony\\Component\\Serializer\\": "" }
diff --git a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php
index aa60c438d7..c3a50cd8bb 100644
--- a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php
+++ b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php
@@ -171,7 +171,7 @@ class XliffFileLoader implements LoaderInterface
LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
$error->code,
trim($error->message),
- $error->file ? $error->file : 'n/a',
+ $error->file ?: 'n/a',
$error->line,
$error->column
);
diff --git a/src/Symfony/Component/Validator/Mapping/Loader/AbstractLoader.php b/src/Symfony/Component/Validator/Mapping/Loader/AbstractLoader.php
index 321f1ea439..2ae89886c8 100644
--- a/src/Symfony/Component/Validator/Mapping/Loader/AbstractLoader.php
+++ b/src/Symfony/Component/Validator/Mapping/Loader/AbstractLoader.php
@@ -58,13 +58,13 @@ abstract class AbstractLoader implements LoaderInterface
/**
* Creates a new constraint instance for the given constraint name.
*
- * @param string $name The constraint name. Either a constraint relative
- * to the default constraint namespace, or a fully
- * qualified class name. Alternatively, the constraint
- * may be preceded by a namespace alias and a colon.
- * The namespace alias must have been defined using
- * {@link addNamespaceAlias()}.
- * @param mixed $options The constraint options
+ * @param string $name The constraint name. Either a constraint relative
+ * to the default constraint namespace, or a fully
+ * qualified class name. Alternatively, the constraint
+ * may be preceded by a namespace alias and a colon.
+ * The namespace alias must have been defined using
+ * {@link addNamespaceAlias()}.
+ * @param mixed $options The constraint options
*
* @return Constraint
*
diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf
index fb00dd5d93..1d0cc13c86 100644
--- a/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf
+++ b/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf
@@ -278,6 +278,38 @@
This value should not be identical to {{ compared_value_type }} {{ compared_value }}.
Este valor non debería ser identico a {{ compared_value_type }} {{ compared_value }}.
+
+ The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}.
+ A proporción da imaxe é demasiado grande ({{ ratio }}). A proporción máxima permitida é {{ max_ratio }}.
+
+
+ The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}.
+ A proporción da é demasiado pequena ({{ ratio }}). A proporción mínima permitida é {{ min_ratio }}.
+
+
+ The image is square ({{ width }}x{{ height }}px). Square images are not allowed.
+ A imaxe é cadrada ({{ width }}x{{ height }}px). As imáxenes cadradas non están permitidas.
+
+
+ The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed.
+ A imaxe está orientada horizontalmente ({{ width }}x{{ height }}px). As imáxenes orientadas horizontalmente non están permitidas.
+
+
+ The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed.
+ A imaxe está orientada verticalmente ({{ width }}x{{ height }}px). As imáxenes orientadas verticalmente non están permitidas.
+
+
+ An empty file is not allowed.
+ Non está permitido un arquivo baleiro.
+
+
+ The host could not be resolved.
+ Non se puido resolver o host.
+
+
+ This value does not match the expected {{ charset }} charset.
+ A codificación de caracteres para este valor debería ser {{ charset }}.
+