Skip to content

Introduce MetadataValidators to test arbitrary metadata for freshness. #15692

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 36 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
60b4089
Introduce MetadataValidators to test arbitrary metadata for freshness.
bnw Sep 3, 2015
673070b
Added trailing newline
bnw Sep 4, 2015
ab39129
Added new parameter to interface
bnw Sep 4, 2015
9546be9
Renamed ResourceValidator to ResourceInterfaceValidator
bnw Sep 4, 2015
0342b73
Added missing import
bnw Sep 4, 2015
7aee891
Correcting classname after renaming
bnw Sep 4, 2015
df526c0
Added ConfigCachePass to framework
bnw Sep 4, 2015
2555d5a
Fixed UnitTests
bnw Sep 4, 2015
4145dc0
Bugfix
bnw Sep 4, 2015
2e49fb2
Better name
bnw Sep 4, 2015
d0f1e2d
Fix test
mpdude Sep 5, 2015
3f9424f
Add docblock
mpdude Sep 5, 2015
b8bb60a
Fix tests while keeping ConfigCacheFactory unchanged (from a client p…
mpdude Sep 5, 2015
2fac8f3
Hello Fabbot
mpdude Sep 5, 2015
1b62130
Forgot import statement
mpdude Sep 5, 2015
b16d9de
The new ConfigCachePass is required
mpdude Sep 6, 2015
97a2cd0
Bump deps
mpdude Sep 6, 2015
aae0a78
Avoid changing the interface
mpdude Sep 6, 2015
024aa8c
Also validate this method in the interface
mpdude Sep 7, 2015
d617943
Rename ResourceInterfaceValidator to ResourceValidator as suggested o…
mpdude Sep 7, 2015
8907a21
Oh Fabbot
mpdude Sep 7, 2015
e471890
Add a note why the ResourceInterface::__toString method is important …
mpdude Sep 7, 2015
e9fd197
Remove totally broken test
mpdude Sep 7, 2015
53ad659
Rewrite ConfigCacheTest because it made many assumptions about how th…
mpdude Sep 7, 2015
594529c
Fix ConfigCache and improve docblock
mpdude Sep 7, 2015
32426ce
If I had to pay just $0.02 for every Fabbot complaint, Fabien would b…
mpdude Sep 7, 2015
f3470e3
Document deprecation in the CHANGELOG file
mpdude Sep 7, 2015
7739878
Some more tweaks to the ConfigCacheTest
mpdude Sep 7, 2015
f9c48a0
Remove unused import
mpdude Sep 7, 2015
c0f416d
Continue with next resource, not next validator
mpdude Sep 8, 2015
00b4efb
Add a shortcut for when we don't have any validators (might be in prod)
mpdude Sep 8, 2015
23a4464
Create a new ValidatorConfigCache class and pass metadata validators …
mpdude Sep 8, 2015
caf6bf1
Fabbot
mpdude Sep 8, 2015
37c96e1
This is not relevant for this PR
mpdude Sep 8, 2015
7bc5ba2
Tweaks as suggested on GH
mpdude Sep 8, 2015
f5add4c
Implement the plan(TM)
mpdude Sep 9, 2015
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,40 @@
<?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\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

/**
* Adds services tagged config_cache.resource_checker to the config_cache_factory service, ordering them by priority.
*
* @author Matthias Pigulla <mp@webfactory.de>
* @author Benjamin Klotz <bk@webfactory.de>
*/
class ConfigCachePass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$resourceCheckers = array();
foreach ($container->findTaggedServiceIds('config_cache.resource_checker') as $id => $attributes) {
$priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
$resourceCheckers[$priority][] = new Reference($id);
}

// sort by priority and flatten
krsort($resourceCheckers);
$resourceCheckers = call_user_func_array('array_merge', $resourceCheckers);

$container->getDefinition('config_cache_factory')->addMethodCall('setResourceCheckers', array($resourceCheckers));
}
}
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationExtractorPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationDumperPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
Expand Down Expand Up @@ -92,6 +93,7 @@ public function build(ContainerBuilder $container)
if ($container->getParameter('kernel.debug')) {
$container->addCompilerPass(new ContainerBuilderDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING);
$container->addCompilerPass(new CompilerDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING);
$container->addCompilerPass(new ConfigCachePass());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,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
17 changes: 17 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
<parameter key="file_locator.class">Symfony\Component\HttpKernel\Config\FileLocator</parameter>
<parameter key="uri_signer.class">Symfony\Component\HttpKernel\UriSigner</parameter>
<parameter key="request_stack.class">Symfony\Component\HttpFoundation\RequestStack</parameter>
<parameter key="config_cache_factory.class">Symfony\Component\Config\ResourceCheckerConfigCacheFactory</parameter>
<parameter key="config_cache_default_resource_checker.class">Symfony\Component\Config\Resource\DefaultResourceChecker</parameter>
<parameter key="config_cache_bc_resource_interface_checker.class">Symfony\Component\Config\Resource\BCResourceInterfaceChecker</parameter>
</parameters>

<services>
Expand Down Expand Up @@ -63,5 +66,19 @@
<service id="uri_signer" class="%uri_signer.class%">
<argument>%kernel.secret%</argument>
</service>

<service id="config_cache_factory" class="%config_cache_factory.class%" />

<service id="config_cache_default_resource_checker" class="%config_cache_default_resource_checker.class%">
<tag name="config_cache.resource_checker" priority="-990" />
</service>

<!--
This service is deprecated and will be removed in 3.0.
-->
<service id="config_cache_bc_resource_interface_checker" class="%config_cache_bc_resource_interface_checker.class%">
<tag name="config_cache.resource_checker" priority="-1000" />
</service>

</services>
</container>
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
<argument key="debug">%kernel.debug%</argument>
</argument>
<argument type="collection" /> <!-- translation resources -->
<call method="setConfigCacheFactory">
<argument type="service" id="config_cache_factory" />
</call>
</service>

<service id="translator.logging" class="Symfony\Component\Translation\LoggingTranslator" public="false">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Tests\Command\CacheClearCommand\Fixture\TestAppKernel;
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\ConfigCacheFactory;
use Symfony\Component\Config\Resource\ResourceInterface;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;
Expand Down Expand Up @@ -47,15 +47,13 @@ public function testCacheIsFreshAfterCacheClearedWithWarmup()
$metaFiles = $finder->files()->in($this->kernel->getCacheDir())->name('*.php.meta');
// simply check that cache is warmed up
$this->assertGreaterThanOrEqual(1, count($metaFiles));
$configCacheFactory = new ConfigCacheFactory(true);
$that = $this;

foreach ($metaFiles as $file) {
$configCache = new ConfigCache(substr($file, 0, -5), true);
$this->assertTrue(
$configCache->isFresh(),
sprintf(
'Meta file "%s" is not fresh',
(string) $file
)
);
$configCacheFactory->cache(substr($file, 0, -5), function () use ($that, $file) {
$that->fail(sprintf('Meta file "%s" is not fresh', (string) $file));
});
}

// check that app kernel file present in meta file of container's cache
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?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\Tests\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Reference;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass;

class ConfigCachePassTest extends \PHPUnit_Framework_TestCase
{
public function testThatCheckersAreProcessedInPriorityOrder()
{
$services = array(
'checker_2' => array(0 => array('priority' => 100)),
'checker_1' => array(0 => array('priority' => 200)),
'checker_3' => array(),
);

$definition = $this->getMock('Symfony\Component\DependencyInjection\Definition');
$container = $this->getMock(
'Symfony\Component\DependencyInjection\ContainerBuilder',
array('findTaggedServiceIds', 'getDefinition', 'hasDefinition')
);

$container->expects($this->atLeastOnce())
->method('findTaggedServiceIds')
->will($this->returnValue($services));
$container->expects($this->atLeastOnce())
->method('getDefinition')
->with('config_cache_factory')
->will($this->returnValue($definition));

$definition->expects($this->once())
->method('addMethodCall')
->with('setResourceCheckers', array(
array(
new Reference('checker_1'),
new Reference('checker_2'),
new Reference('checker_3'),
)
));

$pass = new ConfigCachePass();
$pass->process($container);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -250,15 +250,15 @@ public function testTranslator()
);

$calls = $container->getDefinition('translator.default')->getMethodCalls();
$this->assertEquals(array('fr'), $calls[0][1][0]);
$this->assertEquals(array('fr'), $calls[1][1][0]);
}

public function testTranslatorMultipleFallbacks()
{
$container = $this->createContainerFromFile('translator_fallbacks');

$calls = $container->getDefinition('translator.default')->getMethodCalls();
$this->assertEquals(array('en', 'fr'), $calls[0][1][0]);
$this->assertEquals(array('en', 'fr'), $calls[1][1][0]);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,34 +104,6 @@ public function testTransWithCachingWithInvalidLocale()
$translator->trans('foo');
}

public function testLoadResourcesWithCaching()
{
$loader = new \Symfony\Component\Translation\Loader\YamlFileLoader();
$resourceFiles = array(
'fr' => array(
__DIR__.'/../Fixtures/Resources/translations/messages.fr.yml',
),
);

// prime the cache
$translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir, 'resource_files' => $resourceFiles), 'yml');
$translator->setLocale('fr');

$this->assertEquals('répertoire', $translator->trans('folder'));

// do it another time as the cache is primed now
$translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir), 'yml');
$translator->setLocale('fr');

$this->assertEquals('répertoire', $translator->trans('folder'));

// refresh cache when resources is changed in debug mode.
$translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir, 'debug' => true), 'yml');
$translator->setLocale('fr');

$this->assertEquals('folder', $translator->trans('folder'));
}

public function testLoadResourcesWithoutCaching()
{
$loader = new \Symfony\Component\Translation\Loader\YamlFileLoader();
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Bundle/FrameworkBundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"php": ">=5.3.9",
"symfony/asset": "~2.7|~3.0.0",
"symfony/dependency-injection": "~2.8",
"symfony/config": "~2.4",
"symfony/config": "~2.8",
"symfony/event-dispatcher": "~2.8|~3.0.0",
"symfony/http-foundation": "~2.4.9|~2.5,>=2.5.4|~3.0.0",
"symfony/http-kernel": "~2.7",
Expand Down
101 changes: 21 additions & 80 deletions src/Symfony/Component/Config/ConfigCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,38 @@

namespace Symfony\Component\Config;

use Symfony\Component\Config\Resource\ResourceInterface;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Config\Resource\BCResourceInterfaceChecker;
use Symfony\Component\Config\Resource\DefaultResourceChecker;

/**
* ConfigCache manages PHP cache files.
* ConfigCache caches arbitrary content in files on disk.
*
* When debug is enabled, it knows when to flush the cache
* thanks to an array of ResourceInterface instances.
* When in debug mode, those metadata resources that implement
* \Symfony\Component\Config\Resource\SelfCheckingResourceInterface will
* be used to check cache freshness.
*
* During a transition period, also instances of
* \Symfony\Component\Config\Resource\ResourceInterface will be checked
* by means of the isFresh() method. This behaviour is deprecated since 2.8
* and will be removed in 3.0.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Matthias Pigulla <mp@webfactory.de>
*/
class ConfigCache implements ConfigCacheInterface
class ConfigCache extends ResourceCheckerConfigCache
{
private $debug;
private $file;

/**
* @param string $file The absolute cache path
* @param bool $debug Whether debugging is enabled or not
*/
public function __construct($file, $debug)
{
$this->file = $file;
parent::__construct($file, array(
new DefaultResourceChecker(),
new BCResourceInterfaceChecker()
));
$this->debug = (bool) $debug;
}

Expand All @@ -49,90 +57,23 @@ public function __toString()
{
@trigger_error('ConfigCache::__toString() is deprecated since version 2.7 and will be removed in 3.0. Use the getPath() method instead.', E_USER_DEPRECATED);

return $this->file;
}

/**
* Gets the cache file path.
*
* @return string The cache file path
*/
public function getPath()
{
return $this->file;
return $this->getPath();
}

/**
* Checks if the cache is still fresh.
*
* This method always returns true when debug is off and the
* This implementation always returns true when debug is off and the
* cache file exists.
*
* @return bool true if the cache is fresh, false otherwise
*/
public function isFresh()
{
if (!is_file($this->file)) {
return false;
}

if (!$this->debug) {
if (!$this->debug && is_file($this->getPath())) {
return true;
}

$metadata = $this->getMetaFile();
if (!is_file($metadata)) {
return false;
}

$time = filemtime($this->file);
$meta = unserialize(file_get_contents($metadata));
foreach ($meta as $resource) {
if (!$resource->isFresh($time)) {
return false;
}
}

return true;
}

/**
* Writes cache.
*
* @param string $content The content to write in the cache
* @param ResourceInterface[] $metadata An array of ResourceInterface instances
*
* @throws \RuntimeException When cache file can't be written
*/
public function write($content, array $metadata = null)
{
$mode = 0666;
$umask = umask();
$filesystem = new Filesystem();
$filesystem->dumpFile($this->file, $content, null);
try {
$filesystem->chmod($this->file, $mode, $umask);
} catch (IOException $e) {
// discard chmod failure (some filesystem may not support it)
}

if (null !== $metadata && true === $this->debug) {
$filesystem->dumpFile($this->getMetaFile(), serialize($metadata), null);
try {
$filesystem->chmod($this->getMetaFile(), $mode, $umask);
} catch (IOException $e) {
// discard chmod failure (some filesystem may not support it)
}
}
}

/**
* Gets the meta file path.
*
* @return string The meta file path
*/
private function getMetaFile()
{
return $this->file.'.meta';
return parent::isFresh();
}
}
Loading