[Web] Update composer libs

- Removing symfony/deprecation-contracts (v2.4.0)
  - Upgrading ddeboer/imap (1.12.1 => 1.13.1)
  - Upgrading directorytree/ldaprecord (v2.6.3 => v2.10.1)
  - Upgrading illuminate/contracts (v8.53.1 => v9.3.0)
  - Upgrading nesbot/carbon (2.51.1 => 2.57.0)
  - Upgrading phpmailer/phpmailer (v6.5.0 => v6.6.0)
  - Upgrading psr/container (1.1.1 => 2.0.2)
  - Upgrading psr/log (1.1.4 => 3.0.0)
  - Upgrading psr/simple-cache (1.0.1 => 2.0.0)
  - Upgrading robthree/twofactorauth (1.8.0 => 1.8.1)
  - Upgrading symfony/polyfill-ctype (v1.23.0 => v1.24.0)
  - Upgrading symfony/polyfill-mbstring (v1.23.1 => v1.24.0)
  - Upgrading symfony/polyfill-php80 (v1.23.1 => v1.24.0)
  - Upgrading symfony/translation (v5.3.4 => v6.0.5)
  - Upgrading symfony/translation-contracts (v2.4.0 => v3.0.0)
  - Upgrading symfony/var-dumper (v5.3.6 => v6.0.5)
  - Upgrading tightenco/collect (v8.34.0 => v8.83.2)
  - Upgrading twig/twig (v3.3.2 => v3.3.8)
This commit is contained in:
andryyy
2022-03-02 20:08:24 +01:00
parent 24275ffdbf
commit 98bc947d00
940 changed files with 7649 additions and 14226 deletions

View File

@@ -1,3 +0,0 @@
vendor/
composer.lock
phpunit.xml

View File

@@ -1,5 +0,0 @@
CHANGELOG
=========
The changelog is maintained for all Symfony contracts at the following URL:
https://github.com/symfony/contracts/blob/main/CHANGELOG.md

View File

@@ -1,19 +0,0 @@
Copyright (c) 2020-2021 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,26 +0,0 @@
Symfony Deprecation Contracts
=============================
A generic function and convention to trigger deprecation notices.
This package provides a single global function named `trigger_deprecation()` that triggers silenced deprecation notices.
By using a custom PHP error handler such as the one provided by the Symfony ErrorHandler component,
the triggered deprecations can be caught and logged for later discovery, both on dev and prod environments.
The function requires at least 3 arguments:
- the name of the Composer package that is triggering the deprecation
- the version of the package that introduced the deprecation
- the message of the deprecation
- more arguments can be provided: they will be inserted in the message using `printf()` formatting
Example:
```php
trigger_deprecation('symfony/blockchain', '8.9', 'Using "%s" is deprecated, use "%s" instead.', 'bitcoin', 'fabcoin');
```
This will generate the following message:
`Since symfony/blockchain 8.9: Using "bitcoin" is deprecated, use "fabcoin" instead.`
While not necessarily recommended, the deprecation notices can be completely ignored by declaring an empty
`function trigger_deprecation() {}` in your application.

View File

@@ -1,35 +0,0 @@
{
"name": "symfony/deprecation-contracts",
"type": "library",
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"license": "MIT",
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"require": {
"php": ">=7.1"
},
"autoload": {
"files": [
"function.php"
]
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-main": "2.4-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
}
}

View File

@@ -1,27 +0,0 @@
<?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.
*/
if (!function_exists('trigger_deprecation')) {
/**
* Triggers a silenced deprecation notice.
*
* @param string $package The name of the Composer package that is triggering the deprecation
* @param string $version The version of the package that introduced the deprecation
* @param string $message The message of the deprecation
* @param mixed ...$args Values to insert in the message using printf() formatting
*
* @author Nicolas Grekas <p@tchwork.com>
*/
function trigger_deprecation(string $package, string $version, string $message, ...$args): void
{
@trigger_error(($package || $version ? "Since $package $version: " : '').($args ? vsprintf($message, $args) : $message), \E_USER_DEPRECATED);
}
}

View File

@@ -25,13 +25,13 @@ final class Ctype
*
* @see https://php.net/ctype-alnum
*
* @param string|int $text
* @param mixed $text
*
* @return bool
*/
public static function ctype_alnum($text)
{
$text = self::convert_int_to_char_for_ctype($text);
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z0-9]/', $text);
}
@@ -41,13 +41,13 @@ final class Ctype
*
* @see https://php.net/ctype-alpha
*
* @param string|int $text
* @param mixed $text
*
* @return bool
*/
public static function ctype_alpha($text)
{
$text = self::convert_int_to_char_for_ctype($text);
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z]/', $text);
}
@@ -57,13 +57,13 @@ final class Ctype
*
* @see https://php.net/ctype-cntrl
*
* @param string|int $text
* @param mixed $text
*
* @return bool
*/
public static function ctype_cntrl($text)
{
$text = self::convert_int_to_char_for_ctype($text);
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^\x00-\x1f\x7f]/', $text);
}
@@ -73,13 +73,13 @@ final class Ctype
*
* @see https://php.net/ctype-digit
*
* @param string|int $text
* @param mixed $text
*
* @return bool
*/
public static function ctype_digit($text)
{
$text = self::convert_int_to_char_for_ctype($text);
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^0-9]/', $text);
}
@@ -89,13 +89,13 @@ final class Ctype
*
* @see https://php.net/ctype-graph
*
* @param string|int $text
* @param mixed $text
*
* @return bool
*/
public static function ctype_graph($text)
{
$text = self::convert_int_to_char_for_ctype($text);
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^!-~]/', $text);
}
@@ -105,13 +105,13 @@ final class Ctype
*
* @see https://php.net/ctype-lower
*
* @param string|int $text
* @param mixed $text
*
* @return bool
*/
public static function ctype_lower($text)
{
$text = self::convert_int_to_char_for_ctype($text);
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^a-z]/', $text);
}
@@ -121,13 +121,13 @@ final class Ctype
*
* @see https://php.net/ctype-print
*
* @param string|int $text
* @param mixed $text
*
* @return bool
*/
public static function ctype_print($text)
{
$text = self::convert_int_to_char_for_ctype($text);
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^ -~]/', $text);
}
@@ -137,13 +137,13 @@ final class Ctype
*
* @see https://php.net/ctype-punct
*
* @param string|int $text
* @param mixed $text
*
* @return bool
*/
public static function ctype_punct($text)
{
$text = self::convert_int_to_char_for_ctype($text);
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text);
}
@@ -153,13 +153,13 @@ final class Ctype
*
* @see https://php.net/ctype-space
*
* @param string|int $text
* @param mixed $text
*
* @return bool
*/
public static function ctype_space($text)
{
$text = self::convert_int_to_char_for_ctype($text);
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^\s]/', $text);
}
@@ -169,13 +169,13 @@ final class Ctype
*
* @see https://php.net/ctype-upper
*
* @param string|int $text
* @param mixed $text
*
* @return bool
*/
public static function ctype_upper($text)
{
$text = self::convert_int_to_char_for_ctype($text);
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^A-Z]/', $text);
}
@@ -185,13 +185,13 @@ final class Ctype
*
* @see https://php.net/ctype-xdigit
*
* @param string|int $text
* @param mixed $text
*
* @return bool
*/
public static function ctype_xdigit($text)
{
$text = self::convert_int_to_char_for_ctype($text);
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^A-Fa-f0-9]/', $text);
}
@@ -204,11 +204,12 @@ final class Ctype
* (negative values have 256 added in order to allow characters in the Extended ASCII range).
* Any other integer is interpreted as a string containing the decimal digits of the integer.
*
* @param string|int $int
* @param mixed $int
* @param string $function
*
* @return mixed
*/
private static function convert_int_to_char_for_ctype($int)
private static function convert_int_to_char_for_ctype($int, $function)
{
if (!\is_int($int)) {
return $int;
@@ -218,6 +219,10 @@ final class Ctype
return (string) $int;
}
if (\PHP_VERSION_ID >= 80100) {
@trigger_error($function.'(): Argument of type int will be interpreted as string in the future', \E_USER_DEPRECATED);
}
if ($int < 0) {
$int += 256;
}

View File

@@ -18,6 +18,9 @@
"require": {
"php": ">=7.1"
},
"provide": {
"ext-ctype": "*"
},
"autoload": {
"psr-4": { "Symfony\\Polyfill\\Ctype\\": "" },
"files": [ "bootstrap.php" ]

View File

@@ -80,7 +80,7 @@ final class Mbstring
public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null)
{
if (\is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) {
if (\is_array($fromEncoding) || ($fromEncoding !== null && false !== strpos($fromEncoding, ','))) {
$fromEncoding = self::mb_detect_encoding($s, $fromEncoding);
} else {
$fromEncoding = self::getEncoding($fromEncoding);
@@ -602,6 +602,9 @@ final class Mbstring
if (80000 > \PHP_VERSION_ID) {
return false;
}
if (\is_int($c) || 'long' === $c || 'entity' === $c) {
return false;
}
throw new \ValueError('Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint');
}

View File

@@ -18,6 +18,9 @@
"require": {
"php": ">=7.1"
},
"provide": {
"ext-mbstring": "*"
},
"autoload": {
"psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" },
"files": [ "bootstrap.php" ]

View File

@@ -1,5 +1,7 @@
<?php
class UnhandledMatchError extends Error
{
if (\PHP_VERSION_ID < 80000) {
class UnhandledMatchError extends Error
{
}
}

View File

@@ -1,5 +1,7 @@
<?php
class ValueError extends Error
{
if (\PHP_VERSION_ID < 80000) {
class ValueError extends Error
{
}
}

View File

@@ -16,16 +16,12 @@ interface LocaleAwareInterface
/**
* Sets the current locale.
*
* @param string $locale The locale
*
* @throws \InvalidArgumentException If the locale contains invalid characters
*/
public function setLocale(string $locale);
/**
* Returns the current locale.
*
* @return string The locale
*/
public function getLocale();
public function getLocale(): string;
}

View File

@@ -30,7 +30,20 @@ use Symfony\Contracts\Translation\TranslatorTrait;
*/
class TranslatorTest extends TestCase
{
public function getTranslator()
private $defaultLocale;
protected function setUp(): void
{
$this->defaultLocale = \Locale::getDefault();
\Locale::setDefault('en');
}
protected function tearDown(): void
{
\Locale::setDefault($this->defaultLocale);
}
public function getTranslator(): TranslatorInterface
{
return new class() implements TranslatorInterface {
use TranslatorTrait;
@@ -53,7 +66,6 @@ class TranslatorTest extends TestCase
public function testTransChoiceWithExplicitLocale($expected, $id, $number)
{
$translator = $this->getTranslator();
$translator->setLocale('en');
$this->assertEquals($expected, $translator->trans($id, ['%count%' => $number]));
}
@@ -65,17 +77,25 @@ class TranslatorTest extends TestCase
*/
public function testTransChoiceWithDefaultLocale($expected, $id, $number)
{
\Locale::setDefault('en');
$translator = $this->getTranslator();
$this->assertEquals($expected, $translator->trans($id, ['%count%' => $number]));
}
/**
* @dataProvider getTransChoiceTests
*/
public function testTransChoiceWithEnUsPosix($expected, $id, $number)
{
$translator = $this->getTranslator();
$translator->setLocale('en_US_POSIX');
$this->assertEquals($expected, $translator->trans($id, ['%count%' => $number]));
}
public function testGetSetLocale()
{
$translator = $this->getTranslator();
$translator->setLocale('en');
$this->assertEquals('en', $translator->getLocale());
}
@@ -294,14 +314,12 @@ class TranslatorTest extends TestCase
* This array should contain all currently known langcodes.
*
* As it is impossible to have this ever complete we should try as hard as possible to have it almost complete.
*
* @return array
*/
public function successLangcodes()
public function successLangcodes(): array
{
return [
['1', ['ay', 'bo', 'cgg', 'dz', 'id', 'ja', 'jbo', 'ka', 'kk', 'km', 'ko', 'ky']],
['2', ['nl', 'fr', 'en', 'de', 'de_GE', 'hy', 'hy_AM']],
['2', ['nl', 'fr', 'en', 'de', 'de_GE', 'hy', 'hy_AM', 'en_US_POSIX']],
['3', ['be', 'bs', 'cs', 'hr']],
['4', ['cy', 'mt', 'sl']],
['6', ['ar']],
@@ -316,7 +334,7 @@ class TranslatorTest extends TestCase
*
* @return array with nplural together with langcodes
*/
public function failingLangcodes()
public function failingLangcodes(): array
{
return [
['1', ['fa']],
@@ -330,11 +348,10 @@ class TranslatorTest extends TestCase
/**
* We validate only on the plural coverage. Thus the real rules is not tested.
*
* @param string $nplural Plural expected
* @param array $matrix Containing langcodes and their plural index values
* @param bool $expectSuccess
* @param string $nplural Plural expected
* @param array $matrix Containing langcodes and their plural index values
*/
protected function validateMatrix($nplural, $matrix, $expectSuccess = true)
protected function validateMatrix(string $nplural, array $matrix, bool $expectSuccess = true)
{
foreach ($matrix as $langCode => $data) {
$indexes = array_flip($data);

View File

@@ -13,8 +13,6 @@ namespace Symfony\Contracts\Translation;
/**
* @author Fabien Potencier <fabien@symfony.com>
*
* @method string getLocale() Returns the default locale
*/
interface TranslatorInterface
{
@@ -59,9 +57,12 @@ interface TranslatorInterface
* @param string|null $domain The domain for the message or null to use the default
* @param string|null $locale The locale or null to use the default
*
* @return string The translated string
*
* @throws \InvalidArgumentException If the locale contains invalid characters
*/
public function trans(string $id, array $parameters = [], string $domain = null, string $locale = null);
public function trans(string $id, array $parameters = [], string $domain = null, string $locale = null): string;
/**
* Returns the default locale.
*/
public function getLocale(): string;
}

View File

@@ -20,7 +20,7 @@ use Symfony\Component\Translation\Exception\InvalidArgumentException;
*/
trait TranslatorTrait
{
private $locale;
private ?string $locale = null;
/**
* {@inheritdoc}
@@ -32,10 +32,8 @@ trait TranslatorTrait
/**
* {@inheritdoc}
*
* @return string
*/
public function getLocale()
public function getLocale(): string
{
return $this->locale ?: (class_exists(\Locale::class) ? \Locale::getDefault() : 'en');
}
@@ -142,7 +140,7 @@ EOF;
{
$number = abs($number);
switch ('pt_BR' !== $locale && \strlen($locale) > 3 ? substr($locale, 0, strrpos($locale, '_')) : $locale) {
switch ('pt_BR' !== $locale && 'en_US_POSIX' !== $locale && \strlen($locale) > 3 ? substr($locale, 0, strrpos($locale, '_')) : $locale) {
case 'af':
case 'bn':
case 'bg':
@@ -151,6 +149,7 @@ EOF;
case 'de':
case 'el':
case 'en':
case 'en_US_POSIX':
case 'eo':
case 'es':
case 'et':

View File

@@ -16,7 +16,7 @@
}
],
"require": {
"php": ">=7.2.5"
"php": ">=8.0.2"
},
"suggest": {
"symfony/translation-implementation": ""
@@ -27,7 +27,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-main": "2.4-dev"
"dev-main": "3.0-dev"
},
"thanks": {
"name": "symfony/contracts",

View File

@@ -1,6 +1,13 @@
CHANGELOG
=========
5.4
---
* Add `github` format & autodetection to render errors as annotations when
running the XLIFF linter command in a Github Actions environment.
* Translation providers are not experimental anymore
5.3
---

View File

@@ -80,10 +80,21 @@ abstract class AbstractOperation implements OperationInterface
/**
* {@inheritdoc}
*/
public function getDomains()
public function getDomains(): array
{
if (null === $this->domains) {
$this->domains = array_values(array_unique(array_merge($this->source->getDomains(), $this->target->getDomains())));
$domains = [];
foreach ([$this->source, $this->target] as $catalogue) {
foreach ($catalogue->getDomains() as $domain) {
$domains[$domain] = $domain;
if ($catalogue->all($domainIcu = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX)) {
$domains[$domainIcu] = $domainIcu;
}
}
}
$this->domains = array_values($domains);
}
return $this->domains;
@@ -92,7 +103,7 @@ abstract class AbstractOperation implements OperationInterface
/**
* {@inheritdoc}
*/
public function getMessages(string $domain)
public function getMessages(string $domain): array
{
if (!\in_array($domain, $this->getDomains())) {
throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain));
@@ -108,7 +119,7 @@ abstract class AbstractOperation implements OperationInterface
/**
* {@inheritdoc}
*/
public function getNewMessages(string $domain)
public function getNewMessages(string $domain): array
{
if (!\in_array($domain, $this->getDomains())) {
throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain));
@@ -124,7 +135,7 @@ abstract class AbstractOperation implements OperationInterface
/**
* {@inheritdoc}
*/
public function getObsoleteMessages(string $domain)
public function getObsoleteMessages(string $domain): array
{
if (!\in_array($domain, $this->getDomains())) {
throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain));
@@ -140,7 +151,7 @@ abstract class AbstractOperation implements OperationInterface
/**
* {@inheritdoc}
*/
public function getResult()
public function getResult(): MessageCatalogueInterface
{
foreach ($this->getDomains() as $domain) {
if (!isset($this->messages[$domain])) {

View File

@@ -36,36 +36,26 @@ interface OperationInterface
{
/**
* Returns domains affected by operation.
*
* @return array
*/
public function getDomains();
public function getDomains(): array;
/**
* Returns all valid messages ('all') after operation.
*
* @return array
*/
public function getMessages(string $domain);
public function getMessages(string $domain): array;
/**
* Returns new messages ('new') after operation.
*
* @return array
*/
public function getNewMessages(string $domain);
public function getNewMessages(string $domain): array;
/**
* Returns obsolete messages ('obsolete') after operation.
*
* @return array
*/
public function getObsoleteMessages(string $domain);
public function getObsoleteMessages(string $domain): array;
/**
* Returns resulting catalogue ('result').
*
* @return MessageCatalogueInterface
*/
public function getResult();
public function getResult(): MessageCatalogueInterface;
}

View File

@@ -49,7 +49,7 @@ class TargetOperation extends AbstractOperation
foreach ($this->source->all($domain) as $id => $message) {
if ($this->target->has($id, $domain)) {
$this->messages[$domain]['all'][$id] = $message;
$d = $this->target->defines($id, $intlDomain) ? $intlDomain : $domain;
$d = $this->source->defines($id, $intlDomain) ? $intlDomain : $domain;
$this->result->add([$id => $message], $d);
if (null !== $keyMetadata = $this->source->getMetadata($id, $d)) {
$this->result->setMetadata($id, $keyMetadata, $d);

View File

@@ -11,7 +11,10 @@
namespace Symfony\Component\Translation\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@@ -25,22 +28,18 @@ use Symfony\Component\Translation\Writer\TranslationWriterInterface;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*
* @experimental in 5.3
*/
#[AsCommand(name: 'translation:pull', description: 'Pull translations from a given provider.')]
final class TranslationPullCommand extends Command
{
use TranslationTrait;
protected static $defaultName = 'translation:pull';
protected static $defaultDescription = 'Pull translations from a given provider.';
private $providerCollection;
private $writer;
private $reader;
private $defaultLocale;
private $transPaths;
private $enabledLocales;
private string $defaultLocale;
private array $transPaths;
private array $enabledLocales;
public function __construct(TranslationProviderCollection $providerCollection, TranslationWriterInterface $writer, TranslationReaderInterface $reader, string $defaultLocale, array $transPaths = [], array $enabledLocales = [])
{
@@ -54,6 +53,36 @@ final class TranslationPullCommand extends Command
parent::__construct();
}
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestArgumentValuesFor('provider')) {
$suggestions->suggestValues($this->providerCollection->keys());
return;
}
if ($input->mustSuggestOptionValuesFor('domains')) {
$provider = $this->providerCollection->get($input->getArgument('provider'));
if ($provider && method_exists($provider, 'getDomains')) {
$domains = $provider->getDomains();
$suggestions->suggestValues($domains);
}
return;
}
if ($input->mustSuggestOptionValuesFor('locales')) {
$suggestions->suggestValues($this->enabledLocales);
return;
}
if ($input->mustSuggestOptionValuesFor('format')) {
$suggestions->suggestValues(['php', 'xlf', 'xlf12', 'xlf20', 'po', 'mo', 'yml', 'yaml', 'ts', 'csv', 'json', 'ini', 'res']);
}
}
/**
* {@inheritdoc}
*/
@@ -81,7 +110,7 @@ You can overwrite existing translations (and remove the missing ones on local si
Full example:
<info>php %command.full_name% provider --force --domains=messages,validators --locales=en</>
<info>php %command.full_name% provider --force --domains=messages --domains=validators --locales=en</>
This command pulls all translations associated with the <comment>messages</> and <comment>validators</> domains for the <comment>en</> locale.
Local translations for the specified domains and locale are deleted if they're not present on the provider and overwritten if it's the case.
@@ -119,6 +148,7 @@ EOF
$writeOptions = [
'path' => end($this->transPaths),
'xliff_version' => $xliffVersion,
'default_locale' => $this->defaultLocale,
];
if (!$domains) {

View File

@@ -11,7 +11,10 @@
namespace Symfony\Component\Translation\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -24,20 +27,16 @@ use Symfony\Component\Translation\TranslatorBag;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*
* @experimental in 5.3
*/
#[AsCommand(name: 'translation:push', description: 'Push translations to a given provider.')]
final class TranslationPushCommand extends Command
{
use TranslationTrait;
protected static $defaultName = 'translation:push';
protected static $defaultDescription = 'Push translations to a given provider.';
private $providers;
private $reader;
private $transPaths;
private $enabledLocales;
private array $transPaths;
private array $enabledLocales;
public function __construct(TranslationProviderCollection $providers, TranslationReaderInterface $reader, array $transPaths = [], array $enabledLocales = [])
{
@@ -49,6 +48,30 @@ final class TranslationPushCommand extends Command
parent::__construct();
}
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestArgumentValuesFor('provider')) {
$suggestions->suggestValues($this->providers->keys());
return;
}
if ($input->mustSuggestOptionValuesFor('domains')) {
$provider = $this->providers->get($input->getArgument('provider'));
if ($provider && method_exists($provider, 'getDomains')) {
$domains = $provider->getDomains();
$suggestions->suggestValues($domains);
}
return;
}
if ($input->mustSuggestOptionValuesFor('locales')) {
$suggestions->suggestValues($this->enabledLocales);
}
}
/**
* {@inheritdoc}
*/
@@ -79,7 +102,7 @@ You can delete provider translations which are not present locally by using the
Full example:
<info>php %command.full_name% provider --force --delete-missing --domains=messages,validators --locales=en</>
<info>php %command.full_name% provider --force --delete-missing --domains=messages --domains=validators --locales=en</>
This command pushes all translations associated with the <comment>messages</> and <comment>validators</> domains for the <comment>en</> locale.
Provider translations for the specified domains and locale are deleted if they're not present locally and overwritten if it's the case.

View File

@@ -32,8 +32,7 @@ trait TranslationTrait
if ($domains) {
foreach ($domains as $domain) {
$catalogue = $this->filterCatalogue($catalogue, $domain);
$bag->addCatalogue($catalogue);
$bag->addCatalogue($this->filterCatalogue($catalogue, $domain));
}
} else {
$bag->addCatalogue($catalogue);

View File

@@ -11,7 +11,11 @@
namespace Symfony\Component\Translation\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\CI\GithubActionReporter;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -28,23 +32,21 @@ use Symfony\Component\Translation\Util\XliffUtils;
* @author Robin Chalas <robin.chalas@gmail.com>
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
*/
#[AsCommand(name: 'lint:xliff', description: 'Lint an XLIFF file and outputs encountered errors')]
class XliffLintCommand extends Command
{
protected static $defaultName = 'lint:xliff';
protected static $defaultDescription = 'Lint an XLIFF file and outputs encountered errors';
private $format;
private $displayCorrectFiles;
private $directoryIteratorProvider;
private $isReadableProvider;
private $requireStrictFileNames;
private string $format;
private bool $displayCorrectFiles;
private ?\Closure $directoryIteratorProvider;
private ?\Closure $isReadableProvider;
private bool $requireStrictFileNames;
public function __construct(string $name = null, callable $directoryIteratorProvider = null, callable $isReadableProvider = null, bool $requireStrictFileNames = true)
{
parent::__construct($name);
$this->directoryIteratorProvider = $directoryIteratorProvider;
$this->isReadableProvider = $isReadableProvider;
$this->directoryIteratorProvider = null === $directoryIteratorProvider || $directoryIteratorProvider instanceof \Closure ? $directoryIteratorProvider : \Closure::fromCallable($directoryIteratorProvider);
$this->isReadableProvider = null === $isReadableProvider || $isReadableProvider instanceof \Closure ? $isReadableProvider : \Closure::fromCallable($isReadableProvider);
$this->requireStrictFileNames = $requireStrictFileNames;
}
@@ -54,9 +56,8 @@ class XliffLintCommand extends Command
protected function configure()
{
$this
->setDescription(self::$defaultDescription)
->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN')
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format')
->setHelp(<<<EOF
The <info>%command.name%</info> command lints an XLIFF file and outputs to STDOUT
the first encountered syntax error.
@@ -79,11 +80,11 @@ EOF
;
}
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$filenames = (array) $input->getArgument('filename');
$this->format = $input->getOption('format');
$this->format = $input->getOption('format') ?? (GithubActionReporter::isGithubActionEnvironment() ? 'github' : 'txt');
$this->displayCorrectFiles = $output->isVerbose();
if (['-'] === $filenames) {
@@ -160,15 +161,18 @@ EOF
return $this->displayTxt($io, $files);
case 'json':
return $this->displayJson($io, $files);
case 'github':
return $this->displayTxt($io, $files, true);
default:
throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format));
}
}
private function displayTxt(SymfonyStyle $io, array $filesInfo)
private function displayTxt(SymfonyStyle $io, array $filesInfo, bool $errorAsGithubAnnotations = false)
{
$countFiles = \count($filesInfo);
$erroredFiles = 0;
$githubReporter = $errorAsGithubAnnotations ? new GithubActionReporter($io) : null;
foreach ($filesInfo as $info) {
if ($info['valid'] && $this->displayCorrectFiles) {
@@ -176,9 +180,15 @@ EOF
} elseif (!$info['valid']) {
++$erroredFiles;
$io->text('<error> ERROR </error>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
$io->listing(array_map(function ($error) {
$io->listing(array_map(function ($error) use ($info, $githubReporter) {
// general document errors have a '-1' line number
return -1 === $error['line'] ? $error['message'] : sprintf('Line %d, Column %d: %s', $error['line'], $error['column'], $error['message']);
$line = -1 === $error['line'] ? null : $error['line'];
if ($githubReporter) {
$githubReporter->error($error['message'], $info['file'], $line, null !== $line ? $error['column'] : null);
}
return null === $line ? $error['message'] : sprintf('Line %d, Column %d: %s', $line, $error['column'], $error['message']);
}, $info['messages']));
}
}
@@ -264,4 +274,11 @@ EOF
return null;
}
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestOptionValuesFor('format')) {
$suggestions->suggestValues(['txt', 'json', 'github']);
}
}
}

View File

@@ -62,34 +62,22 @@ class TranslationDataCollector extends DataCollector implements LateDataCollecto
$this->data = [];
}
/**
* @return array|Data
*/
public function getMessages()
public function getMessages(): array|Data
{
return $this->data['messages'] ?? [];
}
/**
* @return int
*/
public function getCountMissings()
public function getCountMissings(): int
{
return $this->data[DataCollectorTranslator::MESSAGE_MISSING] ?? 0;
}
/**
* @return int
*/
public function getCountFallbacks()
public function getCountFallbacks(): int
{
return $this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK] ?? 0;
}
/**
* @return int
*/
public function getCountDefines()
public function getCountDefines(): int
{
return $this->data[DataCollectorTranslator::MESSAGE_DEFINED] ?? 0;
}
@@ -110,7 +98,7 @@ class TranslationDataCollector extends DataCollector implements LateDataCollecto
/**
* {@inheritdoc}
*/
public function getName()
public function getName(): string
{
return 'translation';
}

View File

@@ -25,15 +25,11 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
public const MESSAGE_MISSING = 1;
public const MESSAGE_EQUALS_FALLBACK = 2;
/**
* @var TranslatorInterface|TranslatorBagInterface
*/
private $translator;
private $messages = [];
private array $messages = [];
/**
* @param TranslatorInterface $translator The translator must implement TranslatorBagInterface
* @param TranslatorInterface&TranslatorBagInterface&LocaleAwareInterface $translator
*/
public function __construct(TranslatorInterface $translator)
{
@@ -47,7 +43,7 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
/**
* {@inheritdoc}
*/
public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null)
public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null): string
{
$trans = $this->translator->trans($id = (string) $id, $parameters, $domain, $locale);
$this->collectMessage($locale, $domain, $id, $trans, $parameters);
@@ -66,7 +62,7 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
/**
* {@inheritdoc}
*/
public function getLocale()
public function getLocale(): string
{
return $this->translator->getLocale();
}
@@ -74,7 +70,7 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
/**
* {@inheritdoc}
*/
public function getCatalogue(string $locale = null)
public function getCatalogue(string $locale = null): MessageCatalogueInterface
{
return $this->translator->getCatalogue($locale);
}
@@ -92,7 +88,7 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
*
* @return string[]
*/
public function warmUp(string $cacheDir)
public function warmUp(string $cacheDir): array
{
if ($this->translator instanceof WarmableInterface) {
return (array) $this->translator->warmUp($cacheDir);
@@ -103,10 +99,8 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
/**
* Gets the fallback locales.
*
* @return array The fallback locales
*/
public function getFallbackLocales()
public function getFallbackLocales(): array
{
if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) {
return $this->translator->getFallbackLocales();
@@ -123,10 +117,7 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
return $this->translator->{$method}(...$args);
}
/**
* @return array
*/
public function getCollectedMessages()
public function getCollectedMessages(): array
{
return $this->messages;
}

View File

@@ -20,28 +20,15 @@ use Symfony\Component\DependencyInjection\Reference;
*/
class TranslationDumperPass implements CompilerPassInterface
{
private $writerServiceId;
private $dumperTag;
public function __construct(string $writerServiceId = 'translation.writer', string $dumperTag = 'translation.dumper')
{
if (1 < \func_num_args()) {
trigger_deprecation('symfony/translation', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
}
$this->writerServiceId = $writerServiceId;
$this->dumperTag = $dumperTag;
}
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition($this->writerServiceId)) {
if (!$container->hasDefinition('translation.writer')) {
return;
}
$definition = $container->getDefinition($this->writerServiceId);
$definition = $container->getDefinition('translation.writer');
foreach ($container->findTaggedServiceIds($this->dumperTag, true) as $id => $attributes) {
foreach ($container->findTaggedServiceIds('translation.dumper', true) as $id => $attributes) {
$definition->addMethodCall('addDumper', [$attributes[0]['alias'], new Reference($id)]);
}
}

View File

@@ -21,28 +21,15 @@ use Symfony\Component\DependencyInjection\Reference;
*/
class TranslationExtractorPass implements CompilerPassInterface
{
private $extractorServiceId;
private $extractorTag;
public function __construct(string $extractorServiceId = 'translation.extractor', string $extractorTag = 'translation.extractor')
{
if (0 < \func_num_args()) {
trigger_deprecation('symfony/translation', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
}
$this->extractorServiceId = $extractorServiceId;
$this->extractorTag = $extractorTag;
}
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition($this->extractorServiceId)) {
if (!$container->hasDefinition('translation.extractor')) {
return;
}
$definition = $container->getDefinition($this->extractorServiceId);
$definition = $container->getDefinition('translation.extractor');
foreach ($container->findTaggedServiceIds($this->extractorTag, true) as $id => $attributes) {
foreach ($container->findTaggedServiceIds('translation.extractor', true) as $id => $attributes) {
if (!isset($attributes[0]['alias'])) {
throw new RuntimeException(sprintf('The alias for the tag "translation.extractor" of service "%s" must be set.', $id));
}

View File

@@ -18,34 +18,15 @@ use Symfony\Component\DependencyInjection\Reference;
class TranslatorPass implements CompilerPassInterface
{
private $translatorServiceId;
private $readerServiceId;
private $loaderTag;
private $debugCommandServiceId;
private $updateCommandServiceId;
public function __construct(string $translatorServiceId = 'translator.default', string $readerServiceId = 'translation.reader', string $loaderTag = 'translation.loader', string $debugCommandServiceId = 'console.command.translation_debug', string $updateCommandServiceId = 'console.command.translation_update')
{
if (0 < \func_num_args()) {
trigger_deprecation('symfony/translation', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
}
$this->translatorServiceId = $translatorServiceId;
$this->readerServiceId = $readerServiceId;
$this->loaderTag = $loaderTag;
$this->debugCommandServiceId = $debugCommandServiceId;
$this->updateCommandServiceId = $updateCommandServiceId;
}
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition($this->translatorServiceId)) {
if (!$container->hasDefinition('translator.default')) {
return;
}
$loaders = [];
$loaderRefs = [];
foreach ($container->findTaggedServiceIds($this->loaderTag, true) as $id => $attributes) {
foreach ($container->findTaggedServiceIds('translation.loader', true) as $id => $attributes) {
$loaderRefs[$id] = new Reference($id);
$loaders[$id][] = $attributes[0]['alias'];
if (isset($attributes[0]['legacy-alias'])) {
@@ -53,8 +34,8 @@ class TranslatorPass implements CompilerPassInterface
}
}
if ($container->hasDefinition($this->readerServiceId)) {
$definition = $container->getDefinition($this->readerServiceId);
if ($container->hasDefinition('translation.reader')) {
$definition = $container->getDefinition('translation.reader');
foreach ($loaders as $id => $formats) {
foreach ($formats as $format) {
$definition->addMethodCall('addLoader', [$format, $loaderRefs[$id]]);
@@ -63,7 +44,7 @@ class TranslatorPass implements CompilerPassInterface
}
$container
->findDefinition($this->translatorServiceId)
->findDefinition('translator.default')
->replaceArgument(0, ServiceLocatorTagPass::register($container, $loaderRefs))
->replaceArgument(3, $loaders)
;
@@ -73,16 +54,16 @@ class TranslatorPass implements CompilerPassInterface
}
$paths = array_keys($container->getDefinition('twig.template_iterator')->getArgument(1));
if ($container->hasDefinition($this->debugCommandServiceId)) {
$definition = $container->getDefinition($this->debugCommandServiceId);
if ($container->hasDefinition('console.command.translation_debug')) {
$definition = $container->getDefinition('console.command.translation_debug');
$definition->replaceArgument(4, $container->getParameter('twig.default_path'));
if (\count($definition->getArguments()) > 6) {
$definition->replaceArgument(6, $paths);
}
}
if ($container->hasDefinition($this->updateCommandServiceId)) {
$definition = $container->getDefinition($this->updateCommandServiceId);
if ($container->hasDefinition('console.command.translation_extract')) {
$definition = $container->getDefinition('console.command.translation_extract');
$definition->replaceArgument(5, $container->getParameter('twig.default_path'));
if (\count($definition->getArguments()) > 7) {

View File

@@ -22,30 +22,26 @@ use Symfony\Component\DependencyInjection\ServiceLocator;
*/
class TranslatorPathsPass extends AbstractRecursivePass
{
private $translatorServiceId;
private $debugCommandServiceId;
private $updateCommandServiceId;
private $resolverServiceId;
private $level = 0;
private $paths = [];
private $definitions = [];
private $controllers = [];
private int $level = 0;
public function __construct(string $translatorServiceId = 'translator', string $debugCommandServiceId = 'console.command.translation_debug', string $updateCommandServiceId = 'console.command.translation_update', string $resolverServiceId = 'argument_resolver.service')
{
if (0 < \func_num_args()) {
trigger_deprecation('symfony/translation', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
}
/**
* @var array<string, bool>
*/
private array $paths = [];
$this->translatorServiceId = $translatorServiceId;
$this->debugCommandServiceId = $debugCommandServiceId;
$this->updateCommandServiceId = $updateCommandServiceId;
$this->resolverServiceId = $resolverServiceId;
}
/**
* @var array<int, Definition>
*/
private array $definitions = [];
/**
* @var array<string, array<string, bool>>
*/
private array $controllers = [];
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition($this->translatorServiceId)) {
if (!$container->hasDefinition('translator')) {
return;
}
@@ -70,12 +66,12 @@ class TranslatorPathsPass extends AbstractRecursivePass
}
}
if ($paths) {
if ($container->hasDefinition($this->debugCommandServiceId)) {
$definition = $container->getDefinition($this->debugCommandServiceId);
if ($container->hasDefinition('console.command.translation_debug')) {
$definition = $container->getDefinition('console.command.translation_debug');
$definition->replaceArgument(6, array_merge($definition->getArgument(6), $paths));
}
if ($container->hasDefinition($this->updateCommandServiceId)) {
$definition = $container->getDefinition($this->updateCommandServiceId);
if ($container->hasDefinition('console.command.translation_extract')) {
$definition = $container->getDefinition('console.command.translation_extract');
$definition->replaceArgument(7, array_merge($definition->getArgument(7), $paths));
}
}
@@ -86,10 +82,10 @@ class TranslatorPathsPass extends AbstractRecursivePass
}
}
protected function processValue($value, bool $isRoot = false)
protected function processValue(mixed $value, bool $isRoot = false): mixed
{
if ($value instanceof Reference) {
if ((string) $value === $this->translatorServiceId) {
if ('translator' === (string) $value) {
for ($i = $this->level - 1; $i >= 0; --$i) {
$class = $this->definitions[$i]->getClass();
@@ -124,8 +120,8 @@ class TranslatorPathsPass extends AbstractRecursivePass
private function findControllerArguments(ContainerBuilder $container): array
{
if ($container->hasDefinition($this->resolverServiceId)) {
$argument = $container->getDefinition($this->resolverServiceId)->getArgument(0);
if ($container->hasDefinition('argument_resolver.service')) {
$argument = $container->getDefinition('argument_resolver.service')->getArgument(0);
if ($argument instanceof Reference) {
$argument = $container->getDefinition($argument);
}
@@ -133,8 +129,8 @@ class TranslatorPathsPass extends AbstractRecursivePass
return $argument->getArgument(0);
}
if ($container->hasDefinition('debug.'.$this->resolverServiceId)) {
$argument = $container->getDefinition('debug.'.$this->resolverServiceId)->getArgument(0);
if ($container->hasDefinition('debug.'.'argument_resolver.service')) {
$argument = $container->getDefinition('debug.'.'argument_resolver.service')->getArgument(0);
if ($argument instanceof Reference) {
$argument = $container->getDefinition($argument);
}

View File

@@ -20,13 +20,13 @@ use Symfony\Component\Translation\MessageCatalogue;
*/
class CsvFileDumper extends FileDumper
{
private $delimiter = ';';
private $enclosure = '"';
private string $delimiter = ';';
private string $enclosure = '"';
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
$handle = fopen('php://memory', 'r+');
@@ -53,7 +53,7 @@ class CsvFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'csv';
}

View File

@@ -86,17 +86,13 @@ abstract class FileDumper implements DumperInterface
/**
* Transforms a domain of a message catalogue to its string representation.
*
* @return string representation
*/
abstract public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []);
abstract public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string;
/**
* Gets the file extension of the dumper.
*
* @return string file extension
*/
abstract protected function getExtension();
abstract protected function getExtension(): string;
/**
* Gets the relative file path using the template.

View File

@@ -28,7 +28,7 @@ class IcuResFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
$data = $indexes = $resources = '';
@@ -97,7 +97,7 @@ class IcuResFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'res';
}

View File

@@ -23,7 +23,7 @@ class IniFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
$output = '';
@@ -38,7 +38,7 @@ class IniFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'ini';
}

View File

@@ -23,7 +23,7 @@ class JsonFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
$flags = $options['json_encoding'] ?? \JSON_PRETTY_PRINT;
@@ -33,7 +33,7 @@ class JsonFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'json';
}

View File

@@ -24,7 +24,7 @@ class MoFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
$sources = $targets = $sourceOffsets = $targetOffsets = '';
$offsets = [];
@@ -70,12 +70,12 @@ class MoFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'mo';
}
private function writeLong($str): string
private function writeLong(mixed $str): string
{
return pack('V*', $str);
}

View File

@@ -23,7 +23,7 @@ class PhpFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
return "<?php\n\nreturn ".var_export($messages->all($domain), true).";\n";
}
@@ -31,7 +31,7 @@ class PhpFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'php';
}

View File

@@ -23,7 +23,7 @@ class PoFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
$output = 'msgid ""'."\n";
$output .= 'msgstr ""'."\n";
@@ -114,7 +114,7 @@ EOF;
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'po';
}
@@ -124,7 +124,7 @@ EOF;
return addcslashes($str, "\0..\37\42\134");
}
private function formatComments($comments, string $prefix = ''): ?string
private function formatComments(string|array $comments, string $prefix = ''): ?string
{
$output = null;

View File

@@ -23,7 +23,7 @@ class QtFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
$dom = new \DOMDocument('1.0', 'utf-8');
$dom->formatOutput = true;
@@ -54,7 +54,7 @@ class QtFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'ts';
}

View File

@@ -24,7 +24,7 @@ class XliffFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
$xliffVersion = '1.2';
if (\array_key_exists('xliff_version', $options)) {
@@ -50,7 +50,7 @@ class XliffFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'xlf';
}

View File

@@ -23,7 +23,7 @@ use Symfony\Component\Yaml\Yaml;
*/
class YamlFileDumper extends FileDumper
{
private $extension;
private string $extension;
public function __construct(string $extension = 'yml')
{
@@ -33,7 +33,7 @@ class YamlFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
if (!class_exists(Yaml::class)) {
throw new LogicException('Dumping translations in the YAML format requires the Symfony Yaml component.');
@@ -55,7 +55,7 @@ class YamlFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return $this->extension;
}

View File

@@ -15,18 +15,16 @@ use Symfony\Contracts\HttpClient\ResponseInterface;
/**
* @author Fabien Potencier <fabien@symfony.com>
*
* @experimental in 5.3
*/
class ProviderException extends RuntimeException implements ProviderExceptionInterface
{
private $response;
private $debug;
private string $debug;
public function __construct(string $message, ResponseInterface $response, int $code = 0, \Exception $previous = null)
{
$this->response = $response;
$this->debug .= $response->getInfo('debug') ?? '';
$this->debug = $response->getInfo('debug') ?? '';
parent::__construct($message, $code, $previous);
}

View File

@@ -13,8 +13,6 @@ namespace Symfony\Component\Translation\Exception;
/**
* @author Fabien Potencier <fabien@symfony.com>
*
* @experimental in 5.3
*/
interface ProviderExceptionInterface extends ExceptionInterface
{

View File

@@ -20,12 +20,7 @@ use Symfony\Component\Translation\Exception\InvalidArgumentException;
*/
abstract class AbstractFileExtractor
{
/**
* @param string|iterable $resource Files, a file or a directory
*
* @return iterable
*/
protected function extractFiles($resource)
protected function extractFiles(string|iterable $resource): iterable
{
if (is_iterable($resource)) {
$files = [];
@@ -49,11 +44,9 @@ abstract class AbstractFileExtractor
}
/**
* @return bool
*
* @throws InvalidArgumentException
*/
protected function isFile(string $file)
protected function isFile(string $file): bool
{
if (!is_file($file)) {
throw new InvalidArgumentException(sprintf('The "%s" file does not exist.', $file));
@@ -68,9 +61,7 @@ abstract class AbstractFileExtractor
abstract protected function canBeExtracted(string $file);
/**
* @param string|array $resource Files, a file or a directory
*
* @return iterable files to be extracted
* @return iterable
*/
abstract protected function extractFromDirectory($resource);
abstract protected function extractFromDirectory(string|array $resource);
}

View File

@@ -25,7 +25,7 @@ class ChainExtractor implements ExtractorInterface
*
* @var ExtractorInterface[]
*/
private $extractors = [];
private array $extractors = [];
/**
* Adds a loader to the translation extractor.
@@ -48,7 +48,7 @@ class ChainExtractor implements ExtractorInterface
/**
* {@inheritdoc}
*/
public function extract($directory, MessageCatalogue $catalogue)
public function extract(string|iterable $directory, MessageCatalogue $catalogue)
{
foreach ($this->extractors as $extractor) {
$extractor->extract($directory, $catalogue);

View File

@@ -26,7 +26,7 @@ interface ExtractorInterface
*
* @param string|iterable<string> $resource Files, a file or a directory
*/
public function extract($resource, MessageCatalogue $catalogue);
public function extract(string|iterable $resource, MessageCatalogue $catalogue);
/**
* Sets the prefix that should be used for new found messages.

View File

@@ -27,15 +27,11 @@ class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
/**
* Prefix for new found message.
*
* @var string
*/
private $prefix = '';
private string $prefix = '';
/**
* The sequence that captures translation messages.
*
* @var array
*/
protected $sequences = [
[
@@ -135,7 +131,7 @@ class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
/**
* {@inheritdoc}
*/
public function extract($resource, MessageCatalogue $catalog)
public function extract(string|iterable $resource, MessageCatalogue $catalog)
{
$files = $this->extractFiles($resource);
foreach ($files as $file) {
@@ -155,12 +151,8 @@ class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
/**
* Normalizes a token.
*
* @param mixed $token
*
* @return string|null
*/
protected function normalizeToken($token)
protected function normalizeToken(mixed $token): ?string
{
if (isset($token[1]) && 'b"' !== $token) {
return $token[1];
@@ -315,11 +307,9 @@ class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
}
/**
* @return bool
*
* @throws \InvalidArgumentException
*/
protected function canBeExtracted(string $file)
protected function canBeExtracted(string $file): bool
{
return $this->isFile($file) && 'php' === pathinfo($file, \PATHINFO_EXTENSION);
}
@@ -327,8 +317,12 @@ class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
/**
* {@inheritdoc}
*/
protected function extractFromDirectory($directory)
protected function extractFromDirectory(string|array $directory): iterable
{
if (!class_exists(Finder::class)) {
throw new \LogicException(sprintf('You cannot use "%s" as the "symfony/finder" package is not installed. Try running "composer require symfony/finder".', static::class));
}
$finder = new Finder();
return $finder->files()->name('*.php')->in($directory);

View File

@@ -64,10 +64,8 @@ class PhpStringTokenParser
* Parses a string token.
*
* @param string $str String token content
*
* @return string The parsed string
*/
public static function parse(string $str)
public static function parse(string $str): string
{
$bLength = 0;
if ('b' === $str[0]) {
@@ -90,10 +88,8 @@ class PhpStringTokenParser
*
* @param string $str String without quotes
* @param string|null $quote Quote type
*
* @return string String with escape sequences parsed
*/
public static function parseEscapeSequences(string $str, string $quote = null)
public static function parseEscapeSequences(string $str, string $quote = null): string
{
if (null !== $quote) {
$str = str_replace('\\'.$quote, $quote, $str);
@@ -124,10 +120,8 @@ class PhpStringTokenParser
*
* @param string $startToken Doc string start token content (<<<SMTHG)
* @param string $str String token content
*
* @return string Parsed string
*/
public static function parseDocString(string $startToken, string $str)
public static function parseDocString(string $startToken, string $str): string
{
// strip last newline (thanks tokenizer for sticking it into the string!)
$str = preg_replace('~(\r\n|\n|\r)$~', '', $str);

View File

@@ -37,7 +37,7 @@ class MessageFormatter implements MessageFormatterInterface, IntlFormatterInterf
/**
* {@inheritdoc}
*/
public function format(string $message, string $locale, array $parameters = [])
public function format(string $message, string $locale, array $parameters = []): string
{
if ($this->translator instanceof TranslatorInterface) {
return $this->translator->trans($message, $parameters, null, $locale);

View File

@@ -23,8 +23,6 @@ interface MessageFormatterInterface
* @param string $message The message (may also be an object that can be cast to string)
* @param string $locale The message locale
* @param array $parameters An array of parameters for the message
*
* @return string
*/
public function format(string $message, string $locale, array $parameters = []);
public function format(string $message, string $locale, array $parameters = []): string;
}

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2021 Fabien Potencier
Copyright (c) 2004-2022 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -23,7 +23,7 @@ class ArrayLoader implements LoaderInterface
/**
* {@inheritdoc}
*/
public function load($resource, string $locale, string $domain = 'messages')
public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue
{
$resource = $this->flatten($resource);
$catalogue = new MessageCatalogue($locale);

View File

@@ -20,14 +20,14 @@ use Symfony\Component\Translation\Exception\NotFoundResourceException;
*/
class CsvFileLoader extends FileLoader
{
private $delimiter = ';';
private $enclosure = '"';
private $escape = '\\';
private string $delimiter = ';';
private string $enclosure = '"';
private string $escape = '\\';
/**
* {@inheritdoc}
*/
protected function loadResource(string $resource)
protected function loadResource(string $resource): array
{
$messages = [];

View File

@@ -14,6 +14,7 @@ namespace Symfony\Component\Translation\Loader;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Exception\InvalidResourceException;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Translation\MessageCatalogue;
/**
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
@@ -23,7 +24,7 @@ abstract class FileLoader extends ArrayLoader
/**
* {@inheritdoc}
*/
public function load($resource, string $locale, string $domain = 'messages')
public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue
{
if (!stream_is_local($resource)) {
throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
@@ -55,9 +56,7 @@ abstract class FileLoader extends ArrayLoader
}
/**
* @return array
*
* @throws InvalidResourceException if stream content has an invalid format
*/
abstract protected function loadResource(string $resource);
abstract protected function loadResource(string $resource): array;
}

View File

@@ -26,7 +26,7 @@ class IcuDatFileLoader extends IcuResFileLoader
/**
* {@inheritdoc}
*/
public function load($resource, string $locale, string $domain = 'messages')
public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue
{
if (!stream_is_local($resource.'.dat')) {
throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));

View File

@@ -26,7 +26,7 @@ class IcuResFileLoader implements LoaderInterface
/**
* {@inheritdoc}
*/
public function load($resource, string $locale, string $domain = 'messages')
public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue
{
if (!stream_is_local($resource)) {
throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
@@ -72,10 +72,8 @@ class IcuResFileLoader implements LoaderInterface
* @param \ResourceBundle $rb The ResourceBundle that will be flattened
* @param array $messages Used internally for recursive calls
* @param string $path Current path being parsed, used internally for recursive calls
*
* @return array the flattened ResourceBundle
*/
protected function flatten(\ResourceBundle $rb, array &$messages = [], string $path = null)
protected function flatten(\ResourceBundle $rb, array &$messages = [], string $path = null): array
{
foreach ($rb as $key => $value) {
$nodePath = $path ? $path.'.'.$key : $key;

View File

@@ -21,7 +21,7 @@ class IniFileLoader extends FileLoader
/**
* {@inheritdoc}
*/
protected function loadResource(string $resource)
protected function loadResource(string $resource): array
{
return parse_ini_file($resource, true);
}

View File

@@ -23,7 +23,7 @@ class JsonFileLoader extends FileLoader
/**
* {@inheritdoc}
*/
protected function loadResource(string $resource)
protected function loadResource(string $resource): array
{
$messages = [];
if ($data = file_get_contents($resource)) {

View File

@@ -25,14 +25,8 @@ interface LoaderInterface
/**
* Loads a locale.
*
* @param mixed $resource A resource
* @param string $locale A locale
* @param string $domain The domain
*
* @return MessageCatalogue A MessageCatalogue instance
*
* @throws NotFoundResourceException when the resource cannot be found
* @throws InvalidResourceException when the resource cannot be loaded
*/
public function load($resource, string $locale, string $domain = 'messages');
public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue;
}

View File

@@ -22,13 +22,13 @@ class MoFileLoader extends FileLoader
* Magic used for validating the format of an MO file as well as
* detecting if the machine used to create that file was little endian.
*/
public const MO_LITTLE_ENDIAN_MAGIC = 0x950412de;
public const MO_LITTLE_ENDIAN_MAGIC = 0x950412DE;
/**
* Magic used for validating the format of an MO file as well as
* detecting if the machine used to create that file was big endian.
*/
public const MO_BIG_ENDIAN_MAGIC = 0xde120495;
public const MO_BIG_ENDIAN_MAGIC = 0xDE120495;
/**
* The size of the header of an MO file in bytes.
@@ -41,7 +41,7 @@ class MoFileLoader extends FileLoader
*
* {@inheritdoc}
*/
protected function loadResource(string $resource)
protected function loadResource(string $resource): array
{
$stream = fopen($resource, 'r');

View File

@@ -18,12 +18,12 @@ namespace Symfony\Component\Translation\Loader;
*/
class PhpFileLoader extends FileLoader
{
private static $cache = [];
private static ?array $cache = [];
/**
* {@inheritdoc}
*/
protected function loadResource(string $resource)
protected function loadResource(string $resource): array
{
if ([] === self::$cache && \function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) || filter_var(ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOLEAN))) {
self::$cache = null;

View File

@@ -60,7 +60,7 @@ class PoFileLoader extends FileLoader
*
* {@inheritdoc}
*/
protected function loadResource(string $resource)
protected function loadResource(string $resource): array
{
$stream = fopen($resource, 'r');

View File

@@ -28,7 +28,7 @@ class QtFileLoader implements LoaderInterface
/**
* {@inheritdoc}
*/
public function load($resource, string $locale, string $domain = 'messages')
public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue
{
if (!class_exists(XmlUtils::class)) {
throw new RuntimeException('Loading translations from the QT format requires the Symfony Config component.');

View File

@@ -31,7 +31,7 @@ class XliffFileLoader implements LoaderInterface
/**
* {@inheritdoc}
*/
public function load($resource, string $locale, string $domain = 'messages')
public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue
{
if (!class_exists(XmlUtils::class)) {
throw new RuntimeException('Loading translations from the Xliff format requires the Symfony Config component.');
@@ -57,7 +57,7 @@ class XliffFileLoader implements LoaderInterface
} else {
$dom = XmlUtils::loadFile($resource);
}
} catch (\InvalidArgumentException | XmlParsingException | InvalidXmlException $e) {
} catch (\InvalidArgumentException|XmlParsingException|InvalidXmlException $e) {
throw new InvalidResourceException(sprintf('Unable to load "%s": ', $resource).$e->getMessage(), $e->getCode(), $e);
}

View File

@@ -29,7 +29,7 @@ class YamlFileLoader extends FileLoader
/**
* {@inheritdoc}
*/
protected function loadResource(string $resource)
protected function loadResource(string $resource): array
{
if (null === $this->yamlParser) {
if (!class_exists(\Symfony\Component\Yaml\Parser::class)) {

View File

@@ -21,15 +21,11 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface, LocaleAwareInterface
{
/**
* @var TranslatorInterface|TranslatorBagInterface
*/
private $translator;
private $logger;
/**
* @param TranslatorInterface $translator The translator must implement TranslatorBagInterface
* @param TranslatorInterface&TranslatorBagInterface&LocaleAwareInterface $translator The translator must implement TranslatorBagInterface
*/
public function __construct(TranslatorInterface $translator, LoggerInterface $logger)
{
@@ -44,7 +40,7 @@ class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface,
/**
* {@inheritdoc}
*/
public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null)
public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null): string
{
$trans = $this->translator->trans($id = (string) $id, $parameters, $domain, $locale);
$this->log($id, $domain, $locale);
@@ -69,7 +65,7 @@ class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface,
/**
* {@inheritdoc}
*/
public function getLocale()
public function getLocale(): string
{
return $this->translator->getLocale();
}
@@ -77,7 +73,7 @@ class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface,
/**
* {@inheritdoc}
*/
public function getCatalogue(string $locale = null)
public function getCatalogue(string $locale = null): MessageCatalogueInterface
{
return $this->translator->getCatalogue($locale);
}
@@ -92,10 +88,8 @@ class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface,
/**
* Gets the fallback locales.
*
* @return array The fallback locales
*/
public function getFallbackLocales()
public function getFallbackLocales(): array
{
if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) {
return $this->translator->getFallbackLocales();

View File

@@ -19,12 +19,12 @@ use Symfony\Component\Translation\Exception\LogicException;
*/
class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterface
{
private $messages = [];
private $metadata = [];
private $resources = [];
private $locale;
private $fallbackCatalogue;
private $parent;
private array $messages = [];
private array $metadata = [];
private array $resources = [];
private string $locale;
private $fallbackCatalogue = null;
private ?self $parent = null;
/**
* @param array $messages An array of messages classified by domain
@@ -38,7 +38,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function getLocale()
public function getLocale(): string
{
return $this->locale;
}
@@ -46,7 +46,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function getDomains()
public function getDomains(): array
{
$domains = [];
@@ -63,7 +63,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function all(string $domain = null)
public function all(string $domain = null): array
{
if (null !== $domain) {
// skip messages merge if intl-icu requested explicitly
@@ -99,7 +99,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function has(string $id, string $domain = 'messages')
public function has(string $id, string $domain = 'messages'): bool
{
if (isset($this->messages[$domain][$id]) || isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id])) {
return true;
@@ -115,7 +115,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function defines(string $id, string $domain = 'messages')
public function defines(string $id, string $domain = 'messages'): bool
{
return isset($this->messages[$domain][$id]) || isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id]);
}
@@ -123,7 +123,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function get(string $id, string $domain = 'messages')
public function get(string $id, string $domain = 'messages'): string
{
if (isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id])) {
return $this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id];
@@ -233,7 +233,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function getFallbackCatalogue()
public function getFallbackCatalogue(): ?MessageCatalogueInterface
{
return $this->fallbackCatalogue;
}
@@ -241,7 +241,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function getResources()
public function getResources(): array
{
return array_values($this->resources);
}
@@ -257,7 +257,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function getMetadata(string $key = '', string $domain = 'messages')
public function getMetadata(string $key = '', string $domain = 'messages'): mixed
{
if ('' == $domain) {
return $this->metadata;
@@ -279,7 +279,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function setMetadata(string $key, $value, string $domain = 'messages')
public function setMetadata(string $key, mixed $value, string $domain = 'messages')
{
$this->metadata[$domain][$key] = $value;
}

View File

@@ -24,17 +24,13 @@ interface MessageCatalogueInterface
/**
* Gets the catalogue locale.
*
* @return string The locale
*/
public function getLocale();
public function getLocale(): string;
/**
* Gets the domains.
*
* @return array An array of domains
*/
public function getDomains();
public function getDomains(): array;
/**
* Gets the messages within a given domain.
@@ -42,10 +38,8 @@ interface MessageCatalogueInterface
* If $domain is null, it returns all messages.
*
* @param string $domain The domain name
*
* @return array An array of messages
*/
public function all(string $domain = null);
public function all(string $domain = null): array;
/**
* Sets a message translation.
@@ -61,30 +55,24 @@ interface MessageCatalogueInterface
*
* @param string $id The message id
* @param string $domain The domain name
*
* @return bool true if the message has a translation, false otherwise
*/
public function has(string $id, string $domain = 'messages');
public function has(string $id, string $domain = 'messages'): bool;
/**
* Checks if a message has a translation (it does not take into account the fallback mechanism).
*
* @param string $id The message id
* @param string $domain The domain name
*
* @return bool true if the message has a translation, false otherwise
*/
public function defines(string $id, string $domain = 'messages');
public function defines(string $id, string $domain = 'messages'): bool;
/**
* Gets a message translation.
*
* @param string $id The message id
* @param string $domain The domain name
*
* @return string The message translation
*/
public function get(string $id, string $domain = 'messages');
public function get(string $id, string $domain = 'messages'): string;
/**
* Sets translations for a given domain.
@@ -119,17 +107,15 @@ interface MessageCatalogueInterface
/**
* Gets the fallback catalogue.
*
* @return self|null A MessageCatalogueInterface instance or null when no fallback has been set
*/
public function getFallbackCatalogue();
public function getFallbackCatalogue(): ?self;
/**
* Returns an array of resources loaded to build this collection.
*
* @return ResourceInterface[] An array of resources
* @return ResourceInterface[]
*/
public function getResources();
public function getResources(): array;
/**
* Adds a resource for this collection.

View File

@@ -27,14 +27,12 @@ interface MetadataAwareInterface
*
* @return mixed The value that was set or an array with the domains/keys or null
*/
public function getMetadata(string $key = '', string $domain = 'messages');
public function getMetadata(string $key = '', string $domain = 'messages'): mixed;
/**
* Adds metadata to a message domain.
*
* @param mixed $value
*/
public function setMetadata(string $key, $value, string $domain = 'messages');
public function setMetadata(string $key, mixed $value, string $domain = 'messages');
/**
* Deletes metadata for the given key and domain.

View File

@@ -20,14 +20,14 @@ use Symfony\Component\Translation\Exception\MissingRequiredOptionException;
*/
final class Dsn
{
private $scheme;
private $host;
private $user;
private $password;
private $port;
private $path;
private $options;
private $originalDsn;
private ?string $scheme;
private ?string $host;
private ?string $user;
private ?string $password;
private ?int $port;
private ?string $path;
private array $options = [];
private string $originalDsn;
public function __construct(string $dsn)
{
@@ -79,7 +79,7 @@ final class Dsn
return $this->port ?? $default;
}
public function getOption(string $key, $default = null)
public function getOption(string $key, mixed $default = null)
{
return $this->options[$key] ?? $default;
}

View File

@@ -18,14 +18,12 @@ use Symfony\Component\Translation\TranslatorBagInterface;
* Filters domains and locales between the Translator config values and those specific to each provider.
*
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*
* @experimental in 5.3
*/
class FilteringProvider implements ProviderInterface
{
private $provider;
private $locales;
private $domains;
private array $locales;
private array $domains;
public function __construct(ProviderInterface $provider, array $locales, array $domains = [])
{

View File

@@ -16,8 +16,6 @@ use Symfony\Component\Translation\TranslatorBagInterface;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*
* @experimental in 5.3
*/
class NullProvider implements ProviderInterface
{

View File

@@ -15,8 +15,6 @@ use Symfony\Component\Translation\Exception\UnsupportedSchemeException;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*
* @experimental in 5.3
*/
final class NullProviderFactory extends AbstractProviderFactory
{

View File

@@ -15,11 +15,12 @@ use Symfony\Component\Translation\Exception\InvalidArgumentException;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*
* @experimental in 5.3
*/
final class TranslationProviderCollection
{
/**
* @var array<string, ProviderInterface>
*/
private $providers;
/**
@@ -27,10 +28,7 @@ final class TranslationProviderCollection
*/
public function __construct(iterable $providers)
{
$this->providers = [];
foreach ($providers as $name => $provider) {
$this->providers[$name] = $provider;
}
$this->providers = \is_array($providers) ? $providers : iterator_to_array($providers);
}
public function __toString(): string

View File

@@ -15,16 +15,14 @@ use Symfony\Component\Translation\Exception\UnsupportedSchemeException;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*
* @experimental in 5.3
*/
class TranslationProviderCollectionFactory
{
private $factories;
private $enabledLocales;
private iterable $factories;
private array $enabledLocales;
/**
* @param ProviderFactoryInterface[] $factories
* @param iterable<mixed, ProviderFactoryInterface> $factories
*/
public function __construct(iterable $factories, array $enabledLocales)
{

View File

@@ -21,11 +21,15 @@ final class PseudoLocalizationTranslator implements TranslatorInterface
private const EXPANSION_CHARACTER = '~';
private $translator;
private $accents;
private $expansionFactor;
private $brackets;
private $parseHTML;
private $localizableHTMLAttributes;
private bool $accents;
private float $expansionFactor;
private bool $brackets;
private bool $parseHTML;
/**
* @var string[]
*/
private array $localizableHTMLAttributes;
/**
* Available options:
@@ -82,7 +86,7 @@ final class PseudoLocalizationTranslator implements TranslatorInterface
/**
* {@inheritdoc}
*/
public function trans(string $id, array $parameters = [], string $domain = null, string $locale = null)
public function trans(string $id, array $parameters = [], string $domain = null, string $locale = null): string
{
$trans = '';
$visibleText = '';

View File

@@ -23,6 +23,16 @@ $translator->addResource('array', [
echo $translator->trans('Hello World!'); // outputs « Bonjour ! »
```
Sponsor
-------
The Translation component for Symfony 5.4/6.0 is [backed][1] by:
* [Crowdin][2], a cloud-based localization management software helping teams to go global and stay agile.
* [Lokalise][3], a continuous localization and translation management platform that integrates into your development workflow so you can ship localized products, faster.
Help Symfony by [sponsoring][4] its development!
Resources
---------
@@ -31,3 +41,8 @@ Resources
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)
[1]: https://symfony.com/backers
[2]: https://crowdin.com
[3]: https://lokalise.com
[4]: https://symfony.com/sponsor

View File

@@ -25,9 +25,9 @@ class TranslationReader implements TranslationReaderInterface
/**
* Loaders used for import.
*
* @var array
* @var array<string, LoaderInterface>
*/
private $loaders = [];
private array $loaders = [];
/**
* Adds a loader to the translation extractor.

View File

@@ -19,13 +19,16 @@ $usageInstructions = <<<END
# show the translation status of all locales
$ php translation-status.php
# show the translation status of all locales and all their missing translations
# only show the translation status of incomplete or erroneous locales
$ php translation-status.php --incomplete
# show the translation status of all locales, all their missing translations and mismatches between trans-unit id and source
$ php translation-status.php -v
# show the status of a single locale
$ php translation-status.php fr
# show the status of a single locale and all its missing translations
# show the status of a single locale, missing translations and mismatches between trans-unit id and source
$ php translation-status.php fr -v
END;
@@ -35,6 +38,8 @@ $config = [
'verbose_output' => false,
// NULL = analyze all locales
'locale_to_analyze' => null,
// append --incomplete to only show incomplete languages
'include_completed_languages' => true,
// the reference files all the other translations are compared to
'original_files' => [
'src/Symfony/Component/Form/Resources/translations/validators.en.xlf',
@@ -46,12 +51,17 @@ $config = [
$argc = $_SERVER['argc'];
$argv = $_SERVER['argv'];
if ($argc > 3) {
if ($argc > 4) {
echo str_replace('translation-status.php', $argv[0], $usageInstructions);
exit(1);
}
foreach (array_slice($argv, 1) as $argumentOrOption) {
if ('--incomplete' === $argumentOrOption) {
$config['include_completed_languages'] = false;
continue;
}
if (str_starts_with($argumentOrOption, '-')) {
$config['verbose_output'] = true;
} else {
@@ -67,6 +77,7 @@ foreach ($config['original_files'] as $originalFilePath) {
}
$totalMissingTranslations = 0;
$totalTranslationMismatches = 0;
foreach ($config['original_files'] as $originalFilePath) {
$translationFilePaths = findTranslationFiles($originalFilePath, $config['locale_to_analyze']);
@@ -75,11 +86,14 @@ foreach ($config['original_files'] as $originalFilePath) {
$totalMissingTranslations += array_sum(array_map(function ($translation) {
return count($translation['missingKeys']);
}, array_values($translationStatus)));
$totalTranslationMismatches += array_sum(array_map(function ($translation) {
return count($translation['mismatches']);
}, array_values($translationStatus)));
printTranslationStatus($originalFilePath, $translationStatus, $config['verbose_output']);
printTranslationStatus($originalFilePath, $translationStatus, $config['verbose_output'], $config['include_completed_languages']);
}
exit($totalMissingTranslations > 0 ? 1 : 0);
exit($totalTranslationMismatches > 0 ? 1 : 0);
function findTranslationFiles($originalFilePath, $localeToAnalyze)
{
@@ -112,21 +126,29 @@ function calculateTranslationStatus($originalFilePath, $translationFilePaths)
foreach ($translationFilePaths as $locale => $translationPath) {
$translatedKeys = extractTranslationKeys($translationPath);
$missingKeys = array_diff_key($allTranslationKeys, $translatedKeys);
$mismatches = findTransUnitMismatches($allTranslationKeys, $translatedKeys);
$translationStatus[$locale] = [
'total' => count($allTranslationKeys),
'translated' => count($translatedKeys),
'missingKeys' => $missingKeys,
'mismatches' => $mismatches,
];
$translationStatus[$locale]['is_completed'] = isTranslationCompleted($translationStatus[$locale]);
}
return $translationStatus;
}
function printTranslationStatus($originalFilePath, $translationStatus, $verboseOutput)
function isTranslationCompleted(array $translationStatus): bool
{
return $translationStatus['total'] === $translationStatus['translated'] && 0 === count($translationStatus['mismatches']);
}
function printTranslationStatus($originalFilePath, $translationStatus, $verboseOutput, $includeCompletedLanguages)
{
printTitle($originalFilePath);
printTable($translationStatus, $verboseOutput);
printTable($translationStatus, $verboseOutput, $includeCompletedLanguages);
echo \PHP_EOL.\PHP_EOL;
}
@@ -152,13 +174,35 @@ function extractTranslationKeys($filePath)
return $translationKeys;
}
/**
* Check whether the trans-unit id and source match with the base translation.
*/
function findTransUnitMismatches(array $baseTranslationKeys, array $translatedKeys): array
{
$mismatches = [];
foreach ($baseTranslationKeys as $translationId => $translationKey) {
if (!isset($translatedKeys[$translationId])) {
continue;
}
if ($translatedKeys[$translationId] !== $translationKey) {
$mismatches[$translationId] = [
'found' => $translatedKeys[$translationId],
'expected' => $translationKey,
];
}
}
return $mismatches;
}
function printTitle($title)
{
echo $title.\PHP_EOL;
echo str_repeat('=', strlen($title)).\PHP_EOL.\PHP_EOL;
}
function printTable($translations, $verboseOutput)
function printTable($translations, $verboseOutput, bool $includeCompletedLanguages)
{
if (0 === count($translations)) {
echo 'No translations found';
@@ -168,24 +212,47 @@ function printTable($translations, $verboseOutput)
$longestLocaleNameLength = max(array_map('strlen', array_keys($translations)));
foreach ($translations as $locale => $translation) {
if (!$includeCompletedLanguages && $translation['is_completed']) {
continue;
}
if ($translation['translated'] > $translation['total']) {
textColorRed();
} elseif ($translation['translated'] === $translation['total']) {
} elseif (count($translation['mismatches']) > 0) {
textColorRed();
} elseif ($translation['is_completed']) {
textColorGreen();
}
echo sprintf('| Locale: %-'.$longestLocaleNameLength.'s | Translated: %d/%d', $locale, $translation['translated'], $translation['total']).\PHP_EOL;
echo sprintf(
'| Locale: %-'.$longestLocaleNameLength.'s | Translated: %2d/%2d | Mismatches: %d |',
$locale,
$translation['translated'],
$translation['total'],
count($translation['mismatches'])
).\PHP_EOL;
textColorNormal();
$shouldBeClosed = false;
if (true === $verboseOutput && count($translation['missingKeys']) > 0) {
echo str_repeat('-', 80).\PHP_EOL;
echo '| Missing Translations:'.\PHP_EOL;
echo '| Missing Translations:'.\PHP_EOL;
foreach ($translation['missingKeys'] as $id => $content) {
echo sprintf('| (id=%s) %s', $id, $content).\PHP_EOL;
echo sprintf('| (id=%s) %s', $id, $content).\PHP_EOL;
}
$shouldBeClosed = true;
}
if (true === $verboseOutput && count($translation['mismatches']) > 0) {
echo '| Mismatches between trans-unit id and source:'.\PHP_EOL;
foreach ($translation['mismatches'] as $id => $content) {
echo sprintf('| (id=%s) Expected: %s', $id, $content['expected']).\PHP_EOL;
echo sprintf('| Found: %s', $content['found']).\PHP_EOL;
}
$shouldBeClosed = true;
}
if ($shouldBeClosed) {
echo str_repeat('-', 80).\PHP_EOL;
}
}

View File

@@ -12,7 +12,6 @@
"en_BS": "en_001",
"en_BW": "en_001",
"en_BZ": "en_001",
"en_CA": "en_001",
"en_CC": "en_001",
"en_CH": "en_150",
"en_CK": "en_001",
@@ -65,7 +64,6 @@
"en_NU": "en_001",
"en_NZ": "en_001",
"en_PG": "en_001",
"en_PH": "en_001",
"en_PK": "en_001",
"en_PN": "en_001",
"en_PW": "en_001",

View File

@@ -33,7 +33,7 @@ abstract class ProviderFactoryTestCase extends TestCase
{
protected $client;
protected $logger;
protected $defaultLocale;
protected string $defaultLocale;
protected $loader;
protected $xliffFileDumper;
@@ -122,26 +122,26 @@ abstract class ProviderFactoryTestCase extends TestCase
protected function getClient(): HttpClientInterface
{
return $this->client ?? $this->client = new MockHttpClient();
return $this->client ??= new MockHttpClient();
}
protected function getLogger(): LoggerInterface
{
return $this->logger ?? $this->logger = $this->createMock(LoggerInterface::class);
return $this->logger ??= $this->createMock(LoggerInterface::class);
}
protected function getDefaultLocale(): string
{
return $this->defaultLocale ?? $this->defaultLocale = 'en';
return $this->defaultLocale ??= 'en';
}
protected function getLoader(): LoaderInterface
{
return $this->loader ?? $this->loader = $this->createMock(LoaderInterface::class);
return $this->loader ??= $this->createMock(LoaderInterface::class);
}
protected function getXliffFileDumper(): XliffFileDumper
{
return $this->xliffFileDumper ?? $this->xliffFileDumper = $this->createMock(XliffFileDumper::class);
return $this->xliffFileDumper ??= $this->createMock(XliffFileDumper::class);
}
}

View File

@@ -11,7 +11,6 @@
namespace Symfony\Component\Translation\Test;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpClient\MockHttpClient;
@@ -31,7 +30,7 @@ abstract class ProviderTestCase extends TestCase
{
protected $client;
protected $logger;
protected $defaultLocale;
protected string $defaultLocale;
protected $loader;
protected $xliffFileDumper;
@@ -52,35 +51,26 @@ abstract class ProviderTestCase extends TestCase
protected function getClient(): MockHttpClient
{
return $this->client ?? $this->client = new MockHttpClient();
return $this->client ??= new MockHttpClient();
}
/**
* @return LoaderInterface&MockObject
*/
protected function getLoader(): LoaderInterface
{
return $this->loader ?? $this->loader = $this->createMock(LoaderInterface::class);
return $this->loader ??= $this->createMock(LoaderInterface::class);
}
/**
* @return LoaderInterface&MockObject
*/
protected function getLogger(): LoggerInterface
{
return $this->logger ?? $this->logger = $this->createMock(LoggerInterface::class);
return $this->logger ??= $this->createMock(LoggerInterface::class);
}
protected function getDefaultLocale(): string
{
return $this->defaultLocale ?? $this->defaultLocale = 'en';
return $this->defaultLocale ??= 'en';
}
/**
* @return LoaderInterface&MockObject
*/
protected function getXliffFileDumper(): XliffFileDumper
{
return $this->xliffFileDumper ?? $this->xliffFileDumper = $this->createMock(XliffFileDumper::class);
return $this->xliffFileDumper ??= $this->createMock(XliffFileDumper::class);
}
}

View File

@@ -19,9 +19,9 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
class TranslatableMessage implements TranslatableInterface
{
private $message;
private $parameters;
private $domain;
private string $message;
private array $parameters;
private ?string $domain;
public function __construct(string $message, array $parameters = [], string $domain = null)
{
@@ -52,6 +52,11 @@ class TranslatableMessage implements TranslatableInterface
public function trans(TranslatorInterface $translator, string $locale = null): string
{
return $translator->trans($this->getMessage(), $this->getParameters(), $this->getDomain(), $locale);
return $translator->trans($this->getMessage(), array_map(
static function ($parameter) use ($translator, $locale) {
return $parameter instanceof TranslatableInterface ? $parameter->trans($translator, $locale) : $parameter;
},
$this->getParameters()
), $this->getDomain(), $locale);
}
}

View File

@@ -37,54 +37,33 @@ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleA
*/
protected $catalogues = [];
/**
* @var string
*/
private $locale;
private string $locale;
/**
* @var array
* @var string[]
*/
private $fallbackLocales = [];
private array $fallbackLocales = [];
/**
* @var LoaderInterface[]
*/
private $loaders = [];
private array $loaders = [];
/**
* @var array
*/
private $resources = [];
private array $resources = [];
/**
* @var MessageFormatterInterface
*/
private $formatter;
/**
* @var string
*/
private $cacheDir;
private ?string $cacheDir;
/**
* @var bool
*/
private $debug;
private bool $debug;
private $cacheVary;
private array $cacheVary;
/**
* @var ConfigCacheFactoryInterface|null
*/
private $configCacheFactory;
/**
* @var array|null
*/
private $parentLocales;
private array $parentLocales;
private $hasIntlFormatter;
private bool $hasIntlFormatter;
/**
* @throws InvalidArgumentException If a locale contains invalid characters
@@ -127,7 +106,7 @@ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleA
*
* @throws InvalidArgumentException If the locale contains invalid characters
*/
public function addResource(string $format, $resource, string $locale, string $domain = null)
public function addResource(string $format, mixed $resource, string $locale, string $domain = null)
{
if (null === $domain) {
$domain = 'messages';
@@ -157,7 +136,7 @@ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleA
/**
* {@inheritdoc}
*/
public function getLocale()
public function getLocale(): string
{
return $this->locale ?: (class_exists(\Locale::class) ? \Locale::getDefault() : 'en');
}
@@ -165,6 +144,8 @@ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleA
/**
* Sets the fallback locales.
*
* @param string[] $locales
*
* @throws InvalidArgumentException If a locale contains invalid characters
*/
public function setFallbackLocales(array $locales)
@@ -192,7 +173,7 @@ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleA
/**
* {@inheritdoc}
*/
public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null)
public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null): string
{
if (null === $id || '' === $id) {
return '';
@@ -227,7 +208,7 @@ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleA
/**
* {@inheritdoc}
*/
public function getCatalogue(string $locale = null)
public function getCatalogue(string $locale = null): MessageCatalogueInterface
{
if (!$locale) {
$locale = $this->getLocale();
@@ -253,9 +234,9 @@ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleA
/**
* Gets the loaders.
*
* @return array LoaderInterface[]
* @return LoaderInterface[]
*/
protected function getLoaders()
protected function getLoaders(): array
{
return $this->loaders;
}
@@ -407,18 +388,10 @@ EOF
protected function computeFallbackLocales(string $locale)
{
if (null === $this->parentLocales) {
$this->parentLocales = json_decode(file_get_contents(__DIR__.'/Resources/data/parents.json'), true);
}
$this->parentLocales ??= json_decode(file_get_contents(__DIR__.'/Resources/data/parents.json'), true);
$originLocale = $locale;
$locales = [];
foreach ($this->fallbackLocales as $fallback) {
if ($fallback === $locale) {
continue;
}
$locales[] = $fallback;
}
while ($locale) {
$parent = $this->parentLocales[$locale] ?? null;
@@ -439,10 +412,18 @@ EOF
}
if (null !== $locale) {
array_unshift($locales, $locale);
$locales[] = $locale;
}
}
foreach ($this->fallbackLocales as $fallback) {
if ($fallback === $originLocale) {
continue;
}
$locales[] = $fallback;
}
return array_unique($locales);
}
@@ -453,7 +434,7 @@ EOF
*/
protected function assertValidLocale(string $locale)
{
if (!preg_match('/^[a-z0-9@_\\.\\-]*$/i', (string) $locale)) {
if (!preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) {
throw new InvalidArgumentException(sprintf('Invalid "%s" locale.', $locale));
}
}
@@ -464,9 +445,7 @@ EOF
*/
private function getConfigCacheFactory(): ConfigCacheFactoryInterface
{
if (!$this->configCacheFactory) {
$this->configCacheFactory = new ConfigCacheFactory($this->debug);
}
$this->configCacheFactory ??= new ConfigCacheFactory($this->debug);
return $this->configCacheFactory;
}

View File

@@ -17,7 +17,7 @@ use Symfony\Component\Translation\Catalogue\TargetOperation;
final class TranslatorBag implements TranslatorBagInterface
{
/** @var MessageCatalogue[] */
private $catalogues = [];
private array $catalogues = [];
public function addCatalogue(MessageCatalogue $catalogue): void
{
@@ -38,7 +38,7 @@ final class TranslatorBag implements TranslatorBagInterface
/**
* {@inheritdoc}
*/
public function getCatalogue(string $locale = null)
public function getCatalogue(string $locale = null): MessageCatalogueInterface
{
if (null === $locale || !isset($this->catalogues[$locale])) {
$this->catalogues[$locale] = new MessageCatalogue($locale);

View File

@@ -14,10 +14,6 @@ namespace Symfony\Component\Translation;
use Symfony\Component\Translation\Exception\InvalidArgumentException;
/**
* TranslatorBagInterface.
*
* @method MessageCatalogueInterface[] getCatalogues() Returns all catalogues of the instance
*
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/
interface TranslatorBagInterface
@@ -27,9 +23,14 @@ interface TranslatorBagInterface
*
* @param string|null $locale The locale or null to use the default
*
* @return MessageCatalogueInterface
*
* @throws InvalidArgumentException If the locale contains invalid characters
*/
public function getCatalogue(string $locale = null);
public function getCatalogue(string $locale = null): MessageCatalogueInterface;
/**
* Returns all catalogues of the instance.
*
* @return MessageCatalogueInterface[]
*/
public function getCatalogues(): array;
}

View File

@@ -30,10 +30,8 @@ class ArrayConverter
* For example this array('foo.bar' => 'value') will be converted to ['foo' => ['bar' => 'value']].
*
* @param array $messages Linear messages array
*
* @return array Tree-like messages array
*/
public static function expandToTree(array $messages)
public static function expandToTree(array $messages): array
{
$tree = [];

View File

@@ -85,11 +85,6 @@ class XliffUtils
private static function shouldEnableEntityLoader(): bool
{
// Version prior to 8.0 can be enabled without deprecation
if (\PHP_VERSION_ID < 80000) {
return true;
}
static $dom, $schema;
if (null === $dom) {
$dom = new \DOMDocument();

View File

@@ -23,7 +23,10 @@ use Symfony\Component\Translation\MessageCatalogue;
*/
class TranslationWriter implements TranslationWriterInterface
{
private $dumpers = [];
/**
* @var array<string, DumperInterface>
*/
private array $dumpers = [];
/**
* Adds a dumper to the writer.
@@ -35,10 +38,8 @@ class TranslationWriter implements TranslationWriterInterface
/**
* Obtains the list of supported formats.
*
* @return array
*/
public function getFormats()
public function getFormats(): array
{
return array_keys($this->dumpers);
}

View File

@@ -16,33 +16,33 @@
}
],
"require": {
"php": ">=7.2.5",
"symfony/deprecation-contracts": "^2.1",
"php": ">=8.0.2",
"symfony/polyfill-mbstring": "~1.0",
"symfony/polyfill-php80": "^1.16",
"symfony/translation-contracts": "^2.3"
"symfony/translation-contracts": "^2.3|^3.0"
},
"require-dev": {
"symfony/config": "^4.4|^5.0",
"symfony/console": "^4.4|^5.0",
"symfony/dependency-injection": "^5.0",
"symfony/http-kernel": "^5.0",
"symfony/intl": "^4.4|^5.0",
"symfony/config": "^5.4|^6.0",
"symfony/console": "^5.4|^6.0",
"symfony/dependency-injection": "^5.4|^6.0",
"symfony/http-client-contracts": "^1.1|^2.0|^3.0",
"symfony/http-kernel": "^5.4|^6.0",
"symfony/intl": "^5.4|^6.0",
"symfony/polyfill-intl-icu": "^1.21",
"symfony/service-contracts": "^1.1.2|^2",
"symfony/yaml": "^4.4|^5.0",
"symfony/finder": "^4.4|^5.0",
"symfony/service-contracts": "^1.1.2|^2|^3",
"symfony/yaml": "^5.4|^6.0",
"symfony/finder": "^5.4|^6.0",
"psr/log": "^1|^2|^3"
},
"conflict": {
"symfony/config": "<4.4",
"symfony/dependency-injection": "<5.0",
"symfony/http-kernel": "<5.0",
"symfony/twig-bundle": "<5.0",
"symfony/yaml": "<4.4"
"symfony/config": "<5.4",
"symfony/dependency-injection": "<5.4",
"symfony/http-kernel": "<5.4",
"symfony/twig-bundle": "<5.4",
"symfony/yaml": "<5.4",
"symfony/console": "<5.4"
},
"provide": {
"symfony/translation-implementation": "2.3"
"symfony/translation-implementation": "2.3|3.0"
},
"suggest": {
"symfony/config": "",

View File

@@ -1,6 +1,13 @@
CHANGELOG
=========
5.4
---
* Add ability to style integer and double values independently
* Add casters for Symfony's UUIDs and ULIDs
* Add support for `Fiber`
5.2.0
-----

View File

@@ -20,7 +20,7 @@ use Symfony\Component\VarDumper\Cloner\Stub;
*/
class ArgsStub extends EnumStub
{
private static $parameters = [];
private static array $parameters = [];
public function __construct(array $args, string $function, ?string $class)
{

View File

@@ -41,8 +41,6 @@ class Caster
* Casts objects to arrays and adds the dynamic property prefix.
*
* @param bool $hasDebugInfo Whether the __debugInfo method exists on $obj or not
*
* @return array The array-cast of the object, with prefixed dynamic properties
*/
public static function castObject(object $obj, string $class, bool $hasDebugInfo = false, string $debugClass = null): array
{
@@ -118,8 +116,6 @@ class Caster
* @param int $filter A bit field of Caster::EXCLUDE_* constants specifying which properties to filter out
* @param string[] $listedProperties List of properties to exclude when Caster::EXCLUDE_VERBOSE is set, and to preserve when Caster::EXCLUDE_NOT_IMPORTANT is set
* @param int &$count Set to the number of removed properties
*
* @return array The filtered array
*/
public static function filter(array $a, int $filter, array $listedProperties = [], ?int &$count = 0): array
{

View File

@@ -24,7 +24,7 @@ class ClassStub extends ConstStub
* @param string $identifier A PHP identifier, e.g. a class, method, interface, etc. name
* @param callable $callable The callable targeted by the identifier when it is ambiguous or not a real PHP identifier
*/
public function __construct(string $identifier, $callable = null)
public function __construct(string $identifier, callable|array|string $callable = null)
{
$this->value = $identifier;
@@ -87,7 +87,7 @@ class ClassStub extends ConstStub
}
}
public static function wrapCallable($callable)
public static function wrapCallable(mixed $callable)
{
if (\is_object($callable) || !\is_callable($callable)) {
return $callable;

View File

@@ -20,16 +20,13 @@ use Symfony\Component\VarDumper\Cloner\Stub;
*/
class ConstStub extends Stub
{
public function __construct(string $name, $value = null)
public function __construct(string $name, string|int|float $value = null)
{
$this->class = $name;
$this->value = 1 < \func_num_args() ? $value : $name;
}
/**
* @return string
*/
public function __toString()
public function __toString(): string
{
return (string) $this->value;
}

View File

@@ -20,7 +20,7 @@ use Symfony\Component\VarDumper\Cloner\Stub;
*/
class CutStub extends Stub
{
public function __construct($value)
public function __construct(mixed $value)
{
$this->value = $value;

View File

@@ -49,7 +49,7 @@ class DateCaster
public static function castInterval(\DateInterval $interval, array $a, Stub $stub, bool $isNested, int $filter)
{
$now = new \DateTimeImmutable();
$now = new \DateTimeImmutable('@0', new \DateTimeZone('UTC'));
$numberOfSeconds = $now->add($interval)->getTimestamp() - $now->getTimestamp();
$title = number_format($numberOfSeconds, 0, '.', ' ').'s';
@@ -63,7 +63,8 @@ class DateCaster
$format = '%R ';
if (0 === $i->y && 0 === $i->m && ($i->h >= 24 || $i->i >= 60 || $i->s >= 60)) {
$i = date_diff($d = new \DateTime(), date_add(clone $d, $i)); // recalculate carry over points
$d = new \DateTimeImmutable('@0', new \DateTimeZone('UTC'));
$i = $d->diff($d->add($i)); // recalculate carry over points
$format .= 0 < $i->days ? '%ad ' : '';
} else {
$format .= ($i->y ? '%yy ' : '').($i->m ? '%mm ' : '').($i->d ? '%dd ' : '');

View File

@@ -18,7 +18,7 @@ use Symfony\Component\VarDumper\Cloner\Stub;
*/
class DsPairStub extends Stub
{
public function __construct($key, $value)
public function __construct(string|int $key, mixed $value)
{
$this->value = [
Caster::PREFIX_VIRTUAL.'key' => $key,

View File

@@ -24,9 +24,9 @@ use Symfony\Component\VarDumper\Exception\ThrowingCasterException;
*/
class ExceptionCaster
{
public static $srcContext = 1;
public static $traceArgs = true;
public static $errorTypes = [
public static int $srcContext = 1;
public static bool $traceArgs = true;
public static array $errorTypes = [
\E_DEPRECATED => 'E_DEPRECATED',
\E_USER_DEPRECATED => 'E_USER_DEPRECATED',
\E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
@@ -44,7 +44,7 @@ class ExceptionCaster
\E_STRICT => 'E_STRICT',
];
private static $framesCache = [];
private static array $framesCache = [];
public static function castError(\Error $e, array $a, Stub $stub, bool $isNested, int $filter = 0)
{
@@ -214,18 +214,24 @@ class ExceptionCaster
if (is_file($f['file']) && 0 <= self::$srcContext) {
if (!empty($f['class']) && (is_subclass_of($f['class'], 'Twig\Template') || is_subclass_of($f['class'], 'Twig_Template')) && method_exists($f['class'], 'getDebugInfo')) {
$template = $f['object'] ?? unserialize(sprintf('O:%d:"%s":0:{}', \strlen($f['class']), $f['class']));
$ellipsis = 0;
$templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : '');
$templateInfo = $template->getDebugInfo();
if (isset($templateInfo[$f['line']])) {
if (!method_exists($template, 'getSourceContext') || !is_file($templatePath = $template->getSourceContext()->getPath())) {
$templatePath = null;
}
if ($templateSrc) {
$src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, 'twig', $templatePath, $f);
$srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']];
$template = null;
if (isset($f['object'])) {
$template = $f['object'];
} elseif ((new \ReflectionClass($f['class']))->isInstantiable()) {
$template = unserialize(sprintf('O:%d:"%s":0:{}', \strlen($f['class']), $f['class']));
}
if (null !== $template) {
$ellipsis = 0;
$templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : '');
$templateInfo = $template->getDebugInfo();
if (isset($templateInfo[$f['line']])) {
if (!method_exists($template, 'getSourceContext') || !is_file($templatePath = $template->getSourceContext()->getPath())) {
$templatePath = null;
}
if ($templateSrc) {
$src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, 'twig', $templatePath, $f);
$srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']];
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More