From 677d8208747282e484be8dfc299ca962565addc2 Mon Sep 17 00:00:00 2001 From: Jakub Zalas Date: Fri, 30 Dec 2016 12:55:25 +0000 Subject: [PATCH 01/11] Fix the condition checking the minimum ICU version --- src/Symfony/Component/Intl/Util/IntlTestHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Intl/Util/IntlTestHelper.php b/src/Symfony/Component/Intl/Util/IntlTestHelper.php index 71fb4acdd7..c1e37a72ec 100644 --- a/src/Symfony/Component/Intl/Util/IntlTestHelper.php +++ b/src/Symfony/Component/Intl/Util/IntlTestHelper.php @@ -41,7 +41,7 @@ class IntlTestHelper // * the intl extension is loaded with version Intl::getIcuStubVersion() // * the intl extension is not loaded - if (($minimumIcuVersion || defined('HHVM_VERSION_ID')) && IcuVersion::compare(Intl::getIcuVersion(), $minimumIcuVersion, '!=', 1)) { + if (($minimumIcuVersion || defined('HHVM_VERSION_ID')) && IcuVersion::compare(Intl::getIcuVersion(), $minimumIcuVersion, '<', 1)) { $testCase->markTestSkipped('ICU version '.$minimumIcuVersion.' is required.'); } From d3b5d8efdf4b936006d52bc6f6d266af1691708e Mon Sep 17 00:00:00 2001 From: Jakub Zalas Date: Fri, 30 Dec 2016 13:41:01 +0000 Subject: [PATCH 02/11] Fix tests with ICU 57.1 --- ...ntegerToLocalizedStringTransformerTest.php | 4 ++-- ...NumberToLocalizedStringTransformerTest.php | 8 +++---- .../Extension/Core/Type/DateTypeTest.php | 24 +++++++++---------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php index b38296813c..af6443ac84 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php @@ -108,10 +108,10 @@ class IntegerToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCas public function testReverseTransformWithGrouping() { - // Since we test against "de_AT", we need the full implementation + // Since we test against "de_DE", we need the full implementation IntlTestHelper::requireFullIntl($this, false); - \Locale::setDefault('de_AT'); + \Locale::setDefault('de_DE'); $transformer = new IntegerToLocalizedStringTransformer(null, true); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php index 90d9719a90..c619ca729f 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php @@ -54,8 +54,8 @@ class NumberToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase public function provideTransformationsWithGrouping() { return array( - array(1234.5, '1.234,5', 'de_AT'), - array(12345.912, '12.345,912', 'de_AT'), + array(1234.5, '1.234,5', 'de_DE'), + array(12345.912, '12.345,912', 'de_DE'), array(1234.5, '1 234,5', 'fr'), array(1234.5, '1 234,5', 'ru'), array(1234.5, '1 234,5', 'fi'), @@ -410,10 +410,10 @@ class NumberToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase */ public function testDecimalSeparatorMayNotBeDotIfGroupingSeparatorIsDotWithNoGroupSep() { - // Since we test against "de_AT", we need the full implementation + // Since we test against "de_DE", we need the full implementation IntlTestHelper::requireFullIntl($this, false); - \Locale::setDefault('de_AT'); + \Locale::setDefault('de_DE'); $transformer = new NumberToLocalizedStringTransformer(null, true); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php index ea302d020e..2b1c9bc198 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php @@ -69,10 +69,10 @@ class DateTypeTest extends TestCase public function testSubmitFromSingleTextDateTime() { - // we test against "de_AT", so we need the full implementation + // we test against "de_DE", so we need the full implementation IntlTestHelper::requireFullIntl($this, false); - \Locale::setDefault('de_AT'); + \Locale::setDefault('de_DE'); $form = $this->factory->create('date', null, array( 'format' => \IntlDateFormatter::MEDIUM, @@ -90,10 +90,10 @@ class DateTypeTest extends TestCase public function testSubmitFromSingleTextString() { - // we test against "de_AT", so we need the full implementation + // we test against "de_DE", so we need the full implementation IntlTestHelper::requireFullIntl($this, false); - \Locale::setDefault('de_AT'); + \Locale::setDefault('de_DE'); $form = $this->factory->create('date', null, array( 'format' => \IntlDateFormatter::MEDIUM, @@ -111,10 +111,10 @@ class DateTypeTest extends TestCase public function testSubmitFromSingleTextTimestamp() { - // we test against "de_AT", so we need the full implementation + // we test against "de_DE", so we need the full implementation IntlTestHelper::requireFullIntl($this, false); - \Locale::setDefault('de_AT'); + \Locale::setDefault('de_DE'); $form = $this->factory->create('date', null, array( 'format' => \IntlDateFormatter::MEDIUM, @@ -134,10 +134,10 @@ class DateTypeTest extends TestCase public function testSubmitFromSingleTextRaw() { - // we test against "de_AT", so we need the full implementation + // we test against "de_DE", so we need the full implementation IntlTestHelper::requireFullIntl($this, false); - \Locale::setDefault('de_AT'); + \Locale::setDefault('de_DE'); $form = $this->factory->create('date', null, array( 'format' => \IntlDateFormatter::MEDIUM, @@ -398,10 +398,10 @@ class DateTypeTest extends TestCase public function testSetDataWithNegativeTimezoneOffsetStringInput() { - // we test against "de_AT", so we need the full implementation + // we test against "de_DE", so we need the full implementation IntlTestHelper::requireFullIntl($this, false); - \Locale::setDefault('de_AT'); + \Locale::setDefault('de_DE'); $form = $this->factory->create('date', null, array( 'format' => \IntlDateFormatter::MEDIUM, @@ -420,10 +420,10 @@ class DateTypeTest extends TestCase public function testSetDataWithNegativeTimezoneOffsetDateTimeInput() { - // we test against "de_AT", so we need the full implementation + // we test against "de_DE", so we need the full implementation IntlTestHelper::requireFullIntl($this, false); - \Locale::setDefault('de_AT'); + \Locale::setDefault('de_DE'); $form = $this->factory->create('date', null, array( 'format' => \IntlDateFormatter::MEDIUM, From 1d298f0417652ce5dac2e0e4756d5c244be57046 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Mon, 23 Jan 2017 17:37:56 +0100 Subject: [PATCH 03/11] [Routing] Fix BC break in AnnotationClassLoader defaults attributes handling --- composer.json | 3 +- .../Functional/AnnotatedControllerTest.php | 39 ++++++++++++++ .../Controller/AnnotatedController.php | 51 +++++++++++++++++++ .../app/AnnotatedController/bundles.php | 20 ++++++++ .../app/AnnotatedController/config.yml | 2 + .../app/AnnotatedController/routing.yml | 4 ++ .../Bundle/FrameworkBundle/composer.json | 5 +- .../Routing/Loader/AnnotationClassLoader.php | 5 ++ 8 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AnnotatedControllerTest.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/AnnotatedController.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AnnotatedController/bundles.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AnnotatedController/config.yml create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AnnotatedController/routing.yml diff --git a/composer.json b/composer.json index 34dfa896e3..2d7b451159 100644 --- a/composer.json +++ b/composer.json @@ -79,7 +79,8 @@ "ircmaxell/password-compat": "~1.0", "ocramius/proxy-manager": "~0.4|~1.0|~2.0", "symfony/phpunit-bridge": "~3.2", - "egulias/email-validator": "~1.2,>=1.2.1" + "egulias/email-validator": "~1.2,>=1.2.1", + "sensio/framework-extra-bundle": "^3.0.2" }, "autoload": { "psr-4": { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AnnotatedControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AnnotatedControllerTest.php new file mode 100644 index 0000000000..2fdbef8839 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AnnotatedControllerTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +class AnnotatedControllerTest extends WebTestCase +{ + /** + * @dataProvider getRoutes + */ + public function testAnnotatedController($path, $expectedValue) + { + $client = $this->createClient(array('test_case' => 'AnnotatedController', 'root_config' => 'config.yml')); + $client->request('GET', '/annotated'.$path); + + $this->assertSame(200, $client->getResponse()->getStatusCode()); + $this->assertSame($expectedValue, $client->getResponse()->getContent()); + } + + public function getRoutes() + { + return array( + array('/null_request', 'Symfony\Component\HttpFoundation\Request'), + array('/null_argument', ''), + array('/null_argument_with_route_param', ''), + array('/null_argument_with_route_param/value', 'value'), + array('/argument_with_route_param_and_default', 'value'), + array('/argument_with_route_param_and_default/custom', 'custom'), + ); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/AnnotatedController.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/AnnotatedController.php new file mode 100644 index 0000000000..98a3ace982 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/AnnotatedController.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Annotation\Route; + +class AnnotatedController +{ + /** + * @Route("/null_request", name="null_request") + */ + public function requestDefaultNullAction(Request $request = null) + { + return new Response($request ? get_class($request) : null); + } + + /** + * @Route("/null_argument", name="null_argument") + */ + public function argumentDefaultNullWithoutRouteParamAction($value = null) + { + return new Response($value); + } + + /** + * @Route("/null_argument_with_route_param/{value}", name="null_argument_with_route_param") + */ + public function argumentDefaultNullWithRouteParamAction($value = null) + { + return new Response($value); + } + + /** + * @Route("/argument_with_route_param_and_default/{value}", defaults={"value": "value"}, name="argument_with_route_param_and_default") + */ + public function argumentWithoutDefaultWithRouteParamAndDefaultAction($value) + { + return new Response($value); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AnnotatedController/bundles.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AnnotatedController/bundles.php new file mode 100644 index 0000000000..f3290d7728 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AnnotatedController/bundles.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle; + +return array( + new FrameworkBundle(), + new TestBundle(), + new SensioFrameworkExtraBundle(), +); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AnnotatedController/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AnnotatedController/config.yml new file mode 100644 index 0000000000..377d3e7852 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AnnotatedController/config.yml @@ -0,0 +1,2 @@ +imports: + - { resource: ../config/default.yml } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AnnotatedController/routing.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AnnotatedController/routing.yml new file mode 100644 index 0000000000..ebd18a0a4c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AnnotatedController/routing.yml @@ -0,0 +1,4 @@ +annotated_controller: + prefix: /annotated + resource: "@TestBundle/Controller/AnnotatedController.php" + type: annotation diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 63c43e0035..5f0c6ce2e1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -25,7 +25,7 @@ "symfony/http-foundation": "~2.7", "symfony/http-kernel": "~2.7.23|~2.8.16", "symfony/filesystem": "~2.3", - "symfony/routing": "~2.6,>2.6.4", + "symfony/routing": "~2.7.24|~2.8.17", "symfony/security-core": "~2.6.13|~2.7.9|~2.8", "symfony/security-csrf": "~2.6", "symfony/stopwatch": "~2.3", @@ -45,7 +45,8 @@ "symfony/expression-language": "~2.6", "symfony/process": "~2.0,>=2.0.5", "symfony/validator": "~2.5", - "symfony/yaml": "~2.0,>=2.0.5" + "symfony/yaml": "~2.0,>=2.0.5", + "sensio/framework-extra-bundle": "^3.0.2" }, "suggest": { "symfony/console": "For using the console commands", diff --git a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php index 339529e519..62ad9c3f9f 100644 --- a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php +++ b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php @@ -138,6 +138,11 @@ abstract class AnnotationClassLoader implements LoaderInterface } $defaults = array_replace($globals['defaults'], $annot->getDefaults()); + foreach ($method->getParameters() as $param) { + if (false !== strpos($globals['path'].$annot->getPath(), sprintf('{%s}', $param->getName())) && !isset($defaults[$param->getName()]) && $param->isDefaultValueAvailable()) { + $defaults[$param->getName()] = $param->getDefaultValue(); + } + } $requirements = array_replace($globals['requirements'], $annot->getRequirements()); $options = array_replace($globals['options'], $annot->getOptions()); $schemes = array_merge($globals['schemes'], $annot->getSchemes()); From 83cad14612b39ee53a6e72f947770f68b6fc8d56 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 24 Jan 2017 11:10:56 +0100 Subject: [PATCH 04/11] [Debug] Remove $context arg from handleError(), preparing for PHP 7.2 --- src/Symfony/Component/Debug/ErrorHandler.php | 21 +++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index 8c664c54f9..1e415604c9 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -349,12 +349,10 @@ class ErrorHandler /** * Handles errors by filtering then logging them according to the configured bit fields. * - * @param int $type One of the E_* constants + * @param int $type One of the E_* constants * @param string $message * @param string $file * @param int $line - * @param array $context - * @param array $backtrace * * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself * @@ -362,7 +360,7 @@ class ErrorHandler * * @internal */ - public function handleError($type, $message, $file, $line, array $context, array $backtrace = null) + public function handleError($type, $message, $file, $line) { $level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED; $log = $this->loggedErrors & $type; @@ -372,8 +370,17 @@ class ErrorHandler if (!$type || (!$log && !$throw)) { return $type && $log; } + $scope = $this->scopedErrors & $type; - if (isset($context['GLOBALS']) && ($this->scopedErrors & $type)) { + if (4 < $numArgs = func_num_args()) { + $context = $scope ? func_get_arg(4) : array(); + $backtrace = 5 < $numArgs ? func_get_arg(5) : null; // defined on HHVM + } else { + $context = array(); + $backtrace = null; + } + + if (isset($context['GLOBALS']) && $scope) { $e = $context; // Whatever the signature of the method, unset($e['GLOBALS'], $context); // $context is always a reference in 5.3 $context = $e; @@ -389,7 +396,7 @@ class ErrorHandler } if ($throw) { - if (($this->scopedErrors & $type) && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) { + if ($scope && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) { // Checking for class existence is a work around for https://bugs.php.net/42098 $throw = new ContextErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line, $context); } else { @@ -420,7 +427,7 @@ class ErrorHandler $e = compact('type', 'file', 'line', 'level'); if ($type & $level) { - if ($this->scopedErrors & $type) { + if ($scope) { $e['scope_vars'] = $context; if ($trace) { $e['stack'] = $backtrace ?: debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT); From 2555f3151d2d8fd452d30ad5815b0e8c0bfd3372 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 25 Jan 2017 13:11:45 +0100 Subject: [PATCH 05/11] [Debug] Workaround "null" $context --- src/Symfony/Component/Debug/ErrorHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index 1e415604c9..78dfba19b1 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -373,7 +373,7 @@ class ErrorHandler $scope = $this->scopedErrors & $type; if (4 < $numArgs = func_num_args()) { - $context = $scope ? func_get_arg(4) : array(); + $context = $scope ? (func_get_arg(4) ?: array()) : array(); $backtrace = 5 < $numArgs ? func_get_arg(5) : null; // defined on HHVM } else { $context = array(); From 89e27240ab9a95ddc3d26280e7d480ea50d77b35 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 25 Jan 2017 13:10:29 +0100 Subject: [PATCH 06/11] [DI] Fix defaults overriding empty strings in AutowirePass --- .../Compiler/AutowirePass.php | 6 ++++-- .../Tests/Compiler/AutowirePassTest.php | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index cd5b61b290..09a237caad 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -95,8 +95,10 @@ class AutowirePass implements CompilerPassInterface throw new RuntimeException(sprintf('Unable to autowire argument index %d ($%s) for the service "%s". If this is an object, give it a type-hint. Otherwise, specify this argument\'s value explicitly.', $index, $parameter->name, $id)); } - // specifically pass the default value - $arguments[$index] = $parameter->getDefaultValue(); + if (!array_key_exists($index, $arguments)) { + // specifically pass the default value + $arguments[$index] = $parameter->getDefaultValue(); + } continue; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index c9445c01dc..2d34cbff1d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -443,6 +443,22 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase $this->assertTrue($container->hasDefinition('bar')); } + + public function testEmptyStringIsKept() + { + $container = new ContainerBuilder(); + + $container->register('a', __NAMESPACE__.'\A'); + $container->register('lille', __NAMESPACE__.'\Lille'); + $container->register('foo', __NAMESPACE__.'\MultipleArgumentsOptionalScalar') + ->setAutowired(true) + ->setArguments(array('', '')); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertEquals(array(new Reference('a'), '', new Reference('lille')), $container->getDefinition('foo')->getArguments()); + } } class Foo From be52b390315e62787179b30a5d95f82519744309 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Fri, 20 Jan 2017 15:18:09 +0100 Subject: [PATCH 07/11] [PropertyAccess] Handle interfaces in the invalid argument exception --- .../PropertyAccess/PropertyAccessor.php | 2 +- .../Tests/Fixtures/TypeHinted.php | 21 +++++++++++++++++++ .../Tests/PropertyAccessorTest.php | 11 ++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index da0c5da93c..d7ccc46438 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -246,7 +246,7 @@ class PropertyAccessor implements PropertyAccessorInterface private static function throwInvalidArgumentException($message, $trace, $i) { if (isset($trace[$i]['file']) && __FILE__ === $trace[$i]['file']) { - $pos = strpos($message, $delim = 'must be of the type ') ?: strpos($message, $delim = 'must be an instance of '); + $pos = strpos($message, $delim = 'must be of the type ') ?: (strpos($message, $delim = 'must be an instance of ') ?: strpos($message, $delim = 'must implement interface ')); $pos += strlen($delim); $type = $trace[$i]['args'][0]; $type = is_object($type) ? get_class($type) : gettype($type); diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TypeHinted.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TypeHinted.php index ca4c5745ae..ce0f3d89aa 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TypeHinted.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TypeHinted.php @@ -18,6 +18,11 @@ class TypeHinted { private $date; + /** + * @var \Countable + */ + private $countable; + public function setDate(\DateTime $date) { $this->date = $date; @@ -27,4 +32,20 @@ class TypeHinted { return $this->date; } + + /** + * @return \Countable + */ + public function getCountable() + { + return $this->countable; + } + + /** + * @param \Countable $countable + */ + public function setCountable(\Countable $countable) + { + $this->countable = $countable; + } } diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index 3f1ef1c040..a7e142e878 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -554,4 +554,15 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase $this->assertSame('baz', $this->propertyAccessor->getValue($object, 'publicAccessor[value2]')); $this->assertSame(array('value1' => 'foo', 'value2' => 'baz'), $object->getPublicAccessor()); } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException + * @expectedExceptionMessage Expected argument of type "Countable", "string" given + */ + public function testThrowTypeErrorWithInterface() + { + $object = new TypeHinted(); + + $this->propertyAccessor->setValue($object, 'countable', 'This is a string, \Countable expected.'); + } } From 428814b25ddd7b0bfbf56d0f4cc3879c3e9bc793 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 26 Jan 2017 11:48:39 +0100 Subject: [PATCH 08/11] clarify exception when no args are configured --- .../Component/DependencyInjection/Definition.php | 4 ++++ .../DependencyInjection/Tests/DefinitionTest.php | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index 4f2ee304ad..49e4e1b0e2 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -302,6 +302,10 @@ class Definition */ public function replaceArgument($index, $argument) { + if (0 === count($this->arguments)) { + throw new OutOfBoundsException('Cannot replace arguments if none have been configured yet.'); + } + if ($index < 0 || $index > count($this->arguments) - 1) { throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, count($this->arguments) - 1)); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php index 3439a59123..0d3cb8a000 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php @@ -232,6 +232,7 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase /** * @expectedException \OutOfBoundsException + * @expectedExceptionMessage The index "1" is not in the range [0, 0]. */ public function testReplaceArgumentShouldCheckBounds() { @@ -241,6 +242,16 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase $def->replaceArgument(1, 'bar'); } + /** + * @expectedException \OutOfBoundsException + * @expectedExceptionMessage Cannot replace arguments if none have been configured yet. + */ + public function testReplaceArgumentWithoutExistingArgumentsShouldCheckBounds() + { + $def = new Definition('stdClass'); + $def->replaceArgument(0, 'bar'); + } + public function testSetGetProperties() { $def = new Definition('stdClass'); From 1e3421d6f03325ef805f5d11c714982b70d6c06f Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 27 Jan 2017 10:10:43 +0100 Subject: [PATCH 09/11] always check for all fields to be mapped --- .../Constraints/UniqueEntityValidatorTest.php | 17 +++++++++++++++++ .../Constraints/UniqueEntityValidator.php | 14 +++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php index 348b46ddaa..39a251c33d 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php @@ -244,6 +244,23 @@ class UniqueEntityValidatorTest extends AbstractConstraintValidatorTest ->assertRaised(); } + /** + * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException + */ + public function testAllConfiguredFieldsAreCheckedOfBeingMappedByDoctrineWithIgnoreNullEnabled() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('name', 'name2'), + 'em' => self::EM_NAME, + 'ignoreNull' => true, + )); + + $entity1 = new SingleIntIdEntity(1, null); + + $this->validator->validate($entity1, $constraint); + } + public function testValidateUniquenessWithValidCustomErrorPath() { $constraint = new UniqueEntity(array( diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index f4c8671aba..61aef64ed8 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -85,12 +85,14 @@ class UniqueEntityValidator extends ConstraintValidator throw new ConstraintDefinitionException(sprintf('The field "%s" is not mapped by Doctrine, so it cannot be validated for uniqueness.', $fieldName)); } - $criteria[$fieldName] = $class->reflFields[$fieldName]->getValue($entity); + $fieldValue = $class->reflFields[$fieldName]->getValue($entity); - if ($constraint->ignoreNull && null === $criteria[$fieldName]) { - return; + if ($constraint->ignoreNull && null === $fieldValue) { + continue; } + $criteria[$fieldName] = $fieldValue; + if (null !== $criteria[$fieldName] && $class->hasAssociation($fieldName)) { /* Ensure the Proxy is initialized before using reflection to * read its identifiers. This is necessary because the wrapped @@ -100,6 +102,12 @@ class UniqueEntityValidator extends ConstraintValidator } } + // skip validation if there are no criteria (this can happen when the + // "ignoreNull" option is enabled and fields to be checked are null + if (empty($criteria)) { + return; + } + $repository = $em->getRepository(get_class($entity)); $result = $repository->{$constraint->repositoryMethod}($criteria); From 4b5930ca442a68434432de28bc6d1a83224a55e1 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 27 Jan 2017 16:19:36 -0800 Subject: [PATCH 10/11] fixed composer.json --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index cdb4f684f4..d661f1b03e 100644 --- a/composer.json +++ b/composer.json @@ -87,11 +87,11 @@ "ocramius/proxy-manager": "~0.4|~1.0|~2.0", "symfony/phpunit-bridge": "~3.2", "egulias/email-validator": "~1.2,>=1.2.1", - "phpdocumentor/reflection": "^1.0.7" + "phpdocumentor/reflection": "^1.0.7", + "sensio/framework-extra-bundle": "^3.0.2" }, "conflict": { "phpdocumentor/reflection": "<1.0.7", - "sensio/framework-extra-bundle": "^3.0.2" }, "autoload": { "psr-4": { From 537b932302603865dd74aa18fa997338c7ed99c9 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 27 Jan 2017 16:27:58 -0800 Subject: [PATCH 11/11] fixed typo --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 6e3588482a..cf80b321ac 100644 --- a/composer.json +++ b/composer.json @@ -94,7 +94,7 @@ }, "conflict": { "phpdocumentor/reflection-docblock": "<3.0", - "phpdocumentor/type-resolver": "<0.2.0", + "phpdocumentor/type-resolver": "<0.2.0" }, "provide": { "psr/cache-implementation": "1.0"