Merge branch '5.4' into 6.0

* 5.4:
  [HttpClient] Remove deprecated usage of `GuzzleHttp\Promise\queue`
  [PropertyAccess] Fix handling of uninitialized property of anonymous class
  [FrameworkBundle] Allow default cache pools to be overwritten by user
  [DependencyInjection] fix test
  ResolveBindingsPass remove loading of class iterable
  [FrameworkBundle] Avoid calling rtrim(null, '/') in AssetsInstallCommand
  Optimization of resolveEnvPlaceholders
  Fix incorrect format
  [DependencyInjection] Fix nested env var with resolve processor
  Allow OutputFormatter::escape() to be used for escaping URLs used in <href>
  allow a zero time-limit
  Use correct tag for ExpoTransportFactory service
  [DependencyInjection] Ignore argument type check in CheckTypeDeclarationsPass if it's a Definition with a factory
  [Validators] Add translations for Slovak #43735
This commit is contained in:
Nicolas Grekas 2022-01-12 19:59:56 +01:00
commit 97d36c6b62
26 changed files with 284 additions and 73 deletions

View File

@ -94,8 +94,7 @@ EOT
{
/** @var KernelInterface $kernel */
$kernel = $this->getApplication()->getKernel();
$targetArg = rtrim($input->getArgument('target'), '/');
$targetArg = rtrim($input->getArgument('target') ?? '', '/');
if (!$targetArg) {
$targetArg = $this->getPublicDirectory($kernel->getContainer());
}

View File

@ -330,34 +330,10 @@ class FrameworkExtension extends Extension
}
}
// register cache before session so both can share the connection services
$this->registerCacheConfiguration($config['cache'], $container);
if ($this->isConfigEnabled($container, $config['session'])) {
if (!\extension_loaded('session')) {
throw new LogicException('Session support cannot be enabled as the session extension is not installed. See https://php.net/session.installation for instructions.');
}
$this->sessionConfigEnabled = true;
$this->registerSessionConfiguration($config['session'], $container, $loader);
if (!empty($config['test'])) {
// test listener will replace the existing session listener
// as we are aliasing to avoid duplicated registered events
$container->setAlias('session_listener', 'test.session.listener');
}
} elseif (!empty($config['test'])) {
$container->removeDefinition('test.session.listener');
}
if ($this->isConfigEnabled($container, $config['request'])) {
$this->registerRequestConfiguration($config['request'], $container, $loader);
}
if (null === $config['csrf_protection']['enabled']) {
$config['csrf_protection']['enabled'] = $this->sessionConfigEnabled && !class_exists(FullStack::class) && ContainerBuilder::willBeAvailable('symfony/security-csrf', CsrfTokenManagerInterface::class, ['symfony/framework-bundle']);
}
$this->registerSecurityCsrfConfiguration($config['csrf_protection'], $container, $loader);
if ($this->isConfigEnabled($container, $config['form'])) {
if (!class_exists(Form::class)) {
throw new LogicException('Form support cannot be enabled as the Form component is not installed. Try running "composer require symfony/form".');
@ -488,6 +464,31 @@ class FrameworkExtension extends Extension
$this->registerUidConfiguration($config['uid'], $container, $loader);
}
// register cache before session so both can share the connection services
$this->registerCacheConfiguration($config['cache'], $container);
if ($this->isConfigEnabled($container, $config['session'])) {
if (!\extension_loaded('session')) {
throw new LogicException('Session support cannot be enabled as the session extension is not installed. See https://php.net/session.installation for instructions.');
}
$this->sessionConfigEnabled = true;
$this->registerSessionConfiguration($config['session'], $container, $loader);
if (!empty($config['test'])) {
// test listener will replace the existing session listener
// as we are aliasing to avoid duplicated registered events
$container->setAlias('session_listener', 'test.session.listener');
}
} elseif (!empty($config['test'])) {
$container->removeDefinition('test.session.listener');
}
// csrf depends on session being registered
if (null === $config['csrf_protection']['enabled']) {
$config['csrf_protection']['enabled'] = $this->sessionConfigEnabled && !class_exists(FullStack::class) && ContainerBuilder::willBeAvailable('symfony/security-csrf', CsrfTokenManagerInterface::class, ['symfony/framework-bundle']);
}
$this->registerSecurityCsrfConfiguration($config['csrf_protection'], $container, $loader);
$this->addAnnotatedClassesToCompile([
'**\\Controller\\',
'**\\Entity\\',

View File

@ -240,6 +240,6 @@ return static function (ContainerConfigurator $container) {
->set('notifier.transport_factory.expo', ExpoTransportFactory::class)
->parent('notifier.transport_factory.abstract')
->tag('chatter.transport_factory')
->tag('texter.transport_factory')
;
};

View File

@ -34,11 +34,11 @@ class OutputFormatter implements WrappableOutputFormatterInterface
}
/**
* Escapes "<" special char in given text.
* Escapes "<" and ">" special chars in given text.
*/
public static function escape(string $text): string
{
$text = preg_replace('/([^\\\\]?)</', '$1\\<', $text);
$text = preg_replace('/([^\\\\]|^)([<>])/', '$1\\\\$2', $text);
return self::escapeTrailingBackslash($text);
}
@ -140,9 +140,10 @@ class OutputFormatter implements WrappableOutputFormatterInterface
{
$offset = 0;
$output = '';
$tagRegex = '[a-z][^<>]*+';
$openTagRegex = '[a-z](?:[^\\\\<>]*+ | \\\\.)*';
$closeTagRegex = '[a-z][^<>]*+';
$currentLineLength = 0;
preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, \PREG_OFFSET_CAPTURE);
preg_match_all("#<(($openTagRegex) | /($closeTagRegex)?)>#ix", $message, $matches, \PREG_OFFSET_CAPTURE);
foreach ($matches[0] as $i => $match) {
$pos = $match[1];
$text = $match[0];
@ -176,11 +177,7 @@ class OutputFormatter implements WrappableOutputFormatterInterface
$output .= $this->applyCurrentStyle(substr($message, $offset), $output, $width, $currentLineLength);
if (str_contains($output, "\0")) {
return strtr($output, ["\0" => '\\', '\\<' => '<']);
}
return str_replace('\\<', '<', $output);
return strtr($output, ["\0" => '\\', '\\<' => '<', '\\>' => '>']);
}
public function getStyleStack(): OutputFormatterStyleStack
@ -211,7 +208,8 @@ class OutputFormatter implements WrappableOutputFormatterInterface
} elseif ('bg' == $match[0]) {
$style->setBackground(strtolower($match[1]));
} elseif ('href' === $match[0]) {
$style->setHref($match[1]);
$url = preg_replace('{\\\\([<>])}', '$1', $match[1]);
$style->setHref($url);
} elseif ('options' === $match[0]) {
preg_match_all('([^,;]+)', strtolower($match[1]), $options);
$options = array_shift($options);

View File

@ -2,9 +2,9 @@
command 2 description
<comment>Usage:</comment>
descriptor:command2 [options] [--] \<argument_name>
descriptor:command2 -o|--option_name \<argument_name>
descriptor:command2 \<argument_name>
descriptor:command2 [options] [--] \<argument_name\>
descriptor:command2 -o|--option_name \<argument_name\>
descriptor:command2 \<argument_name\>
<comment>Arguments:</comment>
<info>argument_name</info>

View File

@ -2,9 +2,9 @@
command åèä description
<comment>Usage:</comment>
descriptor:åèä [options] [--] \<argument_åèä>
descriptor:åèä -o|--option_name \<argument_name>
descriptor:åèä \<argument_name>
descriptor:åèä [options] [--] \<argument_åèä\>
descriptor:åèä -o|--option_name \<argument_name\>
descriptor:åèä \<argument_name\>
<comment>Arguments:</comment>
<info>argument_åèä</info>

View File

@ -1 +1 @@
<info>argument_name</info> argument description<comment> [default: "\<comment>style\</>"]</comment>
<info>argument_name</info> argument description<comment> [default: "\<comment\>style\</\>"]</comment>

View File

@ -1 +1 @@
<info>-o, --option_name=OPTION_NAME</info> option description<comment> [default: "\<comment>style\</>"]</comment>
<info>-o, --option_name=OPTION_NAME</info> option description<comment> [default: "\<comment\>style\</\>"]</comment>

View File

@ -1 +1 @@
<info>-o, --option_name=OPTION_NAME</info> option description<comment> [default: ["\<comment>Hello\</comment>","\<info>world\</info>"]]</comment><comment> (multiple values allowed)</comment>
<info>-o, --option_name=OPTION_NAME</info> option description<comment> [default: ["\<comment\>Hello\</comment\>","\<info\>world\</info\>"]]</comment><comment> (multiple values allowed)</comment>

View File

@ -32,7 +32,10 @@ class OutputFormatterTest extends TestCase
$this->assertEquals('foo << bar \\', $formatter->format('foo << bar \\'));
$this->assertEquals("foo << \033[32mbar \\ baz\033[39m \\", $formatter->format('foo << <info>bar \\ baz</info> \\'));
$this->assertEquals('<info>some info</info>', $formatter->format('\\<info>some info\\</info>'));
$this->assertEquals('\\<info>some info\\</info>', OutputFormatter::escape('<info>some info</info>'));
$this->assertEquals('\\<info\\>some info\\</info\\>', OutputFormatter::escape('<info>some info</info>'));
// every < and > gets escaped if not already escaped, but already escaped ones do not get escaped again
// and escaped backslashes remain as such, same with backslashes escaping non-special characters
$this->assertEquals('foo \\< bar \\< baz \\\\< foo \\> bar \\> baz \\\\> \\x', OutputFormatter::escape('foo < bar \\< baz \\\\< foo > bar \\> baz \\\\> \\x'));
$this->assertEquals(
"\033[33mSymfony\\Component\\Console does work very well!\033[39m",
@ -264,6 +267,7 @@ class OutputFormatterTest extends TestCase
['<question>some question</question>', 'some question', "\033[30;46msome question\033[39;49m"],
['<fg=red>some text with inline style</>', 'some text with inline style', "\033[31msome text with inline style\033[39m"],
['<href=idea://open/?file=/path/SomeFile.php&line=12>some URL</>', 'some URL', "\033]8;;idea://open/?file=/path/SomeFile.php&line=12\033\\some URL\033]8;;\033\\"],
['<href=https://example.com/\<woohoo\>>some URL with \<woohoo\></>', 'some URL with <woohoo>', "\033]8;;https://example.com/<woohoo>\033\\some URL with <woohoo>\033]8;;\033\\"],
['<href=idea://open/?file=/path/SomeFile.php&line=12>some URL</>', 'some URL', 'some URL', 'JetBrains-JediTerm'],
];
}

View File

@ -83,9 +83,9 @@ class FormatterHelperTest extends TestCase
$formatter = new FormatterHelper();
$this->assertEquals(
'<error> </error>'."\n".
'<error> \<info>some info\</info> </error>'."\n".
'<error> </error>',
'<error> </error>'."\n".
'<error> \<info\>some info\</info\> </error>'."\n".
'<error> </error>',
$formatter->formatBlock('<info>some info</info>', 'error', true),
'::formatBlock() escapes \'<\' chars'
);

View File

@ -210,6 +210,10 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass
$class = null;
if ($value instanceof Definition) {
if ($value->getFactory()) {
return;
}
$class = $value->getClass();
if ($class && isset(self::BUILTIN_TYPES[strtolower($class)])) {

View File

@ -126,7 +126,7 @@ class ResolveBindingsPass extends AbstractRecursivePass
$this->unusedBindings[$bindingId] = [$key, $this->currentId, $bindingType, $file];
}
if (preg_match('/^(?:(?:array|bool|float|int|string|([^ $]++)) )\$/', $key, $m)) {
if (preg_match('/^(?:(?:array|bool|float|int|string|iterable|([^ $]++)) )\$/', $key, $m)) {
$bindingNames[substr($key, \strlen($m[0]))] = $binding;
}

View File

@ -1341,7 +1341,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
return $result;
}
if (!\is_string($value) || 38 > \strlen($value)) {
if (!\is_string($value) || 38 > \strlen($value) || !preg_match('/env[_(]/i', $value)) {
return $value;
}
$envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders;

View File

@ -272,11 +272,17 @@ class EnvVarProcessor implements EnvVarProcessorInterface
}
if ('resolve' === $prefix) {
return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($name) {
return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($name, $getEnv) {
if (!isset($match[1])) {
return '%';
}
$value = $this->container->getParameter($match[1]);
if (str_starts_with($match[1], 'env(') && str_ends_with($match[1], ')') && 'env()' !== $match[1]) {
$value = $getEnv(substr($match[1], 4, -1));
} else {
$value = $this->container->getParameter($match[1]);
}
if (!is_scalar($value)) {
throw new RuntimeException(sprintf('Parameter "%s" found when resolving env var "%s" must be scalar, "%s" given.', $match[1], $name, get_debug_type($value)));
}

View File

@ -972,6 +972,20 @@ class CheckTypeDeclarationsPassTest extends TestCase
$this->addToAssertionCount(1);
}
public function testIgnoreDefinitionFactoryArgument()
{
$container = new ContainerBuilder();
$container->register('bar', Bar::class)
->setArguments([
(new Definition(Foo::class))
->setFactory([Foo::class, 'createStdClass']),
]);
(new CheckTypeDeclarationsPass())->process($container);
$this->addToAssertionCount(1);
}
}
class CallableClass

View File

@ -28,6 +28,7 @@ use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum;
use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy;
use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedEnumArgumentDummy;
use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedIterableArgumentDummy;
use Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists;
use Symfony\Component\DependencyInjection\Tests\Fixtures\WithTarget;
use Symfony\Component\DependencyInjection\TypedReference;
@ -212,6 +213,28 @@ class ResolveBindingsPassTest extends TestCase
$pass->process($container);
}
public function testIterableBindingTypehint()
{
$autoloader = static function ($class) {
if ('iterable' === $class) {
throw new \RuntimeException('We should not search pseudo-type iterable as class');
}
};
spl_autoload_register($autoloader);
$container = new ContainerBuilder();
$definition = $container->register('bar', NamedIterableArgumentDummy::class);
$definition->setBindings([
'iterable $items' => new TaggedIteratorArgument('foo'),
]);
$pass = new ResolveBindingsPass();
$pass->process($container);
$this->assertInstanceOf(TaggedIteratorArgument::class, $container->getDefinition('bar')->getArgument(0));
spl_autoload_unregister($autoloader);
}
public function testBindWithTarget()
{
$container = new ContainerBuilder();

View File

@ -506,6 +506,109 @@ class EnvVarProcessorTest extends TestCase
$this->assertEquals('foo', $result);
}
/**
* @dataProvider validResolve
*/
public function testGetEnvResolve($value, $processed)
{
$container = new ContainerBuilder();
$container->setParameter('bar', $value);
$container->compile();
$processor = new EnvVarProcessor($container);
$result = $processor->getEnv('resolve', 'foo', function () {
return '%bar%';
});
$this->assertSame($processed, $result);
}
public function validResolve()
{
return [
['string', 'string'],
[1, '1'],
[1.1, '1.1'],
[true, '1'],
[false, ''],
];
}
public function testGetEnvResolveNoMatch()
{
$processor = new EnvVarProcessor(new Container());
$result = $processor->getEnv('resolve', 'foo', function () {
return '%%';
});
$this->assertSame('%', $result);
}
/**
* @dataProvider notScalarResolve
*/
public function testGetEnvResolveNotScalar($value)
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('Parameter "bar" found when resolving env var "foo" must be scalar');
$container = new ContainerBuilder();
$container->setParameter('bar', $value);
$container->compile();
$processor = new EnvVarProcessor($container);
$processor->getEnv('resolve', 'foo', function () {
return '%bar%';
});
}
public function notScalarResolve()
{
return [
[null],
[[]],
];
}
public function testGetEnvResolveNestedEnv()
{
$container = new ContainerBuilder();
$container->setParameter('env(BAR)', 'BAR in container');
$container->compile();
$processor = new EnvVarProcessor($container);
$getEnv = \Closure::fromCallable([$processor, 'getEnv']);
$result = $processor->getEnv('resolve', 'foo', function ($name) use ($getEnv) {
return 'foo' === $name ? '%env(BAR)%' : $getEnv('string', $name, function () {});
});
$this->assertSame('BAR in container', $result);
}
public function testGetEnvResolveNestedRealEnv()
{
$_ENV['BAR'] = 'BAR in environment';
$container = new ContainerBuilder();
$container->setParameter('env(BAR)', 'BAR in container');
$container->compile();
$processor = new EnvVarProcessor($container);
$getEnv = \Closure::fromCallable([$processor, 'getEnv']);
$result = $processor->getEnv('resolve', 'foo', function ($name) use ($getEnv) {
return 'foo' === $name ? '%env(BAR)%' : $getEnv('string', $name, function () {});
});
$this->assertSame('BAR in environment', $result);
unset($_ENV['BAR']);
}
/**
* @dataProvider validCsv
*/

View File

@ -23,4 +23,9 @@ class Foo
{
return [];
}
public static function createStdClass(): \stdClass
{
return new \stdClass();
}
}

View File

@ -0,0 +1,19 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
class NamedIterableArgumentDummy
{
public function __construct(iterable $items)
{
}
}

View File

@ -13,6 +13,7 @@ namespace Symfony\Component\HttpClient;
use GuzzleHttp\Promise\Promise as GuzzlePromise;
use GuzzleHttp\Promise\RejectedPromise;
use GuzzleHttp\Promise\Utils;
use Http\Client\Exception\NetworkException;
use Http\Client\Exception\RequestException;
use Http\Client\HttpAsyncClient;
@ -73,7 +74,7 @@ final class HttplugClient implements HttplugInterface, HttpAsyncClient, RequestF
{
$this->client = $client ?? HttpClient::create();
$streamFactory ??= $responseFactory instanceof StreamFactoryInterface ? $responseFactory : null;
$this->promisePool = \function_exists('GuzzleHttp\Promise\queue') ? new \SplObjectStorage() : null;
$this->promisePool = class_exists(Utils::class) ? new \SplObjectStorage() : null;
if (null === $responseFactory || null === $streamFactory) {
if (!class_exists(Psr17Factory::class) && !class_exists(Psr17FactoryDiscovery::class)) {

View File

@ -52,7 +52,7 @@ final class HttplugWaitLoop
return 0;
}
$guzzleQueue = \GuzzleHttp\Promise\queue();
$guzzleQueue = \GuzzleHttp\Promise\Utils::queue();
if (0.0 === $remainingDuration = $maxDuration) {
$idleTimeout = 0.0;

View File

@ -189,7 +189,7 @@ EOF
$this->eventDispatcher->addSubscriber(new StopWorkerOnMemoryLimitListener($this->convertToBytes($memoryLimit), $this->logger));
}
if ($timeLimit = $input->getOption('time-limit')) {
if (null !== ($timeLimit = $input->getOption('time-limit'))) {
$stopsWhen[] = "been running for {$timeLimit}s";
$this->eventDispatcher->addSubscriber(new StopWorkerOnTimeLimitListener($timeLimit, $this->logger));
}

View File

@ -427,11 +427,11 @@ class PropertyAccessor implements PropertyAccessorInterface
}
} catch (\Error $e) {
// handle uninitialized properties in PHP >= 7.4
if (preg_match('/^Typed property ([\w\\\]+)::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) {
$r = new \ReflectionProperty($matches[1], $matches[2]);
if (preg_match('/^Typed property ('.preg_quote(get_debug_type($object), '/').')::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) {
$r = new \ReflectionProperty($class, $matches[2]);
$type = ($type = $r->getType()) instanceof \ReflectionNamedType ? $type->getName() : (string) $type;
throw new UninitializedPropertyException(sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $r->getDeclaringClass()->getName(), $r->getName(), $type), 0, $e);
throw new UninitializedPropertyException(sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $matches[1], $r->getName(), $type), 0, $e);
}
throw $e;

View File

@ -13,7 +13,6 @@ namespace Symfony\Component\PropertyAccess\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\PropertyAccess\Exception\AccessException;
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
@ -144,44 +143,73 @@ class PropertyAccessorTest extends TestCase
public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousClass()
{
$this->expectException(AccessException::class);
$this->expectException(UninitializedPropertyException::class);
$this->expectExceptionMessage('The method "class@anonymous::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?');
$object = eval('return new class() {
$object = new class() {
private $uninitialized;
public function getUninitialized(): array
{
return $this->uninitialized;
}
};');
};
$this->propertyAccessor->getValue($object, 'uninitialized');
}
public function testGetValueThrowsExceptionIfUninitializedNotNullablePropertyWithGetterOfAnonymousClass()
{
$this->expectException(UninitializedPropertyException::class);
$this->expectExceptionMessage('The property "class@anonymous::$uninitialized" is not readable because it is typed "string". You should initialize it or declare a default value instead.');
$object = new class() {
private string $uninitialized;
public function getUninitialized(): string
{
return $this->uninitialized;
}
};
$this->propertyAccessor->getValue($object, 'uninitialized');
}
public function testGetValueThrowsExceptionIfUninitializedPropertyOfAnonymousClass()
{
$this->expectException(UninitializedPropertyException::class);
$this->expectExceptionMessage('The property "class@anonymous::$uninitialized" is not readable because it is typed "string". You should initialize it or declare a default value instead.');
$object = new class() {
public string $uninitialized;
};
$this->propertyAccessor->getValue($object, 'uninitialized');
}
public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousStdClass()
{
$this->expectException(AccessException::class);
$this->expectException(UninitializedPropertyException::class);
$this->expectExceptionMessage('The method "stdClass@anonymous::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?');
$object = eval('return new class() extends \stdClass {
$object = new class() extends \stdClass {
private $uninitialized;
public function getUninitialized(): array
{
return $this->uninitialized;
}
};');
};
$this->propertyAccessor->getValue($object, 'uninitialized');
}
public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousChildClass()
{
$this->expectException(AccessException::class);
$this->expectException(UninitializedPropertyException::class);
$this->expectExceptionMessage('The method "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty@anonymous::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?');
$object = eval('return new class() extends \Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty {};');
$object = new class() extends \Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty {};
$this->propertyAccessor->getValue($object, 'uninitialized');
}
@ -684,7 +712,7 @@ class PropertyAccessorTest extends TestCase
private function generateAnonymousClass($value)
{
$obj = eval('return new class($value)
return new class($value)
{
private $foo;
@ -708,9 +736,7 @@ class PropertyAccessorTest extends TestCase
{
$this->foo = $foo;
}
};');
return $obj;
};
}
public function testThrowTypeErrorInsideSetterCall()

View File

@ -394,6 +394,14 @@
<source>This value is not a valid CSS color.</source>
<target>Táto hodnota nie je platná CSS farba.</target>
</trans-unit>
<trans-unit id="102">
<source>This value is not a valid CIDR notation.</source>
<target>Táto hodnota nie je platnou notáciou CIDR.</target>
</trans-unit>
<trans-unit id="103">
<source>The value of the netmask should be between {{ min }} and {{ max }}.</source>
<target>Hodnota masky siete by mala byť medzi {{ min }} a {{ max }}.</target>
</trans-unit>
</body>
</file>
</xliff>