Merge branch '3.4' into 4.2

* 3.4:
  [DI] Removes number of elements information in debug mode
  Update PR template for 4.3
  [Intl] Add FallbackTrait for data generation
  [Console] Commands with an alias should not be recognized as ambiguous
  clarify the possible class/interface of the cache
This commit is contained in:
Nicolas Grekas 2019-05-09 11:19:46 +02:00
commit c083e20cf2
11 changed files with 158 additions and 77 deletions

View File

@ -1,20 +1,21 @@
| Q | A | Q | A
| ------------- | --- | ------------- | ---
| Branch? | master for features / 3.4 up to 4.2 for bug fixes <!-- see below --> | Branch? | master for features / 3.4, 4.2 or 4.3 for bug fixes <!-- see below -->
| Bug fix? | yes/no | Bug fix? | yes/no
| New feature? | yes/no <!-- don't forget to update src/**/CHANGELOG.md files --> | New feature? | yes/no <!-- please update src/**/CHANGELOG.md files -->
| BC breaks? | no <!-- see https://symfony.com/bc --> | BC breaks? | no <!-- see https://symfony.com/bc -->
| Deprecations? | yes/no <!-- don't forget to update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Deprecations? | yes/no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tests pass? | yes <!-- please add some, will be required by reviewers --> | Tests pass? | yes <!-- please add some, will be required by reviewers -->
| Fixed tickets | #... <!-- #-prefixed issue number(s), if any --> | Fixed tickets | #... <!-- #-prefixed issue number(s), if any -->
| License | MIT | License | MIT
| Doc PR | symfony/symfony-docs#... <!-- required for new features --> | Doc PR | symfony/symfony-docs#... <!-- required for new features -->
<!-- <!--
Write a short README entry for your feature/bugfix here (replace this comment block.) Replace this notice by a short README for your feature/bugfix. This will help people
This will help people understand your PR and can be used as a start of the Doc PR. understand your PR and can be used as a start for the documentation.
Additionally:
- Bug fixes must be submitted against the lowest branch where they apply Additionally (see https://symfony.com/roadmap):
- Bug fixes must be submitted against the lowest maintained branch where they apply
(lowest branches are regularly merged to upper ones so they get the fixes too). (lowest branches are regularly merged to upper ones so they get the fixes too).
- Features and deprecations must be submitted against the master branch. - Features and deprecations must be submitted against the master branch.
--> -->

View File

@ -145,6 +145,7 @@ EOF
$options['show_hidden'] = $input->getOption('show-hidden'); $options['show_hidden'] = $input->getOption('show-hidden');
$options['raw_text'] = $input->getOption('raw'); $options['raw_text'] = $input->getOption('raw');
$options['output'] = $io; $options['output'] = $io;
$options['is_debug'] = $this->getApplication()->getKernel()->isDebug();
try { try {
$helper->describe($io, $object, $options); $helper->describe($io, $object, $options);

View File

@ -18,6 +18,7 @@ use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
@ -333,7 +334,11 @@ class TextDescriptor extends Descriptor
if ($argument instanceof Reference) { if ($argument instanceof Reference) {
$argumentsInformation[] = sprintf('Service(%s)', (string) $argument); $argumentsInformation[] = sprintf('Service(%s)', (string) $argument);
} elseif ($argument instanceof IteratorArgument) { } elseif ($argument instanceof IteratorArgument) {
$argumentsInformation[] = sprintf('Iterator (%d element(s))', \count($argument->getValues())); if ($argument instanceof TaggedIteratorArgument) {
$argumentsInformation[] = sprintf('Tagged Iterator for "%s"%s', $argument->getTag(), $options['is_debug'] ? '' : sprintf(' (%d element(s))', \count($argument->getValues())));
} else {
$argumentsInformation[] = sprintf('Iterator (%d element(s))', \count($argument->getValues()));
}
} elseif ($argument instanceof ServiceLocatorArgument) { } elseif ($argument instanceof ServiceLocatorArgument) {
$argumentsInformation[] = sprintf('Service locator (%d element(s))', \count($argument->getValues())); $argumentsInformation[] = sprintf('Service locator (%d element(s))', \count($argument->getValues()));
} elseif ($argument instanceof Definition) { } elseif ($argument instanceof Definition) {

View File

@ -95,7 +95,7 @@
<tag name="kernel.cache_warmer" /> <tag name="kernel.cache_warmer" />
</service> </service>
<service id="serializer.mapping.cache.symfony" class="Symfony\Component\Cache\Adapter\PhpArrayAdapter"> <service id="serializer.mapping.cache.symfony" class="Psr\Cache\CacheItemPoolInterface">
<factory class="Symfony\Component\Cache\Adapter\PhpArrayAdapter" method="create" /> <factory class="Symfony\Component\Cache\Adapter\PhpArrayAdapter" method="create" />
<argument>%serializer.mapping.cache.file%</argument> <argument>%serializer.mapping.cache.file%</argument>
<argument type="service" id="cache.serializer" /> <argument type="service" id="cache.serializer" />

View File

@ -217,6 +217,7 @@ abstract class AbstractDescriptorTest extends TestCase
private function assertDescription($expectedDescription, $describedObject, array $options = []) private function assertDescription($expectedDescription, $describedObject, array $options = [])
{ {
$options['is_debug'] = false;
$options['raw_output'] = true; $options['raw_output'] = true;
$options['raw_text'] = true; $options['raw_text'] = true;
$output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true);

View File

@ -610,6 +610,15 @@ class Application
$this->init(); $this->init();
$aliases = []; $aliases = [];
foreach ($this->commands as $command) {
foreach ($command->getAliases() as $alias) {
if (!$this->has($alias)) {
$this->commands[$alias] = $command;
}
}
}
$allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands); $allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands);
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name); $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name);
$commands = preg_grep('{^'.$expr.'}', $allCommands); $commands = preg_grep('{^'.$expr.'}', $allCommands);

View File

@ -73,8 +73,8 @@ class ApplicationTest extends TestCase
require_once self::$fixturesPath.'/FooSubnamespaced1Command.php'; require_once self::$fixturesPath.'/FooSubnamespaced1Command.php';
require_once self::$fixturesPath.'/FooSubnamespaced2Command.php'; require_once self::$fixturesPath.'/FooSubnamespaced2Command.php';
require_once self::$fixturesPath.'/FooWithoutAliasCommand.php'; require_once self::$fixturesPath.'/FooWithoutAliasCommand.php';
require_once self::$fixturesPath.'/TestTiti.php'; require_once self::$fixturesPath.'/TestAmbiguousCommandRegistering.php';
require_once self::$fixturesPath.'/TestToto.php'; require_once self::$fixturesPath.'/TestAmbiguousCommandRegistering2.php';
} }
protected function normalizeLineBreaks($text) protected function normalizeLineBreaks($text)
@ -165,6 +165,28 @@ class ApplicationTest extends TestCase
$this->assertEquals('foo', $command->getName(), '->register() registers a new command'); $this->assertEquals('foo', $command->getName(), '->register() registers a new command');
} }
public function testRegisterAmbiguous()
{
$code = function (InputInterface $input, OutputInterface $output) {
$output->writeln('It works!');
};
$application = new Application();
$application->setAutoExit(false);
$application
->register('test-foo')
->setAliases(['test'])
->setCode($code);
$application
->register('test-bar')
->setCode($code);
$tester = new ApplicationTester($application);
$tester->run(['test']);
$this->assertContains('It works!', $tester->getDisplay(true));
}
public function testAdd() public function testAdd()
{ {
$application = new Application(); $application = new Application();
@ -304,9 +326,9 @@ class ApplicationTest extends TestCase
public function testFindNonAmbiguous() public function testFindNonAmbiguous()
{ {
$application = new Application(); $application = new Application();
$application->add(new \TestTiti()); $application->add(new \TestAmbiguousCommandRegistering());
$application->add(new \TestToto()); $application->add(new \TestAmbiguousCommandRegistering2());
$this->assertEquals('test-toto', $application->find('test')->getName()); $this->assertEquals('test-ambiguous', $application->find('test')->getName());
} }
/** /**

View File

@ -4,19 +4,19 @@ use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
class TestToto extends Command class TestAmbiguousCommandRegistering extends Command
{ {
protected function configure() protected function configure()
{ {
$this $this
->setName('test-toto') ->setName('test-ambiguous')
->setDescription('The test-toto command') ->setDescription('The test-ambiguous command')
->setAliases(['test']) ->setAliases(['test'])
; ;
} }
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
$output->write('test-toto'); $output->write('test-ambiguous');
} }
} }

View File

@ -4,18 +4,18 @@ use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
class TestTiti extends Command class TestAmbiguousCommandRegistering2 extends Command
{ {
protected function configure() protected function configure()
{ {
$this $this
->setName('test-titi') ->setName('test-ambiguous2')
->setDescription('The test:titi command') ->setDescription('The test-ambiguous2 command')
; ;
} }
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
$output->write('test-titi'); $output->write('test-ambiguous2');
} }
} }

View File

@ -0,0 +1,67 @@
<?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\Intl\Data\Generator;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
use Symfony\Component\Intl\Locale;
/**
* @author Roland Franssen <franssen.roland@gmail.com>
*
* @internal
*/
trait FallbackTrait
{
private $fallbackCache = [];
private $generatingFallback = false;
/**
* @param string $tempDir
* @param string $displayLocale
*
* @return array|null
*
* @see AbstractDataGenerator::generateDataForLocale()
*/
abstract protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale);
/**
* @param string $tempDir
*
* @return array|null
*
* @see AbstractDataGenerator::generateDataForRoot()
*/
abstract protected function generateDataForRoot(BundleEntryReaderInterface $reader, $tempDir);
private function generateFallbackData(BundleEntryReaderInterface $reader, string $tempDir, string $displayLocale): array
{
if (null === $fallback = Locale::getFallback($displayLocale)) {
return [];
}
if (isset($this->fallbackCache[$fallback])) {
return $this->fallbackCache[$fallback];
}
$prevGeneratingFallback = $this->generatingFallback;
$this->generatingFallback = true;
try {
$data = 'root' === $fallback ? $this->generateDataForRoot($reader, $tempDir) : $this->generateDataForLocale($reader, $tempDir, $fallback);
} finally {
$this->generatingFallback = $prevGeneratingFallback;
}
return $this->fallbackCache[$fallback] = $data ?: [];
}
}

View File

@ -16,7 +16,6 @@ use Symfony\Component\Intl\Data\Bundle\Compiler\BundleCompilerInterface;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface; use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
use Symfony\Component\Intl\Data\Util\LocaleScanner; use Symfony\Component\Intl\Data\Util\LocaleScanner;
use Symfony\Component\Intl\Exception\MissingResourceException; use Symfony\Component\Intl\Exception\MissingResourceException;
use Symfony\Component\Intl\Locale;
/** /**
* The rule for compiling the locale bundle. * The rule for compiling the locale bundle.
@ -28,11 +27,11 @@ use Symfony\Component\Intl\Locale;
*/ */
class LocaleDataGenerator extends AbstractDataGenerator class LocaleDataGenerator extends AbstractDataGenerator
{ {
private $locales; use FallbackTrait;
private $localeAliases;
private $localeParents; private $locales = [];
private $fallbackMapping; private $localeAliases = [];
private $fallbackCache = []; private $localeParents = [];
/** /**
* {@inheritdoc} * {@inheritdoc}
@ -42,7 +41,6 @@ class LocaleDataGenerator extends AbstractDataGenerator
$this->locales = $scanner->scanLocales($sourceDir.'/locales'); $this->locales = $scanner->scanLocales($sourceDir.'/locales');
$this->localeAliases = $scanner->scanAliases($sourceDir.'/locales'); $this->localeAliases = $scanner->scanAliases($sourceDir.'/locales');
$this->localeParents = $scanner->scanParents($sourceDir.'/locales'); $this->localeParents = $scanner->scanParents($sourceDir.'/locales');
$this->fallbackMapping = $this->generateFallbackMapping(array_diff($this->locales, array_keys($this->localeAliases)), $this->localeAliases);
return $this->locales; return $this->locales;
} }
@ -66,8 +64,6 @@ class LocaleDataGenerator extends AbstractDataGenerator
*/ */
protected function preGenerate() protected function preGenerate()
{ {
$this->fallbackCache = [];
// Write parents locale file for the Translation component // Write parents locale file for the Translation component
\file_put_contents( \file_put_contents(
__DIR__.'/../../../Translation/Resources/data/parents.json', __DIR__.'/../../../Translation/Resources/data/parents.json',
@ -81,7 +77,8 @@ class LocaleDataGenerator extends AbstractDataGenerator
protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale) protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale)
{ {
// Don't generate aliases, as they are resolved during runtime // Don't generate aliases, as they are resolved during runtime
if (isset($this->localeAliases[$displayLocale])) { // Unless an alias is needed as fallback for de-duplication purposes
if (isset($this->localeAliases[$displayLocale]) && !$this->generatingFallback) {
return; return;
} }
@ -93,7 +90,7 @@ class LocaleDataGenerator extends AbstractDataGenerator
$localeNames = []; $localeNames = [];
foreach ($this->locales as $locale) { foreach ($this->locales as $locale) {
// Ensure a normalized list of pure locales // Ensure a normalized list of pure locales
if (isset($this->localeAliases[$displayLocale]) || \Locale::getAllVariants($locale)) { if (\Locale::getAllVariants($locale)) {
continue; continue;
} }
@ -110,21 +107,27 @@ class LocaleDataGenerator extends AbstractDataGenerator
} }
} }
// Process again to de-duplicate locales and their fallback locales $data = [
// Only keep the differences 'Names' => $localeNames,
$fallback = $displayLocale; ];
while (isset($this->fallbackMapping[$fallback])) {
if (!isset($this->fallbackCache[$fallback = $this->fallbackMapping[$fallback]])) { // Don't de-duplicate a fallback locale
$this->fallbackCache[$fallback] = $this->generateDataForLocale($reader, $tempDir, $fallback) ?: []; // Ensures the display locale can be de-duplicated on itself
} if ($this->generatingFallback) {
if (isset($this->fallbackCache[$fallback]['Names'])) { return $data;
$localeNames = array_diff($localeNames, $this->fallbackCache[$fallback]['Names']);
}
} }
if ($localeNames) { // Process again to de-duplicate locale and its fallback locales
return ['Names' => $localeNames]; // Only keep the differences
$fallbackData = $this->generateFallbackData($reader, $tempDir, $displayLocale);
if (isset($fallbackData['Names'])) {
$data['Names'] = array_diff($data['Names'], $fallbackData['Names']);
} }
if (!$data['Names']) {
return;
}
return $data;
} }
/** /**
@ -139,12 +142,10 @@ class LocaleDataGenerator extends AbstractDataGenerator
*/ */
protected function generateDataForMeta(BundleEntryReaderInterface $reader, $tempDir) protected function generateDataForMeta(BundleEntryReaderInterface $reader, $tempDir)
{ {
if ($this->locales || $this->localeAliases) { return [
return [ 'Locales' => $this->locales,
'Locales' => $this->locales, 'Aliases' => $this->localeAliases,
'Aliases' => $this->localeAliases, ];
];
}
} }
/** /**
@ -183,30 +184,4 @@ class LocaleDataGenerator extends AbstractDataGenerator
return $name; return $name;
} }
private function generateFallbackMapping(array $displayLocales, array $aliases)
{
$displayLocales = array_flip($displayLocales);
$mapping = [];
foreach ($displayLocales as $displayLocale => $_) {
$mapping[$displayLocale] = null;
$fallback = $displayLocale;
// Recursively search for a fallback locale until one is found
while (null !== ($fallback = Locale::getFallback($fallback))) {
// Currently, no locale has an alias as fallback locale.
// If this starts to be the case, we need to add code here.
\assert(!isset($aliases[$fallback]));
// Check whether the fallback exists
if (isset($displayLocales[$fallback])) {
$mapping[$displayLocale] = $fallback;
break;
}
}
}
return $mapping;
}
} }