Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 522594a

Browse files
committed
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
2 parents 0b2a9d5 + 7e5dfcf commit 522594a

File tree

38 files changed

+275
-130
lines changed

38 files changed

+275
-130
lines changed

src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public function __construct($entityManager)
4242
@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);
4343
$this->classMetadataFactory = $entityManager;
4444
} else {
45-
throw new \InvalidArgumentException(sprintf('$entityManager must be an instance of "%s", "%s" given.', EntityManagerInterface::class, \is_object($entityManager) ? \get_class($entityManager) : \gettype($entityManager)));
45+
throw new \TypeError(sprintf('$entityManager must be an instance of "%s", "%s" given.', EntityManagerInterface::class, \is_object($entityManager) ? \get_class($entityManager) : \gettype($entityManager)));
4646
}
4747
}
4848

src/Symfony/Bridge/PhpUnit/bin/simple-phpunit

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,11 @@ if ('phpdbg' === PHP_SAPI) {
7676
$PHP .= ' -qrr';
7777
}
7878

79-
$defaultEnvs = [
79+
$defaultEnvs = array(
8080
'COMPOSER' => 'composer.json',
8181
'COMPOSER_VENDOR_DIR' => 'vendor',
8282
'COMPOSER_BIN_DIR' => 'bin',
83-
];
83+
);
8484

8585
foreach ($defaultEnvs as $envName => $envValue) {
8686
if ($envValue !== getenv($envName)) {

src/Symfony/Bridge/Twig/Command/DebugCommand.php

Lines changed: 60 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Symfony\Component\Console\Style\SymfonyStyle;
2121
use Symfony\Component\Finder\Finder;
2222
use Twig\Environment;
23+
use Twig\Loader\ChainLoader;
2324
use Twig\Loader\FilesystemLoader;
2425

2526
/**
@@ -36,6 +37,7 @@ class DebugCommand extends Command
3637
private $bundlesMetadata;
3738
private $twigDefaultPath;
3839
private $rootDir;
40+
private $filesystemLoaders;
3941

4042
public function __construct(Environment $twig, string $projectDir = null, array $bundlesMetadata = [], string $twigDefaultPath = null, string $rootDir = null)
4143
{
@@ -87,7 +89,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
8789
$name = $input->getArgument('name');
8890
$filter = $input->getOption('filter');
8991

90-
if (null !== $name && !$this->twig->getLoader() instanceof FilesystemLoader) {
92+
if (null !== $name && [] === $this->getFilesystemLoaders()) {
9193
throw new InvalidArgumentException(sprintf('Argument "name" not supported, it requires the Twig loader "%s"', FilesystemLoader::class));
9294
}
9395

@@ -150,9 +152,11 @@ private function displayPathsText(SymfonyStyle $io, string $name)
150152
$message = 'No template paths configured for your application';
151153
} else {
152154
$message = sprintf('No template paths configured for "@%s" namespace', $namespace);
153-
$namespaces = $this->twig->getLoader()->getNamespaces();
154-
foreach ($this->findAlternatives($namespace, $namespaces) as $namespace) {
155-
$alternatives[] = '@'.$namespace;
155+
foreach ($this->getFilesystemLoaders() as $loader) {
156+
$namespaces = $loader->getNamespaces();
157+
foreach ($this->findAlternatives($namespace, $namespaces) as $namespace) {
158+
$alternatives[] = '@'.$namespace;
159+
}
156160
}
157161
}
158162

@@ -243,25 +247,25 @@ private function displayGeneralJson(SymfonyStyle $io, $filter)
243247

244248
private function getLoaderPaths(string $name = null): array
245249
{
246-
/** @var FilesystemLoader $loader */
247-
$loader = $this->twig->getLoader();
248250
$loaderPaths = [];
249-
$namespaces = $loader->getNamespaces();
250-
if (null !== $name) {
251-
$namespace = $this->parseTemplateName($name)[0];
252-
$namespaces = array_intersect([$namespace], $namespaces);
253-
}
251+
foreach ($this->getFilesystemLoaders() as $loader) {
252+
$namespaces = $loader->getNamespaces();
253+
if (null !== $name) {
254+
$namespace = $this->parseTemplateName($name)[0];
255+
$namespaces = array_intersect([$namespace], $namespaces);
256+
}
254257

255-
foreach ($namespaces as $namespace) {
256-
$paths = array_map([$this, 'getRelativePath'], $loader->getPaths($namespace));
258+
foreach ($namespaces as $namespace) {
259+
$paths = array_map([$this, 'getRelativePath'], $loader->getPaths($namespace));
257260

258-
if (FilesystemLoader::MAIN_NAMESPACE === $namespace) {
259-
$namespace = '(None)';
260-
} else {
261-
$namespace = '@'.$namespace;
262-
}
261+
if (FilesystemLoader::MAIN_NAMESPACE === $namespace) {
262+
$namespace = '(None)';
263+
} else {
264+
$namespace = '@'.$namespace;
265+
}
263266

264-
$loaderPaths[$namespace] = $paths;
267+
$loaderPaths[$namespace] = array_merge($loaderPaths[$namespace] ?? [], $paths);
268+
}
265269
}
266270

267271
return $loaderPaths;
@@ -437,22 +441,22 @@ private function error(SymfonyStyle $io, string $message, array $alternatives =
437441

438442
private function findTemplateFiles(string $name): array
439443
{
440-
/** @var FilesystemLoader $loader */
441-
$loader = $this->twig->getLoader();
442-
$files = [];
443444
list($namespace, $shortname) = $this->parseTemplateName($name);
444445

445-
foreach ($loader->getPaths($namespace) as $path) {
446-
if (!$this->isAbsolutePath($path)) {
447-
$path = $this->projectDir.'/'.$path;
448-
}
449-
$filename = $path.'/'.$shortname;
446+
$files = [];
447+
foreach ($this->getFilesystemLoaders() as $loader) {
448+
foreach ($loader->getPaths($namespace) as $path) {
449+
if (!$this->isAbsolutePath($path)) {
450+
$path = $this->projectDir.'/'.$path;
451+
}
452+
$filename = $path.'/'.$shortname;
450453

451-
if (is_file($filename)) {
452-
if (false !== $realpath = realpath($filename)) {
453-
$files[] = $this->getRelativePath($realpath);
454-
} else {
455-
$files[] = $this->getRelativePath($filename);
454+
if (is_file($filename)) {
455+
if (false !== $realpath = realpath($filename)) {
456+
$files[] = $this->getRelativePath($realpath);
457+
} else {
458+
$files[] = $this->getRelativePath($filename);
459+
}
456460
}
457461
}
458462
}
@@ -535,4 +539,28 @@ private function isAbsolutePath(string $file): bool
535539
{
536540
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);
537541
}
542+
543+
/**
544+
* @return FilesystemLoader[]
545+
*/
546+
private function getFilesystemLoaders(): array
547+
{
548+
if (null !== $this->filesystemLoaders) {
549+
return $this->filesystemLoaders;
550+
}
551+
$this->filesystemLoaders = [];
552+
553+
$loader = $this->twig->getLoader();
554+
if ($loader instanceof FilesystemLoader) {
555+
$this->filesystemLoaders[] = $loader;
556+
} elseif ($loader instanceof ChainLoader) {
557+
foreach ($loader->getLoaders() as $l) {
558+
if ($l instanceof FilesystemLoader) {
559+
$this->filesystemLoaders[] = $l;
560+
}
561+
}
562+
}
563+
564+
return $this->filesystemLoaders;
565+
}
538566
}

src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\Console\Application;
1717
use Symfony\Component\Console\Tester\CommandTester;
1818
use Twig\Environment;
19+
use Twig\Loader\ChainLoader;
1920
use Twig\Loader\FilesystemLoader;
2021

2122
class DebugCommandTest extends TestCase
@@ -279,7 +280,16 @@ public function getDebugTemplateNameTestData()
279280
];
280281
}
281282

282-
private function createCommandTester(array $paths = [], array $bundleMetadata = [], string $defaultPath = null, string $rootDir = null): CommandTester
283+
public function testDebugTemplateNameWithChainLoader()
284+
{
285+
$tester = $this->createCommandTester(['templates/' => null], [], null, null, true);
286+
$ret = $tester->execute(['name' => 'base.html.twig'], ['decorated' => false]);
287+
288+
$this->assertEquals(0, $ret, 'Returns 0 in case of success');
289+
$this->assertContains('[OK]', $tester->getDisplay());
290+
}
291+
292+
private function createCommandTester(array $paths = [], array $bundleMetadata = [], string $defaultPath = null, string $rootDir = null, bool $useChainLoader = false): CommandTester
283293
{
284294
$projectDir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures';
285295
$loader = new FilesystemLoader([], $projectDir);
@@ -291,6 +301,10 @@ private function createCommandTester(array $paths = [], array $bundleMetadata =
291301
}
292302
}
293303

304+
if ($useChainLoader) {
305+
$loader = new ChainLoader([$loader]);
306+
}
307+
294308
$application = new Application();
295309
$application->add(new DebugCommand(new Environment($loader), $projectDir, $bundleMetadata, $defaultPath, $rootDir));
296310
$command = $application->find('debug:twig');

src/Symfony/Bridge/Twig/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"require": {
1919
"php": "^7.1.3",
2020
"symfony/contracts": "^1.0.2",
21-
"twig/twig": "^1.37.1|^2.6.2"
21+
"twig/twig": "^1.38.1|^2.7.1"
2222
},
2323
"require-dev": {
2424
"egulias/email-validator": "^2.0",

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
5656
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
5757
use Symfony\Component\Finder\Finder;
58+
use Symfony\Component\Form\ChoiceList\Factory\CachingFactoryDecorator;
5859
use Symfony\Component\Form\FormTypeExtensionInterface;
5960
use Symfony\Component\Form\FormTypeGuesserInterface;
6061
use Symfony\Component\Form\FormTypeInterface;
@@ -440,6 +441,11 @@ private function registerFormConfiguration(array $config, ContainerBuilder $cont
440441
if (!class_exists(Translator::class)) {
441442
$container->removeDefinition('form.type_extension.upload.validator');
442443
}
444+
if (!method_exists(CachingFactoryDecorator::class, 'reset')) {
445+
$container->getDefinition('form.choice_list_factory.cached')
446+
->clearTag('kernel.reset')
447+
;
448+
}
443449
}
444450

445451
private function registerEsiConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)

src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757

5858
<service id="form.choice_list_factory.cached" class="Symfony\Component\Form\ChoiceList\Factory\CachingFactoryDecorator">
5959
<argument type="service" id="form.choice_list_factory.property_access"/>
60+
<tag name="kernel.reset" method="reset" />
6061
</service>
6162

6263
<service id="form.choice_list_factory" alias="form.choice_list_factory.cached" />

src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ protected static function createClient(array $options = [], array $server = [])
3636
try {
3737
$client = $kernel->getContainer()->get('test.client');
3838
} catch (ServiceNotFoundException $e) {
39-
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".');
39+
if (class_exists(Client::class)) {
40+
throw new \LogicException('You cannot create the client used in functional tests if the "framework.test" config is not set to true.');
41+
}
42+
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"');
4043
}
4144

4245
$client->setServerParameters($server);

src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public function __construct(iterable $listeners, ExceptionListener $exceptionLis
4141
$this->logoutListener = $logoutListener;
4242
$this->config = $config;
4343
} else {
44-
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)));
44+
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)));
4545
}
4646
}
4747

src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
* A list of choices with arbitrary data types.
1616
*
1717
* The user of this class is responsible for assigning string values to the
18-
* choices. Both the choices and their values are passed to the constructor.
19-
* Each choice must have a corresponding value (with the same array key) in
20-
* the value array.
18+
* choices annd for their uniqueness.
19+
* Both the choices and their values are passed to the constructor.
20+
* Each choice must have a corresponding value (with the same key) in
21+
* the values array.
2122
*
2223
* @author Bernhard Schussek <[email protected]>
2324
*/
@@ -43,12 +44,6 @@ class ArrayChoiceList implements ChoiceListInterface
4344
* @var int[]|string[]
4445
*/
4546
protected $originalKeys;
46-
47-
/**
48-
* The callback for creating the value for a choice.
49-
*
50-
* @var callable
51-
*/
5247
protected $valueCallback;
5348

5449
/**
@@ -212,6 +207,8 @@ protected function flatten(array $choices, $value, &$choicesByValues, &$keysByVa
212207
/**
213208
* Checks whether the given choices can be cast to strings without
214209
* generating duplicates.
210+
* This method is responsible for preventing conflict between scalar values
211+
* and the empty value.
215212
*
216213
* @param array $choices The choices
217214
* @param array|null $cache The cache for previously checked entries. Internal
@@ -232,6 +229,7 @@ private function castableToString(array $choices, array &$cache = [])
232229
return false;
233230
}
234231

232+
// prevent having false casted to the empty string by isset()
235233
$choice = false === $choice ? '0' : (string) $choice;
236234

237235
if (isset($cache[$choice])) {

src/Symfony/Component/Form/ChoiceList/ChoiceListInterface.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,25 @@ public function getChoices();
3535
/**
3636
* Returns the values for the choices.
3737
*
38-
* The values are strings that do not contain duplicates.
38+
* The values are strings that do not contain duplicates:
39+
*
40+
* $form->add('field', 'choice', [
41+
* 'choices' => [
42+
* 'Decided' => ['Yes' => true, 'No' => false],
43+
* 'Undecided' => ['Maybe' => null],
44+
* ],
45+
* ]);
46+
*
47+
* In this example, the result of this method is:
48+
*
49+
* [
50+
* 'Yes' => '0',
51+
* 'No' => '1',
52+
* 'Maybe' => '2',
53+
* ]
54+
*
55+
* Null and false MUST NOT conflict when being casted to string.
56+
* For this some default incremented values SHOULD be computed.
3957
*
4058
* @return string[] The choice values
4159
*/
@@ -62,6 +80,12 @@ public function getValues();
6280
* 'Undecided' => ['Maybe' => '2'],
6381
* ]
6482
*
83+
* Nested arrays do not make sense in a view format unless
84+
* they are used as a convenient way of grouping.
85+
* If the implementation does not intend to support grouped choices,
86+
* this method SHOULD be equivalent to {@link getValues()}.
87+
* The $groupBy callback parameter SHOULD be used instead.
88+
*
6589
* @return string[] The choice values
6690
*/
6791
public function getStructuredValues();

src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
1515
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
1616
use Symfony\Component\Form\ChoiceList\View\ChoiceListView;
17+
use Symfony\Contracts\Service\ResetInterface;
1718

1819
/**
1920
* Caches the choice lists created by the decorated factory.
2021
*
2122
* @author Bernhard Schussek <[email protected]>
2223
*/
23-
class CachingFactoryDecorator implements ChoiceListFactoryInterface
24+
class CachingFactoryDecorator implements ChoiceListFactoryInterface, ResetInterface
2425
{
2526
private $decoratedFactory;
2627

@@ -134,4 +135,10 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null,
134135

135136
return $this->views[$hash];
136137
}
138+
139+
public function reset()
140+
{
141+
$this->lists = [];
142+
$this->views = [];
143+
}
137144
}

src/Symfony/Component/Form/ChoiceList/Factory/ChoiceListFactoryInterface.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ interface ChoiceListFactoryInterface
2828
* The choices should be passed in the values of the choices array.
2929
*
3030
* Optionally, a callable can be passed for generating the choice values.
31-
* The callable receives the choice as first and the array key as the second
32-
* argument.
31+
* The callable receives the choice as only argument.
32+
* Null may be passed when the choice list contains the empty value.
3333
*
3434
* @param iterable $choices The choices
3535
* @param callable|null $value The callable generating the choice
@@ -43,8 +43,8 @@ public function createListFromChoices($choices, $value = null);
4343
* Creates a choice list that is loaded with the given loader.
4444
*
4545
* Optionally, a callable can be passed for generating the choice values.
46-
* The callable receives the choice as first and the array key as the second
47-
* argument.
46+
* The callable receives the choice as only argument.
47+
* Null may be passed when the choice list contains the empty value.
4848
*
4949
* @param ChoiceLoaderInterface $loader The choice loader
5050
* @param callable|null $value The callable generating the choice

0 commit comments

Comments
 (0)