Merge branch '4.2'

* 4.2:
  [Phpunit] fixed support for PHP 5.3
  Response prepare method update
  [Workflow] Added missing license header
  Fix case when multiple loaders are providing paths for the same namespace
  Check if Client exists when test.client does not exist, to provide clearer exception message
  throw TypeErrors to prepare for type hints in 5.0
  [Form] Preventing validation of children if parent with Valid constraint has no validation groups
  [Form] Added ResetInterface to CachingFactoryDecorator
  Remove deprecated usage
  [Tests] fixed compatbility of assertEquals(): void
  Fixed usage of TranslatorInterface in form extension (fixes #30591)
  [Intl][4.2] Fix test
  [Intl] Fix test
  [Validator] Add the missing translations for the Arabic (ar) locale
  [Intl] Add compile binary
  Fix DebugCommand when chain loader is involved
  [Form] Fixed some phpdocs
This commit is contained in:
Fabien Potencier 2019-03-22 09:16:47 +01:00
commit 522594a69d
38 changed files with 276 additions and 131 deletions

View File

@ -42,7 +42,7 @@ class DoctrineExtractor implements PropertyListExtractorInterface, PropertyTypeE
@trigger_error(sprintf('Injecting an instance of "%s" in "%s" is deprecated since Symfony 4.2, inject an instance of "%s" instead.', ClassMetadataFactory::class, __CLASS__, EntityManagerInterface::class), E_USER_DEPRECATED);
$this->classMetadataFactory = $entityManager;
} else {
throw new \InvalidArgumentException(sprintf('$entityManager must be an instance of "%s", "%s" given.', EntityManagerInterface::class, \is_object($entityManager) ? \get_class($entityManager) : \gettype($entityManager)));
throw new \TypeError(sprintf('$entityManager must be an instance of "%s", "%s" given.', EntityManagerInterface::class, \is_object($entityManager) ? \get_class($entityManager) : \gettype($entityManager)));
}
}

View File

@ -76,11 +76,11 @@ if ('phpdbg' === PHP_SAPI) {
$PHP .= ' -qrr';
}
$defaultEnvs = [
$defaultEnvs = array(
'COMPOSER' => 'composer.json',
'COMPOSER_VENDOR_DIR' => 'vendor',
'COMPOSER_BIN_DIR' => 'bin',
];
);
foreach ($defaultEnvs as $envName => $envValue) {
if ($envValue !== getenv($envName)) {

View File

@ -20,6 +20,7 @@ use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Finder\Finder;
use Twig\Environment;
use Twig\Loader\ChainLoader;
use Twig\Loader\FilesystemLoader;
/**
@ -36,6 +37,7 @@ class DebugCommand extends Command
private $bundlesMetadata;
private $twigDefaultPath;
private $rootDir;
private $filesystemLoaders;
public function __construct(Environment $twig, string $projectDir = null, array $bundlesMetadata = [], string $twigDefaultPath = null, string $rootDir = null)
{
@ -87,7 +89,7 @@ EOF
$name = $input->getArgument('name');
$filter = $input->getOption('filter');
if (null !== $name && !$this->twig->getLoader() instanceof FilesystemLoader) {
if (null !== $name && [] === $this->getFilesystemLoaders()) {
throw new InvalidArgumentException(sprintf('Argument "name" not supported, it requires the Twig loader "%s"', FilesystemLoader::class));
}
@ -150,9 +152,11 @@ EOF
$message = 'No template paths configured for your application';
} else {
$message = sprintf('No template paths configured for "@%s" namespace', $namespace);
$namespaces = $this->twig->getLoader()->getNamespaces();
foreach ($this->findAlternatives($namespace, $namespaces) as $namespace) {
$alternatives[] = '@'.$namespace;
foreach ($this->getFilesystemLoaders() as $loader) {
$namespaces = $loader->getNamespaces();
foreach ($this->findAlternatives($namespace, $namespaces) as $namespace) {
$alternatives[] = '@'.$namespace;
}
}
}
@ -243,25 +247,25 @@ EOF
private function getLoaderPaths(string $name = null): array
{
/** @var FilesystemLoader $loader */
$loader = $this->twig->getLoader();
$loaderPaths = [];
$namespaces = $loader->getNamespaces();
if (null !== $name) {
$namespace = $this->parseTemplateName($name)[0];
$namespaces = array_intersect([$namespace], $namespaces);
}
foreach ($namespaces as $namespace) {
$paths = array_map([$this, 'getRelativePath'], $loader->getPaths($namespace));
if (FilesystemLoader::MAIN_NAMESPACE === $namespace) {
$namespace = '(None)';
} else {
$namespace = '@'.$namespace;
foreach ($this->getFilesystemLoaders() as $loader) {
$namespaces = $loader->getNamespaces();
if (null !== $name) {
$namespace = $this->parseTemplateName($name)[0];
$namespaces = array_intersect([$namespace], $namespaces);
}
$loaderPaths[$namespace] = $paths;
foreach ($namespaces as $namespace) {
$paths = array_map([$this, 'getRelativePath'], $loader->getPaths($namespace));
if (FilesystemLoader::MAIN_NAMESPACE === $namespace) {
$namespace = '(None)';
} else {
$namespace = '@'.$namespace;
}
$loaderPaths[$namespace] = array_merge($loaderPaths[$namespace] ?? [], $paths);
}
}
return $loaderPaths;
@ -437,22 +441,22 @@ EOF
private function findTemplateFiles(string $name): array
{
/** @var FilesystemLoader $loader */
$loader = $this->twig->getLoader();
$files = [];
list($namespace, $shortname) = $this->parseTemplateName($name);
foreach ($loader->getPaths($namespace) as $path) {
if (!$this->isAbsolutePath($path)) {
$path = $this->projectDir.'/'.$path;
}
$filename = $path.'/'.$shortname;
$files = [];
foreach ($this->getFilesystemLoaders() as $loader) {
foreach ($loader->getPaths($namespace) as $path) {
if (!$this->isAbsolutePath($path)) {
$path = $this->projectDir.'/'.$path;
}
$filename = $path.'/'.$shortname;
if (is_file($filename)) {
if (false !== $realpath = realpath($filename)) {
$files[] = $this->getRelativePath($realpath);
} else {
$files[] = $this->getRelativePath($filename);
if (is_file($filename)) {
if (false !== $realpath = realpath($filename)) {
$files[] = $this->getRelativePath($realpath);
} else {
$files[] = $this->getRelativePath($filename);
}
}
}
}
@ -535,4 +539,28 @@ EOF
{
return strspn($file, '/\\', 0, 1) || (\strlen($file) > 3 && ctype_alpha($file[0]) && ':' === $file[1] && strspn($file, '/\\', 2, 1)) || null !== parse_url($file, PHP_URL_SCHEME);
}
/**
* @return FilesystemLoader[]
*/
private function getFilesystemLoaders(): array
{
if (null !== $this->filesystemLoaders) {
return $this->filesystemLoaders;
}
$this->filesystemLoaders = [];
$loader = $this->twig->getLoader();
if ($loader instanceof FilesystemLoader) {
$this->filesystemLoaders[] = $loader;
} elseif ($loader instanceof ChainLoader) {
foreach ($loader->getLoaders() as $l) {
if ($l instanceof FilesystemLoader) {
$this->filesystemLoaders[] = $l;
}
}
}
return $this->filesystemLoaders;
}
}

View File

@ -16,6 +16,7 @@ use Symfony\Bridge\Twig\Command\DebugCommand;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
use Twig\Environment;
use Twig\Loader\ChainLoader;
use Twig\Loader\FilesystemLoader;
class DebugCommandTest extends TestCase
@ -279,7 +280,16 @@ TXT
];
}
private function createCommandTester(array $paths = [], array $bundleMetadata = [], string $defaultPath = null, string $rootDir = null): CommandTester
public function testDebugTemplateNameWithChainLoader()
{
$tester = $this->createCommandTester(['templates/' => null], [], null, null, true);
$ret = $tester->execute(['name' => 'base.html.twig'], ['decorated' => false]);
$this->assertEquals(0, $ret, 'Returns 0 in case of success');
$this->assertContains('[OK]', $tester->getDisplay());
}
private function createCommandTester(array $paths = [], array $bundleMetadata = [], string $defaultPath = null, string $rootDir = null, bool $useChainLoader = false): CommandTester
{
$projectDir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures';
$loader = new FilesystemLoader([], $projectDir);
@ -291,6 +301,10 @@ TXT
}
}
if ($useChainLoader) {
$loader = new ChainLoader([$loader]);
}
$application = new Application();
$application->add(new DebugCommand(new Environment($loader), $projectDir, $bundleMetadata, $defaultPath, $rootDir));
$command = $application->find('debug:twig');

View File

@ -18,7 +18,7 @@
"require": {
"php": "^7.1.3",
"symfony/contracts": "^1.0.2",
"twig/twig": "^1.37.1|^2.6.2"
"twig/twig": "^1.38.1|^2.7.1"
},
"require-dev": {
"egulias/email-validator": "^2.0",

View File

@ -55,6 +55,7 @@ use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Form\ChoiceList\Factory\CachingFactoryDecorator;
use Symfony\Component\Form\FormTypeExtensionInterface;
use Symfony\Component\Form\FormTypeGuesserInterface;
use Symfony\Component\Form\FormTypeInterface;
@ -440,6 +441,11 @@ class FrameworkExtension extends Extension
if (!class_exists(Translator::class)) {
$container->removeDefinition('form.type_extension.upload.validator');
}
if (!method_exists(CachingFactoryDecorator::class, 'reset')) {
$container->getDefinition('form.choice_list_factory.cached')
->clearTag('kernel.reset')
;
}
}
private function registerEsiConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)

View File

@ -57,6 +57,7 @@
<service id="form.choice_list_factory.cached" class="Symfony\Component\Form\ChoiceList\Factory\CachingFactoryDecorator">
<argument type="service" id="form.choice_list_factory.property_access"/>
<tag name="kernel.reset" method="reset" />
</service>
<service id="form.choice_list_factory" alias="form.choice_list_factory.cached" />

View File

@ -36,7 +36,10 @@ abstract class WebTestCase extends KernelTestCase
try {
$client = $kernel->getContainer()->get('test.client');
} catch (ServiceNotFoundException $e) {
throw new \LogicException('You cannot create the client used in functional tests if the BrowserKit component is not available. Try running "composer require symfony/browser-kit".');
if (class_exists(Client::class)) {
throw new \LogicException('You cannot create the client used in functional tests if the "framework.test" config is not set to true.');
}
throw new \LogicException('You cannot create the client used in functional tests if the BrowserKit component is not available. Try running "composer require symfony/browser-kit"');
}
$client->setServerParameters($server);

View File

@ -41,7 +41,7 @@ class FirewallContext
$this->logoutListener = $logoutListener;
$this->config = $config;
} else {
throw new \InvalidArgumentException(sprintf('Argument 3 passed to %s() must be instance of %s or null, %s given.', __METHOD__, LogoutListener::class, \is_object($logoutListener) ? \get_class($logoutListener) : \gettype($logoutListener)));
throw new \TypeError(sprintf('Argument 3 passed to %s() must be instance of %s or null, %s given.', __METHOD__, LogoutListener::class, \is_object($logoutListener) ? \get_class($logoutListener) : \gettype($logoutListener)));
}
}

View File

@ -15,9 +15,10 @@ namespace Symfony\Component\Form\ChoiceList;
* A list of choices with arbitrary data types.
*
* The user of this class is responsible for assigning string values to the
* choices. Both the choices and their values are passed to the constructor.
* Each choice must have a corresponding value (with the same array key) in
* the value array.
* choices annd for their uniqueness.
* Both the choices and their values are passed to the constructor.
* Each choice must have a corresponding value (with the same key) in
* the values array.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
@ -43,12 +44,6 @@ class ArrayChoiceList implements ChoiceListInterface
* @var int[]|string[]
*/
protected $originalKeys;
/**
* The callback for creating the value for a choice.
*
* @var callable
*/
protected $valueCallback;
/**
@ -212,6 +207,8 @@ class ArrayChoiceList implements ChoiceListInterface
/**
* Checks whether the given choices can be cast to strings without
* generating duplicates.
* This method is responsible for preventing conflict between scalar values
* and the empty value.
*
* @param array $choices The choices
* @param array|null $cache The cache for previously checked entries. Internal
@ -232,6 +229,7 @@ class ArrayChoiceList implements ChoiceListInterface
return false;
}
// prevent having false casted to the empty string by isset()
$choice = false === $choice ? '0' : (string) $choice;
if (isset($cache[$choice])) {

View File

@ -35,7 +35,25 @@ interface ChoiceListInterface
/**
* Returns the values for the choices.
*
* The values are strings that do not contain duplicates.
* The values are strings that do not contain duplicates:
*
* $form->add('field', 'choice', [
* 'choices' => [
* 'Decided' => ['Yes' => true, 'No' => false],
* 'Undecided' => ['Maybe' => null],
* ],
* ]);
*
* In this example, the result of this method is:
*
* [
* 'Yes' => '0',
* 'No' => '1',
* 'Maybe' => '2',
* ]
*
* Null and false MUST NOT conflict when being casted to string.
* For this some default incremented values SHOULD be computed.
*
* @return string[] The choice values
*/
@ -62,6 +80,12 @@ interface ChoiceListInterface
* 'Undecided' => ['Maybe' => '2'],
* ]
*
* Nested arrays do not make sense in a view format unless
* they are used as a convenient way of grouping.
* If the implementation does not intend to support grouped choices,
* this method SHOULD be equivalent to {@link getValues()}.
* The $groupBy callback parameter SHOULD be used instead.
*
* @return string[] The choice values
*/
public function getStructuredValues();

View File

@ -14,13 +14,14 @@ namespace Symfony\Component\Form\ChoiceList\Factory;
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
use Symfony\Component\Form\ChoiceList\View\ChoiceListView;
use Symfony\Contracts\Service\ResetInterface;
/**
* Caches the choice lists created by the decorated factory.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class CachingFactoryDecorator implements ChoiceListFactoryInterface
class CachingFactoryDecorator implements ChoiceListFactoryInterface, ResetInterface
{
private $decoratedFactory;
@ -134,4 +135,10 @@ class CachingFactoryDecorator implements ChoiceListFactoryInterface
return $this->views[$hash];
}
public function reset()
{
$this->lists = [];
$this->views = [];
}
}

View File

@ -28,8 +28,8 @@ interface ChoiceListFactoryInterface
* The choices should be passed in the values of the choices array.
*
* Optionally, a callable can be passed for generating the choice values.
* The callable receives the choice as first and the array key as the second
* argument.
* The callable receives the choice as only argument.
* Null may be passed when the choice list contains the empty value.
*
* @param iterable $choices The choices
* @param callable|null $value The callable generating the choice
@ -43,8 +43,8 @@ interface ChoiceListFactoryInterface
* Creates a choice list that is loaded with the given loader.
*
* Optionally, a callable can be passed for generating the choice values.
* The callable receives the choice as first and the array key as the second
* argument.
* The callable receives the choice as only argument.
* Null may be passed when the choice list contains the empty value.
*
* @param ChoiceLoaderInterface $loader The choice loader
* @param callable|null $value The callable generating the choice

View File

@ -28,8 +28,8 @@ interface ChoiceLoaderInterface
* Loads a list of choices.
*
* Optionally, a callable can be passed for generating the choice values.
* The callable receives the choice as first and the array key as the second
* argument.
* The callable receives the choice as only argument.
* Null may be passed when the choice list contains the empty value.
*
* @param callable|null $value The callable which generates the values
* from choices
@ -45,8 +45,8 @@ interface ChoiceLoaderInterface
* corresponding values in the given array.
*
* Optionally, a callable can be passed for generating the choice values.
* The callable receives the choice as first and the array key as the second
* argument.
* The callable receives the choice as only argument.
* Null may be passed when the choice list contains the empty value.
*
* @param string[] $values An array of choice values. Non-existing
* values in this array are ignored
@ -63,8 +63,8 @@ interface ChoiceLoaderInterface
* corresponding choices in the given array.
*
* Optionally, a callable can be passed for generating the choice values.
* The callable receives the choice as first and the array key as the second
* argument.
* The callable receives the choice as only argument.
* Null may be passed when the choice list contains the empty value.
*
* @param array $choices An array of choices. Non-existing choices in
* this array are ignored

View File

@ -19,7 +19,8 @@ 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;
use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* Represents the main form extension, which loads the core functionality.
@ -32,8 +33,14 @@ class CoreExtension extends AbstractExtension
private $choiceListFactory;
private $translator;
public function __construct(PropertyAccessorInterface $propertyAccessor = null, ChoiceListFactoryInterface $choiceListFactory = null, TranslatorInterface $translator = null)
/**
* @param TranslatorInterface|null $translator
*/
public function __construct(PropertyAccessorInterface $propertyAccessor = null, ChoiceListFactoryInterface $choiceListFactory = null, $translator = null)
{
if (null !== $translator && !$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) {
throw new \TypeError(sprintf('Argument 3 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator)));
}
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
$this->choiceListFactory = $choiceListFactory ?: new CachingFactoryDecorator(new PropertyAccessDecorator(new DefaultChoiceListFactory(), $this->propertyAccessor));
$this->translator = $translator;

View File

@ -15,7 +15,8 @@ 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;
use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @author Christian Flothmann <christian.flothmann@sensiolabs.de>
@ -24,8 +25,14 @@ class TransformationFailureListener implements EventSubscriberInterface
{
private $translator;
public function __construct(TranslatorInterface $translator = null)
/**
* @param TranslatorInterface|null $translator
*/
public function __construct($translator = null)
{
if (null !== $translator && !$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) {
throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator)));
}
$this->translator = $translator;
}

View File

@ -14,7 +14,8 @@ 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;
use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @author Christian Flothmann <christian.flothmann@sensiolabs.de>
@ -23,8 +24,14 @@ class TransformationFailureExtension extends AbstractTypeExtension
{
private $translator;
public function __construct(TranslatorInterface $translator = null)
/**
* @param TranslatorInterface|null $translator
*/
public function __construct($translator = null)
{
if (null !== $translator && !$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) {
throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator)));
}
$this->translator = $translator;
}

View File

@ -44,6 +44,11 @@ class FormValidator extends ConstraintValidator
if ($form->isSubmitted() && $form->isSynchronized()) {
// Validate the form data only if transformation succeeded
$groups = self::getValidationGroups($form);
if (!$groups) {
return;
}
$data = $form->getData();
// Validate the data against its own constraints

View File

@ -13,18 +13,11 @@ namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToHtml5LocalDateTimeTransformer;
use Symfony\Component\Form\Tests\Extension\Core\DataTransformer\Traits\DateTimeEqualsTrait;
class DateTimeToHtml5LocalDateTimeTransformerTest extends TestCase
{
public static function assertEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
{
if ($expected instanceof \DateTime && $actual instanceof \DateTime) {
$expected = $expected->format('c');
$actual = $actual->format('c');
}
parent::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase);
}
use DateTimeEqualsTrait;
public function transformProvider()
{
@ -95,7 +88,7 @@ class DateTimeToHtml5LocalDateTimeTransformerTest extends TestCase
$transformer = new DateTimeToHtml5LocalDateTimeTransformer($toTz, $fromTz);
if (null !== $to) {
$this->assertEquals(new \DateTime($to), $transformer->reverseTransform($from));
$this->assertDateTimeEquals(new \DateTime($to), $transformer->reverseTransform($from));
} else {
$this->assertNull($transformer->reverseTransform($from));
}

View File

@ -13,10 +13,13 @@ namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer;
use Symfony\Component\Form\Tests\Extension\Core\DataTransformer\Traits\DateTimeEqualsTrait;
use Symfony\Component\Intl\Util\IntlTestHelper;
class DateTimeToLocalizedStringTransformerTest extends TestCase
{
use DateTimeEqualsTrait;
protected $dateTime;
protected $dateTimeWithoutSeconds;
@ -39,16 +42,6 @@ class DateTimeToLocalizedStringTransformerTest extends TestCase
$this->dateTimeWithoutSeconds = null;
}
public static function assertEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
{
if ($expected instanceof \DateTime && $actual instanceof \DateTime) {
$expected = $expected->format('c');
$actual = $actual->format('c');
}
parent::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase);
}
public function dataProvider()
{
return [
@ -152,7 +145,7 @@ class DateTimeToLocalizedStringTransformerTest extends TestCase
$dateTime = new \DateTime('2010-02-03 04:05');
$this->assertEquals(
$this->assertDateTimeEquals(
$dateTime->format('c'),
$transformer->reverseTransform('03.02.2010, 04:05')->format('c')
);
@ -217,14 +210,14 @@ class DateTimeToLocalizedStringTransformerTest extends TestCase
$output = new \DateTime($output);
$this->assertEquals($output, $transformer->reverseTransform($input));
$this->assertDateTimeEquals($output, $transformer->reverseTransform($input));
}
public function testReverseTransformFullTime()
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::FULL);
$this->assertEquals($this->dateTime, $transformer->reverseTransform('03.02.2010, 04:05:06 GMT+00:00'));
$this->assertDateTimeEquals($this->dateTime, $transformer->reverseTransform('03.02.2010, 04:05:06 GMT+00:00'));
}
public function testReverseTransformFromDifferentLocale()
@ -233,7 +226,7 @@ class DateTimeToLocalizedStringTransformerTest extends TestCase
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC');
$this->assertEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('Feb 3, 2010, 04:05 AM'));
$this->assertDateTimeEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('Feb 3, 2010, 04:05 AM'));
}
public function testReverseTransformWithDifferentTimezones()
@ -243,7 +236,7 @@ class DateTimeToLocalizedStringTransformerTest extends TestCase
$dateTime = new \DateTime('2010-02-03 04:05:00 Asia/Hong_Kong');
$dateTime->setTimezone(new \DateTimeZone('America/New_York'));
$this->assertEquals($dateTime, $transformer->reverseTransform('03.02.2010, 04:05'));
$this->assertDateTimeEquals($dateTime, $transformer->reverseTransform('03.02.2010, 04:05'));
}
public function testReverseTransformOnlyDateWithDifferentTimezones()
@ -252,21 +245,21 @@ class DateTimeToLocalizedStringTransformerTest extends TestCase
$dateTime = new \DateTime('2017-01-10 11:00', new \DateTimeZone('Europe/Berlin'));
$this->assertEquals($dateTime, $transformer->reverseTransform('2017-01-10'));
$this->assertDateTimeEquals($dateTime, $transformer->reverseTransform('2017-01-10'));
}
public function testReverseTransformWithDifferentPatterns()
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, \IntlDateFormatter::GREGORIAN, 'MM*yyyy*dd HH|mm|ss');
$this->assertEquals($this->dateTime, $transformer->reverseTransform('02*2010*03 04|05|06'));
$this->assertDateTimeEquals($this->dateTime, $transformer->reverseTransform('02*2010*03 04|05|06'));
}
public function testReverseTransformDateOnlyWithDstIssue()
{
$transformer = new DateTimeToLocalizedStringTransformer('Europe/Rome', 'Europe/Rome', \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, \IntlDateFormatter::GREGORIAN, 'dd/MM/yyyy');
$this->assertEquals(
$this->assertDateTimeEquals(
new \DateTime('1978-05-28', new \DateTimeZone('Europe/Rome')),
$transformer->reverseTransform('28/05/1978')
);
@ -276,7 +269,7 @@ class DateTimeToLocalizedStringTransformerTest extends TestCase
{
$transformer = new DateTimeToLocalizedStringTransformer('Europe/Rome', 'Europe/Rome', \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, \IntlDateFormatter::GREGORIAN, "'day': dd 'month': MM 'year': yyyy");
$this->assertEquals(
$this->assertDateTimeEquals(
new \DateTime('1978-05-28', new \DateTimeZone('Europe/Rome')),
$transformer->reverseTransform('day: 28 month: 05 year: 1978')
);
@ -314,7 +307,7 @@ class DateTimeToLocalizedStringTransformerTest extends TestCase
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::SHORT);
$this->assertEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('31.04.10 04:05'));
$this->assertDateTimeEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('31.04.10 04:05'));
}
/**

View File

@ -13,9 +13,12 @@ namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToRfc3339Transformer;
use Symfony\Component\Form\Tests\Extension\Core\DataTransformer\Traits\DateTimeEqualsTrait;
class DateTimeToRfc3339TransformerTest extends TestCase
{
use DateTimeEqualsTrait;
protected $dateTime;
protected $dateTimeWithoutSeconds;
@ -33,16 +36,6 @@ class DateTimeToRfc3339TransformerTest extends TestCase
$this->dateTimeWithoutSeconds = null;
}
public static function assertEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
{
if ($expected instanceof \DateTime && $actual instanceof \DateTime) {
$expected = $expected->format('c');
$actual = $actual->format('c');
}
parent::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase);
}
public function allProvider()
{
return [
@ -108,7 +101,7 @@ class DateTimeToRfc3339TransformerTest extends TestCase
$transformer = new DateTimeToRfc3339Transformer($toTz, $fromTz);
if (null !== $to) {
$this->assertEquals(new \DateTime($to), $transformer->reverseTransform($from));
$this->assertDateTimeEquals(new \DateTime($to), $transformer->reverseTransform($from));
} else {
$this->assertNull($transformer->reverseTransform($from));
}

View File

@ -0,0 +1,28 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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\DataTransformer\Traits;
/**
* @internal
*/
trait DateTimeEqualsTrait
{
public static function assertDateTimeEquals($expected, $actual)
{
if ($expected instanceof \DateTime && $actual instanceof \DateTime) {
$expected = $expected->format('c');
$actual = $actual->format('c');
}
parent::assertEquals($expected, $actual);
}
}

View File

@ -220,6 +220,28 @@ class FormValidatorTest extends ConstraintValidatorTestCase
$this->assertNoViolation();
}
public function testDontValidateChildConstraintsIfCallableNoValidationGroups()
{
$formOptions = [
'constraints' => [new Valid()],
'validation_groups' => [],
];
$form = $this->getBuilder('name', null, $formOptions)
->setCompound(true)
->setDataMapper(new PropertyPathMapper())
->getForm();
$childOptions = ['constraints' => [new NotBlank()]];
$child = $this->getCompoundForm(new \stdClass(), $childOptions);
$form->add($child);
$form->submit([]);
$this->expectNoValidate();
$this->validator->validate($form, new Form());
$this->assertNoViolation();
}
public function testDontValidateIfNotSynchronized()
{
$object = new \stdClass();

View File

@ -306,9 +306,9 @@ class Response
}
// Check if we need to send extra expire info headers
if ('1.0' == $this->getProtocolVersion() && false !== strpos($this->headers->get('Cache-Control'), 'no-cache')) {
$this->headers->set('pragma', 'no-cache');
$this->headers->set('expires', -1);
if ('1.0' == $this->getProtocolVersion() && false !== strpos($headers->get('Cache-Control'), 'no-cache')) {
$headers->set('pragma', 'no-cache');
$headers->set('expires', -1);
}
$this->ensureIEOverSSLCompatibility($request);

View File

@ -0,0 +1,12 @@
#!/usr/bin/env bash
if [[ $1 == force || ! $(docker ps -a | grep symfony-intl) ]]; then
docker rm -f symfony-intl
docker create --name symfony-intl -it -v /tmp:/tmp -v $(pwd):/app -w /app jakzal/php-intl:7.2-63.1 bash
docker start symfony-intl
docker exec symfony-intl bash -xc "apt-get update && apt-get -y install build-essential"
else
docker start symfony-intl
fi;
docker exec -u $(id -u):$(id -g) symfony-intl php src/Symfony/Component/Intl/Resources/bin/update-data.php

View File

@ -233,7 +233,6 @@
"ms": "malay",
"mt": "maltese",
"mua": "mundang",
"mul": "plure linguas",
"mus": "creek",
"mwl": "mirandese",
"my": "birmano",
@ -361,7 +360,6 @@
"ug": "uighur",
"uk": "ukrainiano",
"umb": "umbundu",
"und": "lingua incognite",
"ur": "urdu",
"uz": "uzbeko",
"vai": "vai",
@ -388,7 +386,6 @@
"zh_Hant": "chinese traditional",
"zu": "zulu",
"zun": "zuni",
"zxx": "sin contento linguistic",
"zza": "zaza"
}
}

View File

@ -29,7 +29,6 @@
"ru": "Rusia",
"th": "Thailand",
"tr": "Turki",
"und": "Basa Ora Dikenali",
"zh": "Tyonghwa",
"zh_Hant": "Tyonghwa (Tradisional)"
}

View File

@ -23,7 +23,6 @@
"pt_BR": "Pōtikī Parahi",
"pt_PT": "Pōtikī Uropi",
"ru": "Ruhiana",
"und": "Reo Tē Mōhiotia",
"zh": "Hainamana",
"zh_Hans": "Hainamana Māmā",
"zh_Hant": "Hainamana Tukuiho"

View File

@ -232,7 +232,6 @@
"ms": "ملي",
"mt": "مالٽي",
"mua": "من دانگ",
"mul": "هڪ کان وڌيڪ ٻوليون",
"mus": "ڪريڪ",
"mwl": "مرانڊيز",
"my": "برمي",
@ -360,7 +359,6 @@
"ug": "يوغور",
"uk": "يوڪراني",
"umb": "اومبنڊو",
"und": "اڻڄاتل ٻولي",
"ur": "اردو",
"uz": "ازبڪ",
"vai": "يا",
@ -387,7 +385,6 @@
"zh_Hant": "روايتي چيني",
"zu": "زولو",
"zun": "زوني",
"zxx": "ڪوئي ٻولي جو مواد ڪونهي",
"zza": "زازا"
}
}

View File

@ -53,7 +53,6 @@
"th": "Taaylandays",
"tr": "Turkish",
"uk": "Yukreeniyaan",
"und": "Af aan la aqoon ama aan sax ahayn",
"ur": "Urduu",
"vi": "Fiitnaamays",
"yo": "Yoruuba",

View File

@ -226,7 +226,6 @@
"ms": "malaý dili",
"mt": "malta dili",
"mua": "mundang dili",
"mul": "birnäçe dil",
"mus": "krik dili",
"mwl": "mirand dili",
"my": "birma dili",
@ -353,7 +352,6 @@
"ug": "uýgur dili",
"uk": "ukrain dili",
"umb": "umbundu dili",
"und": "näbelli dil",
"ur": "urdu",
"uz": "özbek dili",
"vai": "wai dili",
@ -380,7 +378,6 @@
"zh_Hant": "adaty hytaý dili",
"zu": "zulu dili",
"zun": "zuni dili",
"zxx": "dilçilige degişli mazmun ýok",
"zza": "zazaki dili"
}
}

View File

@ -392,7 +392,6 @@ abstract class AbstractLanguageDataProviderTest extends AbstractDataProviderTest
'ms',
'mt',
'mua',
'mul',
'mus',
'mwl',
'mwr',
@ -596,7 +595,6 @@ abstract class AbstractLanguageDataProviderTest extends AbstractDataProviderTest
'uga',
'uk',
'umb',
'und',
'ur',
'uz',
'vai',
@ -641,7 +639,6 @@ abstract class AbstractLanguageDataProviderTest extends AbstractDataProviderTest
'zh_Hant',
'zu',
'zun',
'zxx',
'zza',
];

View File

@ -45,7 +45,7 @@ class GitRepositoryTest extends TestCase
$this->setExpectedException(RuntimeException::class);
}
@mkdir($this->targetDir, '0777', true);
@mkdir($this->targetDir, 0777, true);
new GitRepository($this->targetDir);
}

View File

@ -46,7 +46,7 @@ class ExpressionVoter implements VoterInterface
} elseif (null === $authChecker) {
@trigger_error(sprintf('Argument 3 passed to "%s()" should be an instance of AuthorizationCheckerInterface, not passing it is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED);
} elseif (!$authChecker instanceof AuthorizationCheckerInterface) {
throw new \InvalidArgumentException(sprintf('Argument 3 passed to %s() must be an instance of %s or null, %s given.', __METHOD__, AuthorizationCheckerInterface::class, \is_object($authChecker) ? \get_class($authChecker) : \gettype($authChecker)));
throw new \TypeError(sprintf('Argument 3 passed to %s() must be an instance of %s or null, %s given.', __METHOD__, AuthorizationCheckerInterface::class, \is_object($authChecker) ? \get_class($authChecker) : \gettype($authChecker)));
}
$this->expressionLanguage = $expressionLanguage;

View File

@ -330,6 +330,10 @@
<source>This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.</source>
<target>رمز المعرّف نشاط تجاري (BIC) هذا لا يرتبط مع IBAN {{ iban }}.</target>
</trans-unit>
<trans-unit id="86">
<source>This value should be valid JSON.</source>
<target>هذه القيمة يجب أن تكون صالحة ل JSON.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -12,7 +12,6 @@
namespace Symfony\Component\Workflow\Event;
use Symfony\Component\EventDispatcher\Event as BaseEvent;
use Symfony\Component\Workflow\Exception\InvalidArgumentException;
use Symfony\Component\Workflow\Marking;
use Symfony\Component\Workflow\Transition;
use Symfony\Component\Workflow\WorkflowInterface;
@ -49,7 +48,7 @@ class Event extends BaseEvent
} elseif ($workflow instanceof WorkflowInterface) {
$this->workflow = $workflow;
} else {
throw new InvalidArgumentException(sprintf('The 4th parameter of "%s" should be a "%s" instance instead.', __METHOD__, WorkflowInterface::class));
throw new \TypeError(sprintf('The 4th parameter of "%s" should be a "%s" instance instead.', __METHOD__, WorkflowInterface::class));
}
}

View File

@ -27,7 +27,7 @@ class GuardEvent extends Event
/**
* {@inheritdoc}
*/
public function __construct($subject, Marking $marking, Transition $transition, $workflowName = 'unnamed')
public function __construct($subject, Marking $marking, Transition $transition, $workflowName = null)
{
parent::__construct($subject, $marking, $transition, $workflowName);

View File

@ -1,5 +1,14 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Workflow;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;