From 8c4a59467f4e4139f9e0a79d89b81cfe29dfa140 Mon Sep 17 00:00:00 2001 From: Alexander Pasichnick Date: Tue, 20 Feb 2018 19:48:33 +0300 Subject: [PATCH 01/31] [BrowserKit] fixed BC Break for HTTP_HOST header; implemented same behaviour for HTTPS server parameter --- src/Symfony/Component/BrowserKit/Client.php | 8 ++++++- .../Component/BrowserKit/Tests/ClientTest.php | 21 +++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/BrowserKit/Client.php b/src/Symfony/Component/BrowserKit/Client.php index 675a608d8dccf..2ab699be8834c 100644 --- a/src/Symfony/Component/BrowserKit/Client.php +++ b/src/Symfony/Component/BrowserKit/Client.php @@ -259,11 +259,17 @@ public function request($method, $uri, array $parameters = array(), array $files ++$this->redirectCount; } + $originalUri = $uri; + $uri = $this->getAbsoluteUri($uri); $server = array_merge($this->server, $server); - if (isset($server['HTTPS'])) { + if (!empty($server['HTTP_HOST']) && null === parse_url($originalUri, PHP_URL_HOST)) { + $uri = preg_replace('{^(https?\://)'.preg_quote($this->extractHost($uri)).'}', '${1}'.$server['HTTP_HOST'], $uri); + } + + if (isset($server['HTTPS']) && null === parse_url($originalUri, PHP_URL_SCHEME)) { $uri = preg_replace('{^'.parse_url($uri, PHP_URL_SCHEME).'}', $server['HTTPS'] ? 'https' : 'http', $uri); } diff --git a/src/Symfony/Component/BrowserKit/Tests/ClientTest.php b/src/Symfony/Component/BrowserKit/Tests/ClientTest.php index e715bb9a4c32b..6cd57e12d550a 100644 --- a/src/Symfony/Component/BrowserKit/Tests/ClientTest.php +++ b/src/Symfony/Component/BrowserKit/Tests/ClientTest.php @@ -622,7 +622,7 @@ public function testSetServerParameterInRequest() $this->assertEquals('', $client->getServerParameter('HTTP_HOST')); $this->assertEquals('Symfony2 BrowserKit', $client->getServerParameter('HTTP_USER_AGENT')); - $this->assertEquals('http://www.example.com/https/www.example.com', $client->getRequest()->getUri()); + $this->assertEquals('https://www.example.com/https/www.example.com', $client->getRequest()->getUri()); $server = $client->getRequest()->getServer(); @@ -636,7 +636,24 @@ public function testSetServerParameterInRequest() $this->assertEquals('new-server-key-value', $server['NEW_SERVER_KEY']); $this->assertArrayHasKey('HTTPS', $server); - $this->assertFalse($server['HTTPS']); + $this->assertTrue($server['HTTPS']); + } + + public function testRequestWithRelativeUri() + { + $client = new TestClient(); + + $client->request('GET', '/', array(), array(), array( + 'HTTP_HOST' => 'testhost', + 'HTTPS' => true, + )); + $this->assertEquals('https://testhost/', $client->getRequest()->getUri()); + + $client->request('GET', 'https://www.example.com/', array(), array(), array( + 'HTTP_HOST' => 'testhost', + 'HTTPS' => false, + )); + $this->assertEquals('https://www.example.com/', $client->getRequest()->getUri()); } public function testInternalRequest() From e9cdabe570fddbe3f721343d2d225d8bbcafe6e7 Mon Sep 17 00:00:00 2001 From: Chris Wilkinson Date: Thu, 18 Oct 2018 11:40:53 +0100 Subject: [PATCH 02/31] Use intersection type when referring to ParentNodeDefinitionInterface --- src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php b/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php index 1fac66fd3702f..95863d68f9bba 100644 --- a/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php +++ b/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php @@ -133,7 +133,7 @@ public function variableNode($name) /** * Returns the parent node. * - * @return ParentNodeDefinitionInterface|NodeDefinition The parent node + * @return NodeDefinition&ParentNodeDefinitionInterface The parent node */ public function end() { From de2ce58a4ff41bf91eedb14ad421c620e4c7f8ee Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 1 Nov 2018 18:15:47 +0100 Subject: [PATCH 03/31] [HttpFoundation] replace any preexisting Content-Type headers --- src/Symfony/Component/HttpFoundation/Response.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index 6b6308e36c2ee..a4ad0e66aedd8 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -344,8 +344,9 @@ public function sendHeaders() // headers foreach ($this->headers->allPreserveCase() as $name => $values) { + $replace = 0 === strcasecmp($name, 'Content-Type'); foreach ($values as $value) { - header($name.': '.$value, false, $this->statusCode); + header($name.': '.$value, $replace, $this->statusCode); } } From d1ca2ac4af9109dba6d445cc84c06ff2daa995e7 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 3 Nov 2018 11:02:24 +0100 Subject: [PATCH 04/31] bumped Symfony version to 2.8.48 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index e8c3182b4a9d2..e91c1d7d4712a 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,12 +59,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.8.47'; - const VERSION_ID = 20847; + const VERSION = '2.8.48-DEV'; + const VERSION_ID = 20848; const MAJOR_VERSION = 2; const MINOR_VERSION = 8; - const RELEASE_VERSION = 47; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = 48; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '11/2018'; const END_OF_LIFE = '11/2019'; From 6e4af32284478488c95fcaa7b8a7af2163fc13ec Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Mon, 5 Nov 2018 22:52:53 +0100 Subject: [PATCH 05/31] [Travis] Bump ext-mongodb to 1.5.2 on Travis --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8ee26e5249af2..2556f94e6e883 100644 --- a/.travis.yml +++ b/.travis.yml @@ -159,7 +159,7 @@ before_install: echo extension = $ext_cache >> $INI elif [[ $PHP = 7.* ]]; then tfold ext.apcu tpecl apcu-5.1.6 apcu.so $INI - tfold ext.mongodb tpecl mongodb-1.5.0 mongodb.so $INI + tfold ext.mongodb tpecl mongodb-1.5.2 mongodb.so $INI fi done @@ -218,7 +218,7 @@ install: fi phpenv global ${PHP/hhvm*/hhvm} if [[ $PHP = 7.* ]]; then - ([[ $deps ]] && cd src/Symfony/Component/HttpFoundation; composer config platform.ext-mongodb 1.5.0; composer require --dev --no-update mongodb/mongodb) + ([[ $deps ]] && cd src/Symfony/Component/HttpFoundation; composer config platform.ext-mongodb 1.5.2; composer require --dev --no-update mongodb/mongodb) fi tfold 'composer update' $COMPOSER_UP if [[ $TRAVIS_PHP_VERSION = 5.* || $TRAVIS_PHP_VERSION = hhvm* ]]; then From 7de10880a49604885d5dc046e5d2c7dd0885d82d Mon Sep 17 00:00:00 2001 From: KatharinaSt Date: Wed, 31 Oct 2018 23:24:21 +0100 Subject: [PATCH 06/31] Add framework asset changes to upgrade 3.0 guide --- UPGRADE-2.7.md | 28 ++++++++++++++++++++++++++++ UPGRADE-3.0.md | 28 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/UPGRADE-2.7.md b/UPGRADE-2.7.md index 0121a39e2057e..8e6a99c62e33c 100644 --- a/UPGRADE-2.7.md +++ b/UPGRADE-2.7.md @@ -646,6 +646,34 @@ FrameworkBundle } ``` + * The assets settings under `framework.templating` were deprecated and will be removed in Symfony 3.0. Use `framework.assets` instead. + + Before: + + ```yml + framework: + templating: + assets_version: 'v123' + assets_version_format: '%%s?version=%%s' + assets_base_urls: + http: ['http://cdn.example.com'] + ssl: ['https://secure.example.com'] + packages: + # ... + ``` + + After: + + ```yml + framework: + assets: + version: 'v123' + version_format: '%%s?version=%%s' + base_urls: ['http://cdn.example.com', 'https://secure.example.com'] + packages: + # ... + ``` + Security --------------- diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md index 5d36e2675a525..1d97d78a82f45 100644 --- a/UPGRADE-3.0.md +++ b/UPGRADE-3.0.md @@ -883,6 +883,34 @@ UPGRADE FROM 2.x to 3.0 engines: ['php'] ``` + * The assets settings under `framework.templating` were moved to `framework.assets`. + + Before: + + ```yml + framework: + templating: + assets_version: 'v123' + assets_version_format: '%%s?version=%%s' + assets_base_urls: + http: ['http://cdn.example.com'] + ssl: ['https://secure.example.com'] + packages: + # ... + ``` + + After: + + ```yml + framework: + assets: + version: 'v123' + version_format: '%%s?version=%%s' + base_urls: ['http://cdn.example.com', 'https://secure.example.com'] + packages: + # ... + ``` + * The `form.csrf_provider` service is removed as it implements an adapter for the new token manager to the deprecated `Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface` From 1fa5a9a0bdf7ca7d26423609f3c828419eb90e7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Tue, 6 Nov 2018 18:42:22 +0100 Subject: [PATCH 07/31] [HttpFoundation] Fixed PHP doc of ParameterBag::getBoolean Since the method should return a bool, then the default value should be a bool too --- src/Symfony/Component/HttpFoundation/ParameterBag.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpFoundation/ParameterBag.php b/src/Symfony/Component/HttpFoundation/ParameterBag.php index fdf60e56e9b2a..6141b1bf6fdd5 100644 --- a/src/Symfony/Component/HttpFoundation/ParameterBag.php +++ b/src/Symfony/Component/HttpFoundation/ParameterBag.php @@ -231,7 +231,7 @@ public function getInt($key, $default = 0, $deep = false) * Returns the parameter value converted to boolean. * * @param string $key The parameter key - * @param mixed $default The default value if the parameter key does not exist + * @param bool $default The default value if the parameter key does not exist * @param bool $deep If true, a path like foo[bar] will find deeper items * * @return bool The filtered value From 385d9df29cdf8a34df4d8f95258d97e85ca3dbae Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 4 Oct 2018 21:52:40 +0200 Subject: [PATCH 08/31] invalidate forms on transformation failures --- .../FrameworkBundle/Resources/config/form.xml | 5 ++ .../Form/Extension/Core/CoreExtension.php | 13 +++- .../TransformationFailureListener.php | 64 +++++++++++++++++++ .../Type/TransformationFailureExtension.php | 42 ++++++++++++ .../Extension/Core/CoreExtensionTest.php | 33 ++++++++++ 5 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Component/Form/Extension/Core/EventListener/TransformationFailureListener.php create mode 100644 src/Symfony/Component/Form/Extension/Core/Type/TransformationFailureExtension.php create mode 100644 src/Symfony/Component/Form/Tests/Extension/Core/CoreExtensionTest.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml index 7e86fb0728a03..b0bcf004437e3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml @@ -166,6 +166,11 @@ + + + + + diff --git a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php index fc9af48891053..e2e40e4634064 100644 --- a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php +++ b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php @@ -16,8 +16,10 @@ use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface; use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory; use Symfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator; +use Symfony\Component\Form\Extension\Core\Type\TransformationFailureExtension; use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; +use Symfony\Component\Translation\TranslatorInterface; /** * Represents the main form extension, which loads the core functionality. @@ -28,11 +30,13 @@ class CoreExtension extends AbstractExtension { private $propertyAccessor; private $choiceListFactory; + private $translator; - public function __construct(PropertyAccessorInterface $propertyAccessor = null, ChoiceListFactoryInterface $choiceListFactory = null) + public function __construct(PropertyAccessorInterface $propertyAccessor = null, ChoiceListFactoryInterface $choiceListFactory = null, TranslatorInterface $translator = null) { $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor(); $this->choiceListFactory = $choiceListFactory ?: new CachingFactoryDecorator(new PropertyAccessDecorator(new DefaultChoiceListFactory(), $this->propertyAccessor)); + $this->translator = $translator; } protected function loadTypes() @@ -71,4 +75,11 @@ protected function loadTypes() new Type\CurrencyType(), ); } + + protected function loadTypeExtensions() + { + return array( + new TransformationFailureExtension($this->translator), + ); + } } diff --git a/src/Symfony/Component/Form/Extension/Core/EventListener/TransformationFailureListener.php b/src/Symfony/Component/Form/Extension/Core/EventListener/TransformationFailureListener.php new file mode 100644 index 0000000000000..f46eb499e0cf9 --- /dev/null +++ b/src/Symfony/Component/Form/Extension/Core/EventListener/TransformationFailureListener.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * @author Christian Flothmann + */ +class TransformationFailureListener implements EventSubscriberInterface +{ + private $translator; + + public function __construct(TranslatorInterface $translator = null) + { + $this->translator = $translator; + } + + public static function getSubscribedEvents() + { + return array( + FormEvents::POST_SUBMIT => array('convertTransformationFailureToFormError', -1024), + ); + } + + public function convertTransformationFailureToFormError(FormEvent $event) + { + $form = $event->getForm(); + + if (null === $form->getTransformationFailure() || !$form->isValid()) { + return; + } + + foreach ($form as $child) { + if (!$child->isSynchronized()) { + return; + } + } + + $clientDataAsString = is_scalar($form->getViewData()) ? (string) $form->getViewData() : \gettype($form->getViewData()); + $messageTemplate = 'The value {{ value }} is not valid.'; + + if (null !== $this->translator) { + $message = $this->translator->trans($messageTemplate, array('{{ value }}' => $clientDataAsString)); + } else { + $message = strtr($messageTemplate, array('{{ value }}' => $clientDataAsString)); + } + + $form->addError(new FormError($message, $messageTemplate, array('{{ value }}' => $clientDataAsString), null, $form->getTransformationFailure())); + } +} diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TransformationFailureExtension.php b/src/Symfony/Component/Form/Extension/Core/Type/TransformationFailureExtension.php new file mode 100644 index 0000000000000..98875594d6f70 --- /dev/null +++ b/src/Symfony/Component/Form/Extension/Core/Type/TransformationFailureExtension.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\Extension\Core\EventListener\TransformationFailureListener; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * @author Christian Flothmann + */ +class TransformationFailureExtension extends AbstractTypeExtension +{ + private $translator; + + public function __construct(TranslatorInterface $translator = null) + { + $this->translator = $translator; + } + + public function buildForm(FormBuilderInterface $builder, array $options) + { + if (!isset($options['invalid_message']) && !isset($options['invalid_message_parameters'])) { + $builder->addEventSubscriber(new TransformationFailureListener($this->translator)); + } + } + + public function getExtendedType() + { + return 'Symfony\Component\Form\Extension\Core\Type\FormType'; + } +} diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/CoreExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/CoreExtensionTest.php new file mode 100644 index 0000000000000..ff85149e21c63 --- /dev/null +++ b/src/Symfony/Component/Form/Tests/Extension/Core/CoreExtensionTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Form\Extension\Core\CoreExtension; +use Symfony\Component\Form\FormFactoryBuilder; + +class CoreExtensionTest extends TestCase +{ + public function testTransformationFailuresAreConvertedIntoFormErrors() + { + $formFactoryBuilder = new FormFactoryBuilder(); + $formFactory = $formFactoryBuilder->addExtension(new CoreExtension()) + ->getFormFactory(); + + $form = $formFactory->createBuilder() + ->add('foo', 'Symfony\Component\Form\Extension\Core\Type\DateType') + ->getForm(); + $form->submit('foo'); + + $this->assertFalse($form->isValid()); + } +} From c0733c22cbd880e5ba47a658c5c2399a123fc58a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sun, 11 Nov 2018 11:07:43 +0100 Subject: [PATCH 09/31] Add required key attribute I am getting warnings when running tests with recent phpunit versions: > - Element 'element': The attribute 'key' is required but missing. This requirement is far from being new, what is recent is phpunit validating its configuration file against the XSD schema. See https://github.com/sebastianbergmann/phpunit/commit/d4484be1a9c56c73353517510ce3b0690f6d738e --- phpunit.xml.dist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index ada17c766aec4..d1244636bb687 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -56,8 +56,8 @@ - Symfony\Component\Console - Symfony\Component\HttpFoundation + Symfony\Component\Console + Symfony\Component\HttpFoundation From 4dce4b7c306d8e0465f8cd23ee5c8695a9a9ea65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sun, 11 Nov 2018 12:11:22 +0100 Subject: [PATCH 10/31] Bump phpunit XSD version to 5.2 Some attributes being used in the phpunit configuration files, namely failOnRisky and failOnWarning were introduced in phpunit 5.2.0. The Composer configuration shows that tests should run with old versions of phpunit, but phpunit only validates the configuration against the XSD since phpunit 7.2.0. These changes can be tested as follows: wget http://schema.phpunit.de/5.2/phpunit.xsd xargs xmllint --schema phpunit.xsd 1>/dev/null find src -name phpunit.xml.dist| xargs xmllint --schema phpunit.xsd 1>/dev/null See https://github.com/sebastianbergmann/phpunit/commit/7e06a82806be004cbfd30a02d92162e9ed4abc7c See https://github.com/symfony/symfony/blob/46e3745a03e199e64cc0fcf3284a96b5a25dcee9/composer.json#L98 --- phpunit.xml.dist | 2 +- src/Symfony/Bridge/Doctrine/phpunit.xml.dist | 2 +- src/Symfony/Bridge/Monolog/phpunit.xml.dist | 2 +- src/Symfony/Bridge/PhpUnit/phpunit.xml.dist | 2 +- src/Symfony/Bridge/ProxyManager/phpunit.xml.dist | 2 +- src/Symfony/Bridge/Twig/phpunit.xml.dist | 2 +- src/Symfony/Bundle/DebugBundle/phpunit.xml.dist | 2 +- src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist | 2 +- src/Symfony/Bundle/SecurityBundle/phpunit.xml.dist | 2 +- src/Symfony/Bundle/TwigBundle/phpunit.xml.dist | 2 +- src/Symfony/Bundle/WebProfilerBundle/phpunit.xml.dist | 2 +- src/Symfony/Component/Asset/phpunit.xml.dist | 2 +- src/Symfony/Component/BrowserKit/phpunit.xml.dist | 2 +- src/Symfony/Component/ClassLoader/phpunit.xml.dist | 2 +- src/Symfony/Component/Config/phpunit.xml.dist | 2 +- src/Symfony/Component/Console/phpunit.xml.dist | 2 +- src/Symfony/Component/CssSelector/phpunit.xml.dist | 2 +- src/Symfony/Component/Debug/phpunit.xml.dist | 2 +- src/Symfony/Component/DependencyInjection/phpunit.xml.dist | 2 +- src/Symfony/Component/DomCrawler/phpunit.xml.dist | 2 +- src/Symfony/Component/EventDispatcher/phpunit.xml.dist | 2 +- src/Symfony/Component/ExpressionLanguage/phpunit.xml.dist | 2 +- src/Symfony/Component/Filesystem/phpunit.xml.dist | 2 +- src/Symfony/Component/Finder/phpunit.xml.dist | 2 +- src/Symfony/Component/Form/phpunit.xml.dist | 2 +- src/Symfony/Component/HttpFoundation/phpunit.xml.dist | 2 +- src/Symfony/Component/HttpKernel/phpunit.xml.dist | 2 +- src/Symfony/Component/Intl/phpunit.xml.dist | 2 +- src/Symfony/Component/Ldap/phpunit.xml.dist | 2 +- src/Symfony/Component/Locale/phpunit.xml.dist | 2 +- src/Symfony/Component/OptionsResolver/phpunit.xml.dist | 2 +- src/Symfony/Component/Process/phpunit.xml.dist | 2 +- src/Symfony/Component/PropertyAccess/phpunit.xml.dist | 2 +- src/Symfony/Component/PropertyInfo/phpunit.xml.dist | 2 +- src/Symfony/Component/Routing/phpunit.xml.dist | 2 +- src/Symfony/Component/Security/Core/phpunit.xml.dist | 2 +- src/Symfony/Component/Security/Csrf/phpunit.xml.dist | 2 +- src/Symfony/Component/Security/Guard/phpunit.xml.dist | 2 +- src/Symfony/Component/Security/Http/phpunit.xml.dist | 2 +- src/Symfony/Component/Security/phpunit.xml.dist | 2 +- src/Symfony/Component/Serializer/phpunit.xml.dist | 2 +- src/Symfony/Component/Stopwatch/phpunit.xml.dist | 2 +- src/Symfony/Component/Templating/phpunit.xml.dist | 2 +- src/Symfony/Component/Translation/phpunit.xml.dist | 2 +- src/Symfony/Component/Validator/phpunit.xml.dist | 2 +- src/Symfony/Component/VarDumper/phpunit.xml.dist | 2 +- src/Symfony/Component/Yaml/phpunit.xml.dist | 2 +- 47 files changed, 47 insertions(+), 47 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index ada17c766aec4..f8d9c1fbbe1e7 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,7 +1,7 @@ Date: Sun, 11 Nov 2018 15:04:13 +0100 Subject: [PATCH 11/31] [Form] Hardened test suite for empty data --- .../Tests/Form/Type/EntityTypeTest.php | 39 +++++++++++++++++++ .../Extension/Core/Type/BaseTypeTest.php | 22 +++++++++++ .../Extension/Core/Type/ButtonTypeTest.php | 12 ++++++ .../Extension/Core/Type/CheckboxTypeTest.php | 13 +++++++ .../Extension/Core/Type/ChoiceTypeTest.php | 9 +++-- .../Core/Type/CollectionTypeTest.php | 6 +++ .../Extension/Core/Type/CountryTypeTest.php | 5 +++ .../Extension/Core/Type/CurrencyTypeTest.php | 5 +++ .../Extension/Core/Type/DateTimeTypeTest.php | 16 ++++++++ .../Extension/Core/Type/DateTypeTest.php | 29 ++++++++++++++ .../Extension/Core/Type/FormTypeTest.php | 5 +++ .../Extension/Core/Type/IntegerTypeTest.php | 12 ++++++ .../Extension/Core/Type/LanguageTypeTest.php | 5 +++ .../Extension/Core/Type/LocaleTypeTest.php | 5 +++ .../Extension/Core/Type/MoneyTypeTest.php | 12 ++++++ .../Extension/Core/Type/NumberTypeTest.php | 12 ++++++ .../Extension/Core/Type/TimeTypeTest.php | 13 +++++++ .../Extension/Core/Type/TimezoneTypeTest.php | 12 ++++++ .../Tests/Extension/Core/Type/UrlTypeTest.php | 13 +++++++ 19 files changed, 241 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php index 3377549187b48..351ff941d55b1 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php @@ -1543,4 +1543,43 @@ public function testSetDataNonEmptyArraySubmitNullMultiple() $this->assertEquals(array(), $form->getNormData()); $this->assertSame(array(), $form->getViewData(), 'View data is always an array'); } + + public function testSubmitNullUsesDefaultEmptyData($emptyData = 'empty', $expectedData = null) + { + $emptyData = '1'; + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $this->persist(array($entity1)); + + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'empty_data' => $emptyData, + )); + $form->submit(null); + + $this->assertSame($emptyData, $form->getViewData()); + $this->assertSame($entity1, $form->getNormData()); + $this->assertSame($entity1, $form->getData()); + } + + public function testSubmitNullMultipleUsesDefaultEmptyData() + { + $emptyData = array('1'); + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $this->persist(array($entity1)); + + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'multiple' => true, + 'empty_data' => $emptyData, + )); + $form->submit(null); + + $collection = new ArrayCollection(array($entity1)); + + $this->assertSame($emptyData, $form->getViewData()); + $this->assertEquals($collection, $form->getNormData()); + $this->assertEquals($collection, $form->getData()); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php index facab9730908b..da3f48b486621 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php @@ -145,6 +145,28 @@ public function testSubmitNull($expected = null, $norm = null, $view = null) $this->assertSame($view, $form->getViewData()); } + public function testSubmitNullUsesDefaultEmptyData($emptyData = 'empty', $expectedData = null) + { + $builder = $this->factory->createBuilder($this->getTestedType()); + + if ($builder->getCompound()) { + $emptyData = array(); + foreach ($builder as $field) { + // empty children should map null (model data) in the compound view data + $emptyData[$field->getName()] = null; + } + } else { + // simple fields share the view and the model format, unless they use a transformer + $expectedData = $emptyData; + } + + $form = $builder->setEmptyData($emptyData)->getForm()->submit(null); + + $this->assertSame($emptyData, $form->getViewData()); + $this->assertSame($expectedData, $form->getNormData()); + $this->assertSame($expectedData, $form->getData()); + } + protected function getTestedType() { return static::TESTED_TYPE; diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ButtonTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ButtonTypeTest.php index 9f105272fe592..dbef3ff9ed6d2 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ButtonTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ButtonTypeTest.php @@ -32,4 +32,16 @@ public function testCreateButtonInstances() { $this->assertInstanceOf('Symfony\Component\Form\Button', $this->factory->create(static::TESTED_TYPE)); } + + /** + * @expectedException \Symfony\Component\Form\Exception\BadMethodCallException + * @expectedExceptionMessage Buttons do not support empty data. + * + * @param string $emptyData + * @param null $expectedData + */ + public function testSubmitNullUsesDefaultEmptyData($emptyData = 'empty', $expectedData = null) + { + parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php index 95f4ad201e4c9..38dbd67db80a0 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php @@ -187,4 +187,17 @@ public function testSubmitNull($expected = null, $norm = null, $view = null) { parent::testSubmitNull(false, false, null); } + + public function testSubmitNullUsesDefaultEmptyData($emptyData = 'empty', $expectedData = true) + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'empty_data' => $emptyData, + )); + $form->submit(null); + + // view data is transformed to the string true value + $this->assertSame('1', $form->getViewData()); + $this->assertSame($expectedData, $form->getNormData()); + $this->assertSame($expectedData, $form->getData()); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php index 59dc05cb681c4..2ef745efa5785 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -684,19 +684,20 @@ public function testSubmitSingleNonExpandedObjectChoices() $this->assertTrue($form->isSynchronized()); } - public function testSubmitSingleChoiceWithEmptyData() + public function testSubmitNullUsesDefaultEmptyData($emptyData = 'empty', $expectedData = null) { $form = $this->factory->create(static::TESTED_TYPE, null, array( 'multiple' => false, 'expanded' => false, - 'choices' => array('test'), + // empty data must match string choice value + 'choices' => array($emptyData), 'choices_as_values' => true, - 'empty_data' => 'test', + 'empty_data' => $emptyData, )); $form->submit(null); - $this->assertSame('test', $form->getData()); + $this->assertSame($emptyData, $form->getData()); } public function testSubmitSingleChoiceWithEmptyDataAndInitialData() diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php index 6fcc4dc6d32a1..bdeb4bd1a67ba 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php @@ -410,4 +410,10 @@ public function testSubmitNull($expected = null, $norm = null, $view = null) { parent::testSubmitNull(array(), array(), array()); } + + public function testSubmitNullUsesDefaultEmptyData($emptyData = array(), $expectedData = array()) + { + // resize form listener always set an empty array + parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php index 2db6daa258103..e9cbc2afd2d9b 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php @@ -66,4 +66,9 @@ public function testSubmitNull($expected = null, $norm = null, $view = null) { parent::testSubmitNull($expected, $norm, ''); } + + public function testSubmitNullUsesDefaultEmptyData($emptyData = 'FR', $expectedData = 'FR') + { + parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php index 739cc408ba5af..ab2b389710549 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php @@ -49,4 +49,9 @@ public function testSubmitNull($expected = null, $norm = null, $view = null) { parent::testSubmitNull($expected, $norm, ''); } + + public function testSubmitNullUsesDefaultEmptyData($emptyData = 'EUR', $expectedData = 'EUR') + { + parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php index 27c6581f0bea0..2531332f054c3 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php @@ -615,4 +615,20 @@ public function testSubmitNullWithSingleText() $this->assertNull($form->getNormData()); $this->assertSame('', $form->getViewData()); } + + public function testSubmitNullUsesDefaultEmptyData($emptyData = array(), $expectedData = null) + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'empty_data' => $emptyData, + )); + $form->submit(null); + + // view transformer writes back empty strings in the view data + $this->assertSame( + array('date' => array('year' => '', 'month' => '', 'day' => ''), 'time' => array('hour' => '', 'minute' => '')), + $form->getViewData() + ); + $this->assertSame($expectedData, $form->getNormData()); + $this->assertSame($expectedData, $form->getData()); + } } 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 1eb2e7887bad4..0f03b1851d1f9 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php @@ -1003,4 +1003,33 @@ public function testSubmitNullWithSingleText() $this->assertNull($form->getNormData()); $this->assertSame('', $form->getViewData()); } + + public function testSubmitNullUsesDefaultEmptyData($emptyData = array(), $expectedData = null) + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'empty_data' => $emptyData, + )); + $form->submit(null); + + // view transformer write back empty strings in the view data + $this->assertSame(array('year' => '', 'month' => '', 'day' => ''), $form->getViewData()); + $this->assertSame($expectedData, $form->getNormData()); + $this->assertSame($expectedData, $form->getData()); + } + + public function testSingleTextSubmitNullUsesDefaultEmptyData() + { + $emptyData = '2018-11-11'; + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'widget' => 'single_text', + 'empty_data' => $emptyData, + )); + $form->submit(null); + + $date = new \DateTime($emptyData); + + $this->assertSame($emptyData, $form->getViewData()); + $this->assertEquals($date, $form->getNormData()); + $this->assertEquals($date, $form->getData()); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php index 7d75dce26dcf2..52722b042065b 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php @@ -227,6 +227,11 @@ public function testDataClassMustBeValidClassOrInterface() )); } + public function testSubmitNullUsesDefaultEmptyData($emptyData = array(), $expectedData = array()) + { + parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData); + } + public function testSubmitWithEmptyDataCreatesObjectIfClassAvailable() { $form = $this->factory->createBuilder(static::TESTED_TYPE, null, array( diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php index 3059e79ce989c..247f956a3bd55 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php @@ -48,4 +48,16 @@ public function testSubmitNull($expected = null, $norm = null, $view = null) { parent::testSubmitNull($expected, $norm, ''); } + + public function testSubmitNullUsesDefaultEmptyData($emptyData = '10', $expectedData = 10) + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'empty_data' => $emptyData, + )); + $form->submit(null); + + $this->assertSame($emptyData, $form->getViewData()); + $this->assertSame($expectedData, $form->getNormData()); + $this->assertSame($expectedData, $form->getData()); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php index beffecdd7ecb5..d8127aa6d7bb0 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php @@ -59,4 +59,9 @@ public function testSubmitNull($expected = null, $norm = null, $view = null) { parent::testSubmitNull($expected, $norm, ''); } + + public function testSubmitNullUsesDefaultEmptyData($emptyData = 'en', $expectedData = 'en') + { + parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php index 0439e7d2ddcf1..ede22932f8056 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php @@ -49,4 +49,9 @@ public function testSubmitNull($expected = null, $norm = null, $view = null) { parent::testSubmitNull($expected, $norm, ''); } + + public function testSubmitNullUsesDefaultEmptyData($emptyData = 'en', $expectedData = 'en') + { + parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php index a865a93c9c651..0b02fffddfc76 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php @@ -80,4 +80,16 @@ public function testMoneyPatternWithoutCurrency() $this->assertSame('{{ widget }}', $view->vars['money_pattern']); } + + public function testSubmitNullUsesDefaultEmptyData($emptyData = '10.00', $expectedData = 10.0) + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'empty_data' => $emptyData, + )); + $form->submit(null); + + $this->assertSame($emptyData, $form->getViewData()); + $this->assertSame($expectedData, $form->getNormData()); + $this->assertSame($expectedData, $form->getData()); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php index 9d53297e8ba24..09c8f2dd2829b 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php @@ -73,4 +73,16 @@ public function testSubmitNull($expected = null, $norm = null, $view = null) { parent::testSubmitNull($expected, $norm, ''); } + + public function testSubmitNullUsesDefaultEmptyData($emptyData = '10', $expectedData = 10.0) + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'empty_data' => $emptyData, + )); + $form->submit(null); + + $this->assertSame($emptyData, $form->getViewData()); + $this->assertSame($expectedData, $form->getNormData()); + $this->assertSame($expectedData, $form->getData()); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php index 611c6e42ac7bd..98d4f902faa7b 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php @@ -797,4 +797,17 @@ public function testSubmitNull($expected = null, $norm = null, $view = null) parent::testSubmitNull($expected, $norm, $view); } + + public function testSubmitNullUsesDefaultEmptyData($emptyData = array(), $expectedData = null) + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'empty_data' => $emptyData, + )); + $form->submit(null); + + // view transformer write back empty strings in the view data + $this->assertSame(array('hour' => '', 'minute' => ''), $form->getViewData()); + $this->assertSame($expectedData, $form->getNormData()); + $this->assertSame($expectedData, $form->getData()); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php index 507b740505ae9..b6b61cb171d84 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php @@ -43,4 +43,16 @@ public function testSubmitNull($expected = null, $norm = null, $view = null) { parent::testSubmitNull($expected, $norm, ''); } + + public function testSubmitNullUsesDefaultEmptyData($emptyData = 'Africa/Kinshasa', $expectedData = 'Africa/Kinshasa') + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'empty_data' => $emptyData, + )); + $form->submit(null); + + $this->assertSame($emptyData, $form->getViewData()); + $this->assertSame($expectedData, $form->getNormData()); + $this->assertSame($expectedData, $form->getData()); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php index a3336970dcab5..95be3775a44c3 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php @@ -100,4 +100,17 @@ public function testSubmitWithNonStringDataDoesNotBreakTheFixUrlProtocolListener $this->assertSame(array('domain.com', 'www.domain.com'), $form->getData()); } + + public function testSubmitNullUsesDefaultEmptyData($emptyData = 'empty', $expectedData = 'http://empty') + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'empty_data' => $emptyData, + )); + $form->submit(null); + + // listener normalizes data on submit + $this->assertSame($expectedData, $form->getViewData()); + $this->assertSame($expectedData, $form->getNormData()); + $this->assertSame($expectedData, $form->getData()); + } } From 9bab1e8abaf57946af9c56d398562909dba46fb5 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Sun, 11 Nov 2018 23:03:02 +0100 Subject: [PATCH 12/31] [Form] Fixed empty data for compound date types --- .../Form/Extension/Core/Type/DateTimeType.php | 14 +++++++++ .../Form/Extension/Core/Type/DateType.php | 17 +++++++++++ .../Form/Extension/Core/Type/TimeType.php | 17 +++++++++++ .../Extension/Core/Type/DateTimeTypeTest.php | 27 +++++++++++++++++ .../Extension/Core/Type/DateTypeTest.php | 27 ++++++++++++----- .../Extension/Core/Type/TimeTypeTest.php | 29 ++++++++++++++++++- 6 files changed, 122 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php index c3f95f59af03c..64d2c2d2f4d20 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php @@ -90,6 +90,9 @@ public function buildForm(FormBuilderInterface $builder, array $options) )); } } else { + // when the form is compound the entries of the array are ignored in favor of children data + // so we need to handle the cascade setting here + $emptyData = $builder->getEmptyData() ?: array(); // Only pass a subset of the options to children $dateOptions = array_intersect_key($options, array_flip(array( 'years', @@ -105,6 +108,10 @@ public function buildForm(FormBuilderInterface $builder, array $options) 'invalid_message_parameters', ))); + if (isset($emptyData['date'])) { + $dateOptions['empty_data'] = $emptyData['date']; + } + $timeOptions = array_intersect_key($options, array_flip(array( 'hours', 'minutes', @@ -121,6 +128,10 @@ public function buildForm(FormBuilderInterface $builder, array $options) 'invalid_message_parameters', ))); + if (isset($emptyData['time'])) { + $timeOptions['empty_data'] = $emptyData['time']; + } + if (false === $options['label']) { $dateOptions['label'] = false; $timeOptions['label'] = false; @@ -226,6 +237,9 @@ public function configureOptions(OptionsResolver $resolver) // this option. 'data_class' => null, 'compound' => $compound, + 'empty_data' => function (Options $options) { + return $options['compound'] ? array() : ''; + }, )); // Don't add some defaults in order to preserve the defaults diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php index ad4499899a3ba..ce41c3c7d3058 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php @@ -75,7 +75,21 @@ public function buildForm(FormBuilderInterface $builder, array $options) $yearOptions = $monthOptions = $dayOptions = array( 'error_bubbling' => true, + 'empty_data' => '', ); + // when the form is compound the entries of the array are ignored in favor of children data + // so we need to handle the cascade setting here + $emptyData = $builder->getEmptyData() ?: array(); + + if (isset($emptyData['year'])) { + $yearOptions['empty_data'] = $emptyData['year']; + } + if (isset($emptyData['month'])) { + $monthOptions['empty_data'] = $emptyData['month']; + } + if (isset($emptyData['day'])) { + $dayOptions['empty_data'] = $emptyData['day']; + } if (isset($options['invalid_message'])) { $dayOptions['invalid_message'] = $options['invalid_message']; @@ -272,6 +286,9 @@ public function configureOptions(OptionsResolver $resolver) // this option. 'data_class' => null, 'compound' => $compound, + 'empty_data' => function (Options $options) { + return $options['compound'] ? array() : ''; + }, 'choice_translation_domain' => false, )); diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php index 4a27cd440b665..facfc098b7693 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php @@ -70,7 +70,15 @@ public function buildForm(FormBuilderInterface $builder, array $options) } else { $hourOptions = $minuteOptions = $secondOptions = array( 'error_bubbling' => true, + 'empty_data' => '', ); + // when the form is compound the entries of the array are ignored in favor of children data + // so we need to handle the cascade setting here + $emptyData = $builder->getEmptyData() ?: array(); + + if (isset($emptyData['hour'])) { + $hourOptions['empty_data'] = $emptyData['hour']; + } if (isset($options['invalid_message'])) { $hourOptions['invalid_message'] = $options['invalid_message']; @@ -138,10 +146,16 @@ public function buildForm(FormBuilderInterface $builder, array $options) $builder->add('hour', self::$widgets[$options['widget']], $hourOptions); if ($options['with_minutes']) { + if (isset($emptyData['minute'])) { + $minuteOptions['empty_data'] = $emptyData['minute']; + } $builder->add('minute', self::$widgets[$options['widget']], $minuteOptions); } if ($options['with_seconds']) { + if (isset($emptyData['second'])) { + $secondOptions['empty_data'] = $emptyData['second']; + } $builder->add('second', self::$widgets[$options['widget']], $secondOptions); } @@ -265,6 +279,9 @@ public function configureOptions(OptionsResolver $resolver) // representation is not \DateTime, but an array, we need to unset // this option. 'data_class' => null, + 'empty_data' => function (Options $options) { + return $options['compound'] ? array() : ''; + }, 'compound' => $compound, 'choice_translation_domain' => false, )); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php index 2531332f054c3..f0e45f5d5600d 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php @@ -631,4 +631,31 @@ public function testSubmitNullUsesDefaultEmptyData($emptyData = array(), $expect $this->assertSame($expectedData, $form->getNormData()); $this->assertSame($expectedData, $form->getData()); } + + /** + * @dataProvider provideEmptyData + */ + public function testSubmitNullUsesDateEmptyData($widget, $emptyData, $expectedData) + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'widget' => $widget, + 'empty_data' => $emptyData, + )); + $form->submit(null); + + $this->assertSame($emptyData, $form->getViewData()); + $this->assertEquals($expectedData, $form->getNormData()); + $this->assertEquals($expectedData, $form->getData()); + } + + public function provideEmptyData() + { + $expectedData = \DateTime::createFromFormat('Y-m-d H:i', '2018-11-11 21:23'); + + return array( + 'Simple field' => array('single_text', '2018-11-11T21:23:00', $expectedData), + 'Compound text field' => array('text', array('date' => array('year' => '2018', 'month' => '11', 'day' => '11'), 'time' => array('hour' => '21', 'minute' => '23')), $expectedData), + 'Compound choice field' => array('choice', array('date' => array('year' => '2018', 'month' => '11', 'day' => '11'), 'time' => array('hour' => '21', 'minute' => '23')), $expectedData), + ); + } } 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 0f03b1851d1f9..a15a6c2b32034 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php @@ -1011,25 +1011,36 @@ public function testSubmitNullUsesDefaultEmptyData($emptyData = array(), $expect )); $form->submit(null); - // view transformer write back empty strings in the view data + // view transformer writes back empty strings in the view data $this->assertSame(array('year' => '', 'month' => '', 'day' => ''), $form->getViewData()); $this->assertSame($expectedData, $form->getNormData()); $this->assertSame($expectedData, $form->getData()); } - public function testSingleTextSubmitNullUsesDefaultEmptyData() + /** + * @dataProvider provideEmptyData + */ + public function testSubmitNullUsesDateEmptyData($widget, $emptyData, $expectedData) { - $emptyData = '2018-11-11'; $form = $this->factory->create(static::TESTED_TYPE, null, array( - 'widget' => 'single_text', + 'widget' => $widget, 'empty_data' => $emptyData, )); $form->submit(null); - $date = new \DateTime($emptyData); - $this->assertSame($emptyData, $form->getViewData()); - $this->assertEquals($date, $form->getNormData()); - $this->assertEquals($date, $form->getData()); + $this->assertEquals($expectedData, $form->getNormData()); + $this->assertEquals($expectedData, $form->getData()); + } + + public function provideEmptyData() + { + $expectedData = \DateTime::createFromFormat('Y-m-d H:i:s', '2018-11-11 00:00:00'); + + return array( + 'Simple field' => array('single_text', '2018-11-11', $expectedData), + 'Compound text fields' => array('text', array('year' => '2018', 'month' => '11', 'day' => '11'), $expectedData), + 'Compound choice fields' => array('choice', array('year' => '2018', 'month' => '11', 'day' => '11'), $expectedData), + ); } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php index 98d4f902faa7b..5ed4a236f1b31 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php @@ -805,9 +805,36 @@ public function testSubmitNullUsesDefaultEmptyData($emptyData = array(), $expect )); $form->submit(null); - // view transformer write back empty strings in the view data + // view transformer writes back empty strings in the view data $this->assertSame(array('hour' => '', 'minute' => ''), $form->getViewData()); $this->assertSame($expectedData, $form->getNormData()); $this->assertSame($expectedData, $form->getData()); } + + /** + * @dataProvider provideEmptyData + */ + public function testSubmitNullUsesDateEmptyData($widget, $emptyData, $expectedData) + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'widget' => $widget, + 'empty_data' => $emptyData, + )); + $form->submit(null); + + $this->assertSame($emptyData, $form->getViewData()); + $this->assertEquals($expectedData, $form->getNormData()); + $this->assertEquals($expectedData, $form->getData()); + } + + public function provideEmptyData() + { + $expectedData = \DateTime::createFromFormat('Y-m-d H:i', '1970-01-01 21:23'); + + return array( + 'Simple field' => array('single_text', '21:23', $expectedData), + 'Compound text field' => array('text', array('hour' => '21', 'minute' => '23'), $expectedData), + 'Compound choice field' => array('choice', array('hour' => '21', 'minute' => '23'), $expectedData), + ); + } } From e1402d495e70c0e03f28bce186fe946a31d81e31 Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Fri, 9 Nov 2018 11:22:20 +0100 Subject: [PATCH 13/31] [Config] Unset key during normalization --- .../Component/Config/Definition/ArrayNode.php | 5 ++++- .../Config/Definition/Builder/ExprBuilder.php | 4 ++-- .../Builder/ArrayNodeDefinitionTest.php | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Config/Definition/ArrayNode.php b/src/Symfony/Component/Config/Definition/ArrayNode.php index c8cb82c41ff9c..13fe1c94bca5c 100644 --- a/src/Symfony/Component/Config/Definition/ArrayNode.php +++ b/src/Symfony/Component/Config/Definition/ArrayNode.php @@ -288,7 +288,10 @@ protected function normalizeValue($value) $normalized = array(); foreach ($value as $name => $val) { if (isset($this->children[$name])) { - $normalized[$name] = $this->children[$name]->normalize($val); + try { + $normalized[$name] = $this->children[$name]->normalize($val); + } catch (UnsetKeyException $e) { + } unset($value[$name]); } elseif (!$this->removeExtraKeys) { $normalized[$name] = $val; diff --git a/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php b/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php index fedbe0cc1bba2..ddbe5b0401ab6 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php +++ b/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php @@ -149,7 +149,7 @@ public function thenEmptyArray() } /** - * Sets a closure marking the value as invalid at validation time. + * Sets a closure marking the value as invalid at processing time. * * if you want to add the value of the node in your message just use a %s placeholder. * @@ -167,7 +167,7 @@ public function thenInvalid($message) } /** - * Sets a closure unsetting this key of the array at validation time. + * Sets a closure unsetting this key of the array at processing time. * * @return $this * diff --git a/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php b/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php index 482ead310be1b..05fcd94e901f5 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php @@ -231,6 +231,25 @@ public function testNormalizeKeys() $this->assertFalse($this->getField($node, 'normalizeKeys')); } + public function testUnsetChild() + { + $node = new ArrayNodeDefinition('root'); + $node + ->children() + ->scalarNode('value') + ->beforeNormalization() + ->ifTrue(function ($value) { + return empty($value); + }) + ->thenUnset() + ->end() + ->end() + ->end() + ; + + $this->assertSame(array(), $node->getNode()->normalize(array('value' => null))); + } + public function getEnableableNodeFixtures() { return array( From de40f5d07b119195ec76b5fc135169667d653839 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Mon, 12 Nov 2018 11:47:20 +0100 Subject: [PATCH 14/31] [Form] Minor fixes in docs and cs --- src/Symfony/Component/Form/Form.php | 24 ++++++++++--------- .../Component/Form/NativeRequestHandler.php | 4 ++-- src/Symfony/Component/Form/Util/FormUtil.php | 2 +- .../Component/Form/Util/OrderedHashMap.php | 2 +- .../Form/Util/OrderedHashMapIterator.php | 4 +--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index 923407383159b..95186fae7684d 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -32,16 +32,18 @@ * * (1) the "model" format required by the form's object * (2) the "normalized" format for internal processing - * (3) the "view" format used for display + * (3) the "view" format used for display simple fields + * or map children model data for compound fields * * A date field, for example, may store a date as "Y-m-d" string (1) in the * object. To facilitate processing in the field, this value is normalized * to a DateTime object (2). In the HTML representation of your form, a - * localized string (3) is presented to and modified by the user. + * localized string (3) may be presented to and modified by the user, or it could be an array of values + * to be mapped to choices fields. * * In most cases, format (1) and format (2) will be the same. For example, * a checkbox field uses a Boolean value for both internal processing and - * storage in the object. In these cases you simply need to set a value + * storage in the object. In these cases you simply need to set a view * transformer to convert between formats (2) and (3). You can do this by * calling addViewTransformer(). * @@ -49,7 +51,7 @@ * demonstrate this, let's extend our above date field to store the value * either as "Y-m-d" string or as timestamp. Internally we still want to * use a DateTime object for processing. To convert the data from string/integer - * to DateTime you can set a normalization transformer by calling + * to DateTime you can set a model transformer by calling * addModelTransformer(). The normalized data is then converted to the displayed * data as described before. * @@ -218,7 +220,7 @@ public function getPropertyPath() } if (null === $this->getName() || '' === $this->getName()) { - return; + return null; } $parent = $this->parent; @@ -341,8 +343,8 @@ public function setData($modelData) $modelData = $event->getData(); } - // Treat data as strings unless a value transformer exists - if (!$this->config->getViewTransformers() && !$this->config->getModelTransformers() && is_scalar($modelData)) { + // Treat data as strings unless a transformer exists + if (is_scalar($modelData) && !$this->config->getViewTransformers() && !$this->config->getModelTransformers()) { $modelData = (string) $modelData; } @@ -1068,7 +1070,7 @@ public function createView(FormView $parent = null) } /** - * Normalizes the value if a normalization transformer is set. + * Normalizes the value if a model transformer is set. * * @param mixed $value The value to transform * @@ -1090,7 +1092,7 @@ private function modelToNorm($value) } /** - * Reverse transforms a value if a normalization transformer is set. + * Reverse transforms a value if a model transformer is set. * * @param string $value The value to reverse transform * @@ -1114,7 +1116,7 @@ private function normToModel($value) } /** - * Transforms the value if a value transformer is set. + * Transforms the value if a view transformer is set. * * @param mixed $value The value to transform * @@ -1145,7 +1147,7 @@ private function normToView($value) } /** - * Reverse transforms a value if a value transformer is set. + * Reverse transforms a value if a view transformer is set. * * @param string $value The value to reverse transform * diff --git a/src/Symfony/Component/Form/NativeRequestHandler.php b/src/Symfony/Component/Form/NativeRequestHandler.php index ccebdab6d80a1..94210d51e85a5 100644 --- a/src/Symfony/Component/Form/NativeRequestHandler.php +++ b/src/Symfony/Component/Form/NativeRequestHandler.php @@ -15,7 +15,7 @@ use Symfony\Component\Form\Util\ServerParams; /** - * A request handler using PHP's super globals $_GET, $_POST and $_SERVER. + * A request handler using PHP super globals $_GET, $_POST and $_SERVER. * * @author Bernhard Schussek */ @@ -213,7 +213,7 @@ private static function stripEmptyFiles($data) if (self::$fileKeys === $keys) { if (UPLOAD_ERR_NO_FILE === $data['error']) { - return; + return null; } return $data; diff --git a/src/Symfony/Component/Form/Util/FormUtil.php b/src/Symfony/Component/Form/Util/FormUtil.php index 0862179f545c1..53053f9d5b791 100644 --- a/src/Symfony/Component/Form/Util/FormUtil.php +++ b/src/Symfony/Component/Form/Util/FormUtil.php @@ -27,7 +27,7 @@ private function __construct() * Returns whether the given data is empty. * * This logic is reused multiple times throughout the processing of - * a form and needs to be consistent. PHP's keyword `empty` cannot + * a form and needs to be consistent. PHP keyword `empty` cannot * be used as it also considers 0 and "0" to be empty. * * @param mixed $data diff --git a/src/Symfony/Component/Form/Util/OrderedHashMap.php b/src/Symfony/Component/Form/Util/OrderedHashMap.php index 6a97559850a31..26e45a462250d 100644 --- a/src/Symfony/Component/Form/Util/OrderedHashMap.php +++ b/src/Symfony/Component/Form/Util/OrderedHashMap.php @@ -128,7 +128,7 @@ public function offsetSet($key, $value) $key = array() === $this->orderedKeys // If the array is empty, use 0 as key ? 0 - // Imitate PHP's behavior of generating a key that equals + // Imitate PHP behavior of generating a key that equals // the highest existing integer key + 1 : 1 + (int) max($this->orderedKeys); } diff --git a/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php b/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php index 3de636392dd85..93a7caa58dabe 100644 --- a/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php +++ b/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php @@ -56,8 +56,6 @@ class OrderedHashMapIterator implements \Iterator private $current; /** - * Creates a new iterator. - * * @param array $elements The elements of the map, indexed by their * keys * @param array $orderedKeys The keys of the map in the order in which @@ -84,7 +82,7 @@ public function __construct(array &$elements, array &$orderedKeys, array &$manag */ public function __destruct() { - // Use array_splice() instead of isset() to prevent holes in the + // Use array_splice() instead of unset() to prevent holes in the // array indices, which would break the initialization of $cursorId array_splice($this->managedCursors, $this->cursorId, 1); } From 78e386e87f369734c96a0873b05d9e370e350dd9 Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Mon, 12 Nov 2018 19:05:42 +0100 Subject: [PATCH 15/31] [PhpUnitBridge] Fix typo --- src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php index f95d1389ff83b..7a48f1a1750b2 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php @@ -30,7 +30,7 @@ class DeprecationErrorHandler * - use "/some-regexp/" to stop the test suite whenever a deprecation * message matches the given regular expression; * - use a number to define the upper bound of allowed deprecations, - * making the test suite fail whenever more notices are trigerred. + * making the test suite fail whenever more notices are triggered. * * @param int|string|false $mode The reporting mode, defaults to not allowing any deprecations */ From bc2e2cb5ad8a347cbab6162ce9157b0af02ec426 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Mon, 12 Nov 2018 11:48:30 +0100 Subject: [PATCH 16/31] [Form] Fixed keeping hash of equal \DateTimeInterface on submit --- .../Core/DataMapper/PropertyPathMapper.php | 9 +++-- .../DataMapper/PropertyPathMapperTest.php | 40 +++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php b/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php index f9721e52b1769..e8dbabdb9fb90 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php +++ b/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php @@ -73,16 +73,17 @@ public function mapFormsToData($forms, &$data) // Write-back is disabled if the form is not synchronized (transformation failed), // if the form was not submitted and if the form is disabled (modification not allowed) if (null !== $propertyPath && $config->getMapped() && $form->isSubmitted() && $form->isSynchronized() && !$form->isDisabled()) { - // If the field is of type DateTime and the data is the same skip the update to + $propertyValue = $form->getData(); + // If the field is of type DateTime or DateTimeInterface and the data is the same skip the update to // keep the original object hash - if ($form->getData() instanceof \DateTime && $form->getData() == $this->propertyAccessor->getValue($data, $propertyPath)) { + if (($propertyValue instanceof \DateTime || $propertyValue instanceof \DateTimeInterface) && $propertyValue == $this->propertyAccessor->getValue($data, $propertyPath)) { continue; } // If the data is identical to the value in $data, we are // dealing with a reference - if (!\is_object($data) || !$config->getByReference() || $form->getData() !== $this->propertyAccessor->getValue($data, $propertyPath)) { - $this->propertyAccessor->setValue($data, $propertyPath, $form->getData()); + if (!\is_object($data) || !$config->getByReference() || $propertyValue !== $this->propertyAccessor->getValue($data, $propertyPath)) { + $this->propertyAccessor->setValue($data, $propertyPath, $propertyValue); } } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php index 979adcdceedd9..1dcff2f4020a4 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php @@ -357,4 +357,44 @@ public function testMapFormsToDataIgnoresDisabled() $this->mapper->mapFormsToData(array($form), $car); } + + /** + * @dataProvider provideDate + */ + public function testMapFormsToDataDoesNotChangeEqualDateTimeInstance($date) + { + $article = array(); + $publishedAt = $date; + $article['publishedAt'] = clone $publishedAt; + $propertyPath = $this->getPropertyPath('[publishedAt]'); + + $this->propertyAccessor->expects($this->once()) + ->method('getValue') + ->willReturn($article['publishedAt']) + ; + $this->propertyAccessor->expects($this->never()) + ->method('setValue') + ; + + $config = new FormConfigBuilder('publishedAt', \get_class($publishedAt), $this->dispatcher); + $config->setByReference(false); + $config->setPropertyPath($propertyPath); + $config->setData($publishedAt); + $form = $this->getForm($config); + + $this->mapper->mapFormsToData(array($form), $article); + } + + public function provideDate() + { + $data = array( + '\DateTime' => array(new \DateTime()), + ); + + if (class_exists('DateTimeImmutable')) { + $data['\DateTimeImmutable'] = array(new \DateTimeImmutable()); + } + + return $data; + } } From 5f8bd898b41491aac84d27504015bc7d7216f6f9 Mon Sep 17 00:00:00 2001 From: Edvin Hultberg Date: Tue, 13 Nov 2018 14:44:43 +0100 Subject: [PATCH 17/31] Command::addOption should allow int in $default The constructor for InputOption allows int on the $default parameter, but not Command::addOption $default parameter fixup: apply coding standards patch --- src/Symfony/Component/Console/Command/Command.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index 00df2d445462b..75d062d56d0ea 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -381,11 +381,11 @@ public function addArgument($name, $mode = null, $description = '', $default = n /** * Adds an option. * - * @param string $name The option name - * @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts - * @param int|null $mode The option mode: One of the VALUE_* constants - * @param string $description A description text - * @param string|string[]|bool|null $default The default value (must be null for self::VALUE_NONE) + * @param string $name The option name + * @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param int|null $mode The option mode: One of the VALUE_* constants + * @param string $description A description text + * @param string|string[]|int|bool|null $default The default value (must be null for self::VALUE_NONE) * * @throws InvalidArgumentException If option mode is invalid or incompatible * From 0ecaead015cf02438df915973828db5d7d6ac330 Mon Sep 17 00:00:00 2001 From: Thomas Bisignani Date: Wed, 14 Nov 2018 14:57:51 +0100 Subject: [PATCH 18/31] [Validator] Added the missing constraints instance checks --- .../Component/Validator/Constraints/BicValidator.php | 5 +++++ .../Component/Validator/Constraints/CountValidator.php | 4 ++++ .../Component/Validator/Constraints/UuidValidator.php | 8 ++++---- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Validator/Constraints/BicValidator.php b/src/Symfony/Component/Validator/Constraints/BicValidator.php index 51aecc384ae78..2a27beff9bf63 100644 --- a/src/Symfony/Component/Validator/Constraints/BicValidator.php +++ b/src/Symfony/Component/Validator/Constraints/BicValidator.php @@ -13,6 +13,7 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; /** * @author Michael Hirschler @@ -26,6 +27,10 @@ class BicValidator extends ConstraintValidator */ public function validate($value, Constraint $constraint) { + if (!$constraint instanceof Bic) { + throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Bic'); + } + if (null === $value || '' === $value) { return; } diff --git a/src/Symfony/Component/Validator/Constraints/CountValidator.php b/src/Symfony/Component/Validator/Constraints/CountValidator.php index cda0f05669685..45be99678bf75 100644 --- a/src/Symfony/Component/Validator/Constraints/CountValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CountValidator.php @@ -26,6 +26,10 @@ class CountValidator extends ConstraintValidator */ public function validate($value, Constraint $constraint) { + if (!$constraint instanceof Count) { + throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Count'); + } + if (null === $value) { return; } diff --git a/src/Symfony/Component/Validator/Constraints/UuidValidator.php b/src/Symfony/Component/Validator/Constraints/UuidValidator.php index 2c8e44a10c1ad..8e2e5da334737 100644 --- a/src/Symfony/Component/Validator/Constraints/UuidValidator.php +++ b/src/Symfony/Component/Validator/Constraints/UuidValidator.php @@ -83,14 +83,14 @@ class UuidValidator extends ConstraintValidator */ public function validate($value, Constraint $constraint) { - if (null === $value || '' === $value) { - return; - } - if (!$constraint instanceof Uuid) { throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Uuid'); } + if (null === $value || '' === $value) { + return; + } + if (!is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) { throw new UnexpectedTypeException($value, 'string'); } From 40f26235eb1926ac7f168b8b7fef4c6a6c571859 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sun, 18 Nov 2018 22:25:02 +0100 Subject: [PATCH 19/31] [cs] correct invalid @param types --- src/Symfony/Component/Config/ConfigCacheFactoryInterface.php | 2 +- src/Symfony/Component/Config/Definition/ArrayNode.php | 2 +- src/Symfony/Component/Config/Definition/BaseNode.php | 2 +- .../Component/Config/Definition/Builder/NodeDefinition.php | 2 +- src/Symfony/Component/Console/Helper/TableStyle.php | 2 +- src/Symfony/Component/Console/Input/InputAwareInterface.php | 2 -- src/Symfony/Component/EventDispatcher/GenericEvent.php | 2 +- .../Component/HttpKernel/EventListener/ExceptionListener.php | 2 +- src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php | 2 +- src/Symfony/Component/Routing/Router.php | 2 +- .../Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php | 3 --- src/Symfony/Component/Templating/PhpEngine.php | 2 +- src/Symfony/Component/Translation/DataCollectorTranslator.php | 2 +- src/Symfony/Component/Translation/Dumper/FileDumper.php | 2 +- src/Symfony/Component/Translation/LoggingTranslator.php | 2 +- src/Symfony/Component/Translation/Translator.php | 2 +- 16 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/Symfony/Component/Config/ConfigCacheFactoryInterface.php b/src/Symfony/Component/Config/ConfigCacheFactoryInterface.php index bd614c4b6b393..8e80142b7816d 100644 --- a/src/Symfony/Component/Config/ConfigCacheFactoryInterface.php +++ b/src/Symfony/Component/Config/ConfigCacheFactoryInterface.php @@ -26,7 +26,7 @@ interface ConfigCacheFactoryInterface * @param string $file The absolute cache file path * @param callable $callable The callable to be executed when the cache needs to be filled (i. e. is not fresh). The cache will be passed as the only parameter to this callback * - * @return ConfigCacheInterface $configCache The cache instance + * @return ConfigCacheInterface The cache instance */ public function cache($file, $callable); } diff --git a/src/Symfony/Component/Config/Definition/ArrayNode.php b/src/Symfony/Component/Config/Definition/ArrayNode.php index 13fe1c94bca5c..86eacae40b3d3 100644 --- a/src/Symfony/Component/Config/Definition/ArrayNode.php +++ b/src/Symfony/Component/Config/Definition/ArrayNode.php @@ -92,7 +92,7 @@ public function setXmlRemappings(array $remappings) /** * Gets the xml remappings that should be performed. * - * @return array $remappings an array of the form array(array(string, string)) + * @return array an array of the form array(array(string, string)) */ public function getXmlRemappings() { diff --git a/src/Symfony/Component/Config/Definition/BaseNode.php b/src/Symfony/Component/Config/Definition/BaseNode.php index c78b6e762738c..7ca956e2113b0 100644 --- a/src/Symfony/Component/Config/Definition/BaseNode.php +++ b/src/Symfony/Component/Config/Definition/BaseNode.php @@ -245,7 +245,7 @@ final public function normalize($value) * * @param $value * - * @return $value The normalized array value + * @return The normalized array value */ protected function preNormalize($value) { diff --git a/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php index f94d3f01f803a..a14161f082b9d 100644 --- a/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php @@ -327,7 +327,7 @@ protected function normalization() /** * Instantiate and configure the node according to this definition. * - * @return NodeInterface $node The node instance + * @return NodeInterface The node instance * * @throws InvalidDefinitionException When the definition is invalid */ diff --git a/src/Symfony/Component/Console/Helper/TableStyle.php b/src/Symfony/Component/Console/Helper/TableStyle.php index 117e311ebe950..0ee9dd16710c0 100644 --- a/src/Symfony/Component/Console/Helper/TableStyle.php +++ b/src/Symfony/Component/Console/Helper/TableStyle.php @@ -125,7 +125,7 @@ public function setCrossingChar($crossingChar) /** * Gets crossing character. * - * @return string $crossingChar + * @return string */ public function getCrossingChar() { diff --git a/src/Symfony/Component/Console/Input/InputAwareInterface.php b/src/Symfony/Component/Console/Input/InputAwareInterface.php index d0f11e986a3b8..5a288de5d45fa 100644 --- a/src/Symfony/Component/Console/Input/InputAwareInterface.php +++ b/src/Symfony/Component/Console/Input/InputAwareInterface.php @@ -21,8 +21,6 @@ interface InputAwareInterface { /** * Sets the Console Input. - * - * @param InputInterface */ public function setInput(InputInterface $input); } diff --git a/src/Symfony/Component/EventDispatcher/GenericEvent.php b/src/Symfony/Component/EventDispatcher/GenericEvent.php index 95c99408de20f..f0be7e18ff3c3 100644 --- a/src/Symfony/Component/EventDispatcher/GenericEvent.php +++ b/src/Symfony/Component/EventDispatcher/GenericEvent.php @@ -38,7 +38,7 @@ public function __construct($subject = null, array $arguments = array()) /** * Getter for subject property. * - * @return mixed $subject The observer subject + * @return mixed The observer subject */ public function getSubject() { diff --git a/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php b/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php index f033e452d6592..16de17816912c 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php @@ -112,7 +112,7 @@ protected function logException(\Exception $exception, $message) * @param \Exception $exception The thrown exception * @param Request $request The original request * - * @return Request $request The cloned request + * @return Request The cloned request */ protected function duplicateRequest(\Exception $exception, Request $request) { diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index f54c87aa4a192..1ccef00563779 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -93,7 +93,7 @@ public function __construct(HttpKernelInterface $kernel, StoreInterface $store, /** * Gets the current store. * - * @return StoreInterface $store A StoreInterface instance + * @return StoreInterface A StoreInterface instance */ public function getStore() { diff --git a/src/Symfony/Component/Routing/Router.php b/src/Symfony/Component/Routing/Router.php index bfb1297a7efe5..6de154dd0a296 100644 --- a/src/Symfony/Component/Routing/Router.php +++ b/src/Symfony/Component/Routing/Router.php @@ -387,7 +387,7 @@ public function getMatcherDumperInstance() * Provides the ConfigCache factory implementation, falling back to a * default implementation if necessary. * - * @return ConfigCacheFactoryInterface $configCacheFactory + * @return ConfigCacheFactoryInterface */ private function getConfigCacheFactory() { diff --git a/src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php b/src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php index 29438cc4d8a06..d2894e7b09d03 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php @@ -331,9 +331,6 @@ public function getRouteCollections() ); } - /** - * @param $dumper - */ private function generateDumpedMatcher(RouteCollection $collection, $redirectableStub = false) { $options = array('class' => $this->matcherClass); diff --git a/src/Symfony/Component/Templating/PhpEngine.php b/src/Symfony/Component/Templating/PhpEngine.php index c41895dcd80de..5ab5c28036d0c 100644 --- a/src/Symfony/Component/Templating/PhpEngine.php +++ b/src/Symfony/Component/Templating/PhpEngine.php @@ -366,7 +366,7 @@ public function setEscaper($context, $escaper) * * @param string $context The context name * - * @return callable $escaper A PHP callable + * @return callable A PHP callable * * @throws \InvalidArgumentException */ diff --git a/src/Symfony/Component/Translation/DataCollectorTranslator.php b/src/Symfony/Component/Translation/DataCollectorTranslator.php index 2916eb9688def..0d2ecb97b515e 100644 --- a/src/Symfony/Component/Translation/DataCollectorTranslator.php +++ b/src/Symfony/Component/Translation/DataCollectorTranslator.php @@ -88,7 +88,7 @@ public function getCatalogue($locale = null) /** * Gets the fallback locales. * - * @return array $locales The fallback locales + * @return array The fallback locales */ public function getFallbackLocales() { diff --git a/src/Symfony/Component/Translation/Dumper/FileDumper.php b/src/Symfony/Component/Translation/Dumper/FileDumper.php index ad37cbee3f603..71bf8a26db560 100644 --- a/src/Symfony/Component/Translation/Dumper/FileDumper.php +++ b/src/Symfony/Component/Translation/Dumper/FileDumper.php @@ -51,7 +51,7 @@ public function setRelativePathTemplate($relativePathTemplate) /** * Sets backup flag. * - * @param bool + * @param bool $backup */ public function setBackup($backup) { diff --git a/src/Symfony/Component/Translation/LoggingTranslator.php b/src/Symfony/Component/Translation/LoggingTranslator.php index 1cefcb6b99196..50a878092effc 100644 --- a/src/Symfony/Component/Translation/LoggingTranslator.php +++ b/src/Symfony/Component/Translation/LoggingTranslator.php @@ -88,7 +88,7 @@ public function getCatalogue($locale = null) /** * Gets the fallback locales. * - * @return array $locales The fallback locales + * @return array The fallback locales */ public function getFallbackLocales() { diff --git a/src/Symfony/Component/Translation/Translator.php b/src/Symfony/Component/Translation/Translator.php index a3aacd4b16650..5310b16615aa5 100644 --- a/src/Symfony/Component/Translation/Translator.php +++ b/src/Symfony/Component/Translation/Translator.php @@ -181,7 +181,7 @@ public function setFallbackLocales(array $locales) /** * Gets the fallback locales. * - * @return array $locales The fallback locales + * @return array The fallback locales */ public function getFallbackLocales() { From f4866bc371cade6a3f492718a1fe0115990b36ad Mon Sep 17 00:00:00 2001 From: Sascha Dens Date: Wed, 21 Nov 2018 13:53:33 +0100 Subject: [PATCH 20/31] [HttpFoundation] Fix trailing space for mime-type with parameters --- src/Symfony/Component/HttpFoundation/Request.php | 2 +- src/Symfony/Component/HttpFoundation/Tests/RequestTest.php | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 49121d465148d..51187d8557aa2 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1326,7 +1326,7 @@ public function getFormat($mimeType) { $canonicalMimeType = null; if (false !== $pos = strpos($mimeType, ';')) { - $canonicalMimeType = substr($mimeType, 0, $pos); + $canonicalMimeType = trim(substr($mimeType, 0, $pos)); } if (null === static::$formats) { diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 26335db903dd0..ac6f1a74ed0c8 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -323,6 +323,9 @@ public function testGetFormatFromMimeTypeWithParameters() { $request = new Request(); $this->assertEquals('json', $request->getFormat('application/json; charset=utf-8')); + $this->assertEquals('json', $request->getFormat('application/json;charset=utf-8')); + $this->assertEquals('json', $request->getFormat('application/json ; charset=utf-8')); + $this->assertEquals('json', $request->getFormat('application/json ;charset=utf-8')); } public function testGetFormatWithCustomMimeType() From 24264facaaa3afe29a4f0d615a1c5184c67a5b72 Mon Sep 17 00:00:00 2001 From: ValentineBoineau <34517639+ValentineBoineau@users.noreply.github.com> Date: Wed, 21 Nov 2018 15:20:20 +0100 Subject: [PATCH 21/31] Indentation error --- .../EventDispatcher/Tests/AbstractEventDispatcherTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php index 1d1c3da99c4a9..48de632130ac6 100644 --- a/src/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php +++ b/src/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php @@ -382,7 +382,7 @@ public static function getSubscribedEvents() return array( 'pre.foo' => array('preFoo', 10), 'post.foo' => array('postFoo'), - ); + ); } } From 0248d4f566bf37a12d26bb792d830e491b76185e Mon Sep 17 00:00:00 2001 From: pf Date: Sat, 19 May 2018 16:39:38 +0300 Subject: [PATCH 22/31] [DoctrineBridge] fix case sensitivity issue in RememberMe\DoctrineTokenProvider --- .../Doctrine/Security/RememberMe/DoctrineTokenProvider.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php b/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php index c3b7588e95049..94aa593f294cd 100644 --- a/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php +++ b/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php @@ -50,7 +50,8 @@ public function __construct(Connection $conn) */ public function loadTokenBySeries($series) { - $sql = 'SELECT class, username, value, lastUsed' + // the alias for lastUsed works around case insensitivity in PostgreSQL + $sql = 'SELECT class, username, value, lastUsed as last_used' .' FROM rememberme_token WHERE series=:series'; $paramValues = array('series' => $series); $paramTypes = array('series' => \PDO::PARAM_STR); @@ -58,7 +59,7 @@ public function loadTokenBySeries($series) $row = $stmt->fetch(\PDO::FETCH_ASSOC); if ($row) { - return new PersistentToken($row['class'], $row['username'], $series, $row['value'], new \DateTime($row['lastUsed'])); + return new PersistentToken($row['class'], $row['username'], $series, $row['value'], new \DateTime($row['last_used'])); } throw new TokenNotFoundException('No token found.'); From 61c4531292f9c8eb56644875747f10cce09290e3 Mon Sep 17 00:00:00 2001 From: Alessandro Chitolina Date: Sat, 24 Nov 2018 18:30:29 +0100 Subject: [PATCH 23/31] [DoctrineBridge] catch errors while converting to db values in data collector --- .../Doctrine/DataCollector/DoctrineDataCollector.php | 10 +++++++++- .../Tests/DataCollector/DoctrineDataCollectorTest.php | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php b/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php index 38c5af47345ee..65df5c5ea6966 100644 --- a/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php +++ b/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php @@ -13,6 +13,7 @@ use Doctrine\Common\Persistence\ManagerRegistry; use Doctrine\DBAL\Logging\DebugStack; +use Doctrine\DBAL\Types\ConversionException; use Doctrine\DBAL\Types\Type; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -132,7 +133,14 @@ private function sanitizeQuery($connectionName, $query) } if ($type instanceof Type) { $query['types'][$j] = $type->getBindingType(); - $param = $type->convertToDatabaseValue($param, $this->registry->getConnection($connectionName)->getDatabasePlatform()); + try { + $param = $type->convertToDatabaseValue($param, $this->registry->getConnection($connectionName)->getDatabasePlatform()); + } catch (\TypeError $e) { + // Error thrown while processing params, query is not explainable. + $query['explainable'] = false; + } catch (ConversionException $e) { + $query['explainable'] = false; + } } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php b/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php index 7557390bc6ab6..f968bbd257abd 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php @@ -12,6 +12,7 @@ namespace Symfony\Bridge\Doctrine\Tests\DataCollector; use Doctrine\DBAL\Platforms\MySqlPlatform; +use Doctrine\DBAL\Version; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector; use Symfony\Component\HttpFoundation\Request; @@ -120,7 +121,7 @@ public function testSerialization($param, $types, $expected, $explainable) public function paramProvider() { - return array( + $tests = array( array('some value', array(), 'some value', true), array(1, array(), 1, true), array(true, array(), true, true), @@ -129,6 +130,13 @@ public function paramProvider() array(fopen(__FILE__, 'r'), array(), 'Resource(stream)', false), array(new \SplFileInfo(__FILE__), array(), 'Object(SplFileInfo)', false), ); + + if (version_compare(Version::VERSION, '2.6', '>=')) { + $tests[] = array('this is not a date', array('date'), 'this is not a date', false); + $tests[] = array(new \stdClass(), array('date'), 'Object(stdClass)', false); + } + + return $tests; } private function createCollector($queries) From 8633ebbca0c6679d5f77f0cfae6659bf731741bf Mon Sep 17 00:00:00 2001 From: Chris Wilkinson Date: Sat, 24 Nov 2018 21:16:41 +0000 Subject: [PATCH 24/31] Use XLIFF source rather than resname when there's no target --- src/Symfony/Component/Translation/Loader/XliffFileLoader.php | 2 +- .../Component/Translation/Tests/Loader/XliffFileLoaderTest.php | 2 +- src/Symfony/Component/Translation/Tests/fixtures/resname.xlf | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php index 4ad35decd1fb5..1c7cba6ee8f86 100644 --- a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php @@ -90,7 +90,7 @@ private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, $ $source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source; // If the xlf file has another encoding specified, try to convert it because // simple_xml will always return utf-8 encoded values - $target = $this->utf8ToCharset((string) (isset($translation->target) ? $translation->target : $source), $encoding); + $target = $this->utf8ToCharset((string) (isset($translation->target) ? $translation->target : $translation->source), $encoding); $catalogue->set((string) $source, $target, $domain); diff --git a/src/Symfony/Component/Translation/Tests/Loader/XliffFileLoaderTest.php b/src/Symfony/Component/Translation/Tests/Loader/XliffFileLoaderTest.php index dc9a06bcb8427..29bec0e61227e 100644 --- a/src/Symfony/Component/Translation/Tests/Loader/XliffFileLoaderTest.php +++ b/src/Symfony/Component/Translation/Tests/Loader/XliffFileLoaderTest.php @@ -66,7 +66,7 @@ public function testLoadWithResname() $loader = new XliffFileLoader(); $catalogue = $loader->load(__DIR__.'/../fixtures/resname.xlf', 'en', 'domain1'); - $this->assertEquals(array('foo' => 'bar', 'bar' => 'baz', 'baz' => 'foo'), $catalogue->all('domain1')); + $this->assertEquals(array('foo' => 'bar', 'bar' => 'baz', 'baz' => 'foo', 'qux' => 'qux source'), $catalogue->all('domain1')); } public function testIncompleteResource() diff --git a/src/Symfony/Component/Translation/Tests/fixtures/resname.xlf b/src/Symfony/Component/Translation/Tests/fixtures/resname.xlf index 2df16af942f43..4fa5c0017eff0 100644 --- a/src/Symfony/Component/Translation/Tests/fixtures/resname.xlf +++ b/src/Symfony/Component/Translation/Tests/fixtures/resname.xlf @@ -14,6 +14,9 @@ baz foo + + qux source + From 19e3e154d7dd6594d82d9a749ccd2c429d110a52 Mon Sep 17 00:00:00 2001 From: Sergei Gorjunov Date: Tue, 7 Aug 2018 01:10:32 +0300 Subject: [PATCH 25/31] [DomCrawler] exclude fields inside "template" tags --- src/Symfony/Component/DomCrawler/Form.php | 4 ++-- src/Symfony/Component/DomCrawler/Tests/FormTest.php | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/DomCrawler/Form.php b/src/Symfony/Component/DomCrawler/Form.php index fc0927f8ac8f0..5b361982fdbca 100644 --- a/src/Symfony/Component/DomCrawler/Form.php +++ b/src/Symfony/Component/DomCrawler/Form.php @@ -433,14 +433,14 @@ private function initialize() // corresponding elements are either descendants or have a matching HTML5 form attribute $formId = Crawler::xpathLiteral($this->node->getAttribute('id')); - $fieldNodes = $xpath->query(sprintf('descendant::input[@form=%s] | descendant::button[@form=%1$s] | descendant::textarea[@form=%1$s] | descendant::select[@form=%1$s] | //form[@id=%1$s]//input[not(@form)] | //form[@id=%1$s]//button[not(@form)] | //form[@id=%1$s]//textarea[not(@form)] | //form[@id=%1$s]//select[not(@form)]', $formId)); + $fieldNodes = $xpath->query(sprintf('( descendant::input[@form=%s] | descendant::button[@form=%1$s] | descendant::textarea[@form=%1$s] | descendant::select[@form=%1$s] | //form[@id=%1$s]//input[not(@form)] | //form[@id=%1$s]//button[not(@form)] | //form[@id=%1$s]//textarea[not(@form)] | //form[@id=%1$s]//select[not(@form)] )[not(ancestor::template)]', $formId)); foreach ($fieldNodes as $node) { $this->addField($node); } } else { // do the xpath query with $this->node as the context node, to only find descendant elements // however, descendant elements with form attribute are not part of this form - $fieldNodes = $xpath->query('descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)]', $this->node); + $fieldNodes = $xpath->query('( descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)] )[not(ancestor::template)]', $this->node); foreach ($fieldNodes as $node) { $this->addField($node); } diff --git a/src/Symfony/Component/DomCrawler/Tests/FormTest.php b/src/Symfony/Component/DomCrawler/Tests/FormTest.php index 02d2ebe593b08..7e259de196310 100644 --- a/src/Symfony/Component/DomCrawler/Tests/FormTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/FormTest.php @@ -394,6 +394,10 @@ public function testGetValues() $form = $this->createForm('
'); $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include disabled fields'); + + $form = $this->createForm('
'); + $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include template fields'); + $this->assertFalse($form->has('foo')); } public function testSetValues() @@ -444,6 +448,10 @@ public function testGetFiles() $form = $this->createForm('
'); $this->assertEquals(array(), $form->getFiles(), '->getFiles() does not include disabled file fields'); + + $form = $this->createForm('
'); + $this->assertEquals(array(), $form->getFiles(), '->getFiles() does not include template file fields'); + $this->assertFalse($form->has('foo')); } public function testGetPhpFiles() @@ -857,7 +865,7 @@ protected function getFormFieldMock($name, $value = null) protected function createForm($form, $method = null, $currentUri = null) { $dom = new \DOMDocument(); - $dom->loadHTML(''.$form.''); + @$dom->loadHTML(''.$form.''); $xPath = new \DOMXPath($dom); $nodes = $xPath->query('//input | //button'); From 9da998005be13118b71fc38e1babca1079b17709 Mon Sep 17 00:00:00 2001 From: Tom Counsell Date: Fri, 23 Nov 2018 15:43:18 +0000 Subject: [PATCH 26/31] Doc fix: clarify isMethodCacheable() returns true only for GET & HEAD The current documentation points to https://tools.ietf.org/html/rfc7231#section-4.2.3. The spec says: "this specification defines GET, HEAD, and POST as cacheable, although the overwhelming majority of cache implementations only support GET and HEAD.". This fix to the documentation clarifies that Symfony follows majority (excluding POST) rather than the spec (including POST). --- src/Symfony/Component/HttpFoundation/Request.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 51187d8557aa2..e025993c6d244 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1475,7 +1475,7 @@ public function isMethodSafe(/* $andCacheable = true */) * * @see https://tools.ietf.org/html/rfc7231#section-4.2.3 * - * @return bool + * @return bool True for GET and HEAD, false otherwise */ public function isMethodCacheable() { From b3742694d9a5f3d04af9b6325d518e4d83f8bfd8 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Sun, 25 Nov 2018 13:33:53 +0200 Subject: [PATCH 27/31] Changes for upcoming Travis' infra migration Travis is deprecating the sudo keyword and moves everything to the same infrastructure (sudo really selects between two infrastructures). See: https://blog.travis-ci.com/2018-11-19-required-linux-infrastructure-migration --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2556f94e6e883..f359dea238156 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: php dist: trusty -sudo: false git: depth: 2 From 18b3a52389a40620ec8a0f4e46fdbf37466c52ad Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 26 Nov 2018 08:26:42 +0100 Subject: [PATCH 28/31] fixed CS --- .../Doctrine/Security/RememberMe/DoctrineTokenProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php b/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php index 94aa593f294cd..5e41b10e14bb2 100644 --- a/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php +++ b/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php @@ -51,7 +51,7 @@ public function __construct(Connection $conn) public function loadTokenBySeries($series) { // the alias for lastUsed works around case insensitivity in PostgreSQL - $sql = 'SELECT class, username, value, lastUsed as last_used' + $sql = 'SELECT class, username, value, lastUsed AS last_used' .' FROM rememberme_token WHERE series=:series'; $paramValues = array('series' => $series); $paramTypes = array('series' => \PDO::PARAM_STR); From 1c548442858d083e68fcc4a8163ef9168f14f273 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 26 Nov 2018 14:20:12 +0100 Subject: [PATCH 29/31] updated CHANGELOG for 2.8.48 --- CHANGELOG-2.8.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG-2.8.md b/CHANGELOG-2.8.md index 358b477b1467b..aa884108b6dd5 100644 --- a/CHANGELOG-2.8.md +++ b/CHANGELOG-2.8.md @@ -7,6 +7,21 @@ in 2.8 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.8.0...v2.8.1 +* 2.8.48 (2018-11-26) + + * bug #28917 [DoctrineBridge] catch errors while converting to db values in data collector (alekitto) + * bug #27314 [DoctrineBridge] fix case sensitivity issue in RememberMe\DoctrineTokenProvider (PF4Public) + * bug #29308 [Translation] Use XLIFF source rather than resname when there's no target (thewilkybarkid) + * bug #26244 [BrowserKit] fixed BC Break for HTTP_HOST header (brizzz) + * bug #28147 [DomCrawler] exclude fields inside "template" tags (Gorjunov) + * bug #29271 [HttpFoundation] Fix trailing space for mime-type with parameters (Sascha Dens) + * bug #29223 [Validator] Added the missing constraints instance checks (thomasbisignani) + * bug #29182 [Form] Fixed empty data for compound date types (HeahDude) + * bug #29185 [Form] Fixed keeping hash of equal \DateTimeInterface on submit (HeahDude) + * bug #28731 [Form] invalidate forms on transformation failures (xabbuh) + * bug #29152 [Config] Unset key during normalization (ro0NL) + * bug #29057 [HttpFoundation] replace any preexisting Content-Type headers (nicolas-grekas) + * 2.8.47 (2018-11-03) * bug #29020 Fix ini_get() for boolean values (deguif) From a84a4488646ced1f468838fabad0c57bc2d1b973 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 26 Nov 2018 14:20:17 +0100 Subject: [PATCH 30/31] update CONTRIBUTORS for 2.8.48 --- CONTRIBUTORS.md | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 969c6a424cefc..1c329f2146c3e 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -14,9 +14,9 @@ Symfony is the result of the work of many people who made the code better - Victor Berchet (victor) - Robin Chalas (chalas_r) - Kévin Dunglas (dunglas) + - Maxime Steinhausser (ogizanagi) - Jakub Zalas (jakubzalas) - Johannes S (johannes) - - Maxime Steinhausser (ogizanagi) - Kris Wallsmith (kriswallsmith) - Ryan Weaver (weaverryan) - Javier Eguiluz (javier.eguiluz) @@ -37,8 +37,8 @@ Symfony is the result of the work of many people who made the code better - Benjamin Eberlei (beberlei) - Igor Wiedler (igorw) - Jules Pietri (heah) - - Eriksen Costa (eriksencosta) - Yonel Ceruto (yonelceruto) + - Eriksen Costa (eriksencosta) - Guilhem Niot (energetick) - Sarah Khalil (saro0h) - Jonathan Wage (jwage) @@ -70,6 +70,7 @@ Symfony is the result of the work of many people who made the code better - Gábor Egyed (1ed) - Mathieu Piot (mpiot) - Titouan Galopin (tgalopin) + - Vladimir Reznichenko (kalessil) - Michel Weimerskirch (mweimerskirch) - Andrej Hudec (pulzarraider) - Konstantin Myakshin (koc) @@ -77,7 +78,6 @@ Symfony is the result of the work of many people who made the code better - Jáchym Toušek (enumag) - Charles Sarrazin (csarrazi) - David Maicher (dmaicher) - - Vladimir Reznichenko (kalessil) - Christian Raue - Issei Murasawa (issei_m) - Arnout Boks (aboks) @@ -86,13 +86,13 @@ Symfony is the result of the work of many people who made the code better - Dariusz Górecki (canni) - Douglas Greenshields (shieldo) - Dariusz Ruminski + - Grégoire Paris (greg0ire) - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) - Graham Campbell (graham) - Daniel Holmes (dholmes) - Toni Uebernickel (havvg) - - Grégoire Paris (greg0ire) - Bart van den Burg (burgov) - Jordan Alliot (jalliot) - Jérôme Tamarelle (gromnan) @@ -113,12 +113,12 @@ Symfony is the result of the work of many people who made the code better - lenar - Alexander Schwenn (xelaris) - Włodzimierz Gajda (gajdaw) + - Tomáš Votruba (tomas_votruba) - Peter Kokot (maastermedia) - Jacob Dreesen (jdreesen) - Florian Voutzinos (florianv) - Colin Frei - Adrien Brault (adrienbrault) - - Tomáš Votruba (tomas_votruba) - Joshua Thijssen - excelwebzone - Gordon Franke (gimler) @@ -179,6 +179,7 @@ Symfony is the result of the work of many people who made the code better - jeremyFreeAgent (Jérémy Romey) (jeremyfreeagent) - James Halsall (jaitsu) - Matthieu Napoli (mnapoli) + - Florent Mata (fmata) - Warnar Boekkooi (boekkooi) - Alessandro Chitolina (alekitto) - Dmitrii Chekaliuk (lazyhammer) @@ -193,7 +194,6 @@ Symfony is the result of the work of many people who made the code better - Dennis Benkert (denderello) - DQNEO - Benjamin Dulau (dbenjamin) - - Florent Mata (fmata) - Mathieu Lemoine (lemoinem) - Thomas Calvet (fancyweb) - Christian Schmidt @@ -258,6 +258,7 @@ Symfony is the result of the work of many people who made the code better - Benoît Burnichon (bburnichon) - Roman Marintšenko (inori) - Xavier Montaña Carreras (xmontana) + - François-Xavier de Guillebon (de-gui_f) - Mickaël Andrieu (mickaelandrieu) - Maxime Veber (nek-) - Xavier Perez @@ -294,7 +295,9 @@ Symfony is the result of the work of many people who made the code better - Thomas Lallement (raziel057) - mcfedr (mcfedr) - Colin O'Dell (colinodell) + - Fabien Bourigault (fbourigault) - Giorgio Premi + - Jan Schädlich (jschaedl) - Beau Simensen (simensen) - Michael Hirschler (mvhirsch) - Robert Kiss (kepten) @@ -305,8 +308,8 @@ Symfony is the result of the work of many people who made the code better - Jérôme Parmentier (lctrs) - Michael Babker (mbabker) - Peter Kruithof (pkruithof) - - François-Xavier de Guillebon (de-gui_f) - Michael Holm (hollo) + - Remon van de Kamp (rpkamp) - Marc Weistroff (futurecat) - Christian Schmidt - MatTheCat @@ -350,8 +353,6 @@ Symfony is the result of the work of many people who made the code better - Ricard Clau (ricardclau) - Mark Challoner (markchalloner) - Gennady Telegin (gtelegin) - - Jan Schädlich (jschaedl) - - Fabien Bourigault (fbourigault) - Ben Davies (bendavies) - Erin Millard - Artur Melo (restless) @@ -445,6 +446,7 @@ Symfony is the result of the work of many people who made the code better - lancergr - Zan Baldwin - Mihai Stancu + - Ivan Nikolaev (destillat) - Olivier Dolbeau (odolbeau) - Jan Rosier (rosier) - Alessandro Lai (jean85) @@ -461,7 +463,6 @@ Symfony is the result of the work of many people who made the code better - Boris Vujicic (boris.vujicic) - Chris Sedlmayr (catchamonkey) - Mateusz Sip (mateusz_sip) - - Remon van de Kamp - Kamil Kokot (pamil) - Seb Koelen - Christoph Mewes (xrstf) @@ -470,6 +471,7 @@ Symfony is the result of the work of many people who made the code better - Dirk Pahl (dirkaholic) - cedric lombardot (cedriclombardot) - Jonas Flodén (flojon) + - Gonzalo Vilaseca (gonzalovilaseca) - Marcin Sikoń (marphi) - Dominik Zogg (dominik.zogg) - Marek Pietrzak @@ -643,7 +645,6 @@ Symfony is the result of the work of many people who made the code better - adev - Stefan Warman - Arkadius Stefanski (arkadius) - - Gonzalo Vilaseca (gonzalovilaseca) - Tristan Maindron (tmaindron) - Wesley Lancel - Ke WANG (yktd26) @@ -665,6 +666,7 @@ Symfony is the result of the work of many people who made the code better - Sergey (upyx) - Michael Devery (mickadoo) - Antoine Corcy + - Dmitrii Poddubnyi (karser) - Sascha Grossenbacher - Szijarto Tamas - Robin Lehrmann (robinlehrmann) @@ -804,7 +806,6 @@ Symfony is the result of the work of many people who made the code better - Sofiane HADDAG (sofhad) - frost-nzcr4 - Bozhidar Hristov - - Ivan Nikolaev (destillat) - Laurent Bassin (lbassin) - andrey1s - Abhoryo @@ -989,6 +990,7 @@ Symfony is the result of the work of many people who made the code better - Mathieu Santostefano - Arjan Keeman - Máximo Cuadros (mcuadros) + - Lukas Mencl - tamirvs - julien.galenski - Christian Neff @@ -1290,6 +1292,7 @@ Symfony is the result of the work of many people who made the code better - Adrien Samson (adriensamson) - Samuel Gordalina (gordalina) - Max Romanovsky (maxromanovsky) + - Nicolas Eeckeloo (neeckeloo) - Mathieu Morlon - Daniel Tschinder - Arnaud CHASSEUX @@ -1351,6 +1354,7 @@ Symfony is the result of the work of many people who made the code better - Andrew (drew) - kor3k kor3k (kor3k) - Stelian Mocanita (stelian) + - Thomas Bisignani (toma) - Justin (wackymole) - Flavian (2much) - Gautier Deuette @@ -1448,6 +1452,7 @@ Symfony is the result of the work of many people who made the code better - Phobetor - Andreas - Markus + - Daniel Gorgan - Thomas Chmielowiec - shdev - Andrey Ryaguzov @@ -1499,6 +1504,7 @@ Symfony is the result of the work of many people who made the code better - David Barratt - Pavel.Batanov - avi123 + - Pavel Prischepa - alsar - downace - Aarón Nieves Fernández @@ -1613,6 +1619,7 @@ Symfony is the result of the work of many people who made the code better - David Zuelke - Adrian - Oleg Andreyev + - neFAST - Pierre Rineau - Maxim Lovchikov - adenkejawen @@ -1710,7 +1717,6 @@ Symfony is the result of the work of many people who made the code better - Giovanni Albero (johntree) - Jorge Martin (jorgemartind) - Joeri Verdeyen (jverdeyen) - - Dmitrii Poddubnyi (karser) - Kevin Verschaeve (keversc) - Kevin Herrera (kherge) - Luis Ramón López López (lrlopez) @@ -1872,6 +1878,7 @@ Symfony is the result of the work of many people who made the code better - zorn - Yuriy Potemkin - Emilie Lorenzo + - Edvin Hultberg - Benjamin Long - Matt Janssen - Ben Miller @@ -1992,6 +1999,7 @@ Symfony is the result of the work of many people who made the code better - Alex Carol (picard89) - Daniel Perez Pinazo (pitiflautico) - Phil Taylor (prazgod) + - Maxim Pustynnikov (pustynnikov) - Brayden Williams (redstar504) - Rich Sage (richsage) - Rokas Mikalkėnas (rokasm) From a1a21f97a2189944b3b1907ac8b733df01a4132a Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 26 Nov 2018 14:20:43 +0100 Subject: [PATCH 31/31] updated VERSION for 2.8.48 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index e91c1d7d4712a..b35ddef8b1cc4 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,12 +59,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.8.48-DEV'; + const VERSION = '2.8.48'; const VERSION_ID = 20848; const MAJOR_VERSION = 2; const MINOR_VERSION = 8; const RELEASE_VERSION = 48; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '11/2018'; const END_OF_LIFE = '11/2019';