Merge branch '5.2' into 5.x
* 5.2: [HttpClient][PHPDoc] Fix 2 remaining return mixed [Cache] [FrameworkBundle] Fix logging for TagAwareAdapter [Route] Better inline requirements and defaults parsing Simplified condition and removed unused code from AbstractSessionListener::onKernelRequest [PhpUnitBridge] Fix phpunit symlink on Windows [Yaml] Fixed infinite loop when parser goes through an additional and invalid closing tag [Form] Fix 'invalid_message' use in multiple ChoiceType
This commit is contained in:
commit
364742f18c
@ -148,8 +148,8 @@ if ('disabled' === $getEnvVar('SYMFONY_DEPRECATIONS_HELPER')) {
|
||||
}
|
||||
|
||||
$COMPOSER = file_exists($COMPOSER = $oldPwd.'/composer.phar')
|
||||
|| ($COMPOSER = rtrim('\\' === \DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer.phar`) : `which composer.phar 2> /dev/null`))
|
||||
|| ($COMPOSER = rtrim('\\' === \DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer`) : `which composer 2> /dev/null`))
|
||||
|| ($COMPOSER = rtrim('\\' === \DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer.phar 2> NUL`) : `which composer.phar 2> /dev/null`))
|
||||
|| ($COMPOSER = rtrim('\\' === \DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer 2> NUL`) : `which composer 2> /dev/null`))
|
||||
|| file_exists($COMPOSER = rtrim('\\' === \DIRECTORY_SEPARATOR ? `git rev-parse --show-toplevel 2> NUL` : `git rev-parse --show-toplevel 2> /dev/null`).\DIRECTORY_SEPARATOR.'composer.phar')
|
||||
? ('#!/usr/bin/env php' === file_get_contents($COMPOSER, false, null, 0, 18) ? $PHP : '').' '.escapeshellarg($COMPOSER) // detect shell wrappers by looking at the shebang
|
||||
: 'composer';
|
||||
@ -176,9 +176,9 @@ if (!file_exists("$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit") || $configurationH
|
||||
@mkdir($PHPUNIT_DIR, 0777, true);
|
||||
chdir($PHPUNIT_DIR);
|
||||
if (file_exists("$PHPUNIT_VERSION_DIR")) {
|
||||
passthru(sprintf('\\' === \DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s > NUL' : 'rm -rf %s', "$PHPUNIT_VERSION_DIR.old"));
|
||||
passthru(sprintf('\\' === \DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s 2> NUL' : 'rm -rf %s', escapeshellarg("$PHPUNIT_VERSION_DIR.old")));
|
||||
rename("$PHPUNIT_VERSION_DIR", "$PHPUNIT_VERSION_DIR.old");
|
||||
passthru(sprintf('\\' === \DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s' : 'rm -rf %s', "$PHPUNIT_VERSION_DIR.old"));
|
||||
passthru(sprintf('\\' === \DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s' : 'rm -rf %s', escapeshellarg("$PHPUNIT_VERSION_DIR.old")));
|
||||
}
|
||||
|
||||
$info = [];
|
||||
@ -307,10 +307,15 @@ EOPHP
|
||||
// This is useful for static analytics tools such as PHPStan having to load PHPUnit's classes
|
||||
// and for other testing libraries such as Behat using PHPUnit's assertions.
|
||||
chdir($PHPUNIT_DIR);
|
||||
if ('\\' === \DIRECTORY_SEPARATOR) {
|
||||
passthru('rmdir /S /Q phpunit 2> NUL');
|
||||
passthru(sprintf('mklink /j phpunit %s > NUL 2>&1', escapeshellarg($PHPUNIT_VERSION_DIR)));
|
||||
} else {
|
||||
if (file_exists('phpunit')) {
|
||||
@unlink('phpunit');
|
||||
}
|
||||
@symlink($PHPUNIT_VERSION_DIR, 'phpunit');
|
||||
}
|
||||
chdir($oldPwd);
|
||||
|
||||
if ($PHPUNIT_VERSION < 8.0) {
|
||||
|
@ -2109,6 +2109,12 @@ class FrameworkExtension extends Extension
|
||||
->setPublic($pool['public'])
|
||||
;
|
||||
|
||||
if (method_exists(TagAwareAdapter::class, 'setLogger')) {
|
||||
$container
|
||||
->getDefinition($name)
|
||||
->addMethodCall('setLogger', [new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)]);
|
||||
}
|
||||
|
||||
$pool['name'] = $tagAwareId = $name;
|
||||
$pool['public'] = false;
|
||||
$name = '.'.$name.'.inner';
|
||||
|
@ -35,6 +35,11 @@ $container->loadFromExtension('framework', [
|
||||
'redis://foo' => 'cache.adapter.redis',
|
||||
],
|
||||
],
|
||||
'cache.ccc' => [
|
||||
'adapter' => 'cache.adapter.array',
|
||||
'default_lifetime' => 410,
|
||||
'tags' => true,
|
||||
],
|
||||
'cache.redis_tag_aware.foo' => [
|
||||
'adapter' => 'cache.adapter.redis_tag_aware',
|
||||
],
|
||||
|
@ -18,6 +18,7 @@
|
||||
<framework:adapter name="cache.adapter.filesystem" />
|
||||
<framework:adapter name="cache.adapter.redis" provider="redis://foo" />
|
||||
</framework:pool>
|
||||
<framework:pool name="cache.ccc" adapter="cache.adapter.array" default-lifetime="410" tags="true" />
|
||||
<framework:pool name="cache.redis_tag_aware.foo" adapter="cache.adapter.redis_tag_aware" />
|
||||
<framework:pool name="cache.redis_tag_aware.foo2" tags="true" adapter="cache.adapter.redis_tag_aware" />
|
||||
<framework:pool name="cache.redis_tag_aware.bar" adapter="cache.redis_tag_aware.foo" />
|
||||
|
@ -25,6 +25,10 @@ framework:
|
||||
- cache.adapter.array
|
||||
- cache.adapter.filesystem
|
||||
- {name: cache.adapter.redis, provider: 'redis://foo'}
|
||||
cache.ccc:
|
||||
adapter: cache.adapter.array
|
||||
default_lifetime: 410
|
||||
tags: true
|
||||
cache.redis_tag_aware.foo:
|
||||
adapter: cache.adapter.redis_tag_aware
|
||||
cache.redis_tag_aware.foo2:
|
||||
|
@ -30,6 +30,7 @@ use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use Symfony\Component\Cache\Adapter\ProxyAdapter;
|
||||
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||||
use Symfony\Component\Cache\Adapter\RedisTagAwareAdapter;
|
||||
use Symfony\Component\Cache\Adapter\TagAwareAdapter;
|
||||
use Symfony\Component\Cache\DependencyInjection\CachePoolPass;
|
||||
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
|
||||
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
|
||||
@ -1537,6 +1538,17 @@ abstract class FrameworkExtensionTest extends TestCase
|
||||
12,
|
||||
];
|
||||
$this->assertEquals($expected, $chain->getArguments());
|
||||
|
||||
// Test "tags: true" wrapping logic
|
||||
$tagAwareDefinition = $container->getDefinition('cache.ccc');
|
||||
$this->assertSame(TagAwareAdapter::class, $tagAwareDefinition->getClass());
|
||||
$this->assertCachePoolServiceDefinitionIsCreated($container, (string) $tagAwareDefinition->getArgument(0), 'cache.adapter.array', 410);
|
||||
|
||||
if (method_exists(TagAwareAdapter::class, 'setLogger')) {
|
||||
$this->assertEquals([
|
||||
['setLogger', [new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)]],
|
||||
], $tagAwareDefinition->getMethodCalls());
|
||||
}
|
||||
}
|
||||
|
||||
public function testRedisTagAwareAdapter()
|
||||
@ -1948,6 +1960,9 @@ abstract class FrameworkExtensionTest extends TestCase
|
||||
case 'cache.adapter.redis':
|
||||
$this->assertSame(RedisAdapter::class, $parentDefinition->getClass());
|
||||
break;
|
||||
case 'cache.adapter.array':
|
||||
$this->assertSame(ArrayAdapter::class, $parentDefinition->getClass());
|
||||
break;
|
||||
default:
|
||||
$this->fail('Unresolved adapter: '.$adapter);
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ namespace Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\InvalidArgumentException;
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Psr\Log\LoggerAwareTrait;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
@ -23,11 +25,12 @@ use Symfony\Contracts\Cache\TagAwareCacheInterface;
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class TagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface, PruneableInterface, ResettableInterface
|
||||
class TagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface, PruneableInterface, ResettableInterface, LoggerAwareInterface
|
||||
{
|
||||
public const TAGS_PREFIX = "\0tags\0";
|
||||
|
||||
use ContractsTrait;
|
||||
use LoggerAwareTrait;
|
||||
use ProxyTrait;
|
||||
|
||||
private $deferred = [];
|
||||
|
@ -82,7 +82,7 @@ final class LockRegistry
|
||||
|
||||
public static function compute(callable $callback, ItemInterface $item, bool &$save, CacheInterface $pool, \Closure $setMetadata = null, LoggerInterface $logger = null)
|
||||
{
|
||||
$key = self::$files ? crc32($item->getKey()) % \count(self::$files) : -1;
|
||||
$key = self::$files ? abs(crc32($item->getKey())) % \count(self::$files) : -1;
|
||||
|
||||
if ($key < 0 || (self::$lockedFiles[$key] ?? false) || !$lock = self::open($key)) {
|
||||
return $callback($item, $save);
|
||||
|
@ -14,6 +14,7 @@ namespace Symfony\Component\Cache\Tests\Adapter;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
@ -197,6 +198,20 @@ class TagAwareAdapterTest extends AdapterTestCase
|
||||
$this->assertFalse($item->isHit());
|
||||
}
|
||||
|
||||
public function testLog()
|
||||
{
|
||||
$logger = $this->createMock(LoggerInterface::class);
|
||||
$logger
|
||||
->expects($this->atLeastOnce())
|
||||
->method($this->anything());
|
||||
|
||||
$cache = new TagAwareAdapter(new ArrayAdapter());
|
||||
$cache->setLogger($logger);
|
||||
|
||||
// Computing will produce at least one log
|
||||
$cache->get('foo', static function (): string { return 'ccc'; });
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MockObject|PruneableCacheInterface
|
||||
*/
|
||||
|
@ -187,13 +187,14 @@ class ChoiceType extends AbstractType
|
||||
}
|
||||
|
||||
if ($options['multiple']) {
|
||||
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use (&$unknownValues) {
|
||||
$messageTemplate = $options['invalid_message'] ?? 'The value {{ value }} is not valid.';
|
||||
|
||||
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use (&$unknownValues, $messageTemplate) {
|
||||
// Throw exception if unknown values were submitted
|
||||
if (\count($unknownValues) > 0) {
|
||||
$form = $event->getForm();
|
||||
|
||||
$clientDataAsString = is_scalar($form->getViewData()) ? (string) $form->getViewData() : \gettype($form->getViewData());
|
||||
$messageTemplate = 'The value {{ value }} is not valid.';
|
||||
$clientDataAsString = is_scalar($form->getViewData()) ? (string) $form->getViewData() : (\is_array($form->getViewData()) ? implode('", "', array_keys($unknownValues)) : \gettype($form->getViewData()));
|
||||
|
||||
if (null !== $this->translator) {
|
||||
$message = $this->translator->trans($messageTemplate, ['{{ value }}' => $clientDataAsString], 'validators');
|
||||
@ -201,7 +202,7 @@ class ChoiceType extends AbstractType
|
||||
$message = strtr($messageTemplate, ['{{ value }}' => $clientDataAsString]);
|
||||
}
|
||||
|
||||
$form->addError(new FormError($message, $messageTemplate, ['{{ value }}' => $clientDataAsString], null, new TransformationFailedException(sprintf('The choices "%s" do not exist in the choice list.', implode('", "', array_keys($unknownValues))))));
|
||||
$form->addError(new FormError($message, $messageTemplate, ['{{ value }}' => $clientDataAsString], null, new TransformationFailedException(sprintf('The choices "%s" do not exist in the choice list.', $clientDataAsString))));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1861,6 +1861,32 @@ class ChoiceTypeTest extends BaseTypeTest
|
||||
$this->assertSame('name[]', $view->vars['full_name']);
|
||||
}
|
||||
|
||||
public function testInvalidMessageAwarenessForMultiple()
|
||||
{
|
||||
$form = $this->factory->create(static::TESTED_TYPE, null, [
|
||||
'multiple' => true,
|
||||
'expanded' => false,
|
||||
'choices' => $this->choices,
|
||||
'invalid_message' => 'You are not able to use value "{{ value }}"',
|
||||
]);
|
||||
|
||||
$form->submit(['My invalid choice']);
|
||||
$this->assertEquals("ERROR: You are not able to use value \"My invalid choice\"\n", (string) $form->getErrors(true));
|
||||
}
|
||||
|
||||
public function testInvalidMessageAwarenessForMultipleWithoutScalarOrArrayViewData()
|
||||
{
|
||||
$form = $this->factory->create(static::TESTED_TYPE, null, [
|
||||
'multiple' => true,
|
||||
'expanded' => false,
|
||||
'choices' => $this->choices,
|
||||
'invalid_message' => 'You are not able to use value "{{ value }}"',
|
||||
]);
|
||||
|
||||
$form->submit(new \stdClass());
|
||||
$this->assertEquals("ERROR: You are not able to use value \"stdClass\"\n", (string) $form->getErrors(true));
|
||||
}
|
||||
|
||||
// https://github.com/symfony/symfony/issues/3298
|
||||
public function testInitializeWithEmptyChoices()
|
||||
{
|
||||
|
@ -53,10 +53,8 @@ final class HttplugPromise implements HttplugPromiseInterface
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return Psr7ResponseInterface|mixed
|
||||
*/
|
||||
public function wait($unwrap = true)
|
||||
public function wait($unwrap = true): ?Psr7ResponseInterface
|
||||
{
|
||||
$result = $this->promise->wait($unwrap);
|
||||
|
||||
|
@ -56,14 +56,13 @@ abstract class AbstractSessionListener implements EventSubscriberInterface
|
||||
return;
|
||||
}
|
||||
|
||||
$session = null;
|
||||
$request = $event->getRequest();
|
||||
if (!$request->hasSession()) {
|
||||
$sess = null;
|
||||
$request->setSessionFactory(function () use (&$sess) { return $sess ?? $sess = $this->getSession(); });
|
||||
}
|
||||
|
||||
$session = $session ?? ($this->container && $this->container->has('initialized_session') ? $this->container->get('initialized_session') : null);
|
||||
$session = $this->container && $this->container->has('initialized_session') ? $this->container->get('initialized_session') : null;
|
||||
$this->sessionUsageStack[] = $session instanceof Session ? $session->getUsageIndex() : 0;
|
||||
}
|
||||
|
||||
|
@ -539,15 +539,15 @@ class Route implements \Serializable
|
||||
return $pattern;
|
||||
}
|
||||
|
||||
return preg_replace_callback('#\{(!?\w++)(<.*?>)?(\?[^\}]*+)?\}#', function ($m) {
|
||||
if (isset($m[3][0])) {
|
||||
$this->setDefault($m[1], '?' !== $m[3] ? substr($m[3], 1) : null);
|
||||
return preg_replace_callback('#\{(!?)(\w++)(<.*?>)?(\?[^\}]*+)?\}#', function ($m) {
|
||||
if (isset($m[4][0])) {
|
||||
$this->setDefault($m[2], '?' !== $m[4] ? substr($m[4], 1) : null);
|
||||
}
|
||||
if (isset($m[2][0])) {
|
||||
$this->setRequirement($m[1], substr($m[2], 1, -1));
|
||||
if (isset($m[3][0])) {
|
||||
$this->setRequirement($m[2], substr($m[3], 1, -1));
|
||||
}
|
||||
|
||||
return '{'.$m[1].'}';
|
||||
return '{'.$m[1].$m[2].'}';
|
||||
}, $pattern);
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,14 @@ class RouteTest extends TestCase
|
||||
$this->assertEquals($route, $route->setPath(''), '->setPath() implements a fluent interface');
|
||||
$route->setPath('//path');
|
||||
$this->assertEquals('/path', $route->getPath(), '->setPath() does not allow two slashes "//" at the beginning of the path as it would be confused with a network path when generating the path from the route');
|
||||
$route->setPath('/path/{!foo}');
|
||||
$this->assertEquals('/path/{!foo}', $route->getPath(), '->setPath() keeps ! to pass important params');
|
||||
$route->setPath('/path/{bar<\w++>}');
|
||||
$this->assertEquals('/path/{bar}', $route->getPath(), '->setPath() removes inline requirements');
|
||||
$route->setPath('/path/{foo?value}');
|
||||
$this->assertEquals('/path/{foo}', $route->getPath(), '->setPath() removes inline defaults');
|
||||
$route->setPath('/path/{!bar<\d+>?value}');
|
||||
$this->assertEquals('/path/{!bar}', $route->getPath(), '->setPath() removes all inline settings');
|
||||
}
|
||||
|
||||
public function testOptions()
|
||||
@ -221,17 +229,20 @@ class RouteTest extends TestCase
|
||||
$this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', null), new Route('/foo/{bar?}'));
|
||||
$this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', 'baz'), new Route('/foo/{bar?baz}'));
|
||||
$this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', 'baz<buz>'), new Route('/foo/{bar?baz<buz>}'));
|
||||
$this->assertEquals((new Route('/foo/{!bar}'))->setDefault('!bar', 'baz<buz>'), new Route('/foo/{!bar?baz<buz>}'));
|
||||
$this->assertEquals((new Route('/foo/{!bar}'))->setDefault('bar', 'baz<buz>'), new Route('/foo/{!bar?baz<buz>}'));
|
||||
$this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', 'baz'), new Route('/foo/{bar?}', ['bar' => 'baz']));
|
||||
|
||||
$this->assertEquals((new Route('/foo/{bar}'))->setRequirement('bar', '.*'), new Route('/foo/{bar<.*>}'));
|
||||
$this->assertEquals((new Route('/foo/{bar}'))->setRequirement('bar', '>'), new Route('/foo/{bar<>>}'));
|
||||
$this->assertEquals((new Route('/foo/{bar}'))->setRequirement('bar', '\d+'), new Route('/foo/{bar<.*>}', [], ['bar' => '\d+']));
|
||||
$this->assertEquals((new Route('/foo/{bar}'))->setRequirement('bar', '[a-z]{2}'), new Route('/foo/{bar<[a-z]{2}>}'));
|
||||
$this->assertEquals((new Route('/foo/{!bar}'))->setRequirement('bar', '\d+'), new Route('/foo/{!bar<\d+>}'));
|
||||
|
||||
$this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', null)->setRequirement('bar', '.*'), new Route('/foo/{bar<.*>?}'));
|
||||
$this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', '<>')->setRequirement('bar', '>'), new Route('/foo/{bar<>>?<>}'));
|
||||
|
||||
$this->assertEquals((new Route('/{foo}/{!bar}'))->setDefaults(['bar' => '<>', 'foo' => '\\'])->setRequirements(['bar' => '\\', 'foo' => '.']), new Route('/{foo<.>?\}/{!bar<\>?<>}'));
|
||||
|
||||
$this->assertEquals((new Route('/'))->setHost('{bar}')->setDefault('bar', null), (new Route('/'))->setHost('{bar?}'));
|
||||
$this->assertEquals((new Route('/'))->setHost('{bar}')->setDefault('bar', 'baz'), (new Route('/'))->setHost('{bar?baz}'));
|
||||
$this->assertEquals((new Route('/'))->setHost('{bar}')->setDefault('bar', 'baz<buz>'), (new Route('/'))->setHost('{bar?baz<buz>}'));
|
||||
|
@ -1233,6 +1233,10 @@ class Parser
|
||||
$offset = $cursor;
|
||||
$cursor += strcspn($this->currentLine, '[]{},: ', $cursor);
|
||||
|
||||
if ($cursor === $offset) {
|
||||
throw new ParseException('Malformed unquoted YAML string.');
|
||||
}
|
||||
|
||||
return substr($this->currentLine, $offset, $cursor - $offset);
|
||||
}
|
||||
|
||||
|
@ -2675,6 +2675,25 @@ YAML;
|
||||
);
|
||||
}
|
||||
|
||||
public function testThrowExceptionIfInvalidAdditionalClosingTagOccurs()
|
||||
{
|
||||
$yaml = '{
|
||||
"object": {
|
||||
"array": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
]
|
||||
],
|
||||
}
|
||||
}';
|
||||
|
||||
$this->expectException(ParseException::class);
|
||||
$this->expectExceptionMessage('Malformed unquoted YAML string at line 8 (near " ],").');
|
||||
|
||||
$this->parser->parse($yaml);
|
||||
}
|
||||
|
||||
public function testWhitespaceAtEndOfLine()
|
||||
{
|
||||
$yaml = "\nfoo:\n arguments: [ '@bar' ] \n";
|
||||
|
@ -102,7 +102,7 @@ interface ResponseInterface
|
||||
*
|
||||
* Other info SHOULD be named after curl_getinfo()'s associative return value.
|
||||
*
|
||||
* @return array|mixed An array of all available info, or one of them when $type is
|
||||
* @return mixed An array of all available info, or one of them when $type is
|
||||
* provided, or null when an unsupported type is requested
|
||||
*/
|
||||
public function getInfo(string $type = null);
|
||||
|
Reference in New Issue
Block a user