From ebf967de8f9cc2fe8ec10b9fa1243f5bfdf0385f Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Sun, 6 Jul 2014 15:41:58 +0200 Subject: [PATCH 1/8] [Form] Check if IntlDateFormatter constructor returned a valid object before using it --- .../DateTimeToLocalizedStringTransformer.php | 8 ++++++++ .../Component/Form/Extension/Core/Type/DateType.php | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php index 56a3ca928b..e992b01fc1 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php @@ -152,6 +152,8 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer * Returns a preconfigured IntlDateFormatter instance * * @return \IntlDateFormatter + * + * @throws TransformationFailedException in case the date formatter can not be constructed. */ protected function getIntlDateFormatter() { @@ -162,6 +164,12 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer $pattern = $this->pattern; $intlDateFormatter = new \IntlDateFormatter(\Locale::getDefault(), $dateFormat, $timeFormat, $timezone, $calendar, $pattern); + + // new \intlDateFormatter may return null instead of false in case of failure, see https://bugs.php.net/bug.php?id=66323 + if (!$intlDateFormatter) { + throw new TransformationFailedException(intl_get_error_message(), intl_get_error_code()); + } + $intlDateFormatter->setLenient(false); return $intlDateFormatter; diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php index 022a414932..1748d2e31f 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php @@ -77,6 +77,12 @@ class DateType extends AbstractType $calendar, $pattern ); + + // new \intlDateFormatter may return null instead of false in case of failure, see https://bugs.php.net/bug.php?id=66323 + if (!$formatter) { + throw new InvalidOptionsException(intl_get_error_message(), intl_get_error_code()); + } + $formatter->setLenient(false); if ('choice' === $options['widget']) { From ff0bb01a9180f39876c4d73c5ff7fc043e84eccf Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Sat, 14 Jun 2014 14:38:29 +0200 Subject: [PATCH 2/8] [Process] Reduce I/O load on Windows platform --- src/Symfony/Component/Process/ProcessPipes.php | 2 ++ .../Component/Process/Tests/AbstractProcessTest.php | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Process/ProcessPipes.php b/src/Symfony/Component/Process/ProcessPipes.php index f35d1c1287..8b11a930fc 100644 --- a/src/Symfony/Component/Process/ProcessPipes.php +++ b/src/Symfony/Component/Process/ProcessPipes.php @@ -284,6 +284,8 @@ class ProcessPipes private function readStreams($blocking, $close = false) { if (empty($this->pipes)) { + usleep(Process::TIMEOUT_PRECISION * 1E4); + return array(); } diff --git a/src/Symfony/Component/Process/Tests/AbstractProcessTest.php b/src/Symfony/Component/Process/Tests/AbstractProcessTest.php index 91b8aca20d..d5bb967972 100644 --- a/src/Symfony/Component/Process/Tests/AbstractProcessTest.php +++ b/src/Symfony/Component/Process/Tests/AbstractProcessTest.php @@ -583,7 +583,14 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase } $duration = microtime(true) - $start; - $this->assertLessThan($timeout + Process::TIMEOUT_PRECISION, $duration); + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + // Windows is a bit slower as it read file handles, then allow twice the precision + $maxDuration = $timeout + 2 * Process::TIMEOUT_PRECISION; + } else { + $maxDuration = $timeout + Process::TIMEOUT_PRECISION; + } + + $this->assertLessThan($maxDuration, $duration); } public function testCheckTimeoutOnNonStartedProcess() From 3176f8bb98c9634be77d4b5b50df054a47f2079c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Muszy=C5=84ski?= Date: Wed, 16 Jul 2014 21:26:05 +0200 Subject: [PATCH 3/8] [Translator][FrameworkBundle] Added @ to the list of allowed chars in Translator --- .../Tests/Translation/TranslatorTest.php | 17 ++++++++++++++--- .../Translation/Tests/TranslatorTest.php | 3 +-- .../Component/Translation/Translator.php | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php index 715c44fade..9f2012816a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php @@ -45,7 +45,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase { $translator = $this->getTranslator($this->getLoader()); $translator->setLocale('fr'); - $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8')); + $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); $this->assertEquals('foo (FR)', $translator->trans('foo')); $this->assertEquals('bar (EN)', $translator->trans('bar')); @@ -55,6 +55,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); + $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); } public function testTransWithCaching() @@ -62,7 +63,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase // prime the cache $translator = $this->getTranslator($this->getLoader(), array('cache_dir' => $this->tmpDir)); $translator->setLocale('fr'); - $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8')); + $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); $this->assertEquals('foo (FR)', $translator->trans('foo')); $this->assertEquals('bar (EN)', $translator->trans('bar')); @@ -72,12 +73,13 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); + $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); // do it another time as the cache is primed now $loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface'); $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir)); $translator->setLocale('fr'); - $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8')); + $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); $this->assertEquals('foo (FR)', $translator->trans('foo')); $this->assertEquals('bar (EN)', $translator->trans('bar')); @@ -87,6 +89,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); + $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); } public function testGetLocale() @@ -185,6 +188,13 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase 'foobarbaz' => 'foobarbaz (fr.UTF-8)', )))) ; + $loader + ->expects($this->at(6)) + ->method('load') + ->will($this->returnValue($this->getCatalogue('sr@latin', array( + 'foobarbax' => 'foobarbax (sr@latin)', + )))) + ; return $loader; } @@ -216,6 +226,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase $translator->addResource('loader', 'foo', 'pt-PT'); // European Portuguese $translator->addResource('loader', 'foo', 'pt_BR'); // Brazilian Portuguese $translator->addResource('loader', 'foo', 'fr.UTF-8'); + $translator->addResource('loader', 'foo', 'sr@latin'); // Latin Serbian return $translator; } diff --git a/src/Symfony/Component/Translation/Tests/TranslatorTest.php b/src/Symfony/Component/Translation/Tests/TranslatorTest.php index 40a0b1d0d9..cf9c8df659 100644 --- a/src/Symfony/Component/Translation/Tests/TranslatorTest.php +++ b/src/Symfony/Component/Translation/Tests/TranslatorTest.php @@ -103,7 +103,6 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase $this->assertEquals('bar (fr)', $translator->trans('bar')); } - /** * @dataProvider getInvalidLocalesTests * @expectedException \InvalidArgumentException @@ -329,7 +328,6 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase // no assertion. this method just asserts that no exception is thrown } - public function getTransFileTests() { return array( @@ -429,6 +427,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase array('fr_FR'), array('fr.FR'), array('fr-FR.UTF8'), + array('sr@latin'), ); } diff --git a/src/Symfony/Component/Translation/Translator.php b/src/Symfony/Component/Translation/Translator.php index e54b300ffd..8e3cda857c 100644 --- a/src/Symfony/Component/Translation/Translator.php +++ b/src/Symfony/Component/Translation/Translator.php @@ -308,7 +308,7 @@ class Translator implements TranslatorInterface */ private function assertValidLocale($locale) { - if (0 !== preg_match('/[^a-z0-9_\\.\\-]+/i', $locale, $match)) { + if (0 !== preg_match('/[^a-z0-9@_\\.\\-]+/i', $locale, $match)) { throw new \InvalidArgumentException(sprintf('Invalid locale: %s.', $locale)); } } From 291cbf9efa14460a2c77a6d6ca9bbcc2603db639 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Thu, 17 Jul 2014 16:48:59 +0200 Subject: [PATCH 4/8] [Validator] Backported #11410 to 2.3: Object initializers are called only once per object --- .../Validator/Tests/Fixtures/Entity.php | 1 + .../Validator/Tests/ValidationVisitorTest.php | 48 +++++++++++++++++++ .../Component/Validator/ValidationVisitor.php | 17 ++++--- 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php b/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php index e1cb3e0490..f9e9e7ff3b 100644 --- a/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php +++ b/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php @@ -35,6 +35,7 @@ class Entity extends EntityParent implements EntityInterface public $reference; private $internal; public $data = 'Overridden data'; + public $initialized = false; public function __construct($internal = null) { diff --git a/src/Symfony/Component/Validator/Tests/ValidationVisitorTest.php b/src/Symfony/Component/Validator/Tests/ValidationVisitorTest.php index 2868f57a82..f2838737a2 100644 --- a/src/Symfony/Component/Validator/Tests/ValidationVisitorTest.php +++ b/src/Symfony/Component/Validator/Tests/ValidationVisitorTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Tests; +use Symfony\Component\Validator\Constraints\Callback; +use Symfony\Component\Validator\ExecutionContextInterface; use Symfony\Component\Validator\Tests\Fixtures\FakeMetadataFactory; use Symfony\Component\Validator\Constraints\Valid; use Symfony\Component\Validator\Tests\Fixtures\Reference; @@ -561,4 +563,50 @@ class ValidationVisitorTest extends \PHPUnit_Framework_TestCase $this->visitor->validate($entity, 'Default', ''); } + + public function testInitializeObjectsOnFirstValidation() + { + $test = $this; + $entity = new Entity(); + $entity->initialized = false; + + // prepare initializers that set "initialized" to true + $initializer1 = $this->getMock('Symfony\\Component\\Validator\\ObjectInitializerInterface'); + $initializer2 = $this->getMock('Symfony\\Component\\Validator\\ObjectInitializerInterface'); + + $initializer1->expects($this->once()) + ->method('initialize') + ->with($entity) + ->will($this->returnCallback(function ($object) { + $object->initialized = true; + })); + + $initializer2->expects($this->once()) + ->method('initialize') + ->with($entity); + + $this->visitor = new ValidationVisitor('Root', $this->metadataFactory, new ConstraintValidatorFactory(), new DefaultTranslator(), null, array( + $initializer1, + $initializer2 + )); + + // prepare constraint which + // * checks that "initialized" is set to true + // * validates the object again + $callback = function ($object, ExecutionContextInterface $context) use ($test) { + $test->assertTrue($object->initialized); + + // validate again in same group + $context->validate($object); + + // validate again in other group + $context->validate($object, '', 'SomeGroup'); + }; + + $this->metadata->addConstraint(new Callback(array($callback))); + + $this->visitor->validate($entity, 'Default', ''); + + $this->assertTrue($entity->initialized); + } } diff --git a/src/Symfony/Component/Validator/ValidationVisitor.php b/src/Symfony/Component/Validator/ValidationVisitor.php index ddff8adc60..510c61e2eb 100644 --- a/src/Symfony/Component/Validator/ValidationVisitor.php +++ b/src/Symfony/Component/Validator/ValidationVisitor.php @@ -127,16 +127,19 @@ class ValidationVisitor implements ValidationVisitorInterface, GlobalExecutionCo return; } + // Initialize if the object wasn't initialized before + if (!isset($this->validatedObjects[$hash])) { + foreach ($this->objectInitializers as $initializer) { + if (!$initializer instanceof ObjectInitializerInterface) { + throw new \LogicException('Validator initializers must implement ObjectInitializerInterface.'); + } + $initializer->initialize($value); + } + } + // Remember validating this object before starting and possibly // traversing the object graph $this->validatedObjects[$hash][$group] = true; - - foreach ($this->objectInitializers as $initializer) { - if (!$initializer instanceof ObjectInitializerInterface) { - throw new \LogicException('Validator initializers must implement ObjectInitializerInterface.'); - } - $initializer->initialize($value); - } } // Validate arrays recursively by default, otherwise every driver needs From 28424ee79d4f8660f4d5c6a29bc5d794e2c6d786 Mon Sep 17 00:00:00 2001 From: Andreia Bohner Date: Fri, 18 Jul 2014 14:36:11 -0300 Subject: [PATCH 5/8] Add some tweaks to the pt_BR translations --- .../Validator/Resources/translations/validators.pt_BR.xlf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf index f74fe90615..f08cfc8063 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf @@ -280,11 +280,11 @@ The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. - O formato da imagem é muito grande ({{ ratio }}). O formato máximo é {{ max_ratio }}. + A proporção da imagem é muito grande ({{ ratio }}). A proporção máxima permitida é {{ max_ratio }}. The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. - O formato da imagem é muito pequeno ({{ ratio }}). O formato mínimo esperado é {{ min_ratio }}. + A proporção da imagem é muito pequena ({{ ratio }}). A proporção mínima esperada é {{ min_ratio }}. The image is square ({{ width }}x{{ height }}px). Square images are not allowed. @@ -300,7 +300,7 @@ An empty file is not allowed. - Ficheiro vazio não é permitido. + Arquivo vazio não é permitido. From 0c2622e2e7973342981a4f9c0293d11cf3b3df2c Mon Sep 17 00:00:00 2001 From: Pierre-Yves LEBECQ Date: Tue, 22 Jul 2014 15:26:16 +0200 Subject: [PATCH 6/8] [ExpressionLanguage] Fixed an issue with # characters in double quoted string literals --- src/Symfony/Component/ExpressionLanguage/Lexer.php | 2 +- .../Component/ExpressionLanguage/Tests/LexerTest.php | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/ExpressionLanguage/Lexer.php b/src/Symfony/Component/ExpressionLanguage/Lexer.php index aba51bb3fb..26bb51d42e 100644 --- a/src/Symfony/Component/ExpressionLanguage/Lexer.php +++ b/src/Symfony/Component/ExpressionLanguage/Lexer.php @@ -69,7 +69,7 @@ class Lexer $tokens[] = new Token(Token::PUNCTUATION_TYPE, $expression[$cursor], $cursor + 1); ++$cursor; - } elseif (preg_match('/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As', $expression, $match, null, $cursor)) { + } elseif (preg_match('/"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As', $expression, $match, null, $cursor)) { // strings $tokens[] = new Token(Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)), $cursor + 1); $cursor += strlen($match[0]); diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php index 25c1e5ac59..8af5c1c101 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php @@ -78,6 +78,14 @@ class LexerTest extends \PHPUnit_Framework_TestCase array(new Token('operator', '..', 1)), '..', ), + array( + array(new Token('string', '#foo', 1)), + "'#foo'", + ), + array( + array(new Token('string', '#foo', 1)), + '"#foo"', + ), ); } } From 537c39b11ec808ca5c29a50e3367472449a87aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Tue, 22 Jul 2014 23:54:07 +0200 Subject: [PATCH 7/8] Optimize assertLocale regexp --- src/Symfony/Component/Translation/Translator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Translation/Translator.php b/src/Symfony/Component/Translation/Translator.php index 8e3cda857c..d13adc07c6 100644 --- a/src/Symfony/Component/Translation/Translator.php +++ b/src/Symfony/Component/Translation/Translator.php @@ -308,7 +308,7 @@ class Translator implements TranslatorInterface */ private function assertValidLocale($locale) { - if (0 !== preg_match('/[^a-z0-9@_\\.\\-]+/i', $locale, $match)) { + if (1 !== preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) { throw new \InvalidArgumentException(sprintf('Invalid locale: %s.', $locale)); } } From c9742efe991732b22ec9fedf83ae2a926af99b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 24 Jul 2014 17:02:45 +0200 Subject: [PATCH 8/8] [Translator] Use quote to surround invalid locale --- src/Symfony/Component/Translation/Translator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Translation/Translator.php b/src/Symfony/Component/Translation/Translator.php index d13adc07c6..0ed30526d2 100644 --- a/src/Symfony/Component/Translation/Translator.php +++ b/src/Symfony/Component/Translation/Translator.php @@ -309,7 +309,7 @@ class Translator implements TranslatorInterface private function assertValidLocale($locale) { if (1 !== preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) { - throw new \InvalidArgumentException(sprintf('Invalid locale: %s.', $locale)); + throw new \InvalidArgumentException(sprintf('Invalid "%s" locale.', $locale)); } } }