Merge branch '4.4'
* 4.4: [PhpUnitBridge] fix running simple-phpunit on Windows fixed UPGRADE fixed phpdocs fixed phpdocs [ErrorCatcher] Fixed some escaping in XML errors [Messenger] fix broken key normalization [FrameworkBundle] Allow creating chained cache pools by providing several adapters [FrameworkBundle] Use default_locale as default value for translator.fallbacks
This commit is contained in:
commit
22437d41bd
@ -339,7 +339,9 @@ Process
|
||||
PropertyAccess
|
||||
--------------
|
||||
|
||||
* Removed support of passing `null` as 2nd argument of `PropertyAccessor::createCache()` method (`$defaultLifetime`), pass `0` instead.
|
||||
* Removed support of passing `null` as 2nd argument of
|
||||
`PropertyAccessor::createCache()` method (`$defaultLifetime`), pass `0`
|
||||
instead.
|
||||
|
||||
Routing
|
||||
-------
|
||||
|
@ -130,8 +130,9 @@ if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__
|
||||
}
|
||||
$prevRoot = getenv('COMPOSER_ROOT_VERSION');
|
||||
putenv("COMPOSER_ROOT_VERSION=$PHPUNIT_VERSION.99");
|
||||
$q = '\\' === DIRECTORY_SEPARATOR ? '"' : '';
|
||||
// --no-suggest is not in the list to keep compat with composer 1.0, which is shipped with Ubuntu 16.04LTS
|
||||
$exit = proc_close(proc_open("$COMPOSER install --no-dev --prefer-dist --no-progress --ansi", array(), $p, getcwd()));
|
||||
$exit = proc_close(proc_open("$q$COMPOSER install --no-dev --prefer-dist --no-progress --ansi$q", array(), $p, getcwd()));
|
||||
putenv('COMPOSER_ROOT_VERSION'.(false !== $prevRoot ? '='.$prevRoot : ''));
|
||||
if ($exit) {
|
||||
exit($exit);
|
||||
|
@ -25,6 +25,7 @@ CHANGELOG
|
||||
* Deprecated the `$parser` argument of `ControllerResolver::__construct()` and `DelegatingLoader::__construct()`
|
||||
* Deprecated the `controller_name_converter` and `resolve_controller_name_subscriber` services
|
||||
* The `ControllerResolver` and `DelegatingLoader` classes have been marked as `final`
|
||||
* Added support for configuring chained cache pools
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
@ -639,9 +639,10 @@ class Configuration implements ConfigurationInterface
|
||||
->fixXmlConfig('path')
|
||||
->children()
|
||||
->arrayNode('fallbacks')
|
||||
->info('Defaults to the value of "default_locale".')
|
||||
->beforeNormalization()->ifString()->then(function ($v) { return [$v]; })->end()
|
||||
->prototype('scalar')->end()
|
||||
->defaultValue(['en'])
|
||||
->defaultValue([])
|
||||
->end()
|
||||
->booleanNode('logging')->defaultValue(false)->end()
|
||||
->scalarNode('formatter')->defaultValue('translator.formatter.default')->end()
|
||||
@ -846,8 +847,38 @@ class Configuration implements ConfigurationInterface
|
||||
->arrayNode('pools')
|
||||
->useAttributeAsKey('name')
|
||||
->prototype('array')
|
||||
->fixXmlConfig('adapter')
|
||||
->beforeNormalization()
|
||||
->ifTrue(function ($v) { return (isset($v['adapters']) || \is_array($v['adapter'] ?? null)) && isset($v['provider']); })
|
||||
->thenInvalid('Pool cannot have a "provider" while "adapter" is set to a map')
|
||||
->end()
|
||||
->children()
|
||||
->scalarNode('adapter')->defaultValue('cache.app')->end()
|
||||
->arrayNode('adapters')
|
||||
->info('One or more adapters to chain for creating the pool, defaults to "cache.app".')
|
||||
->beforeNormalization()
|
||||
->always()->then(function ($values) {
|
||||
if ([0] === array_keys($values) && \is_array($values[0])) {
|
||||
return $values[0];
|
||||
}
|
||||
$adapters = [];
|
||||
|
||||
foreach ($values as $k => $v) {
|
||||
if (\is_int($k) && \is_string($v)) {
|
||||
$adapters[] = $v;
|
||||
} elseif (!\is_array($v)) {
|
||||
$adapters[$k] = $v;
|
||||
} elseif (isset($v['provider'])) {
|
||||
$adapters[$v['provider']] = $v['name'] ?? $v;
|
||||
} else {
|
||||
$adapters[] = $v['name'] ?? $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $adapters;
|
||||
})
|
||||
->end()
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->scalarNode('tags')->defaultNull()->end()
|
||||
->booleanNode('public')->defaultFalse()->end()
|
||||
->integerNode('default_lifetime')->end()
|
||||
@ -967,6 +998,7 @@ class Configuration implements ConfigurationInterface
|
||||
->end()
|
||||
->children()
|
||||
->arrayNode('routing')
|
||||
->normalizeKeys(false)
|
||||
->useAttributeAsKey('message_class')
|
||||
->beforeNormalization()
|
||||
->always()
|
||||
@ -1026,6 +1058,7 @@ class Configuration implements ConfigurationInterface
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('transports')
|
||||
->normalizeKeys(false)
|
||||
->useAttributeAsKey('name')
|
||||
->arrayPrototype()
|
||||
->beforeNormalization()
|
||||
@ -1073,6 +1106,7 @@ class Configuration implements ConfigurationInterface
|
||||
->scalarNode('default_bus')->defaultNull()->end()
|
||||
->arrayNode('buses')
|
||||
->defaultValue(['messenger.bus.default' => ['default_middleware' => true, 'middleware' => []]])
|
||||
->normalizeKeys(false)
|
||||
->useAttributeAsKey('name')
|
||||
->arrayPrototype()
|
||||
->addDefaultsIfNotSet()
|
||||
|
@ -28,6 +28,7 @@ use Symfony\Component\BrowserKit\AbstractBrowser;
|
||||
use Symfony\Component\Cache\Adapter\AbstractAdapter;
|
||||
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Component\Cache\Adapter\ChainAdapter;
|
||||
use Symfony\Component\Cache\Adapter\TagAwareAdapter;
|
||||
use Symfony\Component\Cache\DependencyInjection\CachePoolPass;
|
||||
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||
@ -278,7 +279,7 @@ class FrameworkExtension extends Extension
|
||||
$this->registerEsiConfiguration($config['esi'], $container, $loader);
|
||||
$this->registerSsiConfiguration($config['ssi'], $container, $loader);
|
||||
$this->registerFragmentsConfiguration($config['fragments'], $container, $loader);
|
||||
$this->registerTranslatorConfiguration($config['translator'], $container, $loader);
|
||||
$this->registerTranslatorConfiguration($config['translator'], $container, $loader, $config['default_locale']);
|
||||
$this->registerProfilerConfiguration($config['profiler'], $container, $loader);
|
||||
$this->registerCacheConfiguration($config['cache'], $container);
|
||||
$this->registerWorkflowConfiguration($config['workflows'], $container, $loader);
|
||||
@ -952,7 +953,7 @@ class FrameworkExtension extends Extension
|
||||
return new Reference('assets.empty_version_strategy');
|
||||
}
|
||||
|
||||
private function registerTranslatorConfiguration(array $config, ContainerBuilder $container, LoaderInterface $loader)
|
||||
private function registerTranslatorConfiguration(array $config, ContainerBuilder $container, LoaderInterface $loader, string $defaultLocale)
|
||||
{
|
||||
if (!$this->isConfigEnabled($container, $config)) {
|
||||
$container->removeDefinition('console.command.translation_debug');
|
||||
@ -967,7 +968,7 @@ class FrameworkExtension extends Extension
|
||||
$container->setAlias('translator', 'translator.default')->setPublic(true);
|
||||
$container->setAlias('translator.formatter', new Alias($config['formatter'], false));
|
||||
$translator = $container->findDefinition('translator.default');
|
||||
$translator->addMethodCall('setFallbackLocales', [$config['fallbacks']]);
|
||||
$translator->addMethodCall('setFallbackLocales', [$config['fallbacks'] ?: [$defaultLocale]]);
|
||||
|
||||
$container->setParameter('translator.logging', $config['logging']);
|
||||
$container->setParameter('translator.default_path', $config['default_path']);
|
||||
@ -1650,16 +1651,29 @@ class FrameworkExtension extends Extension
|
||||
}
|
||||
foreach (['app', 'system'] as $name) {
|
||||
$config['pools']['cache.'.$name] = [
|
||||
'adapter' => $config[$name],
|
||||
'adapters' => [$config[$name]],
|
||||
'public' => true,
|
||||
'tags' => false,
|
||||
];
|
||||
}
|
||||
foreach ($config['pools'] as $name => $pool) {
|
||||
if ($config['pools'][$pool['adapter']]['tags'] ?? false) {
|
||||
$pool['adapter'] = '.'.$pool['adapter'].'.inner';
|
||||
$pool['adapters'] = $pool['adapters'] ?: ['cache.app'];
|
||||
|
||||
foreach ($pool['adapters'] as $provider => $adapter) {
|
||||
if ($config['pools'][$adapter]['tags'] ?? false) {
|
||||
$pool['adapters'][$provider] = $adapter = '.'.$adapter.'.inner';
|
||||
}
|
||||
}
|
||||
|
||||
if (1 === \count($pool['adapters'])) {
|
||||
if (!isset($pool['provider']) && !\is_int($provider)) {
|
||||
$pool['provider'] = $provider;
|
||||
}
|
||||
$definition = new ChildDefinition($adapter);
|
||||
} else {
|
||||
$definition = new Definition(ChainAdapter::class, [$pool['adapters'], 0]);
|
||||
$pool['reset'] = 'reset';
|
||||
}
|
||||
$definition = new ChildDefinition($pool['adapter']);
|
||||
|
||||
if ($pool['tags']) {
|
||||
if (true !== $pool['tags'] && ($config['pools'][$pool['tags']]['tags'] ?? false)) {
|
||||
@ -1690,7 +1704,7 @@ class FrameworkExtension extends Extension
|
||||
}
|
||||
|
||||
$definition->setPublic($pool['public']);
|
||||
unset($pool['adapter'], $pool['public'], $pool['tags']);
|
||||
unset($pool['adapters'], $pool['public'], $pool['tags']);
|
||||
|
||||
$definition->addTag('cache.pool', $pool);
|
||||
$container->setDefinition($name, $definition);
|
||||
|
@ -256,6 +256,10 @@
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="cache_pool">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="adapter" type="cache_pool_adapter" minOccurs="0" maxOccurs="unbounded" />
|
||||
</xsd:sequence>
|
||||
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="adapter" type="xsd:string" />
|
||||
<xsd:attribute name="tags" type="xsd:string" />
|
||||
@ -265,6 +269,11 @@
|
||||
<xsd:attribute name="clearer" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="cache_pool_adapter">
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="provider" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="workflow">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="initial-marking" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
|
||||
|
@ -232,7 +232,7 @@ class ConfigurationTest extends TestCase
|
||||
],
|
||||
'translator' => [
|
||||
'enabled' => !class_exists(FullStack::class),
|
||||
'fallbacks' => ['en'],
|
||||
'fallbacks' => [],
|
||||
'logging' => false,
|
||||
'formatter' => 'translator.formatter.default',
|
||||
'paths' => [],
|
||||
|
@ -24,6 +24,14 @@ $container->loadFromExtension('framework', [
|
||||
'cache.def' => [
|
||||
'default_lifetime' => 11,
|
||||
],
|
||||
'cache.chain' => [
|
||||
'default_lifetime' => 12,
|
||||
'adapter' => [
|
||||
'cache.adapter.array',
|
||||
'cache.adapter.filesystem',
|
||||
'redis://foo' => 'cache.adapter.redis',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
@ -12,6 +12,11 @@
|
||||
<framework:pool name="cache.baz" adapter="cache.adapter.filesystem" default-lifetime="7" />
|
||||
<framework:pool name="cache.foobar" adapter="cache.adapter.psr6" default-lifetime="10" provider="app.cache_pool" />
|
||||
<framework:pool name="cache.def" default-lifetime="11" />
|
||||
<framework:pool name="cache.chain" default-lifetime="12">
|
||||
<framework:adapter name="cache.adapter.array" />
|
||||
<framework:adapter name="cache.adapter.filesystem" />
|
||||
<framework:adapter name="cache.adapter.redis" provider="redis://foo" />
|
||||
</framework:pool>
|
||||
</framework:cache>
|
||||
</framework:config>
|
||||
</container>
|
||||
|
@ -17,3 +17,9 @@ framework:
|
||||
provider: app.cache_pool
|
||||
cache.def:
|
||||
default_lifetime: 11
|
||||
cache.chain:
|
||||
default_lifetime: 12
|
||||
adapter:
|
||||
- cache.adapter.array
|
||||
- cache.adapter.filesystem
|
||||
- {name: cache.adapter.redis, provider: 'redis://foo'}
|
||||
|
@ -20,10 +20,12 @@ use Symfony\Bundle\FullStack;
|
||||
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||
use Symfony\Component\Cache\Adapter\ApcuAdapter;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Component\Cache\Adapter\ChainAdapter;
|
||||
use Symfony\Component\Cache\Adapter\DoctrineAdapter;
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use Symfony\Component\Cache\Adapter\ProxyAdapter;
|
||||
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||||
use Symfony\Component\Cache\DependencyInjection\CachePoolPass;
|
||||
use Symfony\Component\Config\Resource\DirectoryResource;
|
||||
use Symfony\Component\Config\Resource\FileExistenceResource;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
@ -1219,13 +1221,36 @@ abstract class FrameworkExtensionTest extends TestCase
|
||||
|
||||
public function testCachePoolServices()
|
||||
{
|
||||
$container = $this->createContainerFromFile('cache');
|
||||
$container = $this->createContainerFromFile('cache', [], true, false);
|
||||
$container->setParameter('cache.prefix.seed', 'test');
|
||||
$container->addCompilerPass(new CachePoolPass());
|
||||
$container->compile();
|
||||
|
||||
$this->assertCachePoolServiceDefinitionIsCreated($container, 'cache.foo', 'cache.adapter.apcu', 30);
|
||||
$this->assertCachePoolServiceDefinitionIsCreated($container, 'cache.bar', 'cache.adapter.doctrine', 5);
|
||||
$this->assertCachePoolServiceDefinitionIsCreated($container, 'cache.baz', 'cache.adapter.filesystem', 7);
|
||||
$this->assertCachePoolServiceDefinitionIsCreated($container, 'cache.foobar', 'cache.adapter.psr6', 10);
|
||||
$this->assertCachePoolServiceDefinitionIsCreated($container, 'cache.def', 'cache.app', 11);
|
||||
|
||||
$chain = $container->getDefinition('cache.chain');
|
||||
|
||||
$this->assertSame(ChainAdapter::class, $chain->getClass());
|
||||
|
||||
$expected = [
|
||||
[
|
||||
(new ChildDefinition('cache.adapter.array'))
|
||||
->replaceArgument(0, 12),
|
||||
(new ChildDefinition('cache.adapter.filesystem'))
|
||||
->replaceArgument(0, 'x5nX4TVTWn')
|
||||
->replaceArgument(1, 12),
|
||||
(new ChildDefinition('cache.adapter.redis'))
|
||||
->replaceArgument(0, new Reference('.cache_connection.kYdiLgf'))
|
||||
->replaceArgument(1, 'x5nX4TVTWn')
|
||||
->replaceArgument(2, 12),
|
||||
],
|
||||
12,
|
||||
];
|
||||
$this->assertEquals($expected, $chain->getArguments());
|
||||
}
|
||||
|
||||
public function testRemovesResourceCheckerConfigCacheFactoryArgumentOnlyIfNoDebug()
|
||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Cache\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\AbstractAdapter;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Component\Cache\Adapter\ChainAdapter;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
@ -97,7 +98,50 @@ class CachePoolPass implements CompilerPassInterface
|
||||
if (isset($tags[0]['provider'])) {
|
||||
$tags[0]['provider'] = new Reference(static::getServiceProvider($container, $tags[0]['provider']));
|
||||
}
|
||||
|
||||
if (ChainAdapter::class === $class) {
|
||||
$adapters = [];
|
||||
foreach ($adapter->getArgument(0) as $provider => $adapter) {
|
||||
$chainedPool = $adapter = new ChildDefinition($adapter);
|
||||
$chainedTags = [\is_int($provider) ? [] : ['provider' => $provider]];
|
||||
$chainedClass = '';
|
||||
|
||||
while ($adapter instanceof ChildDefinition) {
|
||||
$adapter = $container->findDefinition($adapter->getParent());
|
||||
$chainedClass = $chainedClass ?: $adapter->getClass();
|
||||
if ($t = $adapter->getTag($this->cachePoolTag)) {
|
||||
$chainedTags[0] += $t[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (ChainAdapter::class === $chainedClass) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid service "%s": chain of adapters cannot reference another chain, found "%s".', $id, $chainedPool->getParent()));
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
|
||||
if (isset($chainedTags[0]['provider'])) {
|
||||
$chainedPool->replaceArgument($i++, new Reference(static::getServiceProvider($container, $chainedTags[0]['provider'])));
|
||||
}
|
||||
|
||||
if (isset($tags[0]['namespace']) && ArrayAdapter::class !== $adapter->getClass()) {
|
||||
$chainedPool->replaceArgument($i++, $tags[0]['namespace']);
|
||||
}
|
||||
|
||||
if (isset($tags[0]['default_lifetime'])) {
|
||||
$chainedPool->replaceArgument($i++, $tags[0]['default_lifetime']);
|
||||
}
|
||||
|
||||
$adapters[] = $chainedPool;
|
||||
}
|
||||
|
||||
$pool->replaceArgument(0, $adapters);
|
||||
unset($tags[0]['provider'], $tags[0]['namespace']);
|
||||
$i = 1;
|
||||
} else {
|
||||
$i = 0;
|
||||
}
|
||||
|
||||
foreach ($attributes as $attr) {
|
||||
if (!isset($tags[0][$attr])) {
|
||||
// no-op
|
||||
@ -105,7 +149,7 @@ class CachePoolPass implements CompilerPassInterface
|
||||
if ($tags[0][$attr]) {
|
||||
$pool->addTag($this->kernelResetTag, ['method' => $tags[0][$attr]]);
|
||||
}
|
||||
} elseif ('namespace' !== $attr || ArrayAdapter::class !== $adapter->getClass()) {
|
||||
} elseif ('namespace' !== $attr || ArrayAdapter::class !== $class) {
|
||||
$pool->replaceArgument($i++, $tags[0][$attr]);
|
||||
}
|
||||
unset($tags[0][$attr]);
|
||||
|
@ -55,14 +55,16 @@ class HtmlErrorRenderer implements ErrorRendererInterface
|
||||
{
|
||||
$css = $this->getStylesheet();
|
||||
$body = $this->getBody($exception);
|
||||
$charset = $this->escapeHtml($this->charset);
|
||||
$title = $this->escapeHtml($exception->getTitle());
|
||||
|
||||
return <<<EOF
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="{$this->charset}" />
|
||||
<meta charset="{$charset}" />
|
||||
<meta name="robots" content="noindex,nofollow,noarchive" />
|
||||
<title>{$exception->getTitle()}</title>
|
||||
<title>{$title}</title>
|
||||
<style>$css</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -94,11 +96,14 @@ EOF;
|
||||
*/
|
||||
public function getBody(FlattenException $exception)
|
||||
{
|
||||
$statusCode = $this->escapeHtml($exception->getStatusCode());
|
||||
$title = $this->escapeHtml($exception->getTitle());
|
||||
|
||||
if (!$this->debug) {
|
||||
return <<<EOF
|
||||
<div class="container">
|
||||
<h1>Oops! An Error Occurred</h1>
|
||||
<h2>The server returned a "{$exception->getStatusCode()} {$exception->getTitle()}".</h2>
|
||||
<h2>The server returned a "{$statusCode} {$title}".</h2>
|
||||
<p>
|
||||
Something is broken. Please let us know what you were doing when this error occurred.
|
||||
We will fix it as soon as possible. Sorry for any inconvenience caused.
|
||||
|
@ -40,7 +40,10 @@ class XmlErrorRenderer implements ErrorRendererInterface
|
||||
*/
|
||||
public function render(FlattenException $exception): string
|
||||
{
|
||||
$title = $this->escapeXml($exception->getTitle());
|
||||
$message = $this->escapeXml($exception->getMessage());
|
||||
$statusCode = $this->escapeXml($exception->getStatusCode());
|
||||
$charset = $this->escapeXml($this->charset);
|
||||
|
||||
$exceptions = '';
|
||||
if ($this->debug) {
|
||||
@ -63,10 +66,10 @@ class XmlErrorRenderer implements ErrorRendererInterface
|
||||
}
|
||||
|
||||
return <<<EOF
|
||||
<?xml version="1.0" encoding="{$this->charset}" ?>
|
||||
<?xml version="1.0" encoding="{$charset}" ?>
|
||||
<problem xmlns="urn:ietf:rfc:7807">
|
||||
<title>{$exception->getTitle()}</title>
|
||||
<status>{$exception->getStatusCode()}</status>
|
||||
<title>{$title}</title>
|
||||
<status>{$statusCode}</status>
|
||||
<detail>{$message}</detail>
|
||||
{$exceptions}
|
||||
</problem>
|
||||
|
Reference in New Issue
Block a user