Skip to content

[Config] Allow for service-based Resource (cache) validation #7230

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
74a6541
Interchangeable ConfigCache
Nov 5, 2012
3af65f1
Blank Lines
Nov 23, 2012
c98adc5
Use a common ConfigCacheFactory service
mpdude Jan 25, 2013
5487e6a
Whitespace fix
mpdude Jan 25, 2013
39014d5
Got the whitespace fix wrong :(
mpdude Jan 25, 2013
532fbf7
Remove service definition and references for config.config_cache_fact…
mpdude Jan 26, 2013
1261053
Forgot one reference
mpdude Jan 26, 2013
82f20ec
Remove unused parameters
mpdude Feb 13, 2013
9f39b4c
Move initial cache fill to the ConfigCacheFactory as well.
mpdude Feb 13, 2013
9ad5630
Basic rework of the ConfigCache and ResourceInterface as outlined in …
mpdude Mar 1, 2013
a06ad73
Fixups as suggested on GH.
mpdude Mar 1, 2013
207354e
Fix CS violations and scrutinizer feedback
mpdude Mar 1, 2013
1e3f214
Even more scrutinizer feedback
mpdude Mar 1, 2013
c9bbe77
Renamed the method to comply with CS; it is named like that in Router…
mpdude Mar 1, 2013
250e084
whitespace fix
mpdude Mar 1, 2013
ec06311
Use a hash-based approach for detecting changes in directory structures.
mpdude Mar 1, 2013
daf6ad2
The set of resources and their meaning should be opaque for clients o…
mpdude Mar 1, 2013
752106b
Re-use code we already got
mpdude Mar 1, 2013
fa8ad4b
Renamed the two currently available cache implementations
mpdude Mar 1, 2013
7d1778f
Added abstract base class for factories, plan to use it for a Framewo…
mpdude Mar 1, 2013
87b4acb
Intention revealing names
mpdude Mar 1, 2013
944a9cf
Fix handling of nonexistent dirs.
mpdude Mar 4, 2013
58dc59f
Fix DocBlocks
mpdude Mar 4, 2013
ca36f94
Added a ConfigCacheFactory implementation to FrameworkBundle.
mpdude Mar 4, 2013
cbef5a5
Fix tests
mpdude Mar 7, 2013
1b2e60e
Add array_unique(...) for getResource() methods again.
mpdude Mar 7, 2013
d4e9dea
Fix test (wrong assumptions)
mpdude Mar 7, 2013
4b45252
Resources are not always just filenames; the array_unique in getResou…
mpdude Mar 25, 2013
346103b
Fix merge regression
mpdude Mar 25, 2013
2a084eb
Added supports() method for validators. Only serialize supported Reso…
mpdude Apr 1, 2013
101428b
Revert changes previously made on this branch on ResourceInterface an…
mpdude Apr 13, 2013
2957057
Accept arbitrary objects as Resources here as well.
mpdude Apr 13, 2013
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?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\Bundle\FrameworkBundle\Config;

use Symfony\Component\Config\AbstractConfigCacheFactory;
use Symfony\Component\Config\ResourceValidatingCache;
use Symfony\Component\Config\NonvalidatingCache;
use Symfony\Component\Config\Resource\ResourceValidatorInterface;

/**
* This implementation of ConfigCacheFactoryInterface will use a given
* set of ResourceValidators to check caches for freshness. If no
* ResourceValidators are used, a Non-validating cache will be used.
*
* @author Matthias Pigulla <mp@webfactory.de>
*/
class FrameworkConfigCacheFactory extends AbstractConfigCacheFactory
{
protected $resourceValidators = array();

public function addResourceValidator(ResourceValidatorInterface $validator)
{
$this->resourceValidators[] = $validator;
}

public function setResourceValidators(array $validators)
{
$this->resourceValidators = $validators;
}

public function createCache($cacheFilename)
{
if ($this->resourceValidators) {
$cache = new ResourceValidatingCache($cacheFilename);
$cache->setResourceValidators($this->resourceValidators);
} else {
$cache = new NonvalidatingCache($cacheFilename);
}
return $cache;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,15 @@
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\Util\CacheFileUtils;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;

class CompilerDebugDumpPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$cache = new ConfigCache($this->getCompilerLogFilename($container), false);
$cache->write(implode("\n", $container->getCompiler()->getLog()));
CacheFileUtils::dumpInFile($this->getCompilerLogFilename($container), implode("\n", $container->getCompiler()->getLog()));
}

public static function getCompilerLogFilename(ContainerInterface $container)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?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\Bundle\FrameworkBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;

/**
* Finds ResourceValidators and adds them to the ConfigCache factory service.
*
* @author Matthias Pigulla <mp@webfactory.de>
*/
class ConfigResourceValidatorPass implements CompilerPassInterface
{
/**
* {@inheritDoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('config.cache_factory.default')) {
return;
}

$definition = $container->getDefinition('config.cache_factory.default');
$debug = $container->getParameter('kernel.debug');

foreach ($container->findTaggedServiceIds('config.resource_validator') as $id => $attributesList) {
foreach ($attributesList as $attributes) {
if (!$debug && !(isset($attributes['debugOnly']) && $attributes['debugOnly'] == false)) {
continue;
}

// We must assume that the class value has been correctly filled, even if the service is created by a factory
$class = $container->getDefinition($id)->getClass();

$refClass = new \ReflectionClass($class);
$interface = 'Symfony\Component\Config\Resource\ResourceValidatorInterface';

if (!$refClass->implementsInterface($interface)) {
throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface));
}

$definition->addMethodCall('addResourceValidator', array(new Reference($id)));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Dumper\XmlDumper;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\Util\CacheFileUtils;

/**
* Dumps the ContainerBuilder to a cache file so that it can be used by
Expand All @@ -28,7 +28,6 @@ class ContainerBuilderDebugDumpPass implements CompilerPassInterface
public function process(ContainerBuilder $container)
{
$dumper = new XmlDumper($container);
$cache = new ConfigCache($container->getParameter('debug.container.dump'), false);
$cache->write($dumper->dump());
CacheFileUtils::dumpInFile($container->getParameter('debug.container.dump'), $dumper->dump());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ public function load(array $configs, ContainerBuilder $container)
$loader->load('serializer.xml');
}

$this->registerConfigCacheConfiguration(array(), $container, $loader);

$this->addClassesToCompile(array(
'Symfony\\Component\\HttpFoundation\\ParameterBag',
'Symfony\\Component\\HttpFoundation\\HeaderBag',
Expand Down Expand Up @@ -680,6 +682,11 @@ private function registerAnnotationsConfiguration(array $config, ContainerBuilde
}
}

private function registerConfigCacheConfiguration(array $config, ContainerBuilder $container,$loader)
{
$loader->load('config_cache.xml');
}

/**
* Returns the base path for the XSD files.
*
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationDumperPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FragmentRendererPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigResourceValidatorPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\HttpFoundation\Request;
Expand Down Expand Up @@ -68,6 +69,7 @@ public function build(ContainerBuilder $container)
$container->addCompilerPass(new TranslationDumperPass());
$container->addCompilerPass(new FragmentRendererPass(), PassConfig::TYPE_AFTER_REMOVING);
$container->addCompilerPass(new SerializerPass());
$container->addCompilerPass(new ConfigResourceValidatorPass());

if ($container->getParameter('kernel.debug')) {
$container->addCompilerPass(new ContainerBuilderDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<parameters>
<parameter key="config.cache_factory.class">Symfony\Bundle\FrameworkBundle\Config\FrameworkConfigCacheFactory</parameter>
</parameters>

<services>
<service id="config.cache_factory.default" class="%config.cache_factory.class%" public="false" />
<service id="config.cache_factory" alias="config.cache_factory.default" />

<service class="Symfony\Component\Config\Resource\ResourceInterfaceValidator" public="false">
<tag name="config.resource_validator" />
</service>
</services>
</container>
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@
</argument>
<argument type="service" id="router.request_context" on-invalid="ignore" />
<argument type="service" id="logger" on-invalid="ignore" />
<call method="setConfigCacheFactory">
<argument type="service" id="config.cache_factory"/>
</call>
</service>

<service id="router" alias="router.default" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
<argument key="cache_dir">%kernel.cache_dir%/translations</argument>
<argument key="debug">%kernel.debug%</argument>
</argument>
<call method="setConfigCacheFactory">
<argument type="service" id="config.cache_factory"/>
</call>
</service>

<service id="translator" class="%translator.identity.class%">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,13 @@ public function testTranslator()
'->registerTranslatorConfiguration() finds Security translation resources'
);

$calls = $container->getDefinition('translator.default')->getMethodCalls();
$this->assertEquals(array('fr'), $calls[0][1][0]);
$haveFallbackLocale = false;
foreach ($container->getDefinition('translator.default')->getMethodCalls() as $call) {
if ('setFallbackLocales' == $call[0] && array('fr') == $call[1][0]) {
$haveFallbackLocale = true;
}
}
$this->assertTrue($haveFallbackLocale, '-> setFallbackLocale() is called with the expected value.');
}

/**
Expand Down
94 changes: 64 additions & 30 deletions src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
use Symfony\Component\Translation\Translator as BaseTranslator;
use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\ConfigCacheFactoryInterface;
use Symfony\Component\Config\DefaultConfigCacheFactory;
use Symfony\Component\Config\ConfigCacheInterface;

/**
* Translator.
Expand All @@ -26,6 +28,7 @@ class Translator extends BaseTranslator
protected $container;
protected $options;
protected $loaderIds;
protected $configCacheFactory;

/**
* Constructor.
Expand Down Expand Up @@ -62,6 +65,30 @@ public function __construct(ContainerInterface $container, MessageSelector $sele
parent::__construct(null, $selector);
}

/**
* Sets the configCacheFactory
*
* @param ConfigCacheFactoryInterface $configCacheFactory
*/
public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory)
{
$this->configCacheFactory = $configCacheFactory;
}

/**
* Returns the configCacheFactory set by setConfigCacheFactory or a BC default implementation
*
* @return ConfigCacheFactoryInterface $configCacheFactory
*/
private function getConfigCacheFactory()
{
if (!$this->configCacheFactory) {
$this->configCacheFactory = new DefaultConfigCacheFactory($this->options['debug']);
}

return $this->configCacheFactory;
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -89,32 +116,44 @@ protected function loadCatalogue($locale)
return parent::loadCatalogue($locale);
}

$cache = new ConfigCache($this->options['cache_dir'].'/catalogue.'.$locale.'.php', $this->options['debug']);
if (!$cache->isFresh()) {
$this->initialize();
$self = $this;
$cache = $this->getConfigCacheFactory()->cache($this->options['cache_dir'].'/catalogue.'.$locale.'.php', function ($cache) use ($self, $locale) {
$self->fillCache($cache, $locale);
});

if (!isset($this->catalogues[$locale])) {
$this->catalogues[$locale] = include $cache;
}

}

/* This method is only public to allow it to be called from a callback (prior PHP 5.4?) */
public function fillCache(ConfigCacheInterface $cache, $locale)
{
$this->initialize();

parent::loadCatalogue($locale);
parent::loadCatalogue($locale);

$fallbackContent = '';
$current = '';
foreach ($this->computeFallbackLocales($locale) as $fallback) {
$fallbackContent .= sprintf(<<<EOF
$fallbackContent = '';
$current = '';
foreach ($this->computeFallbackLocales($locale) as $fallback) {
$fallbackContent .= sprintf(<<<EOF
\$catalogue%s = new MessageCatalogue('%s', %s);
\$catalogue%s->addFallbackCatalogue(\$catalogue%s);


EOF
,
ucfirst($fallback),
$fallback,
var_export($this->catalogues[$fallback]->all(), true),
ucfirst($current),
ucfirst($fallback)
);
$current = $fallback;
}
,
ucfirst($fallback),
$fallback,
var_export($this->catalogues[$fallback]->all(), true),
ucfirst($current),
ucfirst($fallback)
);
$current = $fallback;
}

$content = sprintf(<<<EOF
$content = sprintf(<<<EOF
<?php

use Symfony\Component\Translation\MessageCatalogue;
Expand All @@ -125,18 +164,13 @@ protected function loadCatalogue($locale)
return \$catalogue;

EOF
,
$locale,
var_export($this->catalogues[$locale]->all(), true),
$fallbackContent
);

$cache->write($content, $this->catalogues[$locale]->getResources());

return;
}
,
$locale,
var_export($this->catalogues[$locale]->all(), true),
$fallbackContent
);

$this->catalogues[$locale] = include $cache;
$cache->write($content, $this->catalogues[$locale]->getResources());
}

protected function initialize()
Expand Down
Loading