Merge branch '2.5'
* 2.5: [Translator] Use quote to surround invalid locale [Validator] Fixed memory leak in ValidatorBuilder [FrameworkBundle] changed KernelTestCase::getKernelClass() to check $_SERVER['KERNEL_DIR'] before invoking getPhpUnitXmlDir() Optimize assertLocale regexp [ExpressionLanguage] Fixed an issue with # characters in double quoted string literals [Validator] Fixed object initializers in 2.5 version of the Validator Add some tweaks to the pt_BR translations [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 Conflicts: src/Symfony/Component/Process/ProcessPipes.php
This commit is contained in:
commit
3b837dc5c1
@ -100,15 +100,18 @@ abstract class KernelTestCase extends \PHPUnit_Framework_TestCase
|
|||||||
*/
|
*/
|
||||||
protected static function getKernelClass()
|
protected static function getKernelClass()
|
||||||
{
|
{
|
||||||
$dir = $phpUnitDir = static::getPhpUnitXmlDir();
|
|
||||||
|
|
||||||
if (isset($_SERVER['KERNEL_DIR'])) {
|
if (isset($_SERVER['KERNEL_DIR'])) {
|
||||||
$dir = $_SERVER['KERNEL_DIR'];
|
$dir = $_SERVER['KERNEL_DIR'];
|
||||||
|
|
||||||
if (!is_dir($dir) && is_dir("$phpUnitDir/$dir")) {
|
if (!is_dir($dir)) {
|
||||||
|
$phpUnitDir = static::getPhpUnitXmlDir();
|
||||||
|
if (is_dir("$phpUnitDir/$dir")) {
|
||||||
$dir = "$phpUnitDir/$dir";
|
$dir = "$phpUnitDir/$dir";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$dir = static::getPhpUnitXmlDir();
|
||||||
|
}
|
||||||
|
|
||||||
$finder = new Finder();
|
$finder = new Finder();
|
||||||
$finder->name('*Kernel.php')->depth(0)->in($dir);
|
$finder->name('*Kernel.php')->depth(0)->in($dir);
|
||||||
|
@ -46,7 +46,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
{
|
{
|
||||||
$translator = $this->getTranslator($this->getLoader());
|
$translator = $this->getTranslator($this->getLoader());
|
||||||
$translator->setLocale('fr');
|
$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('foo (FR)', $translator->trans('foo'));
|
||||||
$this->assertEquals('bar (EN)', $translator->trans('bar'));
|
$this->assertEquals('bar (EN)', $translator->trans('bar'));
|
||||||
@ -56,6 +56,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
|
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
|
||||||
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
|
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
|
||||||
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
|
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
|
||||||
|
$this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTransWithCaching()
|
public function testTransWithCaching()
|
||||||
@ -63,7 +64,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
// prime the cache
|
// prime the cache
|
||||||
$translator = $this->getTranslator($this->getLoader(), array('cache_dir' => $this->tmpDir));
|
$translator = $this->getTranslator($this->getLoader(), array('cache_dir' => $this->tmpDir));
|
||||||
$translator->setLocale('fr');
|
$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('foo (FR)', $translator->trans('foo'));
|
||||||
$this->assertEquals('bar (EN)', $translator->trans('bar'));
|
$this->assertEquals('bar (EN)', $translator->trans('bar'));
|
||||||
@ -73,12 +74,13 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
|
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
|
||||||
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
|
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
|
||||||
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
|
$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
|
// do it another time as the cache is primed now
|
||||||
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
|
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
|
||||||
$translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir));
|
$translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir));
|
||||||
$translator->setLocale('fr');
|
$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('foo (FR)', $translator->trans('foo'));
|
||||||
$this->assertEquals('bar (EN)', $translator->trans('bar'));
|
$this->assertEquals('bar (EN)', $translator->trans('bar'));
|
||||||
@ -88,6 +90,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
|
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
|
||||||
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
|
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
|
||||||
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
|
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
|
||||||
|
$this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -189,6 +192,13 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
'foobarbaz' => 'foobarbaz (fr.UTF-8)',
|
'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;
|
return $loader;
|
||||||
}
|
}
|
||||||
@ -220,6 +230,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
$translator->addResource('loader', 'foo', 'pt-PT'); // European Portuguese
|
$translator->addResource('loader', 'foo', 'pt-PT'); // European Portuguese
|
||||||
$translator->addResource('loader', 'foo', 'pt_BR'); // Brazilian Portuguese
|
$translator->addResource('loader', 'foo', 'pt_BR'); // Brazilian Portuguese
|
||||||
$translator->addResource('loader', 'foo', 'fr.UTF-8');
|
$translator->addResource('loader', 'foo', 'fr.UTF-8');
|
||||||
|
$translator->addResource('loader', 'foo', 'sr@latin'); // Latin Serbian
|
||||||
|
|
||||||
return $translator;
|
return $translator;
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ class Lexer
|
|||||||
|
|
||||||
$tokens[] = new Token(Token::PUNCTUATION_TYPE, $expression[$cursor], $cursor + 1);
|
$tokens[] = new Token(Token::PUNCTUATION_TYPE, $expression[$cursor], $cursor + 1);
|
||||||
++$cursor;
|
++$cursor;
|
||||||
} elseif (preg_match('/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As', $expression, $match, null, $cursor)) {
|
} elseif (preg_match('/"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As', $expression, $match, null, $cursor)) {
|
||||||
// strings
|
// strings
|
||||||
$tokens[] = new Token(Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)), $cursor + 1);
|
$tokens[] = new Token(Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)), $cursor + 1);
|
||||||
$cursor += strlen($match[0]);
|
$cursor += strlen($match[0]);
|
||||||
|
@ -78,6 +78,14 @@ class LexerTest extends \PHPUnit_Framework_TestCase
|
|||||||
array(new Token('operator', '..', 1)),
|
array(new Token('operator', '..', 1)),
|
||||||
'..',
|
'..',
|
||||||
),
|
),
|
||||||
|
array(
|
||||||
|
array(new Token('string', '#foo', 1)),
|
||||||
|
"'#foo'",
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
array(new Token('string', '#foo', 1)),
|
||||||
|
'"#foo"',
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,6 +152,8 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
|
|||||||
* Returns a preconfigured IntlDateFormatter instance
|
* Returns a preconfigured IntlDateFormatter instance
|
||||||
*
|
*
|
||||||
* @return \IntlDateFormatter
|
* @return \IntlDateFormatter
|
||||||
|
*
|
||||||
|
* @throws TransformationFailedException in case the date formatter can not be constructed.
|
||||||
*/
|
*/
|
||||||
protected function getIntlDateFormatter()
|
protected function getIntlDateFormatter()
|
||||||
{
|
{
|
||||||
@ -162,6 +164,12 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
|
|||||||
$pattern = $this->pattern;
|
$pattern = $this->pattern;
|
||||||
|
|
||||||
$intlDateFormatter = new \IntlDateFormatter(\Locale::getDefault(), $dateFormat, $timeFormat, $timezone, $calendar, $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);
|
$intlDateFormatter->setLenient(false);
|
||||||
|
|
||||||
return $intlDateFormatter;
|
return $intlDateFormatter;
|
||||||
|
@ -77,6 +77,12 @@ class DateType extends AbstractType
|
|||||||
$calendar,
|
$calendar,
|
||||||
$pattern
|
$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);
|
$formatter->setLenient(false);
|
||||||
|
|
||||||
if ('choice' === $options['widget']) {
|
if ('choice' === $options['widget']) {
|
||||||
|
@ -673,7 +673,14 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
$duration = microtime(true) - $start;
|
$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()
|
public function testCheckTimeoutOnNonStartedProcess()
|
||||||
|
@ -103,7 +103,6 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals('bar (fr)', $translator->trans('bar'));
|
$this->assertEquals('bar (fr)', $translator->trans('bar'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider getInvalidLocalesTests
|
* @dataProvider getInvalidLocalesTests
|
||||||
* @expectedException \InvalidArgumentException
|
* @expectedException \InvalidArgumentException
|
||||||
@ -329,7 +328,6 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
// no assertion. this method just asserts that no exception is thrown
|
// no assertion. this method just asserts that no exception is thrown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function getTransFileTests()
|
public function getTransFileTests()
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
@ -430,6 +428,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
array('fr_FR'),
|
array('fr_FR'),
|
||||||
array('fr.FR'),
|
array('fr.FR'),
|
||||||
array('fr-FR.UTF8'),
|
array('fr-FR.UTF8'),
|
||||||
|
array('sr@latin'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,8 +318,8 @@ class Translator implements TranslatorInterface
|
|||||||
*/
|
*/
|
||||||
private function assertValidLocale($locale)
|
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));
|
throw new \InvalidArgumentException(sprintf('Invalid "%s" locale.', $locale));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ use Symfony\Component\Validator\ConstraintViolationList;
|
|||||||
use Symfony\Component\Validator\Exception\BadMethodCallException;
|
use Symfony\Component\Validator\Exception\BadMethodCallException;
|
||||||
use Symfony\Component\Validator\Mapping\MetadataInterface;
|
use Symfony\Component\Validator\Mapping\MetadataInterface;
|
||||||
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
|
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
|
||||||
|
use Symfony\Component\Validator\ObjectInitializerInterface;
|
||||||
use Symfony\Component\Validator\Util\PropertyPath;
|
use Symfony\Component\Validator\Util\PropertyPath;
|
||||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||||
use Symfony\Component\Validator\Violation\ConstraintViolationBuilder;
|
use Symfony\Component\Validator\Violation\ConstraintViolationBuilder;
|
||||||
@ -113,6 +114,13 @@ class ExecutionContext implements ExecutionContextInterface
|
|||||||
*/
|
*/
|
||||||
private $validatedConstraints = array();
|
private $validatedConstraints = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores which objects have been initialized.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $initializedObjects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new execution context.
|
* Creates a new execution context.
|
||||||
*
|
*
|
||||||
@ -360,4 +368,36 @@ class ExecutionContext implements ExecutionContextInterface
|
|||||||
{
|
{
|
||||||
return isset($this->validatedConstraints[$cacheKey.':'.$constraintHash]);
|
return isset($this->validatedConstraints[$cacheKey.':'.$constraintHash]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks that an object was initialized.
|
||||||
|
*
|
||||||
|
* @param string $cacheKey The hash of the object
|
||||||
|
*
|
||||||
|
* @internal Used by the validator engine. Should not be called by user
|
||||||
|
* code.
|
||||||
|
*
|
||||||
|
* @see ObjectInitializerInterface
|
||||||
|
*/
|
||||||
|
public function markObjectAsInitialized($cacheKey)
|
||||||
|
{
|
||||||
|
$this->initializedObjects[$cacheKey] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether an object was initialized.
|
||||||
|
*
|
||||||
|
* @param string $cacheKey The hash of the object
|
||||||
|
*
|
||||||
|
* @return bool Whether the object was already initialized
|
||||||
|
*
|
||||||
|
* @internal Used by the validator engine. Should not be called by user
|
||||||
|
* code.
|
||||||
|
*
|
||||||
|
* @see ObjectInitializerInterface
|
||||||
|
*/
|
||||||
|
public function isObjectInitialized($cacheKey)
|
||||||
|
{
|
||||||
|
return isset($this->initializedObjects[$cacheKey]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -280,11 +280,11 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="73">
|
<trans-unit id="73">
|
||||||
<source>The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}.</source>
|
<source>The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}.</source>
|
||||||
<target>O formato da imagem é muito grande ({{ ratio }}). O formato máximo é {{ max_ratio }}.</target>
|
<target>A proporção da imagem é muito grande ({{ ratio }}). A proporção máxima permitida é {{ max_ratio }}.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="74">
|
<trans-unit id="74">
|
||||||
<source>The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}.</source>
|
<source>The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}.</source>
|
||||||
<target>O formato da imagem é muito pequeno ({{ ratio }}). O formato mínimo esperado é {{ min_ratio }}.</target>
|
<target>A proporção da imagem é muito pequena ({{ ratio }}). A proporção mínima esperada é {{ min_ratio }}.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="75">
|
<trans-unit id="75">
|
||||||
<source>The image is square ({{ width }}x{{ height }}px). Square images are not allowed.</source>
|
<source>The image is square ({{ width }}x{{ height }}px). Square images are not allowed.</source>
|
||||||
@ -300,7 +300,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="78">
|
<trans-unit id="78">
|
||||||
<source>An empty file is not allowed.</source>
|
<source>An empty file is not allowed.</source>
|
||||||
<target>Ficheiro vazio não é permitido.</target>
|
<target>Arquivo vazio não é permitido.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
@ -38,6 +38,7 @@ class Entity extends EntityParent implements EntityInterface
|
|||||||
public $reference2;
|
public $reference2;
|
||||||
private $internal;
|
private $internal;
|
||||||
public $data = 'Overridden data';
|
public $data = 'Overridden data';
|
||||||
|
public $initialized = false;
|
||||||
|
|
||||||
public function __construct($internal = null)
|
public function __construct($internal = null)
|
||||||
{
|
{
|
||||||
|
@ -42,7 +42,7 @@ abstract class Abstract2Dot5ApiTest extends AbstractValidatorTest
|
|||||||
*
|
*
|
||||||
* @return ValidatorInterface
|
* @return ValidatorInterface
|
||||||
*/
|
*/
|
||||||
abstract protected function createValidator(MetadataFactoryInterface $metadataFactory);
|
abstract protected function createValidator(MetadataFactoryInterface $metadataFactory, array $objectInitializers = array());
|
||||||
|
|
||||||
protected function setUp()
|
protected function setUp()
|
||||||
{
|
{
|
||||||
@ -678,4 +678,52 @@ abstract class Abstract2Dot5ApiTest extends AbstractValidatorTest
|
|||||||
|
|
||||||
$this->assertTrue($called);
|
$this->assertTrue($called);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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->validator = $this->createValidator($this->metadataFactory, 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
|
||||||
|
$validator = $context->getValidator()->inContext($context);
|
||||||
|
|
||||||
|
$validator->validate($object);
|
||||||
|
|
||||||
|
// validate again in other group
|
||||||
|
$validator->validate($object, null, 'SomeGroup');
|
||||||
|
};
|
||||||
|
|
||||||
|
$this->metadata->addConstraint(new Callback($callback));
|
||||||
|
|
||||||
|
$this->validate($entity);
|
||||||
|
|
||||||
|
$this->assertTrue($entity->initialized);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ abstract class AbstractLegacyApiTest extends AbstractValidatorTest
|
|||||||
*
|
*
|
||||||
* @return LegacyValidatorInterface
|
* @return LegacyValidatorInterface
|
||||||
*/
|
*/
|
||||||
abstract protected function createValidator(MetadataFactoryInterface $metadataFactory);
|
abstract protected function createValidator(MetadataFactoryInterface $metadataFactory, array $objectInitializers = array());
|
||||||
|
|
||||||
protected function setUp()
|
protected function setUp()
|
||||||
{
|
{
|
||||||
@ -238,6 +238,52 @@ abstract class AbstractLegacyApiTest extends AbstractValidatorTest
|
|||||||
$this->assertSame('Code', $violations[0]->getCode());
|
$this->assertSame('Code', $violations[0]->getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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->validator = $this->createValidator($this->metadataFactory, 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($callback));
|
||||||
|
|
||||||
|
$this->validate($entity);
|
||||||
|
|
||||||
|
$this->assertTrue($entity->initialized);
|
||||||
|
}
|
||||||
|
|
||||||
public function testGetMetadataFactory()
|
public function testGetMetadataFactory()
|
||||||
{
|
{
|
||||||
$this->assertSame($this->metadataFactory, $this->validator->getMetadataFactory());
|
$this->assertSame($this->metadataFactory, $this->validator->getMetadataFactory());
|
||||||
|
@ -28,10 +28,10 @@ class LegacyValidator2Dot5ApiTest extends Abstract2Dot5ApiTest
|
|||||||
parent::setUp();
|
parent::setUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createValidator(MetadataFactoryInterface $metadataFactory)
|
protected function createValidator(MetadataFactoryInterface $metadataFactory, array $objectInitializers = array())
|
||||||
{
|
{
|
||||||
$contextFactory = new LegacyExecutionContextFactory($metadataFactory, new DefaultTranslator());
|
$contextFactory = new LegacyExecutionContextFactory($metadataFactory, new DefaultTranslator());
|
||||||
|
|
||||||
return new LegacyValidator($contextFactory, $metadataFactory, new ConstraintValidatorFactory());
|
return new LegacyValidator($contextFactory, $metadataFactory, new ConstraintValidatorFactory(), $objectInitializers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,10 +28,10 @@ class LegacyValidatorLegacyApiTest extends AbstractLegacyApiTest
|
|||||||
parent::setUp();
|
parent::setUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createValidator(MetadataFactoryInterface $metadataFactory)
|
protected function createValidator(MetadataFactoryInterface $metadataFactory, array $objectInitializers = array())
|
||||||
{
|
{
|
||||||
$contextFactory = new LegacyExecutionContextFactory($metadataFactory, new DefaultTranslator());
|
$contextFactory = new LegacyExecutionContextFactory($metadataFactory, new DefaultTranslator());
|
||||||
|
|
||||||
return new LegacyValidator($contextFactory, $metadataFactory, new ConstraintValidatorFactory());
|
return new LegacyValidator($contextFactory, $metadataFactory, new ConstraintValidatorFactory(), $objectInitializers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,10 @@ use Symfony\Component\Validator\Validator\RecursiveValidator;
|
|||||||
|
|
||||||
class RecursiveValidator2Dot5ApiTest extends Abstract2Dot5ApiTest
|
class RecursiveValidator2Dot5ApiTest extends Abstract2Dot5ApiTest
|
||||||
{
|
{
|
||||||
protected function createValidator(MetadataFactoryInterface $metadataFactory)
|
protected function createValidator(MetadataFactoryInterface $metadataFactory, array $objectInitializers = array())
|
||||||
{
|
{
|
||||||
$contextFactory = new ExecutionContextFactory(new DefaultTranslator());
|
$contextFactory = new ExecutionContextFactory(new DefaultTranslator());
|
||||||
|
|
||||||
return new RecursiveValidator($contextFactory, $metadataFactory, new ConstraintValidatorFactory());
|
return new RecursiveValidator($contextFactory, $metadataFactory, new ConstraintValidatorFactory(), $objectInitializers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,9 @@ use Symfony\Component\Validator\Validator as LegacyValidator;
|
|||||||
|
|
||||||
class ValidatorTest extends AbstractLegacyApiTest
|
class ValidatorTest extends AbstractLegacyApiTest
|
||||||
{
|
{
|
||||||
protected function createValidator(MetadataFactoryInterface $metadataFactory)
|
protected function createValidator(MetadataFactoryInterface $metadataFactory, array $objectInitializers = array())
|
||||||
{
|
{
|
||||||
return new LegacyValidator($metadataFactory, new ConstraintValidatorFactory(), new DefaultTranslator());
|
return new LegacyValidator($metadataFactory, new ConstraintValidatorFactory(), new DefaultTranslator(), 'validators', $objectInitializers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -129,10 +129,8 @@ class ValidationVisitor implements ValidationVisitorInterface, GlobalExecutionCo
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remember validating this object before starting and possibly
|
// Initialize if the object wasn't initialized before
|
||||||
// traversing the object graph
|
if (!isset($this->validatedObjects[$hash])) {
|
||||||
$this->validatedObjects[$hash][$group] = true;
|
|
||||||
|
|
||||||
foreach ($this->objectInitializers as $initializer) {
|
foreach ($this->objectInitializers as $initializer) {
|
||||||
if (!$initializer instanceof ObjectInitializerInterface) {
|
if (!$initializer instanceof ObjectInitializerInterface) {
|
||||||
throw new \LogicException('Validator initializers must implement ObjectInitializerInterface.');
|
throw new \LogicException('Validator initializers must implement ObjectInitializerInterface.');
|
||||||
@ -141,6 +139,11 @@ class ValidationVisitor implements ValidationVisitorInterface, GlobalExecutionCo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remember validating this object before starting and possibly
|
||||||
|
// traversing the object graph
|
||||||
|
$this->validatedObjects[$hash][$group] = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Validate arrays recursively by default, otherwise every driver needs
|
// Validate arrays recursively by default, otherwise every driver needs
|
||||||
// to implement special handling for arrays.
|
// to implement special handling for arrays.
|
||||||
// https://github.com/symfony/symfony/issues/6246
|
// https://github.com/symfony/symfony/issues/6246
|
||||||
|
@ -27,6 +27,7 @@ use Symfony\Component\Validator\Mapping\MetadataInterface;
|
|||||||
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
|
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
|
||||||
use Symfony\Component\Validator\Mapping\TraversalStrategy;
|
use Symfony\Component\Validator\Mapping\TraversalStrategy;
|
||||||
use Symfony\Component\Validator\MetadataFactoryInterface;
|
use Symfony\Component\Validator\MetadataFactoryInterface;
|
||||||
|
use Symfony\Component\Validator\ObjectInitializerInterface;
|
||||||
use Symfony\Component\Validator\Util\PropertyPath;
|
use Symfony\Component\Validator\Util\PropertyPath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,6 +53,11 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
|
|||||||
*/
|
*/
|
||||||
private $validatorFactory;
|
private $validatorFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ObjectInitializerInterface[]
|
||||||
|
*/
|
||||||
|
private $objectInitializers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a validator for the given context.
|
* Creates a validator for the given context.
|
||||||
*
|
*
|
||||||
@ -61,14 +67,16 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
|
|||||||
* of validated objects
|
* of validated objects
|
||||||
* @param ConstraintValidatorFactoryInterface $validatorFactory The factory for creating
|
* @param ConstraintValidatorFactoryInterface $validatorFactory The factory for creating
|
||||||
* constraint validators
|
* constraint validators
|
||||||
|
* @param ObjectInitializerInterface[] $objectInitializers The object initializers
|
||||||
*/
|
*/
|
||||||
public function __construct(ExecutionContextInterface $context, MetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $validatorFactory)
|
public function __construct(ExecutionContextInterface $context, MetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $validatorFactory, array $objectInitializers = array())
|
||||||
{
|
{
|
||||||
$this->context = $context;
|
$this->context = $context;
|
||||||
$this->defaultPropertyPath = $context->getPropertyPath();
|
$this->defaultPropertyPath = $context->getPropertyPath();
|
||||||
$this->defaultGroups = array($context->getGroup() ?: Constraint::DEFAULT_GROUP);
|
$this->defaultGroups = array($context->getGroup() ?: Constraint::DEFAULT_GROUP);
|
||||||
$this->metadataFactory = $metadataFactory;
|
$this->metadataFactory = $metadataFactory;
|
||||||
$this->validatorFactory = $validatorFactory;
|
$this->validatorFactory = $validatorFactory;
|
||||||
|
$this->objectInitializers = $objectInitializers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -432,6 +440,14 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
|
|||||||
{
|
{
|
||||||
$context->setNode($object, $object, $metadata, $propertyPath);
|
$context->setNode($object, $object, $metadata, $propertyPath);
|
||||||
|
|
||||||
|
if (!$context->isObjectInitialized($cacheKey)) {
|
||||||
|
foreach ($this->objectInitializers as $initializer) {
|
||||||
|
$initializer->initialize($object);
|
||||||
|
}
|
||||||
|
|
||||||
|
$context->markObjectAsInitialized($cacheKey);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($groups as $key => $group) {
|
foreach ($groups as $key => $group) {
|
||||||
// If the "Default" group is replaced by a group sequence, remember
|
// If the "Default" group is replaced by a group sequence, remember
|
||||||
// to cascade the "Default" group when traversing the group
|
// to cascade the "Default" group when traversing the group
|
||||||
|
@ -15,6 +15,7 @@ use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;
|
|||||||
use Symfony\Component\Validator\Context\ExecutionContextFactoryInterface;
|
use Symfony\Component\Validator\Context\ExecutionContextFactoryInterface;
|
||||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
use Symfony\Component\Validator\MetadataFactoryInterface;
|
use Symfony\Component\Validator\MetadataFactoryInterface;
|
||||||
|
use Symfony\Component\Validator\ObjectInitializerInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursive implementation of {@link ValidatorInterface}.
|
* Recursive implementation of {@link ValidatorInterface}.
|
||||||
@ -39,6 +40,11 @@ class RecursiveValidator implements ValidatorInterface
|
|||||||
*/
|
*/
|
||||||
protected $validatorFactory;
|
protected $validatorFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ObjectInitializerInterface[]
|
||||||
|
*/
|
||||||
|
protected $objectInitializers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new validator.
|
* Creates a new validator.
|
||||||
*
|
*
|
||||||
@ -49,12 +55,14 @@ class RecursiveValidator implements ValidatorInterface
|
|||||||
* of validated objects
|
* of validated objects
|
||||||
* @param ConstraintValidatorFactoryInterface $validatorFactory The factory for creating
|
* @param ConstraintValidatorFactoryInterface $validatorFactory The factory for creating
|
||||||
* constraint validators
|
* constraint validators
|
||||||
|
* @param ObjectInitializerInterface[] $objectInitializers The object initializers
|
||||||
*/
|
*/
|
||||||
public function __construct(ExecutionContextFactoryInterface $contextFactory, MetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $validatorFactory)
|
public function __construct(ExecutionContextFactoryInterface $contextFactory, MetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $validatorFactory, array $objectInitializers = array())
|
||||||
{
|
{
|
||||||
$this->contextFactory = $contextFactory;
|
$this->contextFactory = $contextFactory;
|
||||||
$this->metadataFactory = $metadataFactory;
|
$this->metadataFactory = $metadataFactory;
|
||||||
$this->validatorFactory = $validatorFactory;
|
$this->validatorFactory = $validatorFactory;
|
||||||
|
$this->objectInitializers = $objectInitializers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,7 +73,8 @@ class RecursiveValidator implements ValidatorInterface
|
|||||||
return new RecursiveContextualValidator(
|
return new RecursiveContextualValidator(
|
||||||
$this->contextFactory->createContext($this, $root),
|
$this->contextFactory->createContext($this, $root),
|
||||||
$this->metadataFactory,
|
$this->metadataFactory,
|
||||||
$this->validatorFactory
|
$this->validatorFactory,
|
||||||
|
$this->objectInitializers
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +86,8 @@ class RecursiveValidator implements ValidatorInterface
|
|||||||
return new RecursiveContextualValidator(
|
return new RecursiveContextualValidator(
|
||||||
$context,
|
$context,
|
||||||
$this->metadataFactory,
|
$this->metadataFactory,
|
||||||
$this->validatorFactory
|
$this->validatorFactory,
|
||||||
|
$this->objectInitializers
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
namespace Symfony\Component\Validator;
|
namespace Symfony\Component\Validator;
|
||||||
|
|
||||||
use Doctrine\Common\Annotations\AnnotationReader;
|
use Doctrine\Common\Annotations\AnnotationReader;
|
||||||
use Doctrine\Common\Annotations\AnnotationRegistry;
|
|
||||||
use Doctrine\Common\Annotations\CachedReader;
|
use Doctrine\Common\Annotations\CachedReader;
|
||||||
use Doctrine\Common\Annotations\Reader;
|
use Doctrine\Common\Annotations\Reader;
|
||||||
use Doctrine\Common\Cache\ArrayCache;
|
use Doctrine\Common\Cache\ArrayCache;
|
||||||
@ -367,20 +366,6 @@ class ValidatorBuilder implements ValidatorBuilderInterface
|
|||||||
|
|
||||||
if ($this->annotationReader) {
|
if ($this->annotationReader) {
|
||||||
$loaders[] = new AnnotationLoader($this->annotationReader);
|
$loaders[] = new AnnotationLoader($this->annotationReader);
|
||||||
|
|
||||||
AnnotationRegistry::registerLoader(function ($class) {
|
|
||||||
if (0 === strpos($class, __NAMESPACE__.'\\Constraints\\')) {
|
|
||||||
$file = str_replace(__NAMESPACE__.'\\Constraints\\', __DIR__.'/Constraints/', $class).'.php';
|
|
||||||
|
|
||||||
if (is_file($file)) {
|
|
||||||
require_once $file;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$loader = null;
|
$loader = null;
|
||||||
@ -411,9 +396,9 @@ class ValidatorBuilder implements ValidatorBuilderInterface
|
|||||||
$contextFactory = new LegacyExecutionContextFactory($metadataFactory, $translator, $this->translationDomain);
|
$contextFactory = new LegacyExecutionContextFactory($metadataFactory, $translator, $this->translationDomain);
|
||||||
|
|
||||||
if (Validation::API_VERSION_2_5 === $apiVersion) {
|
if (Validation::API_VERSION_2_5 === $apiVersion) {
|
||||||
return new RecursiveValidator($contextFactory, $metadataFactory, $validatorFactory);
|
return new RecursiveValidator($contextFactory, $metadataFactory, $validatorFactory, $this->initializers);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LegacyValidator($contextFactory, $metadataFactory, $validatorFactory);
|
return new LegacyValidator($contextFactory, $metadataFactory, $validatorFactory, $this->initializers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user