From 6cbee0944c6bbf5be0ed46d8546ef3b7d80850ac Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Sat, 26 Oct 2019 12:21:13 +0200 Subject: [PATCH 1/4] [FrameworkBundle][Translation] Invalidate cached catalogues when the scanned directories change --- .../Resources/translations2/ccc.fr.yml | 1 + .../Tests/Translation/TranslatorTest.php | 36 +++++++++++++++++++ .../Translation/Translator.php | 4 ++- .../Bundle/FrameworkBundle/composer.json | 4 +-- .../Translation/Tests/TranslatorCacheTest.php | 16 +++++++++ .../Component/Translation/Translator.php | 9 +++-- 6 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/translations2/ccc.fr.yml diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/translations2/ccc.fr.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/translations2/ccc.fr.yml new file mode 100644 index 0000000000..20e9ff3fea --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/translations2/ccc.fr.yml @@ -0,0 +1 @@ +foo: bar diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php index 26753e8a8a..cd3f3d9fba 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php @@ -18,6 +18,7 @@ use Symfony\Component\Config\Resource\DirectoryResource; use Symfony\Component\Config\Resource\FileExistenceResource; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Translation\Formatter\MessageFormatter; +use Symfony\Component\Translation\Loader\YamlFileLoader; use Symfony\Component\Translation\MessageCatalogue; class TranslatorTest extends TestCase @@ -244,6 +245,41 @@ class TranslatorTest extends TestCase $this->assertEquals(new FileExistenceResource('/tmp/I/sure/hope/this/does/not/exist'), $resources[2]); } + public function testCachedCatalogueIsReDumpedWhenScannedDirectoriesChange() + { + /** @var Translator $translator */ + $translator = $this->getTranslator(new YamlFileLoader(), [ + 'cache_dir' => $this->tmpDir, + 'resource_files' => [ + 'fr' => [ + __DIR__.'/../Fixtures/Resources/translations/messages.fr.yml', + ], + ], + 'scanned_directories' => [ + __DIR__.'/../Fixtures/Resources/translations/', + ], + ], 'yml'); + + // Cached catalogue is dumped + $this->assertSame('répertoire', $translator->trans('folder', [], 'messages', 'fr')); + + $translator = $this->getTranslator(new YamlFileLoader(), [ + 'cache_dir' => $this->tmpDir, + 'resource_files' => [ + 'fr' => [ + __DIR__.'/../Fixtures/Resources/translations/messages.fr.yml', + __DIR__.'/../Fixtures/Resources/translations2/ccc.fr.yml', + ], + ], + 'scanned_directories' => [ + __DIR__.'/../Fixtures/Resources/translations/', + __DIR__.'/../Fixtures/Resources/translations2/', + ], + ], 'yml'); + + $this->assertSame('bar', $translator->trans('foo', [], 'ccc', 'fr')); + } + protected function getCatalogue($locale, $messages, $resources = []) { $catalogue = new MessageCatalogue($locale); diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php index 511da9fb22..f718c8d015 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php +++ b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php @@ -82,7 +82,9 @@ class Translator extends BaseTranslator implements WarmableInterface $this->resourceFiles = $this->options['resource_files']; $this->scannedDirectories = $this->options['scanned_directories']; - parent::__construct($defaultLocale, $formatter, $this->options['cache_dir'], $this->options['debug']); + parent::__construct($defaultLocale, $formatter, $this->options['cache_dir'], $this->options['debug'], [ + 'scanned_directories' => $this->scannedDirectories, + ]); } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 57c344f68f..55b9e0cbf0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -49,7 +49,7 @@ "symfony/security-http": "~3.4|~4.0", "symfony/serializer": "^4.3", "symfony/stopwatch": "~3.4|~4.0", - "symfony/translation": "~4.3", + "symfony/translation": "^4.3.6", "symfony/templating": "~3.4|~4.0", "symfony/twig-bundle": "~2.8|~3.2|~4.0", "symfony/validator": "^4.1", @@ -77,7 +77,7 @@ "symfony/property-info": "<3.4", "symfony/serializer": "<4.2", "symfony/stopwatch": "<3.4", - "symfony/translation": "<4.3", + "symfony/translation": "<4.3.6", "symfony/twig-bridge": "<4.1.1", "symfony/validator": "<4.1", "symfony/workflow": "<4.3.6" diff --git a/src/Symfony/Component/Translation/Tests/TranslatorCacheTest.php b/src/Symfony/Component/Translation/Tests/TranslatorCacheTest.php index 08add3404b..ae5c75ba82 100644 --- a/src/Symfony/Component/Translation/Tests/TranslatorCacheTest.php +++ b/src/Symfony/Component/Translation/Tests/TranslatorCacheTest.php @@ -269,6 +269,22 @@ class TranslatorCacheTest extends TestCase $translator->trans('foo'); } + public function testCachedCatalogueIsReDumpedWhenCacheVaryChange() + { + $translator = new Translator('a', null, $this->tmpDir, false, []); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', ['foo' => 'bar'], 'a', 'messages'); + + // Cached catalogue is dumped + $this->assertSame('bar', $translator->trans('foo', [], 'messages', 'a')); + + $translator = new Translator('a', null, $this->tmpDir, false, ['vary']); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', ['foo' => 'ccc'], 'a', 'messages'); + + $this->assertSame('ccc', $translator->trans('foo', [], 'messages', 'a')); + } + protected function getCatalogue($locale, $messages, $resources = []) { $catalogue = new MessageCatalogue($locale); diff --git a/src/Symfony/Component/Translation/Translator.php b/src/Symfony/Component/Translation/Translator.php index 9846c8338b..d9241d5a82 100644 --- a/src/Symfony/Component/Translation/Translator.php +++ b/src/Symfony/Component/Translation/Translator.php @@ -71,6 +71,8 @@ class Translator implements LegacyTranslatorInterface, TranslatorInterface, Tran */ private $debug; + private $cacheVary; + /** * @var ConfigCacheFactoryInterface|null */ @@ -86,7 +88,7 @@ class Translator implements LegacyTranslatorInterface, TranslatorInterface, Tran /** * @throws InvalidArgumentException If a locale contains invalid characters */ - public function __construct(?string $locale, MessageFormatterInterface $formatter = null, string $cacheDir = null, bool $debug = false) + public function __construct(?string $locale, MessageFormatterInterface $formatter = null, string $cacheDir = null, bool $debug = false, array $cacheVary = []) { $this->setLocale($locale); @@ -97,6 +99,7 @@ class Translator implements LegacyTranslatorInterface, TranslatorInterface, Tran $this->formatter = $formatter; $this->cacheDir = $cacheDir; $this->debug = $debug; + $this->cacheVary = $cacheVary; $this->hasIntlFormatter = $formatter instanceof IntlFormatterInterface; } @@ -176,7 +179,7 @@ class Translator implements LegacyTranslatorInterface, TranslatorInterface, Tran $this->assertValidLocale($locale); } - $this->fallbackLocales = $locales; + $this->fallbackLocales = $this->cacheVary['fallback_locales'] = $locales; } /** @@ -392,7 +395,7 @@ EOF private function getCatalogueCachePath($locale) { - return $this->cacheDir.'/catalogue.'.$locale.'.'.strtr(substr(base64_encode(hash('sha256', serialize($this->fallbackLocales), true)), 0, 7), '/', '_').'.php'; + return $this->cacheDir.'/catalogue.'.$locale.'.'.strtr(substr(base64_encode(hash('sha256', serialize($this->cacheVary), true)), 0, 7), '/', '_').'.php'; } /** From 022cf67f19e80c8c08f857e5069119df00892b6c Mon Sep 17 00:00:00 2001 From: tamar peled Date: Sat, 26 Oct 2019 21:55:55 +0200 Subject: [PATCH 2/4] [Validator] Add the missing translations for the Hebrew (\"he\") locale and fix 2 typos --- .../Resources/translations/validators.he.xlf | 108 ++++++++++++++---- 1 file changed, 86 insertions(+), 22 deletions(-) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.he.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.he.xlf index 6510514900..6f9ab0a1cf 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.he.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.he.xlf @@ -72,7 +72,7 @@ This value should be {{ limit }} or less. - הערך צריל להכיל {{ limit }} תווים לכל היותר. + הערך צריך להכיל {{ limit }} תווים לכל היותר. This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less. @@ -84,7 +84,7 @@ This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more. - הערך קצר מידיץ הוא צריך להכיל {{ limit }} תווים לפחות.|הערך קצר מידיץ הוא צריך להכיל {{ limit }} תווים לפחות. + הערך קצר מידי. הוא צריך להכיל {{ limit }} תווים לפחות.|הערך קצר מידיץ הוא צריך להכיל {{ limit }} תווים לפחות. This value should not be blank. @@ -224,83 +224,147 @@ This is not a valid International Bank Account Number (IBAN). - This is not a valid International Bank Account Number (IBAN). + מספר חשבון בנק בינלאומי אינו חוקי (IBAN). This value is not a valid ISBN-10. - This value is not a valid ISBN-10. + הערך אינו ערך ISBN-10 חוקי. This value is not a valid ISBN-13. - This value is not a valid ISBN-13. + הערך אינו ערך ISBN-13 חוקי. This value is neither a valid ISBN-10 nor a valid ISBN-13. - This value is neither a valid ISBN-10 nor a valid ISBN-13. + הערך אינו ערך ISBN-10 חוקי או ערך ISBN-13 חוקי. This value is not a valid ISSN. - This value is not a valid ISSN. + הערך אינו ערך ISSN חוקי. This value is not a valid currency. - This value is not a valid currency. + הערך אינו ערך מטבע חוקי. This value should be equal to {{ compared_value }}. - This value should be equal to {{ compared_value }}. + הערך חייב להיות שווה ל {{ compared_value }}. This value should be greater than {{ compared_value }}. - This value should be greater than {{ compared_value }}. + הערך חייב להיות גדול מ {{ compared_value }}. This value should be greater than or equal to {{ compared_value }}. - This value should be greater than or equal to {{ compared_value }}. + הערך חייב להיות גדול או שווה ל {{ compared_value }}. This value should be identical to {{ compared_value_type }} {{ compared_value }}. - This value should be identical to {{ compared_value_type }} {{ compared_value }}. + הערך חייב להיות זהה ל {{ compared_value_type }} {{ compared_value }}. This value should be less than {{ compared_value }}. - This value should be less than {{ compared_value }}. + הערך חייב להיות קטן מ {{ compared_value }}. This value should be less than or equal to {{ compared_value }}. - This value should be less than or equal to {{ compared_value }}. + הערך חייב להיות קטן או שווה ל {{ compared_value }}. This value should not be equal to {{ compared_value }}. - This value should not be equal to {{ compared_value }}. + הערך חייב להיות לא שווה ל {{ compared_value }}. This value should not be identical to {{ compared_value_type }} {{ compared_value }}. - This value should not be identical to {{ compared_value_type }} {{ compared_value }}. + הערך חייב להיות לא זהה ל {{ compared_value_type }} {{ compared_value }}. The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. - The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. + היחס של התמונה הוא גדול מדי ({{ ratio }}). היחס המקסימלי האפשרי הוא {{ max_ratio }}. The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. - The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. + היחס של התמונה הוא קטן מדי ({{ ratio }}). היחס המינימלי האפשרי הוא {{ min_ratio }}. The image is square ({{ width }}x{{ height }}px). Square images are not allowed. - The image is square ({{ width }}x{{ height }}px). Square images are not allowed. + התמונה מרובעת ({{ width }}x{{ height }}px). אסורות תמונות מרובעות. The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed. - The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed. + התמונה היא לרוחב ({{ width }}x{{ height }}px). אסורות תמונות לרוחב. The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. - The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. + התמונה היא לאורך ({{ width }}x{{ height }}px). אסורות תמונות לאורך. An empty file is not allowed. - An empty file is not allowed. + אסור קובץ ריק. + + + The host could not be resolved. + לא הייתה אפשרות לזהות את המארח. + + + This value does not match the expected {{ charset }} charset. + הערך אינו תואם למערך התווים {{ charset }} הצפוי. + + + This is not a valid Business Identifier Code (BIC). + קוד זיהוי עסקי אינו חוקי (BIC). + + + Error + שגיאה + + + This is not a valid UUID. + הערך אינו ערך UUID חוקי. + + + This value should be a multiple of {{ compared_value }}. + הערך חייב להיות כפולה של {{ compared_value }}. + + + This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}. + הקוד זיהוי עסקי (BIC) אינו משוייך ל IBAN {{ iban }}. + + + This value should be valid JSON. + הערך אינו ערך JSON תקין. + + + This collection should contain only unique elements. + האוסף חייב להכיל רק אלמנטים ייחודיים. + + + This value should be positive. + הערך חייב להיות חיובי. + + + This value should be either positive or zero. + הערך חייב להיות חיובי או אפס. + + + This value should be negative. + הערך חייב להיות שלילי. + + + This value should be either negative or zero. + הערך חייב להיות שלילי או אפס. + + + This value is not a valid timezone. + הערך אינו אזור זמן תקין. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + סיסמא זו הודלפה בהדלפת מידע, אסור להשתמש בה. אנא השתמש בסיסמה אחרת. + + + This value should be between {{ min }} and {{ max }}. + הערך חייב להיות בין {{ min }} ו- {{ max }}. From d2c5ffda52a67c548f20fc8003d9aeb2aaf12867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 8 Nov 2019 00:12:08 +0100 Subject: [PATCH 3/4] [HttpClient] Fix a crash when calling CurlHttpClient::__destruct() --- .../Component/HttpClient/CurlHttpClient.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index e52a2cd0a5..632822b1ed 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -301,15 +301,20 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface public function __destruct() { $this->multi->pushedResponses = []; - if (\defined('CURLMOPT_PUSHFUNCTION')) { - curl_multi_setopt($this->multi->handle, CURLMOPT_PUSHFUNCTION, null); + + if (\is_resource($this->multi->handle)) { + if (\defined('CURLMOPT_PUSHFUNCTION')) { + curl_multi_setopt($this->multi->handle, CURLMOPT_PUSHFUNCTION, null); + } + + $active = 0; + while (CURLM_CALL_MULTI_PERFORM === curl_multi_exec($this->multi->handle, $active)); } - $active = 0; - while (CURLM_CALL_MULTI_PERFORM === curl_multi_exec($this->multi->handle, $active)); - foreach ($this->multi->openHandles as [$ch]) { - curl_setopt($ch, CURLOPT_VERBOSE, false); + if (\is_resource($ch)) { + curl_setopt($ch, CURLOPT_VERBOSE, false); + } } } From 1606430cfd1c8c797f49680b460a43a02b8aba0f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 7 Nov 2019 14:06:15 +0100 Subject: [PATCH 4/4] [DI] Dont cache classes with missing parents --- .../Component/Config/Resource/ClassExistenceResource.php | 2 +- .../Config/Tests/Resource/ClassExistenceResourceTest.php | 2 +- .../Component/DependencyInjection/ContainerBuilder.php | 3 +-- .../Component/DependencyInjection/Loader/FileLoader.php | 7 +------ .../Tests/Compiler/AutowirePassTest.php | 2 +- .../DependencyInjection/Tests/Loader/FileLoaderTest.php | 6 +++--- 6 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/Symfony/Component/Config/Resource/ClassExistenceResource.php b/src/Symfony/Component/Config/Resource/ClassExistenceResource.php index e7ab1b8d72..1ba8e76248 100644 --- a/src/Symfony/Component/Config/Resource/ClassExistenceResource.php +++ b/src/Symfony/Component/Config/Resource/ClassExistenceResource.php @@ -155,7 +155,7 @@ class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializ throw $previous; } - $e = new \ReflectionException("Class $class not found", 0, $previous); + $e = new \ReflectionException(sprintf('Class "%s" not found while loading "%s".', $class, self::$autoloadedClass), 0, $previous); if (null !== $previous) { throw $e; diff --git a/src/Symfony/Component/Config/Tests/Resource/ClassExistenceResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/ClassExistenceResourceTest.php index ad331240de..b7ae81eaa6 100644 --- a/src/Symfony/Component/Config/Tests/Resource/ClassExistenceResourceTest.php +++ b/src/Symfony/Component/Config/Tests/Resource/ClassExistenceResourceTest.php @@ -84,7 +84,7 @@ EOF public function testBadParentWithNoTimestamp() { $this->expectException('ReflectionException'); - $this->expectExceptionMessage('Class Symfony\Component\Config\Tests\Fixtures\MissingParent not found'); + $this->expectExceptionMessage('Class "Symfony\Component\Config\Tests\Fixtures\MissingParent" not found while loading "Symfony\Component\Config\Tests\Fixtures\BadParent".'); $res = new ClassExistenceResource(BadParent::class, false); $res->isFresh(0); diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 53873c15e6..f9bfcb5210 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -361,7 +361,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface return null; } - $resource = null; + $resource = $classReflector = null; try { if (isset($this->classReflectors[$class])) { @@ -376,7 +376,6 @@ class ContainerBuilder extends Container implements TaggedContainerInterface if ($throw) { throw $e; } - $classReflector = false; } if ($this->trackResources) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php index 186c9c4cec..77cad3c046 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php @@ -149,12 +149,7 @@ abstract class FileLoader extends BaseFileLoader try { $r = $this->container->getReflectionClass($class); } catch (\ReflectionException $e) { - $classes[$class] = sprintf( - 'While discovering services from namespace "%s", an error was thrown when processing the class "%s": "%s".', - $namespace, - $class, - $e->getMessage() - ); + $classes[$class] = $e->getMessage(); continue; } // check to make sure the expected class exists diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index 7eb7f60246..c5bcc660a5 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -380,7 +380,7 @@ class AutowirePassTest extends TestCase public function testParentClassNotFoundThrowsException() { $this->expectException('Symfony\Component\DependencyInjection\Exception\AutowiringFailedException'); - $this->expectExceptionMessage('Cannot autowire service "a": argument "$r" of method "Symfony\Component\DependencyInjection\Tests\Compiler\BadParentTypeHintedArgument::__construct()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\OptionalServiceClass" but this class is missing a parent class (Class Symfony\Bug\NotExistClass not found).'); + $this->expectExceptionMessageRegExp('{^Cannot autowire service "a": argument "\$r" of method "(Symfony\\\\Component\\\\DependencyInjection\\\\Tests\\\\Compiler\\\\)BadParentTypeHintedArgument::__construct\(\)" has type "\1OptionalServiceClass" but this class is missing a parent class \(Class "?Symfony\\\\Bug\\\\NotExistClass"? not found}'); $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php index b6a816e408..ffe58a67ef 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php @@ -178,9 +178,9 @@ class FileLoaderTest extends TestCase $this->assertTrue($container->has(MissingParent::class)); - $this->assertSame( - ['While discovering services from namespace "Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\BadClasses\", an error was thrown when processing the class "Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\BadClasses\MissingParent": "Class Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\BadClasses\MissingClass not found".'], - $container->getDefinition(MissingParent::class)->getErrors() + $this->assertRegExp( + '{Class "?Symfony\\\\Component\\\\DependencyInjection\\\\Tests\\\\Fixtures\\\\Prototype\\\\BadClasses\\\\MissingClass"? not found}', + $container->getDefinition(MissingParent::class)->getErrors()[0] ); }