8000 Merge branch '2.3' into 2.4 · symfony/symfony@b07a6b6 · GitHub
[go: up one dir, main page]

Skip to content

Commit b07a6b6

Browse files
committed
Merge branch '2.3' into 2.4
* 2.3: [Translator] Use quote to surround invalid locale Optimize assertLocale regexp [Validator] Backported #11410 to 2.3: Object initializers are called only once per object [Translator][FrameworkBundle] Added @ to the list of allowed chars in Translator [Process] Reduce I/O load on Windows platform [Form] Check if IntlDateFormatter constructor returned a valid object before using it
2 parents 0032abd + 3177be5 commit b07a6b6

File tree

10 files changed

+100
-15
lines changed

10 files changed

+100
-15
lines changed

src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function testTransWithoutCaching()
4545
{
4646
$translator = $this->getTranslator($this->getLoader());
4747
$translator->setLocale('fr');
48-
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8'));
48+
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin'));
4949

5050
$this->assertEquals('foo (FR)', $translator->trans('foo'));
5151
$this->assertEquals('bar (EN)', $translator->trans('bar'));
@@ -55,14 +55,15 @@ public function testTransWithoutCaching()
5555
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
5656
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
5757
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
58+
$this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax'));
5859
}
5960

6061
public function testTransWithCaching()
6162
{
6263
// prime the cache
6364
$translator = $this->getTranslator($this->getLoader(), array('cache_dir' => $this->tmpDir));
6465
$translator->setLocale('fr');
65-
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8'));
66+
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin'));
6667

6768
$this->assertEquals('foo (FR)', $translator->trans('foo'));
6869
$this->assertEquals('bar (EN)', $translator->trans('bar'));
@@ -72,12 +73,13 @@ public function testTransWithCaching()
7273
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
7374
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
7475
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
76+
$this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax'));
7577

7678
// do it another time as the cache is primed now
7779
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
7880
$translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir));
7981
$translator->setLocale('fr');
80-
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8'));
82+
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin'));
8183

8284
$this->assertEquals('foo (FR)', $translator->trans('foo'));
8385
$this->assertEquals('bar (EN)', $translator->trans('bar'));
@@ -87,6 +89,7 @@ public function testTransWithCaching()
8789
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
8890
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
8991
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
92+
$this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax'));
9093
}
9194

9295
public function testGetLocale()
@@ -185,6 +188,13 @@ protected function getLoader()
185188
'foobarbaz' => 'foobarbaz (fr.UTF-8)',
186189
))))
187190
;
191+
$loader
192+
->expects($this->at(6))
193+
->method('load')
194+
->will($this->returnValue($this->getCatalogue('sr@latin', array(
195+
'foobarbax' => 'foobarbax (sr@latin)',
196+
))))
197+
;
188198

189199
return $loader;
190200
}
@@ -216,6 +226,7 @@ public function getTranslator($loader, $options = array())
216226
$translator->addResource('loader', 'foo', 'pt-PT'); // European Portuguese
217227
$translator->addResource('loader', 'foo', 'pt_BR'); // Brazilian Portuguese
218228
$translator->addResource('loader', 'foo', 'fr.UTF-8');
229+
$translator->addResource('loader', 'foo', 'sr@latin'); // Latin Serbian
219230

220231
return $translator;
221232
}

src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ public function reverseTransform($value)
152152
* Returns a preconfigured IntlDateFormatter instance
153153
*
154154
* @return \IntlDateFormatter
155+
*
156+
* @throws TransformationFailedException in case the date formatter can not be constructed.
155157
*/
156158
protected function getIntlDateFormatter()
157159
{
@@ -162,6 +164,12 @@ protected function getIntlDateFormatter()
162164
$pattern = $this->pattern;
163165

164166
$intlDateFormatter = new \IntlDateFormatter(\Locale::getDefault(), $dateFormat, $timeFormat, $timezone, $calendar, $pattern);
167+
168+
// new \intlDateFormatter may return null instead of false in case of failure, see https://bugs.php.net/bug.php?id=66323
169+
if (!$intlDateFormatter) {
170+
throw new TransformationFailedException(intl_get_error_message(), intl_get_error_code());
171+
}
172+
165173
$intlDateFormatter->setLenient(false);
166174

167175
return $intlDateFormatter;

src/Symfony/Component/Form/Extension/Core/Type/DateType.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ public function buildForm(FormBuilderInterface $builder, array $options)
7777
$calendar,
7878
$pattern
7979
);
80+
81+
// new \intlDateFormatter may return null instead of false in case of failure, see https://bugs.php.net/bug.php?id=66323
82+
if (!$formatter) {
83+
throw new InvalidOptionsException(intl_get_error_message(), intl_get_error_code());
84+
}
85+
8086
$formatter->setLenient(false);
8187

8288
if ('choice' === $options['widget']) {

src/Symfony/Component/Process/ProcessPipes.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,8 @@ private function readFileHandles($close = false)
284284
private function readStreams($blocking, $close = false)
285285
{
286286
if (empty($this->pipes)) {
287+
usleep(Process::TIMEOUT_PRECISION * 1E4);
288+
287289
return array();
288290
}
289291

src/Symfony/Component/Process/Tests/AbstractProcessTest.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,14 @@ public function testRunProcessWithTimeout()
615615
}
616616
$duration = microtime(true) - $start;
617617

618-
$this->assertLessThan($timeout + Process::TIMEOUT_PRECISION, $duration);
618+
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
619+
// Windows is a bit slower as it read file handles, then allow twice the precision
620+
$maxDuration = $timeout + 2 * Process::TIMEOUT_PRECISION;
621+
} else {
622+
$maxDuration = $timeout + Process::TIMEOUT_PRECISION;
623+
}
624+
625+
$this->assertLessThan($maxDuration, $duration);
619626
}
620627

621628
public function testCheckTimeoutOnNonStartedProcess()

src/Symfony/Component/Translation/Tests/TranslatorTest.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ public function testSetFallbackLocalesMultiple()
103103
$this->assertEquals('bar (fr)', $translator->trans('bar'));
104104
}
105105

106-
107106
/**
108107
* @dataProvider getInvalidLocalesTests
109108
* @expectedException \InvalidArgumentException
@@ -329,7 +328,6 @@ public function testTransChoiceValidLocale($locale)
329328
// no assertion. this method just asserts that no exception is thrown
330329
}
331330

332-
333331
public function getTransFileTests()
334332
{
335333
return array(
@@ -430,6 +428,7 @@ public function getValidLocalesTests()
430428
array('fr_FR'),
431429
array('fr.FR'),
432430
array('fr-FR.UTF8'),
431+
array('sr@latin'),
433432
);
434433
}
435434

src/Symfony/Component/Translation/Translator.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,8 @@ protected function computeFallbackLocales($locale)
318318
*/
319319
private function assertValidLocale($locale)
320320
{
321-
if (0 !== preg_match('/[^a-z0-9_\\.\\-]+/i', $locale, $match)) {
322-
throw new \InvalidArgumentException(sprintf('Invalid locale: %s.', $locale));
321+
if (1 !== preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) {
322+
throw new \InvalidArgumentException(sprintf('Invalid "%s" locale.', $locale));
323323
}
324324
}
325325
}

src/Symfony/Component/Validator/Tests/Fixtures/Entity.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class Entity extends EntityParent implements EntityInterface
3737
public $reference;
3838
private $internal;
3939
public $data = 'Overridden data';
40+
public $initialized = false;
4041

4142
public function __construct($internal = null)
4243
{

src/Symfony/Component/Validator/Tests/ValidationVisitorTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Validator\Tests;
1313

14+
use Symfony\Component\Validator\Constraints\Callback;
15+
use Symfony\Component\Validator\ExecutionContextInterface;
1416
use Symfony\Component\Validator\Tests\Fixtures\FakeMetadataFactory;
1517
use Symfony\Component\Validator\Constraints\Valid;
1618
use Symfony\Component\Validator\Tests\Fixtures\Reference;
@@ -561,4 +563,50 @@ public function testValidateCascadedPropertyRequiresObjectOrArray()
561563

562564
$this->visitor->validate($entity, 'Default', '');
563565
}
566+
567+
public function testInitializeObjectsOnFirstValidation()
568+
{
569+
$test = $this;
570+
$entity = new Entity();
571+
$entity->initialized = false;
572+
573+
// prepare initializers that set "initialized" to true
574+
$initializer1 = $this->getMock('Symfony\\Component\\Validator\\ObjectInitializerInterface');
575+
$initializer2 = $this->getMock('Symfony\\Component\\Validator\\ObjectInitializerInterface');
576+
577+
$initializer1->expects($this->once())
578+
->method('initialize')
579+
->with($entity)
580+
->will($this->returnCallback(function ($object) {
581+
$object->initialized = true;
582+
}));
583+
584+
$initializer2->expects($this->once())
585+
->method('initialize')
586+
->with($entity);
587+
588+
$this->visitor = new ValidationVisitor('Root', $this->metadataFactory, new ConstraintValidatorFactory(), new DefaultTranslator(), null, array(
589+
$initializer1,
590+
$initializer2
591+
));
592+
593+
// prepare constraint which
594+
// * checks that "initialized" is set to true
595+
// * validates the object again
596+
$callback = function ($object, ExecutionContextInterface $context) use ($test) {
597+
$test->assertTrue($object->initialized);
598+
599+
// validate again in same group
600+
$context->validate($object);
601+
602+
// validate again in other group
603+
$context->validate($object, '', 'SomeGroup');
604+
};
605+
606+
$this->metadata->addConstraint(new Callback(array($callback)));
607+
608+
$this->visitor->validate($entity, 'Default', '');
609+
610+
$this->assertTrue($entity->initialized);
611+
}
564612
}

src/Symfony/Component/Validator/ValidationVisitor.php

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,19 @@ public function validate($value, $group, $propertyPath, $traverse = false, $deep
127127
return;
128128
}
129129

130+
// Initialize if the object wasn't initialized before
131+
if (!isset($this->validatedObjects[$hash])) {
132+
foreach ($this->objectInitializers as $initializer) {
133+
if (!$initializer instanceof ObjectInitializerInterface) {
134+
throw new \LogicException('Validator initializers must implement ObjectInitializerInterface.');
135+
}
136+
$initializer->initialize($value);
137+
}
138+
}
139+
130140
// Remember validating this object before starting and possibly
131141
// traversing the object graph
132142
$this->validatedObjects[$hash][$group] = true;
133-
134-
foreach ($this->objectInitializers as $initializer) {
135-
if (!$initializer instanceof ObjectInitializerInterface) {
136-
throw new \LogicException('Validator initializers must implement ObjectInitializerInterface.');
137-
}
138-
$initializer->initialize($value);
139-
}
140143
}
141144

142145
// Validate arrays recursively by default, otherwise every driver needs

0 commit comments

Comments
 (0)
0